在for循环期间附加到元组

时间:2018-08-20 21:45:21

标签: python iterator tuples

我需要在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,因为我正尝试插入我所插入的代码中不能改变。

3 个答案:

答案 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)