【问题标题】:infinite recursion when overriding __setattr__覆盖 __setattr__ 时的无限递归
【发布时间】:2018-08-17 00:53:50
【问题描述】:

我想创建具有“名称”和“性别”属性的“人类”类。我想将“性别”属性的分配限制为“男性”或“女性”。为此,我们重写了 __setattr__(self, name, value)。

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: Gender can only be "male" or "female")

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

【问题讨论】:

    标签: exception recursion python-3.6 setattr


    【解决方案1】:

    问题在于您的__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)
    

    【讨论】:

      【解决方案2】:

      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 函数名称相同。 否则你最终会得到两个属性,一个是只读的,一个是可读写的。

      欲了解更多信息,请参阅:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-05
        • 1970-01-01
        • 2019-02-24
        • 1970-01-01
        • 2020-11-26
        • 1970-01-01
        • 2012-07-12
        • 2012-08-15
        相关资源
        最近更新 更多