Python @property装饰器引发栈溢出错误的原因咨询
为什么使用@property时直接赋值self.attribute_1会导致栈溢出?
这个问题是@property使用时最容易踩的递归坑,我来给你拆解清楚:
错误代码的递归逻辑分析
当你运行class_instance = ClassWithValidator(11)时,整个执行流程是这样的:
- 进入
__init__方法,执行self.attribute_1 = 11——因为attribute_1被@property装饰了,这行代码会触发对应的setter方法。 - 进入setter方法后,逻辑判断通过,执行
self.attribute_1 = value——注意!这里的self.attribute_1不是直接给实例的属性赋值,而是又一次触发了attribute_1的setter方法(因为@property把这个名字变成了属性访问器)。 - 就这样,setter方法里不断调用自己,形成了无限递归,每调用一次就会在Python的调用栈里加一层,直到栈空间耗尽,最终抛出栈溢出错误。
同样,你的getter方法里return self.attribute_1也有问题——如果有人尝试读取class_instance.attribute_1,这行代码会递归调用getter方法,同样会导致栈溢出。
为什么用self._attribute_1就能解决问题?
下划线开头的_attribute_1是一个普通的实例属性,没有被@property装饰。当你在setter里执行self._attribute_1 = value时,这是直接给实例的底层属性赋值,不会触发任何@property的逻辑;对应的getter里return self._attribute_1也是直接读取这个普通属性,完全不会触发递归。
简单说,@property的attribute_1是“门面”,而_attribute_1是背后真正存储数据的“仓库”,两者名字不能一样,否则就会把门面当成仓库,无限调用自己。
关键代码对比
错误的递归写法
class ClassWithValidator: def __init__(self, attribute_1): self.attribute_1 = attribute_1 # 触发setter @property def attribute_1(self): return self.attribute_1 # 递归调用getter @attribute_1.setter def attribute_1(self, value): print("Setting attribute_1...") if value < 10: raise ValueError("attribute_1 cannot be greater or equal to 10!") else: self.attribute_1 = value # 递归调用setter
正确的写法(用底层属性存储)
class FixedClassWithValidator: def __init__(self, attribute_1): self.attribute_1 = attribute_1 # 触发setter @property def attribute_1(self): return self._attribute_1 # 直接读取底层属性 @attribute_1.setter def attribute_1(self, value): print("Setting attribute_1...") if value < 10: raise ValueError("attribute_1 cannot be greater or equal to 10!") else: self._attribute_1 = value # 直接赋值到底层属性
总结
@property的核心是把方法包装成属性访问的形式,所以在getter和setter内部,一定要用一个不同名称的实例属性来存储实际值(通常约定用下划线开头表示私有),否则就会陷入无限递归的死循环,最终导致栈溢出。
内容的提问来源于stack exchange,提问作者kaksat
相关产品推荐
相关产品推荐

