Python @property 致命 Python 错误:无法从堆栈溢出中恢复

问题描述 投票:0回答:1

我正在学习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
)意味着,按照惯例,它是私有的。但是,我仍然不明白为什么第一个代码会导致堆栈溢出。

python oop properties decorator getter-setter
1个回答
0
投票

不确定为什么会出现该错误,您应该得到

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
© www.soinside.com 2019 - 2024. All rights reserved.