覆盖__setattr__时的无限递归

时间:2018-08-17 00:53:51

标签: exception recursion python-3.6 setattr

我想创建具有属性“名称”和“性别”的类“人类”。我想将“性别”属性的分配限制为仅“男性”或“女性”。为此,我们覆盖了 __ setattr __ (自身,名称,值)。

class Human(object):
    def __setattr__(self, name, value):
        if name == 'gender':
            if value in ('male', 'female'):
                self.gender = value
            else:
                raise AttributeError('Gender can only be "male" or "female"')


h = Human()
h.name = 'Sweety'
h.gender = 'female'
print(h.gender)

但是我得到了例外:

  [Previous line repeated 328 more times]
  File "/Users/admin/algorithms/betright_test.py", line 143, in **__setattr__**
    if name == 'gender':
RecursionError: maximum recursion depth exceeded in comparison

但是如果我输入了错误的性别(h.gender ='f'),则会给我适当的错误(AttributeError:性别只能是“男性”或“女性”)

我无法弄清楚当我通过适当的性别时出了什么问题。

2 个答案:

答案 0 :(得分:2)

问题是您的__setattr__函数包含行self.gender = ...,该行在无限循环中调用__setattr__。您需要使用超类方法递归调用__setattr__来存储属性而没有

super().__setattr__(name, value)

还要注意示例代码中的内容,如果尝试打印h.name,则会出现AttributeError,因为__setattr__函数从不设置属性。所以您想要的是这样的:

def __setattr__(self, name, value):
    if name == 'gender':
        if value not in ('male', 'female'):
            raise AttributeError('Gender can only be "male" or "female"')
    super().__setattr__(name, value)

答案 1 :(得分:0)

Chris Dodd's answer回答了问题。

我想分享一个避免这种陷阱的替代实现:

class Human(object):

    ALLOWED_GENDERS = ['male', 'female']

    def __init__(self, name=None, gender=None):
        self.name = name
        self._gender = gender

    @property
    def gender(self):
        return self._gender

    @gender.setter
    def gender(self, newvalue):
        if newvalue not in self.ALLOWED_GENDERS:
            raise ValueError('Invalid value for gender: %s. Should be one of %s.' % (newvalue, ", ".join(self.ALLOWED_GENDERS)))

        self._gender = newvalue


h = Human()
h.name = 'Sweety'
h.gender = 'female'

print(h.gender)

请注意,setter函数名称应与getter函数名称相同。 否则,您将获得两个属性,一个属性为只读,一个属性为可读写。

有关更多信息,请参见: