Python:元类属性有时会覆盖类属性?
下面这段代码的结果让我很困惑:
class MyClass(type):
@property
def a(self):
return 1
class MyObject(object):
__metaclass__ = MyClass
a = 2
print MyObject.a
print object.__getattribute__(MyObject, 'a')
print type.__getattribute__(MyObject, 'a')
print MyObject.__dict__['a']
print MyObject().a
我本以为它只会重复打印 2
,但实际上它打印的是 1 1 1 2 2
。有没有什么方法可以让这个结果更容易理解呢?
为了更清楚一点:我知道这种行为有很多文档说明(在这里,"数据描述符"),但我想理解为什么会这样,以及为什么核心开发者会以这种方式实现描述符。
2 个回答
这里有一些非常相似的代码,可能会帮助你理解发生了什么。
class MyClass(object):
def __init__(self, data):
self.__dict__.update(data) # this would fail if I had self.a = a
@property
def a(self):
return 1
MyObject = MyClass({'a': 2})
print MyObject.a
print object.__getattribute__(MyObject, 'a')
print MyObject.__dict__['a']
你看到的是,实例(类的一个对象)在它的类上有一个描述符(你的属性),同时还有一个同名的属性。通常情况下,会有一些保护措施来防止这种情况,但由于type
的工作方式,这些保护措施被绕过了。
所以你有
print MyObject.a
描述符优先于__dict__
中的条目,因此会调用property
。这是因为object.__getattribute__
的实现,至少从概念上来说是这样的。
print object.__getattribute__(MyObject, 'a')
这和说MyObject.a
是一样的,除非object.__getattribute__
被重写了。__getattribute__
就是最开始尝试描述符的行为来源。
print type.__getattribute__(MyObject, 'a')
这和object.__getattribute__
是一样的,因为type
并没有重写__getattribute__
。
print MyObject.__dict__['a']
这会在你的__dict__
中查找,这是你存储2
的唯一地方。这只是一个字典对象(可能/几乎是),所以它不会去其他地方查找。
print MyObject().a
属性的工作方式是,你并不是以直接访问的方式来访问你类型的属性。这可能是没有特别直观答案的部分。
属性是一些特殊的数据描述符;在查找属性时,它们会优先于类实例字典中同名的条目。这意味着在
MyObject.a
中,MyClass
里的a
属性会优先于MyObject
字典里的a
条目。同样,
object.__getattribute__(MyObject, 'a')
type.__getattribute__(MyObject, 'a')
object.__getattribute__
和type.__getattribute__
这两个方法也会优先考虑数据描述符,而不是实例字典中的条目,所以属性会胜出。
另一方面,
MyObject.__dict__['a']
这个操作是明确地查找字典。它只会查看MyObject
的字典,忽略正常的属性查找机制。
对于最后一行:
MyObject().a
MyClass
的描述符只适用于MyClass
的实例,而不适用于它的实例的实例。属性查找机制不会看到这个属性。