¿Por qué el siguiente código muestra un mensaje de error msg en lugar de 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'))Olvídese de la clase Exception por ahora. Considera esto:
class A: def __str__(self): return 'A' obj = A() print(obj) obj.__str__ = lambda x: 'B' print(obj) A.__str__ = lambda x: 'B' print(obj)producción:
A A # Not B ! BLa búsqueda de métodos de Dunder es diferente en Python. De documentos :
Para las clases personalizadas, solo se garantiza que las invocaciones implícitas de métodos especiales funcionen correctamente si se definen en el tipo de un objeto, no en el diccionario de instancias del objeto.
Lo que quieres lograr es:
class CustomException(Exception): """ABC""" def __init__(self, *args): super().__init__(*args) def __str__(self): return self.__doc__ + '\n' + super().__str__() print(CustomException('error msg'))producción:
ABC error msgLas operaciones respaldadas por métodos especiales generalmente buscan explícitamente el método especial como un método adecuado, no solo como un atributo invocable. Concretamente, en lugar de self.__str__ el intérprete mira aproximadamente a type(self).__str__.__get__(self, type(self)) , es decir, un descriptor __str__ en la clase que se vinculará con la instancia. Para anular un método especial, es necesario anular el descriptor de la clase en lugar del atributo de la instancia.
Esto se puede hacer a) declarando el método especial como una ranura, que maneja la parte type(self).__str__ , yb) asignando una función, que maneja la __get__(self, type(self)) .
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')) Tenga en cuenta que, dado que todas las instancias se comportan de la misma manera en este caso, es recomendable definir un nuevo método __str__ en la práctica.
Según la respuesta de @khelwood en los comentarios, este código funciona según lo previsto:
class CustomException(Exception): """ABC""" def __init__(self, *args): super().__init__(*args) self.__class__.__str__ = self._wrapper(self.__str__) def _wrapper(self, f): def _inner(*args, **kwargs): return self.__doc__ + '\n' + f() return _innerpero terminé con este equivalente ( @MisterMiyagi ):
class CustomException(Exception): """ABC""" def __str__(self): return self.__doc__ + '\n' + super().__str__()