如何创建只读插槽?

Mag*_*ero 2 python super readonly-attribute slots

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

如何创建只读插槽,像插槽'__thisclass__''__self__''__self_class__'之类的super

>>> 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
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 6

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

在C API,槽都使用相同的描述符对象类型,它变为只读在条目PyMemberDef阵列flags设置为READONLY

例如,super您确定的描述符在super_members数组中定义:

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}
};
Run Code Online (Sandbox Code Playgroud)

__slots__正在处理,没有路径,你可以使用这个标志; 中的代码type.__new__仅设置前三个值,分别为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 *);
        }
    }
Run Code Online (Sandbox Code Playgroud)

以供参考:

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

需要注意的是,如果它可以设置这些插槽为只读,你还需要一个机制来提供其价值之前创建一个新的实例!毕竟,如果所有 Python 代码都无法在实例上设置只读属性,那么您的 Python 类将如何设置初始值?