我需要在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,而不是旧的?这有可能吗?
我知道这是一个非常奇怪的情况,我无法更改tup
是一个元组,或者我需要使用一个隐式for in
,因为我正尝试插入我所插入的代码中不能改变。
答案 0 :(得分:1)
在CPython中,没有任何方法可以移植或专门进行,即使通过tuple_iterator
对象的未公开内部结构,也无法从Python中进行操作。元组引用存储在一个不会暴露给Python的变量中,并且(不同于存储的索引)未被__setstate__
或任何其他方法修改。
但是,如果您愿意开始使用CPython背后的C指针胡闹,并且您知道如何调试不可避免的段错误……
幕后有一个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
甚至不是公共类型这一事实之外,因此甚至还有更多漏洞,其他所有内容基本上都是相同的。
答案 1 :(得分:0)
您可以编写自己的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'}
我敢肯定,在上述情况中我遗漏了很多极端情况,但这应该可以使您了解如何完成该工作。
答案 2 :(得分:-1)
由于计划在循环内修改元组,因此最好使用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)