下面代码的结果让我困惑:
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
1 1 1 2 2
你观察到的行为是 Python 中类属性查找机制的结果,涉及到元类 (metaclass) 和类属性的优先级。我们分步骤解释代码并探讨如何让行为更加直观。
metaclass
MyClass
class MyClass(type): @property def a(self): return 1
type
a
@property
1
MyObject
class MyObject(object): __metaclass__ = MyClass a = 2
__metaclass__
class MyObject(metaclass=MyClass):
a = 2
MyObject 的属性 a 有两个来源: 1. 来自元类 MyClass 的属性 a。 2. 定义在类中的属性 a。
当访问 MyObject.a 时,属性查找遵循以下规则: 1. 先查找类 MyObject 自身的属性。 2. 如果在类中找不到,查找元类(MyClass)中的属性。
MyObject.a
但在这个例子中,元类属性通过 @property 形式存在,因此会优先调用元类的 a 属性。
print MyObject.a
print object.__getattribute__(MyObject, 'a')
object.__getattribute__
print type.__getattribute__(MyObject, 'a')
type.__getattribute__
print MyObject.__dict__['a']
MyObject.__dict__
print MyObject().a
元类的属性优先级问题: 元类中的 @property 属性会覆盖类中直接定义的属性,这可能违反直觉。
类和实例的行为差异:
MyObject()
可以避免在元类中定义与子类属性名称冲突的 @property,或者将 a 的逻辑封装到更直观的地方。
在元类中显式优先返回子类定义的属性:
class MyClass(type): @property def a(self): # 优先查找子类的属性 if 'a' in self.__dict__: return self.__dict__['a'] return 1
__getattribute__
改写类的 __getattribute__ 以调整查找优先级:
class MyObject(object): __metaclass__ = MyClass a = 2 def __getattribute__(cls, name): if name == 'a': return cls.__dict__.get(name, super(MyObject, cls).__getattribute__(name)) return super(MyObject, cls).__getattribute__(name)