如何动态更改__slots__属性? [英] How to dynamically change __slots__ attribute?
问题描述
假设我有一个__slots__
class A:
__slots__ = ['x']
a = A()
a.x = 1 # works fine
a.y = 1 # AttributeError (as expected)
现在,我要更改A
的__slots__
.
A.__slots__.append('y')
print(A.__slots__) # ['x', 'y']
b = A()
b.x = 1 # OK
b.y = 1 # AttributeError (why?)
b
是在A
的__slots__
更改后创建的,因此Python原则上可以为b.y
分配内存.为什么没有呢?
b
was created after __slots__
of A
had changed, so Python, in principle, could allocate memory for b.y
. Why it didn't?
如何正确修改类的__slots__
,以使新实例具有修改后的属性?
How to properly modify __slots__
of a class, so that new instances have the modified attributes?
推荐答案
创建类后,您不能动态更改__slots__
属性.这是因为该值用于创建特殊的 描述符 每个插槽.从 __slots__
文档中:>
You cannot dynamically alter the __slots__
attribute after creating the class, no. That's because the value is used to create special descriptors for each slot. From the __slots__
documentation:
__slots__
在类级别通过为每个变量名称创建描述符(实施描述符)来实现.结果,类属性不能用于为__slots__
定义的实例变量设置默认值;否则,class属性将覆盖描述符分配.
__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__
中看到描述符:
You can see the descriptors in the class __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代码可访问的方式.
You cannot yourself create these additional descriptors. Even if you could, you cannot allocate more memory space for the extra slot references on the instances produced for this class, as that's information stored in the C struct for the class, and not in a manner accessible to Python code.
这就是所有原因,因为__slots__
只是对构成Python实例的元素的低级处理的扩展,而不仅仅是Python代码. regular Python实例上的__dict__
和__weakref__
属性始终实现为插槽:
That's all because __slots__
is only an extension of the low-level handling of the elements that make up Python instances to Python code; the __dict__
and __weakref__
attributes on regular Python instances were always implemented as slots:
>>> 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__
序列中.
All the Python developers did here was extend the system to add a few more of such slots using arbitrary names, with those names taken from the __slots__
attribute on the class being created, so that you can save memory; dictionaries take more memory than simple references to values in slots do. By specifying __slots__
you disable the __dict__
and __weakref__
slots, unless you explicitly include those in the __slots__
sequence.
然后扩展插槽的唯一方法是子类化.您可以使用type()
函数或使用工厂函数动态创建子类:
The only way to extend slots then is to subclass; you can dynamically create a subclass with the type()
function or by using a factory function:
def extra_slots_subclass(base, *slots):
class ExtraSlots(base):
__slots__ = slots
ExtraSlots.__name__ = base.__name__
return ExtraSlots
这篇关于如何动态更改__slots__属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!