【问题标题】:How to create read-only slots?如何创建只读插槽?
【发布时间】:2021-07-04 15:26:24
【问题描述】:

默认情况下,插槽是可写的

>>> class A: __slots__ = ('x',)
... 
>>> list(vars(A))
['__module__', '__slots__', 'x', '__doc__']
>>> vars(A)['x']
<member 'x' of 'A' objects>
>>> a = A()
>>> a.x = 'foo'
>>> del a.x

如何创建只读槽,例如super类的槽'__thisclass__''__self__''__self_class__'

>>> list(vars(super))
['__repr__', '__getattribute__', '__get__', '__init__', '__new__',
 '__thisclass__', '__self__', '__self_class__', '__doc__']
>>> vars(super)['__thisclass__']
<member '__thisclass__' of 'super' objects>
>>> s = super(int, 123)
>>> s.__thisclass__ = float
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: readonly attribute
>>> del s.__thisclass__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: readonly attribute

【问题讨论】:

    标签: python super slots readonly-attribute


    【解决方案1】:

    您不能,Python 代码没有选项可以创建只读描述符,例如用于 __thisclass__ 等的描述符。

    在 C API 中,所有插槽都使用相同的描述符对象类型,对于 PyMemberDef arrays 中将 flags 设置为 READONLY 的条目,这些对象类型变为只读。

    例如您识别的super 描述符在super_members array 中定义:

    static PyMemberDef super_members[] = {
        {"__thisclass__", T_OBJECT, offsetof(superobject, type), READONLY,
         "the class invoking super()"},
        {"__self__",  T_OBJECT, offsetof(superobject, obj), READONLY,
         "the instance invoking super(); may be None"},
        {"__self_class__", T_OBJECT, offsetof(superobject, obj_type), READONLY,
         "the type of the instance invoking super(); may be None"},
        {0}
    };
    

    在处理__slots__ 时,没有可以设置此标志的路径; code in type.__new__ just sets the first three values,分别是nametypeoffset

        mp = PyHeapType_GET_MEMBERS(et);
        slotoffset = base->tp_basicsize;
        if (et->ht_slots != NULL) {
            for (i = 0; i < nslots; i++, mp++) {
                mp->name = PyUnicode_AsUTF8(
                    PyTuple_GET_ITEM(et->ht_slots, i));
                if (mp->name == NULL)
                    goto error;
                mp->type = T_OBJECT_EX;
                mp->offset = slotoffset;
    
    
                /* __dict__ and __weakref__ are already filtered out */
                assert(strcmp(mp->name, "__dict__") != 0);
                assert(strcmp(mp->name, "__weakref__") != 0);
    
    
                slotoffset += sizeof(PyObject *);
            }
        }
    

    供参考:

    • PyHeapType_GET_MEMBERS 访问 PyMemberDef 数组以获取正在创建的新类型对象。它已经分配了正确数量的插槽。
    • et-&gt;ht_slots 是插槽名称的元组。
    • slotoffset 是实例对象内存区域的相对偏移量,用于存储插槽内容。
    • type 字段的T_OBJECT_EX 值表示该槽存储指向 Python 对象的指针,如果指针设置为 NULL,则在您尝试获取该值时会引发 AttributeError

    请注意,如果可以将这些插槽设置为只读,那么您还需要一种机制在创建新实例之前提供它们的值!毕竟,如果阻止所有 Python 代码在实例上设置只读属性,那么您的 Python 类将如何设置初始值?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-22
      • 1970-01-01
      • 1970-01-01
      • 2020-04-03
      • 2012-10-04
      • 2018-10-13
      • 1970-01-01
      • 2013-06-21
      相关资源
      最近更新 更多