假设我有一个 __slots__
的类(class)
class A:
__slots__ = ['x']
a = A()
a.x = 1 # works fine
a.y = 1 # AttributeError (as expected)
现在我要改__slots__
的 A
.
A.__slots__.append('y')
print(A.__slots__) # ['x', 'y']
b = A()
b.x = 1 # OK
b.y = 1 # AttributeError (why?)
b
创建于 __slots__
之后的 A
已经改变,所以 Python 原则上可以为 b.y
分配内存。 .为什么没有?
如何正确修改__slots__
一个类,以便新实例具有修改后的属性?
最佳答案
您不能在创建类后动态更改 __slots__
属性,不行。那是因为该值用于创建特殊的 descriptors对于每个插槽。来自__slots__
documentation :
__slots__
are implemented at the class level by creating descriptors (Implementing Descriptors) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by__slots__
; otherwise, the class attribute would overwrite the descriptor assignment.
可以看到类__dict__
中的描述符:
>>> class A:
... __slots__ = ['x']
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, 'x': <member 'x' of 'A' objects>, '__slots__': ['x']})
>>> A.__dict__['x']
<member 'x' of 'A' objects>
>>> a = A()
>>> A.__dict__['x'].__get__(a, A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: x
>>> A.__dict__['x'].__set__(a, 'foobar')
>>> A.__dict__['x'].__get__(a, A)
'foobar'
>>> a.x
'foobar'
您不能自己创建这些额外的描述符。即使可以,您也不能为此类生成的实例上的额外槽引用分配更多内存空间,因为这些信息存储在该类的 C 结构中,而不是以 Python 代码可访问的方式。
那是因为 __slots__
只是对构成 Python 实例的元素的低级处理对 Python 代码的扩展; 常规 Python 实例上的 __dict__
和 __weakref__
属性始终作为插槽实现:
>>> class Regular: pass
...
>>> Regular.__dict__['__dict__']
<attribute '__dict__' of 'Regular' objects>
>>> Regular.__dict__['__weakref__']
<attribute '__weakref__' of 'Regular' objects>
>>> r = Regular()
>>> Regular.__dict__['__dict__'].__get__(r, Regular) is r.__dict__
True
所有 Python 开发人员在这里所做的就是扩展系统以使用任意名称添加更多这样的插槽,这些名称取自正在创建的类的 __slots__
属性,这样您就可以保存内存;字典比对槽中值的简单引用占用更多的内存。通过指定 __slots__
,您可以禁用 __dict__
和 __weakref__
槽,除非您明确地将它们包含在 __slots__
序列中。
扩展插槽的唯一方法是子类化;您可以使用 type()
函数或使用工厂函数动态创建子类:
def extra_slots_subclass(base, *slots):
class ExtraSlots(base):
__slots__ = slots
ExtraSlots.__name__ = base.__name__
return ExtraSlots
关于python - 如何动态更改 __slots__ 属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27907373/