__str__ 自定义异常中的包装器
__str__ wrapper in custom Exception
为什么下面的代码打印 error msg
而不是 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'))
暂时忘记异常 class。考虑一下:
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)
输出:
A
A # Not B !
B
Dunder 方法的查找在 Python 中有所不同。来自 docs:
For custom classes, implicit invocations of special methods are only
guaranteed to work correctly if defined on an object’s type, not in
the object’s instance dictionary.
你想要达到的是:
class CustomException(Exception):
"""ABC"""
def __init__(self, *args):
super().__init__(*args)
def __str__(self):
return self.__doc__ + '\n' + super().__str__()
print(CustomException('error msg'))
输出:
ABC
error msg
根据 @khelwood 在评论中的回复,此代码按预期工作:
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 _inner
但我最终得到了这个等价物 (@MisterMiyagi):
class CustomException(Exception):
"""ABC"""
def __str__(self):
return self.__doc__ + '\n' + super().__str__()
由特殊方法支持的操作通常将特殊方法显式查找为正确方法,而不仅仅是可调用属性。具体来说,解释器不是 self.__str__
,而是粗略地查看 type(self).__str__.__get__(self, type(self))
——即 a descriptor __str__
on the class to be bound with the instance. 要覆盖特殊方法,因此有必要覆盖 class' 描述符而不是实例' 属性。
这可以通过 a) 将特殊方法声明为处理 type(self).__str__
部分的槽,以及 b) 分配一个处理 __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'))
请注意,由于在这种情况下每个实例的行为都相同,因此建议在实践中只定义一个新的 __str__
方法。
为什么下面的代码打印 error msg
而不是 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'))
暂时忘记异常 class。考虑一下:
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)
输出:
A
A # Not B !
B
Dunder 方法的查找在 Python 中有所不同。来自 docs:
For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.
你想要达到的是:
class CustomException(Exception):
"""ABC"""
def __init__(self, *args):
super().__init__(*args)
def __str__(self):
return self.__doc__ + '\n' + super().__str__()
print(CustomException('error msg'))
输出:
ABC
error msg
根据 @khelwood 在评论中的回复,此代码按预期工作:
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 _inner
但我最终得到了这个等价物 (@MisterMiyagi):
class CustomException(Exception):
"""ABC"""
def __str__(self):
return self.__doc__ + '\n' + super().__str__()
由特殊方法支持的操作通常将特殊方法显式查找为正确方法,而不仅仅是可调用属性。具体来说,解释器不是 self.__str__
,而是粗略地查看 type(self).__str__.__get__(self, type(self))
——即 a descriptor __str__
on the class to be bound with the instance. 要覆盖特殊方法,因此有必要覆盖 class' 描述符而不是实例' 属性。
这可以通过 a) 将特殊方法声明为处理 type(self).__str__
部分的槽,以及 b) 分配一个处理 __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'))
请注意,由于在这种情况下每个实例的行为都相同,因此建议在实践中只定义一个新的 __str__
方法。