构建自引用元组] >

问题描述 投票:15回答:5

[多年以前在一个论坛上看到一个对话之后,这个对话一直没有解决,这让我想知道人们是如何正确创建一个引用自己的元组的。从技术上讲,这是一个非常糟糕的主意,因为元组应该是不可变的。不可变的对象如何包含自身?但是,此问题不是关于最佳实践的问题,而是关于Python可能实现的问题的询问。

import ctypes

def self_reference(array, index):
    if not isinstance(array, tuple):
        raise TypeError('array must be a tuple')
    if not isinstance(index, int):
        raise TypeError('index must be an int')
    if not 0 <= index < len(array):
        raise ValueError('index is out of range')
    address = id(array)
    obj_refcnt = ctypes.cast(address, ctypes.POINTER(ctypes.c_ssize_t))
    obj_refcnt.contents.value += 1
    if ctypes.cdll.python32.PyTuple_SetItem(ctypes.py_object(array),
                                            ctypes.c_ssize_t(index),
                                            ctypes.py_object(array)):
        raise RuntimeError('PyTuple_SetItem signaled an error')

先前的功能旨在访问Python的C API,同时牢记内部结构和数据类型。但是,运行该函数时通常会产生以下错误。通过未知的过程,以前可以通过类似的技术创建自引用元组。

问题:

应该如何修改函数self_reference以始终保持一致?
>>> import string
>>> a = tuple(string.ascii_lowercase)
>>> self_reference(a, 2)
Traceback (most recent call last):
  File "<pyshell#56>", line 1, in <module>
    self_reference(a, 2)
  File "C:/Users/schappell/Downloads/srt.py", line 15, in self_reference
    ctypes.py_object(array)):
WindowsError: exception: access violation reading 0x0000003C
>>> 

Edit:

这是与解释器的两种不同的对话,有些混淆。如果我正确理解文档,上面的代码似乎是正确的。但是,下面的对话似乎互相冲突,并且上面的self_reference功能。

对话1:

Python 3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
on win32
Type "copyright", "credits" or "license()" for more information.
>>> from ctypes import *
>>> array = tuple(range(10))
>>> cast(id(array), POINTER(c_ssize_t)).contents.value
1
>>> cast(id(array), POINTER(c_ssize_t)).contents.value += 1
>>> cast(id(array), POINTER(c_ssize_t)).contents.value
2
>>> array
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> cdll.python32.PyTuple_SetItem(c_void_p(id(array)), 0,
                                  c_void_p(id(array)))
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    cdll.python32.PyTuple_SetItem(c_void_p(id(array)), 0,
                                  c_void_p(id(array)))
WindowsError: exception: access violation reading 0x0000003C
>>> cdll.python32.PyTuple_SetItem(c_void_p(id(array)), 0,
                                  c_void_p(id(array)))
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    cdll.python32.PyTuple_SetItem(c_void_p(id(array)), 0,
                                  c_void_p(id(array)))
WindowsError: exception: access violation reading 0x0000003C
>>> array
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> cdll.python32.PyTuple_SetItem(c_void_p(id(array)), 0,
                                  c_void_p(id(array)))
0
>>> array
((<NULL>, <code object __init__ at 0x02E68C50, file "C:\Python32\lib
kinter\simpledialog.py", line 121>, <code object destroy at 0x02E68CF0,
file "C:\Python32\lib   kinter\simpledialog.py", line 171>, <code object
body at 0x02E68D90, file "C:\Python32\lib      kinter\simpledialog.py",
line 179>, <code object buttonbox at 0x02E68E30, file "C:\Python32\lib
kinter\simpledialog.py", line 188>, <code object ok at 0x02E68ED0, file
"C:\Python32\lib        kinter\simpledialog.py", line 209>, <code object
cancel at 0x02E68F70, file "C:\Python32\lib    kinter\simpledialog.py",
line 223>, <code object validate at 0x02E6F070, file "C:\Python32\lib
kinter\simpledialog.py", line 233>, <code object apply at 0x02E6F110, file
"C:\Python32\lib     kinter\simpledialog.py", line 242>, None), 1, 2, 3, 4,
5, 6, 7, 8, 9)
>>>

对话2:

Python 3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
on win32
Type "copyright", "credits" or "license()" for more information.
>>> from ctypes import *
>>> array = tuple(range(10))
>>> cdll.python32.PyTuple_SetItem(c_void_p(id(array)), c_ssize_t(1),
                                  c_void_p(id(array)))
0
>>> array
(0, (...), 2, 3, 4, 5, 6, 7, 8, 9)
>>> array[1] is array
True
>>>

[多年以前在一个论坛上看到一个对话之后,这个对话一直没有解决,这让我想知道人们是如何正确创建一个引用自己的元组的。从技术上讲,这是一个非常...

python tuples ctypes creation self-reference
5个回答
6
投票

[AFAICT,您看到问题的原因是,如果元组的引用计数不正确,则PyTuple_SetItem失败。如果元组已经在其他地方使用过,这是为了防止使用该功能。我不确定为什么会收到访问冲突,但这可能是因为PyTuple_SetItem引发的异常未得到正确处理。此外,数组似乎突变为其他对象的原因是因为PyTuple_SetItem DECREF是每次失败时的元组。在两次失败之后,引用计数为零,因此该对象被释放(并且其他一些对象显然位于同一内存位置)。


6
投票

感谢nneonneo的帮助,我决定采用self_reference方法的以下实现。


1
投票

从技术上讲,您可以将对元组的引用包装在可变对象内。


1
投票

不可移植性不应阻止对象引用自身。在Haskell中,这很容易做到,因为它的评估很懒。这是一个模仿,通过使用一个thunk:


0
投票

更简单的解决方案:

© www.soinside.com 2019 - 2024. All rights reserved.