2

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

   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__。

4

2 回答 2

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__。

于 2014-03-02T20:18:51.910 回答
1

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

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

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

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是一个类属性而不是实例属性。这个例子很好,但可能不是你想要的。

于 2014-03-02T18:32:07.547 回答