Abd*_*P M 12 python ctypes tuples cpython python-3.x
python中的元组在设计上是不可变的,因此如果我们尝试改变元组对象,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
Run Code Online (Sandbox Code Playgroud)
所以我的问题是,如果元组在设计上是不可变的,为什么cpython 公开PyTuple_SetItem
为 C-API?
从文档中它被描述为
int PyTuple_SetItem(PyObject *p, Py_ssize_t pos, PyObject *o)
o
在 指向的元组的 pos 位置插入对对象的引用p
。0
成功归来。如果 pos 越界,则返回-1
并设置 IndexError 异常。
这句话不是和tuple[index] = value
python层中的完全一样吗?如果目标是从项目集合创建一个元组,我们可以使用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)`
Run Code Online (Sandbox Code Playgroud)
tde*_*ney 11
同样,有一个PyTuple_Resize
带有警告的函数
因为元组应该是不可变的,所以只有在只有一个对象引用时才应该使用它。如果代码的其他部分可能已经知道该元组,请勿使用此选项。元组总是会在最后增大或缩小。将此视为销毁旧元组并创建新元组,只会更有效。
看源码,函数上有一个guard
if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) {
.... error ....
Run Code Online (Sandbox Code Playgroud)
果然,只有当只有 1 个对元组的引用时才允许这样做 - 该引用是认为更改它是个好主意的东西。因此,元组“大部分是不可变的”,但 C 代码可以在有限的情况下更改它,以避免创建新元组的惩罚。