如何创建只读插槽? [英] How to create read-only slots?
问题描述
插槽默认可写:
>>>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__
,分别是name
、type
和offset
:
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 *);}}
供参考:
PyHeapType_GET_MEMBERS
访问正在创建的新类型对象的PyMemberDef
数组.它已经分配了正确数量的插槽.et->ht_slots
是插槽名称的元组.slotoffset
是实例对象内存区域的相对偏移量,用于存储插槽内容.type
字段的T_OBJECT_EX
值意味着该槽存储了一个指向 Python 对象的指针,如果该指针设置为NULL
当您尝试获取值时会引发AttributeError
.
请注意,如果可以将这些插槽设置为只读,那么您还需要一种机制来在创建新实例之前提供它们的值!毕竟,如果所有 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 thePyMemberDef
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 thetype
field means the slot stores a pointer to a Python object, and if the pointer is set toNULL
anAttributeError
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屋!