python中的元组在设计上是不可变的,所以如果我们尝试改变一个Tuple对象,python就会发出下面的TypeError,这是有意义的。
>>> a = (1, 2, 3)
>>> a[0] = 12
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment因此,我的问题是,如果元组在设计上是不可变的,为什么cpython将PyTuple_SetItem公开为C?
从它描述的文档中
int PyTuple_SetItem(PyObject *p, Py_ssize_t pos, PyObject *o)在o指向的元组的位置插入对对象p的引用。成功后返回0。如果pos超出界限,返回-1并设置IndexError异常。
这个语句不完全等于python层中的tuple[index] = value吗?如果目标是从项目集合中创建一个元组,我们可以使用PyTuple_Pack。
补充说明:
经过多次使用ctypes.pythonapi的尝试和错误之后,我成功地使用PyTuple_SetItem修改了元组对象。
import ctypes
from ctypes import py_object
my_tuple = (1, 2, 3)
newObj = py_object(my_tuple)
m = "hello"
# I don't know why I need to Py_DecRef here.
# Although to reproduce this in your system, no of times you have
# to do `Py_DecRef` depends on no of ref count of `newObj` in your system
ctypes.pythonapi.Py_DecRef(newObj)
ctypes.pythonapi.Py_DecRef(newObj)
ctypes.pythonapi.Py_DecRef(newObj)
ctypes.pythonapi.Py_IncRef(m)
PyTuple_SetItem = ctypes.pythonapi.PyTuple_SetItem
PyTuple_SetItem.argtypes = ctypes.py_object, ctypes.c_size_t, ctypes.py_object
PyTuple_SetItem(newObj, 0, m)
print(my_tuple) # this will print `('hello', 2, 3)`发布于 2022-11-11 16:34:17
类似地,有一个带有警告的PyTuple_Resize函数
因为元组应该是不可变的,所以只有在只有一个对对象的引用时才应该使用元组。如果代码的其他部分可能已经知道元组,请不要使用此方法。元组在结束时总是会生长或收缩。把这想成是摧毁旧的元组,创建一个新的元组,只是效率更高。
从源代码来看,函数上有一个保护程序。
if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) {
.... error ....当然,这是允许的,只有当只有一个引用元组-那个引用是一个东西,认为是一个好主意来改变它。因此,元组“基本上是不可变的”,但是C代码可以在有限的情况下更改它,以避免创建新元组的代价。
发布于 2022-11-11 16:38:12
有一个文件中的说明说:
注意,此函数“窃取”对o的引用,并在受影响的位置丢弃对元组中已经存在的项的引用。
否则,在Cpython Github博士中,我们可以看到关于这个函数的更多细节,特别是这个函数如何以及为什么steals对象引用。我们可以读到:
顺便提一句,:c:func:
PyTuple_SetItem是设置元组项的唯一方法;:c:func:PySequence_SetItem和:c:func:PyObject_SetItem拒绝这样做,因为元组是不可变的数据类型。您应该只对自己创建的元组使用:c:func:PyTuple_SetItem。 可以使用:c:func:PyList_New和:c:func:PyList_SetItem编写用于填充列表的等效代码。 然而,在实践中,您很少使用这些创建和填充元组或列表的方法。有一个通用函数:c:func:Py_BuildValue,它可以从C值创建最常见的对象,由a :dfn:format string指导。
https://stackoverflow.com/questions/74405180
复制相似问题