如何验证Python中这一晦涩的元编程行为是有意设计的?
类与元类同名数据描述符的访问规则解释
这个行为完全符合Python的核心属性查找规则,属于有文档依据的标准行为,可以安全依赖,在所有标准Python实现(CPython、PyPy等)的2.6+版本中表现一致。
核心逻辑拆解
- 类的本质:Python中的类本身是元类的实例。比如示例中的
MyObj是MyMeta的实例,就像myobj是MyObj的实例一样。 - 数据描述符的优先级:当访问一个对象的属性时,Python会优先查找该对象所属类(及MRO链)中的数据描述符(拥有
__set__或__delete__方法的描述符),优先级高于对象自身__dict__中的同名属性。
对应示例的行为解释
针对你的代码示例:
- 访问
MyObj.myattr时,MyObj是MyMeta的实例,Python会先检查MyMeta(即MyObj的所属类)中是否有同名数据描述符。由于MyMeta的myattr是数据描述符,直接触发它的__get__/__set__/__delete__方法,完全不会读取MyObj自身定义的myattr。 - 访问
myobj.myattr时,myobj是MyObj的实例,Python会优先检查MyObj(即myobj的所属类)中的数据描述符,也就是MyObj定义的myattr,因此触发该描述符的方法。
代码示例
class ExampleDescriptor: def __init__(self, name, getter_result): self.name = name self.getter_result = getter_result def __get__(self, obj, type=None): print(f'"{self.name}" getter invoked.') return self.getter_result def __set__(self, obj, value): print(f'"{self.name}" setter invoked with value={value}.') def __delete__(self, obj): print(f'"{self.name}" deleter invoked.') class MyMeta(type): myattr = ExampleDescriptor('class level', 1) class MyObj(metaclass=MyMeta): myattr = ExampleDescriptor('instance level', 2) print(MyObj.myattr) MyObj.myattr = 3 del MyObj.myattr myobj = MyObj() print(myobj.myattr) myobj.myattr = 4 del myobj.myattr
示例输出
"class level" getter invoked. 1 "class level" setter invoked with value=3. "class level" deleter invoked. "instance level" getter invoked. 2 "instance level" setter invoked with value=4. "instance level" deleter invoked.
文档依据
这个行为是两个基础规则的组合结果,均在Python官方数据模型文档中有明确说明:
- 类是元类的实例:所有类(包括自定义类)都是
type或其子类的实例,这是元编程的基础。 - 描述符优先级:数据描述符的优先级高于实例自身的属性,当对象所属类存在同名数据描述符时,会直接使用该描述符。
内容的提问来源于stack exchange,提问作者Steve Jorgensen
相关产品推荐
相关产品推荐

