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/30746356/

相关文章:

python-3.x - 如何获取 Skype 群聊 ID

python - Python 中的正则表达式反向引用问题

python - 未为 django.db.models 字段默认定义可调用

c++ - QT,C++ : How do Signal and Slots works with listWidgetItems?

python - 递归地颠倒链表中数字的顺序

python - 有什么方法可以将函数存储在变量中并创建变量容纳函数的列表吗?

python - 如何创建只读插槽?

Watson Conversation Service 与运算符(operator)一起使用槽

javascript - 如何从获取响应中获取gif

python - Django Rest Framework,查找字段配置不正确