python-3.x - 如何创建元组的循环引用?

标签 python-3.x tuples cpython circular-reference

由于历史原因(阅读:可怕的 type(t) == tuple 检查),我发现自己需要将循环图卡住为 tuple 对象的集合。显然,这并不理想:

>>> head = ("head", None)
>>> a = ("a", ("b", ("c", head)))
>>> head[1] = a
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    head[1] = a
TypeError: 'tuple' object does not support item assignment

但是,我并不是 TypeError 的忠实拥护者,并且怀疑通过特定于实现的黑客技术可以做到这一点。

  • 如何在不冒出界或导致其他 C 未定义行为的风险的情况下产生这种怪物?
  • 垃圾收集器的循环依赖释放部分可以处理这样的事情吗?

最佳答案

I, however, am not really a great believer in TypeErrors, and suspect that, through implementation-specific hackery, this can be done.

可悲的是,你是对的:

from ctypes import Structure, c_ssize_t, c_void_p, py_object, pythonapi

pythonapi.Py_DecRef.argtypes = py_object,


def mutable(tup):
    # We are generating this class dynamically because the size of ob_item
    # varies according to the size of the given tuple
    class PyTupleObject(Structure):
        _fields_ = [('ob_refcnt', c_ssize_t),
                    ('ob_type', c_void_p),
                    ('ob_size', c_ssize_t),
                    ('ob_item', py_object * len(tup))]

        @classmethod
        def from_tuple(cls, tup):
            instance = cls.from_address(id(tup))
            # Save a reference to tup on the instance, as we are using it directly from memory
            # and don't want it to be garbage collected
            instance.original = tup
            return instance

        def __setitem__(self, idx, val):
            # Replacing a value in self.ob_item doesn't decref the old value but does indref the new value
            pythonapi.Py_DecRef(self.ob_item[idx])
            self.ob_item[idx] = val

        def __getitem__(self, idx):
            return self.ob_item[idx]

        def __iter__(self):
            return iter(self.ob_item)

        def __len__(self):
            return len(self.ob_item)

        def __contains__(self, val):
            return val in self.ob_item

    return PyTupleObject.from_tuple(tup)


if __name__ == '__main__':
    tup = (None,)
    mut_tup = mutable(tup)
    mut_tup[0] = tup
    print(tup is tup[0]) # Outputs "True"

How can I produce this monstrosity without risking going Out of Bounds or causing other C Undefined Behaviour?

我们通过将成员 ob_item 定义为 py_object * len(tup) 来防止越界访问。

Can the circular dependency freeing part of the Garbage Collector cope with such a thing?

不!元组应该是不可变的,因此不应该对其自身有循环引用。这就是为什么他们不实现 tp_clear 方法,Python 垃圾收集器使用该方法来打破引用循环并收集涉及的垃圾。更多详情here

关于python-3.x - 如何创建元组的循环引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58783138/

相关文章:

python - 用 Python 制作收银机

python - 为什么我创建字典时数据循环多次?

python - fetchone() 正在将 int 变量设置为元组

ios - 这个函数返回的元组有什么问题?

python - 通过字典的字典的元组获取值

python - 如何在 Python 中获取 mmap-ed 内存的地址?

cpython - Python 的抽象使用导致 TypeError

python-3.x - Python 3 - 将字典动态嵌套到给定未知数量类别的列表中

python-3.x - SSDP M-search不适用于单播-单个IP

python - 错误 LNK2001 : unresolved external symbol PyInit_