python - 如何动态更改 __slots__ 属性?

标签 python python-3.x slots

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

相关文章:

python - for循环中的问题(python)

python - brew python3 突然停止在 MacO 上运行

python - 获取派生类的所有 __slots__

c++ - 为什么Qt默认信号是公开的?

vuejs3 - Vue3 Composition Api 检查空槽

python - 使用滚动,俯仰和偏航使图像变形

python - 将Python 3转换为autodoc可以读取的 "simple"python

python - cpdef 和封装在 def 中的 cdef 有什么区别?

python - 如何通过使用多个文件在 Python 中正确使用多处理?

python-3.x - 如何在文本分类的拆分数据测试和训练中修复 'typeerror: only integer scalar arrays can be converted to a scalar index'