Python 旧式类 __getattr__: "TypeError: 'NoneType' 对象不可调用
我正在实现一个简单的类,用来表示一个二维向量。以下是相关的代码片段:
class Vector:
def __init__( self, x, y ):
self.vec_repr = x, y
def __add__( self, other ):
new_x = self.x + other.x
new_y = self.y + other.y
return Vector( new_x, new_y )
def __getattr__( self, name ):
if name == "x":
return self.vec_repr[0]
elif name == "y":
return self.vec_repr[1]
后来,我有这样的代码:
a = Vector( 1, 1 )
b = Vector( 2, 2 )
a + b
我遇到了一个错误:TypeError: 'NoneType' object is not callable
。这特别奇怪,因为错误没有指明具体的行数,所以我不知道该从哪里查起!
这太奇怪了,所以我做了一些实验,发现这个错误发生在 a+b
这一行。而且,当我把类改成如下形式:
class Vector:
def __init__( self, x, y ):
self.x, self.y = x, y
def __add__( self, other ):
new_x = self.x + other.x
new_y = self.y + other.y
return Vector( new_x, new_y )
错误就消失了!
我发现有很多问题和这个错误类似——似乎都是某个函数名被某个变量覆盖了,但我找不到这个覆盖发生的地方!
作为另一个线索,当我把 __getattr__()
的默认返回类型改成其他类型,比如字符串(str)时,错误变成了 TypeError: 'str' object is not callable
。
有没有人知道这是怎么回事?我是不是对 __getattr__()
的某些行为理解得不够?
1 个回答
12
问题在于你的 __getattr__
方法对除了 x
和 y
以外的属性没有返回任何东西,也没有抛出一个属性错误(AttributeError)。所以当查找 __add__
方法时,__getattr__
返回了 None
,这就导致了你的错误。
你可以通过让 __getattr__
返回其他属性的值来解决这个问题。实际上,你需要确保 __getattr__
对于所有没有处理的属性调用其父类的方法。但实际上,__getattr__
在这里并不是合适的选择。它应该谨慎使用,只有在没有更明显、更高级的解决方案时才使用。例如,__getattr__
对于动态调度是必不可少的。但在你的情况下,x
和 y
的值在代码运行之前是明确且已知的。
正确的解决方案是将 x
和 y
定义为属性,而根本不实现 __getattr__
。
@property
def x(self):
return self.vec_repr[0]
@property
def y(self):
return self.vec_repr[1]