标准 Python 类在底层存储实例属性dict
(名为__dict__
)。没有特殊的规则可以让它在内部__init__
分配和其他任何地方的分配之间做出有意义的区分;在__init__
它是一个空的命名空间之前,__init__
可以添加到该命名空间,但其他任何人都可以(如果您只在其中创建相同的属性集__init__
并且从不创建更多属性,现代 CPython 有一些优化可以减少内存使用,但它会回退到旧的,如果您违反该规则以保留现有行为,则需要更多内存密集型存储)。
这有时很方便,例如,当类上的另一个方法希望仅在调用该方法并且需要计算时才懒惰地计算属性。它只是使属性未定义,并在需要它的地方捕获AttributeError
并计算(并缓存)当时的值。
这是高级脚本类语言中非常常见的设计(除了 Python,其他默认允许这样做的语言包括 Perl、Ruby 和 JavaScript,仅举几例),因为它们对类实例的基本定义只是“一个字符串dict
上带有一些魔法键”。虽然他们可以制定规则以使事情更具限制性,但这并没有什么好处,所以他们只是让事情尽可能灵活。
正如您所注意到的,像这样的属性的自动激活可能会变得混乱,而且有时是不可取的。如果这是一个问题,并且您想预先定义一组可以定义的受限属性,只需__slots__
在类本身上定义有效属性的字符串名称即可。这将用底层dict
属性数组中连续分配的插槽替换底层属性(插槽名称成为知道如何唯一访问每个插槽的描述符)。它通过避免每个实例相对浪费来节省内存dict
,并且会阻止创建新属性。对于您的情况,您只需执行以下操作:
class Test():
__slots__ = 'a',
def __init__(self):
self.a = 0
并尝试分配给一个b
属性(在类内部或外部)将死于:
AttributeError: 'Test' object has no attribute 'b'
请注意,这也会禁用对类实例的弱引用;'__weakref__'
如果你想允许的话,你必须在类中明确列出一个槽。