Создание самонастраивающихся кортежей

Увидев разговор на форуме много лет назад, который никогда не был разрешен, мне стало интересно, как правильно создать кортеж, который ссылался на себя. Технически это очень плохая идея, поскольку кортежи должны быть неизменными. Как может неизменный объект, возможно, содержать себя? Однако этот вопрос не касается лучших практик, а является запросом о том, что возможно в 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') 

Предыдущая функция была разработана для доступа к C API Python, сохраняя при этом внутренние структуры и типы данных. Однако при запуске функции обычно генерируется следующая ошибка. По неизвестным процессам до этого можно было создать саморегуляционный кортеж.

Вопрос: Как должна быть изменена функция 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 >>> 

Изменить: Вот два разных диалога с интерпретатором, которые несколько запутывают. Приведенный выше код представляется правильным, если я правильно понимаю документацию. Тем не менее, разговоры, приведенные ниже, кажутся как конфликтующими друг с другом, так и функцией 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 >>> 

One Solution collect form web for “Создание самонастраивающихся кортежей”

AFAICT, причина, по которой вы видите проблемы, заключается в том, что PyTuple_SetItem сбой, если пересчет кортежа не является одним. Это делается для предотвращения использования функции, если кортеж уже используется в другом месте. Я не уверен, почему вы получаете нарушение доступа от этого, но может быть, потому что исключение, созданное PyTuple_SetItem , не рассматривается должным образом. Кроме того, причина, по которой массив, по-видимому, мутирует к другому объекту, заключается в том, что PyTuple_SetItem DECREF является кортежем при каждом сбое; после двух отказов refcount равен нулю, поэтому объект освобождается (и какой-то другой объект, по-видимому, попадает в ту же ячейку памяти).

Использование объекта pythonapi в ctypes является предпочтительным способом получить доступ к DLL Python, поскольку он правильно обрабатывает исключения Python и гарантированно использует правильное соглашение о вызовах.

У меня нет операционной системы Windows, чтобы проверить это, но в Mac OS X (как Python 2.7.3, так и 3.2.2) отлично работает:

 import ctypes def self_reference(array, index): # Sanity check. We can't let PyTuple_SetItem fail, or it will Py_DECREF # the object and destroy it. if not isinstance(array, tuple): raise TypeError("array must be a tuple") if not 0 <= index < len(array): raise IndexError("tuple assignment index out of range") arrayobj = ctypes.py_object(array) # Need to drop the refcount to 1 in order to use PyTuple_SetItem. # Needless to say, this is incredibly dangerous. refcnt = ctypes.pythonapi.Py_DecRef(arrayobj) for i in range(refcnt-1): ctypes.pythonapi.Py_DecRef(arrayobj) try: ret = ctypes.pythonapi.PyTuple_SetItem(arrayobj, ctypes.c_ssize_t(index), arrayobj) if ret != 0: raise RuntimeError("PyTuple_SetItem failed") except: raise SystemError("FATAL: PyTuple_SetItem failed: tuple probably unusable") # Restore refcount and add one more for the new self-reference for i in range(refcnt+1): ctypes.pythonapi.Py_IncRef(arrayobj) 

Результат:

 >>> x = (1,2,3,4,5) >>> self_reference(x, 1) >>> import pprint >>> pprint.pprint(x) (1, <Recursion on tuple with id=4299516720>, 3, 4, 5) 
  • Как преобразовать список Python в массив C с помощью ctypes?
  • Не удалось выполнить функцию readlink ()
  • Имитация целочисленного переполнения в Python
  • Использование модуля ctypes
  • Могу ли я получить доступ к API ImageMagick с помощью Python?
  • Ошибка генерирования create_string_buffer ТипError: ожидаемая строка / байты вместо str instance
  • Доступ c_char_p_Array_256 в Python с использованием ctypes
  • Передать список Python во встроенную функцию Rust
  • Python - лучший язык программирования в мире.