内置操作的新样式类属性搜索

时间:2014-03-02 14:50:30

标签: python python-2.7 new-style-class

考虑经典类和新样式类之间的以下区别。

   class A():
        data = 'abcd'
        def __getattr__(self, name): 
            return getattr(self.data, name)

    class B(object):
        data = 'abcd'
        def __getattr__(self, name): 
            return getattr(self.data, name)



    print(A()[0])       # Prints 'a'
    print(B()[0])       # TypeError: 'B' object does not support indexing

我知道这个属性的解释是新的样式对象属性搜索从类开始而不是内置操作的实例。 但是类对象也定义了__getattr__,为什么不为此处的缺失属性调用__getitem__。

2 个答案:

答案 0 :(得分:1)

正如@Jon在评论中提到的那样,您可以在问题Asymmetric behavior for __getattr__, newstyle vs oldstyle classesSpecial method lookup for new-style classes的文档中找到答案。

出于性能原因,直接在类对象中查找特殊方法。

我想补充一点,据我所知,这意味着虽然你仍然可以将所有非特殊方法转发到带有__getattr__的封装类,但你必须明确转发所有特殊方法: / p>

class A():
    data = 'abcd'
    def __getattr__(self, name): 
        return getattr(self.data, name)

class B(object):
    data = 'abcd'
    # forward all non-special methods to data
    def __getattr__(self, name): 
        return getattr(self.data, name)
    # forward __getitem__ to data
    def __getitem__(self, index):
        return self.data[index]


print(A()[0])       # Prints 'a'
print(B()[0])       # explicitly defined Prints 'a'
print(B().join([' 1 ',' 2 ']))  # forwarded to data prints ' 1 abcd 2 '

我还想指出B.data是一个类属性而不是一个实例属性。这个例子很好,但可能不是你想要的。

答案 1 :(得分:1)

我发现答案是只有在实例对象的属性搜索开始时才会调用__getattr__。但是如果跳过显式关于类和实例的属性搜索,则永远不会调用__getattr__。

class B():
    data = 'abcd'
    def __getattr__(self, name):
        print('You are looking for something that doesn\'t exist')
        return None

b = B()
b.a
You are looking for something that doesn't exist
B.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class B has no attribute 'a'

因此,在经典类中,搜索__getitem__从实例对象开始,并调用__getattr__,而在新样式类中,搜索从类对象开始,因此不会调用__getattr__。