我建议使用__setattr__来避免__slots__.
弄乱 时总是要小心__setattr__,因为它负责设置所有实例属性,包括您在__init__. 因此,它必须有某种方式知道何时允许设置属性,何时拒绝设置。在这个解决方案中,我指定了一个特殊属性来控制是否允许新属性:
class A(object):
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
self.freeze = True
def __setattr__(self, attr, value):
if getattr(self, "freeze", False) and not hasattr(self, attr):
raise AttributeError("You shall not set attributes!")
super(A, self).__setattr__(attr, value)
测试:
a = A()
try:
a.d = 89
except AttributeError:
print "It works!"
else:
print "It doesn't work."
a.c = 42
print a.a
print a.c
a.freeze = False
a.d = 28
a.freeze = True
print a.d
结果:
有用!
1
42
28
另请参阅gnibblers 的答案,该答案将这个概念巧妙地包装在类装饰器中,因此它不会弄乱类定义,并且可以在多个类中重用而无需重复代码。
编辑:
一年后回到这个答案,我意识到上下文管理器可能会更好地解决这个问题。这是 gnibbler 类装饰器的修改版本:
from contextlib import contextmanager
@contextmanager
def declare_attributes(self):
self._allow_declarations = True
try:
yield
finally:
self._allow_declarations = False
def restrict_attributes(cls):
cls.declare_attributes = declare_attributes
def _setattr(self, attr, value):
disallow_declarations = not getattr(self, "_allow_declarations", False)
if disallow_declarations and attr != "_allow_declarations":
if not hasattr(self, attr):
raise AttributeError("You shall not set attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
以下是如何使用它:
@restrict_attributes
class A(object):
def __init__(self):
with self.declare_attributes():
self.a = 1
self.b = 2
self.c = 3
因此,每当您想设置新属性时,只需使用上述with语句即可。也可以从实例外部完成:
a = A()
try:
a.d = 89
except AttributeError:
print "It works!"
else:
print "It doesn't work."
a.c = 42
print a.a
print a.c
with a.declare_attributes():
a.d = 28
print a.d