我正在学习Python中的@property装饰器。在一个玩具示例中,我遇到了一个我不明白的错误。
我的玩具课:
class ClassWithValidator:
def __init__(self, attribute_1):
self.attribute_1 = attribute_1
@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
尝试创建其实例:
class_instance = ClassWithValidator(11)
触发以下错误:
Setting attribute_1... # repeated multiple times before the following error message:
>Fatal Python error: Cannot recover from stack overflow.
解决此问题的方法是在属性及其设置器中将
self.attribute_1
替换为 self._attribute_1
:
class FixedClassWithValidator:
def __init__(self, attribute_1):
self.attribute_1 = attribute_1
@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
class_instance = FixedClassWithValidator(11)
我知道属性名称前面带有下划线(此处:
_attribute_1
)意味着,按照惯例,它是私有的。但是,我仍然不明白为什么第一个代码会导致堆栈溢出。
不确定为什么会出现该错误,您应该得到
RecursionError: maximum recursion depth exceeded in comparison
。问题出在setter的最后一行self.attribute_1 = value
,它在无限循环中再次调用setter。
您的第二个解决方案工作完美,但您也可以考虑使用 descriptor 来验证您的数据。
class MinValidator: # Descriptor
def __init__(self, min_value):
self.min_value = min_value
def __set_name__(self, owner, name):
self.name = name
def __set__(self, obj, value):
print(f"Setting {self.name}...")
if value < self.min_value:
raise ValueError(f"attribute_1 cannot be lower than {self.min_value}!")
else:
obj.__dict__[self.name] = value
class ClassWithValidator:
attribute_1 = MinValidator(10)
def __init__(self, attribute_1):
self.attribute_1 = attribute_1