如何创建只读插槽? [英] How to create read-only slots?

查看:35
本文介绍了如何创建只读插槽?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

插槽默认可写:

>>>A类:__slots__ = ('x',)...>>>列表(变量(A))['__module__', '__slots__', 'x', '__doc__']>>>变量(A)['x']<A"对象的成员x">>>>a = A()>>>a.x = 'foo'>>>删除 a.x

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

>>>列表(变量(超级))['__repr__', '__getattribute__', '__get__', '__init__', '__new__','__thisclass__', '__self__', '__self_class__', '__doc__']>>>vars(super)['__thisclass__']<超级"对象的成员__thisclass__">>>>s = 超级(整数,123)>>>s.__thisclass__ = 浮动回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中.AttributeError: 只读属性>>>del s.__thisclass__回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中.AttributeError: 只读属性

解决方案

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

在 C API 中,槽都使用相同的描述符对象类型,对于 PyMemberDef 数组,将 flags 设置为 READONLY.>

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

static PyMemberDef super_members[] = {{__thisclass__", T_OBJECT, offsetof(superobject, type), READONLY,调用 super() 的类"},{__self__", T_OBJECT, offsetof(superobject, obj), READONLY,"调用 super() 的实例;可能是 None"},{__self_class__", T_OBJECT, offsetof(superobject, obj_type), READONLY,"调用 super() 的实例类型;可能是 None"},{0}};

在处理__slots__时,没有路径可以设置这个标志;代码在type.__new__,分别是nametypeoffset:

 mp = PyHeapType_GET_MEMBERS(et);slotoffset = base->tp_basicsize;如果(et-> ht_slots != NULL){for (i = 0; i  名称 = PyUnicode_AsUTF8(PyTuple_GET_ITEM(et->ht_slots, i));如果 (mp-> 名称 == NULL)转到错误;mp->type = T_OBJECT_EX;mp->offset = slotoffset;/* __dict__ 和 __weakref__ 已经被过滤掉了 */断言(strcmp(mp->名称,__dict__")!= 0);断言(strcmp(mp->名称,__weakref__")!= 0);slotoffset += sizeof(PyObject *);}}

供参考:

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

Slots are writable by default:

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

How to create read-only slots, like the slots '__thisclass__' , '__self__', and '__self_class__' of the 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

You can't, there is no option from Python code to create read-only descriptors like the ones used for __thisclass__, etc.

In the C API, slots all use the same descriptor object type, which become read-only for entries in PyMemberDef arrays that have flags set to READONLY.

E.g. the super descriptors you identified are defined in the 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}
};

When __slots__ is being processed, there is no path where you can set this flag; the code in type.__new__ just sets the first three values, which are name, type and offset, respectively:

    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 *);
        }
    }

For reference:

  • PyHeapType_GET_MEMBERS accesses the PyMemberDef array for the new type object being created. It has the right number of slots allocated already.
  • et->ht_slots is the tuple of slot names.
  • slotoffset is the relative offset into the instance object memory area to store the slot contents.
  • the T_OBJECT_EX value for the type field means the slot stores a pointer to a Python object, and if the pointer is set to NULL an AttributeError is raised when you try to get the value.

Note that if it was possible to set these slots to read-only, you'd also need a mechanism to provide their value before creating a new instance! After all, if all Python code was prevented from setting a readonly attribute on instances, how would your Python class ever get to set the initial value?

这篇关于如何创建只读插槽?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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