使用__getattr__导致TypeError: 'str' object is not callable - Python 2.7

3
我想在Python 2.7中定义一个简单的类和实例,但我在使用__getattr__时遇到了问题。以下是最小可行示例:
class MyClass:

    def __init__(self,value):
        self.a = value

    def __getattr__(self,name):
        return 'hello'

class MyOtherClass:

    def __init__(self,value):
        self.a = value

MyInstance = MyClass(6)

MyOtherInstance = MyOtherClass(6)

现在,如果我输入dir(MyInstance),我会得到:

TypeError: 'str' object is not callable

但是如果我输入dir(MyOtherInstance),我会得到:

['__doc__', '__init__', '__module__', 'a']

同样地,如果我输入MyInstance,我会得到以下结果:
TypeError: 'str' object is not callable

但是如果我输入MyOtherInstance,我会得到以下结果:

<__main__.MyOtherClass instance at 0x0000000003458648>

MyOtherInstance 的行为是我期望的。为什么我在 MyInstance 中没有得到这种行为呢?


3
第一猜测:与 __getattr__ 的交互不良以及 MyClass 是旧式类的事实有关。 - chepner
4
如果你使用 class MyClass(object): 替换原来的类定义,问题就会消失。故事的寓意是:不要使用旧式类。 - chepner
3
如果有选择的话,建议使用Python 3,并不必担心旧式类;它们只存在于Python 2中。即使在Python 2中,也不应该使用它们;总是继承自“object”(或其他已知的新式类)。 - chepner
1
请参见 https://dev59.com/-XVD5IYBdhLWcg3wNIrc#16193572。 - chepner
注意:如果您必须使用Python 2,一个比显式继承object更容易的方法是在您编写的每个源文件的顶部放置__metaclass__ = type(在任何__future__导入之后,但在顶级,在任何类定义之前)。这在Python 3上不起作用(完全被忽略),但会使该模块中定义的所有类在Python 2上隐式地成为新样式。 - ShadowRanger
显示剩余2条评论
1个回答

3
问题在于MyClass是一个旧式类(即它没有明确从object或另一个新式类继承),这意味着__getattr__用于魔术方法,而这些方法在新式类中不会触发对__getattr__的调用。
为了验证这一点,将你的类改为:
class MyClass:
    def __init__(self,value):
        self.a = value

    def __getattr__(self,name):
        print("Looking up %s" % (name,))
        return 'hello'

MyInstance 的简单使用会触发对 MyInstance.__repr__ 的调用,但是 __repr__ 返回的字符串是 'hello',而不是类的 __repr__ 方法。

>>> MyInstance
Looking up __repr__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable

同样地,dir(MyInstance) 会触发对 MyClass.__dir__ 的调用,而 __dir__ 同样是字符串 'hello',而不是相应的方法。
>>> dir(MyInstance)
Looking up __dir__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable

你没有与MyOtherClass相同的问题,因为你没有覆盖__getattr__

继承自object可以解决这个问题;魔术方法会在回退到__getattr__之前被单独查找。

class MyClass(object):

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接