Странности с self.args при работе с объектами типа "исключение"

Анализируя работу с исключениями, наткнулся на вот такую ситуацию. Берем пример который в разных видах гуляет по источникам и учебникам:

class UppercaseException(Exception):
    pass
words = ['я', 'ты', 'он', 'ОНА', 'оно']
for word in words:
        if word.isupper():
            raise UppercaseException (word)

Результат получаем ожидаемый:

Traceback (most recent call last):

  File "<ipython-input-54-fc1eb7bb7c0c>", line 4, in <module>
    raise UppercaseException (word)

UppercaseException: ОНА

Ввожу конструктор класса. На случай, если мне в дальнейшем потребуется некоторая обработка в нем - ввожу определение нового атрибута:

class UppercaseException ( Exception ):
    def __init__(self, ar):
        print ('Обработка исключений')
        self.atr = ar
        return
words = ['я', 'ты', 'он', 'ОНА', 'оно']
for word in words:
        if word.isupper():
            raise UppercaseException (word)

Результат практически тождественен предыдущему:

Обработка исключений
Traceback (most recent call last):

  File "<ipython-input-55-b239d3ebd306>", line 10, in <module>
    raise UppercaseException (word)

UppercaseException: ОНА

Пока все понятно и ожидаемо. А теперь - собственно вопрос. Изменяю имя атрибута:

class UppercaseException ( Exception ):
    def __init__(self, ar):
        print ('Обработка исключений')
        self.args = ar
        return
words = ['я', 'ты', 'он', 'ОНА', 'оно']
for word in words:
        if word.isupper():
            raise UppercaseException (word)

И результат меняется кардинально!

Обработка исключений
Traceback (most recent call last):

  File "<ipython-input-56-6460513f725f>", line 9, in <module>
    raise UppercaseException (word)

UppercaseException: ('О', 'Н', 'А')

Для облегчения анализа вывожу типы (ну и по ходу - сами значения) внутри конструктора

class UppercaseException ( Exception ):
    def __init__(self, ar):
        print ('Входной параметр: ', type(ar),ar)
        self.args = ar
        print ('Атрибут класса: ', type(self.args),self.args)
        return
words = ['я', 'ты', 'он', 'ОНА', 'оно']
for word in words:
        if word.isupper():
            raise UppercaseException (word)

Обратите внимание как определяются типы:

Входной параметр:  <class 'str'> ОНА
Атрибут класса:  <class 'tuple'> ('О', 'Н', 'А')
Traceback (most recent call last):

  File "<ipython-input-64-448db16a8ad8>", line 10, in <module>
    raise UppercaseException (word)

UppercaseException: ('О', 'Н', 'А')

Я даже могу предположить, что это как-то связано с тем, что класс UppercaseException наследуется от класса Exception. Но и это как-бы не должно влиять на тип переопределяемого атрибута, причем даже если будет явно (!) вызываться конструктор класса-родителя (при неявном вызове - ситуация аналогичная) :

class A:
    def __init__(self, a):
        self.args = tuple(a)
        print("class A: ",self.args,type(self.args))
class B(A):
    def __init__(self, a):
        super().__init__( a)
        print("Параметр a :",a,type(a))
        self.args = a
        print("class B: ",self.args,type(self.args))        
b=B('qwerty')

Результат:

class A:  ('q', 'w', 'e', 'r', 't', 'y') <class 'tuple'>
Параметр a : qwerty <class 'str'>
class B:  qwerty <class 'str'>

Тип как был строковым, так таким и остался, т.е. произошло именно переопределение. Как и ожидалось.

Может кто-то разбирался с этим вопросом, или имеет хоть какие-то предположение о том, что и почему происходит при работе с исключениями?


Ответы (1 шт):

Автор решения: splash58

Добавим и изменим пару строк:

def __init__(self, ar):
    print ('На входе: ', type(self.args),self.args)           # Здесь
    print ('Входной параметр: ', type(ar),ar)
    self.newAr = ar                                           # И здесь
    print ('Атрибут класса: ', type(self.newAr),self.newAr)
    return

и получим

На входе:  <class 'tuple'> ('ОНА',)
Входной параметр:  <class 'str'> ОНА
Атрибут класса:  <class 'str'> ОНА

args определятся в родительском классе как tuple. И при вашем присвоении происходит преобразование. Никаких чудес


Built-in Exceptions

exception BaseException

args

The tuple of arguments given to the exception constructor. Some built-in exceptions (like OSError) expect a certain number of arguments and assign a special meaning to the elements of this tuple, while others are usually called only with a single string giving an error message.

→ Ссылка