覆盖 __setattr__ 时无限递归
infinite recursion when overriding __setattr__
我想创建具有属性 "name" 和 "gender" 的 class 'Human'。我想将 "gender" 属性的分配限制为仅 "male" 或 "female"。为此,我们重写了 __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")
我无法弄清楚当我通过正确的性别时出了什么问题。
问题是您的 __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)
回答了问题。
我想分享一个避免这个陷阱的替代实现:
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 函数名相同。
否则你将得到两个属性,一个是只读的,一个是可读写的。
有关详细信息,请参阅:
我想创建具有属性 "name" 和 "gender" 的 class 'Human'。我想将 "gender" 属性的分配限制为仅 "male" 或 "female"。为此,我们重写了 __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")
我无法弄清楚当我通过正确的性别时出了什么问题。
问题是您的 __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)
我想分享一个避免这个陷阱的替代实现:
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 函数名相同。 否则你将得到两个属性,一个是只读的,一个是可读写的。
有关详细信息,请参阅: