如何动态更改__slots__属性? [英] How to dynamically change __slots__ attribute?

查看:140
本文介绍了如何动态更改__slots__属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个__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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆