Why does the below code print error msg
instead of ABC\nerror msg
?
class CustomException(Exception):
"""ABC"""
def __init__(self, *args):
super().__init__(*args)
self.__str__ = self._wrapper(self.__str__)
def _wrapper(self, f):
def _inner(*args, **kwargs):
return self.__doc__ + '\n' + f(*args, **kwargs)
return _inner
print(CustomException('error msg'))
Operations backed by special methods usually explicitly look up the special method as a proper method not just as a callable attribute. Concretely, instead of self.__str__
the interpreter roughly looks at type(self).__str__.__get__(self, type(self))
– i.e. a descriptor __str__
on the class to be bound with the instance. To override a special method, it is thus necessary to override the class' descriptor instead of the instance' attribute.
This can be done by a) declaring the special method as a slot, which handles the type(self).__str__
part, and b) assigning a function, which handles the __get__(self, type(self))
part.
class CustomException(Exception):
"""ABC"""
__slots__ = ("__str__",) # <<< magic
def __init__(self, *args):
super().__init__(*args)
# vvv self.__str__ is the class' slot
self.__str__ = self._wrapper(super().__str__)
# AAA real __str__ lives on the super class
def _wrapper(self, f):
def _inner(*args, **kwargs):
return self.__doc__ + '\n' + f(*args, **kwargs)
return _inner
print(CustomException('error msg'))
Note that since every instance behaves the same in this case, it is advisable to just define a new __str__
method in practice.