You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python使用property描述符触发递归错误及私有变量关联原理咨询

核心原理解释

递归错误的触发逻辑

Python的property是典型的数据描述符,只要你在类层面定义了age = property(get_age, set_age, deleter),那么所有对该类实例的age属性的操作(读、写、删),都会优先触发你绑定的对应方法,不会直接读写实例的属性字典。
你最初的代码中:

  • 初始化时self.age = age会触发set_age方法
  • set_age方法末尾的self.age = value又会再次触发set_age,直接进入无限循环,直到达到Python默认的递归深度上限,抛出RecursionError
  • 同理get_age中的return self.age也会重复触发get_age本身,一样会导致递归

你将内部操作的属性改为self._age后,_age没有被绑定为property,所以对self._age的读写会直接操作实例的属性字典,不会触发任何绑定方法,递归自然就消失了。这里的_age就是专门用来存储实际值的后台变量,单下划线开头是Python的约定,代表这个属性是类内部使用的,不建议外部直接访问。

私有变量与名字修饰规则

Python没有语法层面强制的私有属性,只有两类约定俗成的私有实现:

  • 单下划线开头的属性(如_age):仅为开发者层面的约定,提示该属性属于类内部实现细节,外部直接访问虽然语法合法,但不被推荐。
  • 双下划线开头的属性(如__age):Python会自动对这类属性做名字修饰(Name Mangling),将属性名重命名为_类名__属性名的格式,比如Employer类中的__age会被处理为_Employer__age。

你看到的官方文档说明的意思是:名字修饰的作用是让子类可以重写父类的方法或属性,而不会破坏父类内部的方法调用逻辑。举个例子,如果父类内部用__age存储数据,子类就算也定义了同名的__age属性,父类内部的方法访问的仍然是自己的_Employer__age,不会被子类的同名属性覆盖,避免父类的原有逻辑被意外破坏。

属性访问的优先级逻辑

你的推导是正确的:self.属性名的操作并不是直接读写实例的属性字典,Python处理属性访问时有固定的优先级:

  • 首先查找类中定义的数据描述符(比如property就属于实现了__get__、__set__方法的数据描述符),如果存在,优先调用描述符的对应方法
  • 没有匹配的数据描述符时,才会去实例的__dict__属性字典中查找对应属性
  • 仍然找不到的话会依次查找父类的属性,最终匹配失败则抛出AttributeError

正是因为property的优先级高于实例字典的普通属性,所以你所有对self.age的操作都会走绑定的getter/setter,才会出现最初的递归问题。


内容的提问来源于stack exchange,提问作者Not_here_to_play

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 14:39:04