Python 旧式类 __getattr__: "TypeError: 'NoneType' 对象不可调用

7 投票
1 回答
20383 浏览
提问于 2025-04-16 22:37

我正在实现一个简单的类,用来表示一个二维向量。以下是相关的代码片段:

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__ 方法对除了 xy 以外的属性没有返回任何东西,也没有抛出一个属性错误(AttributeError)。所以当查找 __add__ 方法时,__getattr__ 返回了 None,这就导致了你的错误。

你可以通过让 __getattr__ 返回其他属性的值来解决这个问题。实际上,你需要确保 __getattr__ 对于所有没有处理的属性调用其父类的方法。但实际上,__getattr__ 在这里并不是合适的选择。它应该谨慎使用,只有在没有更明显、更高级的解决方案时才使用。例如,__getattr__ 对于动态调度是必不可少的。但在你的情况下,xy 的值在代码运行之前是明确且已知的。

正确的解决方案是将 xy 定义为属性,而根本不实现 __getattr__

@property
def x(self):
    return self.vec_repr[0]

@property
def y(self):
    return self.vec_repr[1]

撰写回答