Мне нужно изменить кортеж во время цикла for in
, чтобы итератор итерировал по кортежу.
Насколько я понимаю, кортежи неизменны; поэтому tup = tup + (to_add,)
просто переназначает tup
, а не меняет исходный кортеж. Так что это сложно.
Вот тестовый сценарий:
tup = ({'abc': 'a'}, {'2': '2'})
blah = True
to_add = {'goof': 'abcde'}
for i in tup:
if blah:
tup = tup + (to_add,)
blah = False
print(i)
Какие отпечатки:
{'abc': 'a'}
{'2': '2'}
То, что я хотел бы, чтобы это напечатало:
{'abc': 'a'}
{'2': '2'}
{'goof': 'abcde'}
Из того, что я понимаю, мне нужно «переписать» неявный промежуточный сценарий итератора кортежа, чтобы он вместо этого указывал на новый кортеж. (Я знаю, что это серьезно хакерская вещь).
Этот скрипт обращается к рассматриваемому tuple_generator:
import gc
tup = ({'abc': 'a'}, {'2': '2'})
blah = True
to_add = {'goof': 'abcde'}
for i in tup:
if blah:
tup = tup + (to_add,)
blah = False
refs = gc.get_referrers(i)
for ref in refs:
if type(ref) == tuple and ref != tup:
refs_to_tup = gc.get_referrers(ref)
for j in refs_to_tup:
if str(type(j)) == "<class 'tuple_iterator'>":
tuple_iterator = j
print(i)
Как я могу изменить этот tuple_generator, чтобы он указывал на новый, а не на старый? Это вообще возможно?
Я знаю, что это действительно странная ситуация, я не могу изменить то, что tup
является кортежем или что мне нужно использовать неявный for in
, так как я пытаюсь подключиться к коду, который не могу изменить.
3 ответа
В CPython нет способа - ни переносимого, ни специально - сделать то, что вы пытаетесь сделать из Python, даже через недокументированные внутренние объекты объекта tuple_iterator
. Ссылка на кортеж хранится в переменной, которая не доступна Python, и (в отличие от сохраненного индекса) не изменяется __setstate__
или любым другим методом.
Тем не менее, если вы готовы начать обезьяны с указателями C за спиной CPython, и вы знаете, как отлаживать неизбежные ошибки сегмента…
Под обложками есть структура C, представляющая tuple_iterator
. Я думаю, что это либо seqiterobject
или структура с точно такой же формой, но вы должны прочитать исходный код tupleobject, чтобы убедиться.
Вот как этот тип выглядит в C:
typedef struct {
PyObject_HEAD
Py_ssize_t it_index;
PyObject *it_seq; /* Set to NULL when iterator is exhausted */
} seqiterobject;
Итак, что произойдет, если вы создадите подкласс ctypes.Structure
такого же размера, как этот:
class seqiterobject(ctypes.Structure):
_fields_ = (
('ob_refcnt', ctypes.c_ssize_t),
('ob_type', ctypes.c_void_p),
('it_index', ctypes.c_ssize_t),
('it_seq', ctypes.POINTER(ctypes.pyobject)))
… А затем сделайте следующее:
seqiter = seqiterobject.from_address(id(j))
… А затем сделайте следующее:
seqiter.it_seq = id(other_tuple)
... ? Что ж, вы, вероятно, повредили кучу, сославшись на новое значение (а также утечку старого), поэтому вам нужно сначала увеличить новое значение и уменьшить старое значение.
Но, если вы сделаете это ... скорее всего, в следующий раз, когда вы позвоните __next__
, он либо будет зависать, либо сработает.
Если вам нужно больше примеров кода, который выполняет похожие действия, см. superhackyinternals
. Кроме того факта, что seqiterobject
даже не является публичным типом, так что это даже более хакерский, все остальное в основном то же самое.
Поскольку вы планируете модифицировать кортеж внутри цикла, вам, вероятно, лучше использовать цикл while для отслеживания текущего индекса, а не полагаться на итератор. Итераторы хороши только для циклического просмотра коллекций, которые не добавляются / не удаляются в цикле.
Если вы запустите приведенный ниже пример, к полученному объекту tup будут добавлены элементы, и все это будет повторяться 3 раза.
tup = ({'abc': 'a'}, {'2': '2'})
blah = True
to_add = {'goof': 'abcde'}
i = 0
while i < len(tup):
cur = tup[i]
if blah:
tup = tup + (to_add,)
blah = False
i += 1
print(tup)
Вы могли бы написать свой собственный coroutine
и отправить ему новый Tup.
def coro(iterable):
iterable = iter(iterable)
while True:
try:
v = next(iterable)
i = yield v
except StopIteration:
break
if i:
yield v
iterable = it.chain(iterable, i)
Тогда это работает, как вы описываете:
In []:
blah = True
tup = ({'abc': 'a'}, {'2': '2'})
to_add = {'goof': 'abcde'}
c = coro(tup)
for i in c:
if blah:
i = c.send((to_add,))
blah = False
print(i)
Out[]:
{'abc': 'a'}
{'2': '2'}
{'goof': 'abcde'}
Я уверен, что есть много крайних случаев, которые я пропускаю в вышеупомянутом, но это должно дать вам представление о том, как это можно сделать.
Похожие вопросы
Связанные вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.