无法理解python生成器和可迭代参数

时间:2016-04-27 05:01:36

标签: python iterator generator

我应该编写一个生成器,给出一个可迭代参数列表,从第一个参数生成第一个元素,从第二个参数生成第一个元素,从第三个元素生成第一个元素,从第一个参数生成第二个元素,依此类推。

所以

''.join([v for v in alternate('abcde','fg','hijk')]) == afhbgicjdke

我的函数适用于像这样的字符串参数但是当我尝试使用这样的给定测试用例时遇到问题

def hide(iterable):
for v in iterable:
    yield v

''.join([v for v in alternate(hide('abcde'),hide('fg'),hide('hijk'))])= afhbgicjdke

这是我的发电机:

def alternate(*args):
    for i in range(10):
        for arg in args:
            arg_num = 0
            for thing in arg:
                if arg_num == i:
                    yield thing
                arg_num+=1

我可以更改此内容以使其按照描述工作,或者我的功能是否存在根本性错误?

编辑:作为作业的一部分,我不允许使用itertools

3 个答案:

答案 0 :(得分:3)

这样的工作正常:

def alternate(*iterables):
    iterators = [iter(iterable) for iterable in iterables]

    sentinel = object()

    keep_going = True
    while keep_going:
        keep_going = False
        for iterator in iterators:
            maybe_yield = next(iterator, sentinel)
            if maybe_yield != sentinel:
                keep_going = True
                yield maybe_yield

print ''.join(alternate('abcde','fg','hijk'))

诀窍是意识到当生成器耗尽时,next将返回sentinel值。只要有一个迭代器返回一个标记,那么我们需要继续前进,直到它耗尽为止。如果哨兵没有从next返回,那么价值就很好,我们需要让它产生。

请注意,如果可迭代的数量很大,则此实现是次优的(最好将迭代存储在支持O(1)删除的数据结构中,并尽快删除迭代因为它被检测到已经用尽了 - collections.OrderedDict可能会用于此目的,但我会将其留作感兴趣的读者的练习。)

如果我们想要打开标准库,itertools也可以在这里提供帮助:

from itertools import izip_longest, chain
def alternate2(*iterables):
    sentinel = object()
    result = chain.from_iterable(izip_longest(*iterables, fillvalue=sentinel))
    return (item for item in result if item is not sentinel)

在这里,我返回一个生成器表达式... 略微与编写生成器函数不同,但实际上并不多:-)。同样,如果存在大量迭代,并且其中一个比其他迭代更长 ,那么这可能效率稍低(考虑到您有100个长度为1且可迭代长度为101的迭代的情况 - - 这将有效地运行101 * 101步,而你应该能够以大约101 * 2 + 1步完成迭代。)

答案 1 :(得分:0)

您的代码中可以改进一些内容。造成问题的原因是它们中最大的错误 - 你实际上是在每个参数上迭代几次次 - 并且基本上不对每次传递中的中间值做任何事情。

当您为for thing in arg的每个值迭代i时,就会发生这种情况。

虽然这在任何帐户中都是浪费资源,但它也不适用于迭代器(这是你用hide函数得到的),因为它们在迭代了它的元素之后就会耗尽 - 那个与序列形成对比 - 可以迭代 - 重新重复几个关系(比如你用来测试的字符串)

(另一个错误的事情就是让那里的10硬编码为你所拥有的最长的序列值 - 在Python中你迭代生成器和序列 - 与它们的大小无关)

无论如何,修复此问题的方法是确保只迭代每个参数一次 - 内置zip可以执行此操作 - 或者对于您的用例itertools.zip_longest({{ Python 2.x中的1}}可以在单个izip_longest结构中从args中检索所需的值:

for

答案 2 :(得分:0)

如果你只想传递迭代器(这不适用于静态字符串),请使用以下代码:

def alternate(*args):
    for i in range(10):
        for arg in args:
            arg_num = i
            for thing in arg:
                if arg_num == i:
                    yield thing
                    break
                else:
                    arg_num+=1

这只是您原始代码的一点点变化。 当您每次调用alternate函数时使用静态字符串时,将传入一个新字符串,您可以从0开始计数(arg_num = 0)。

但是当你通过调用hide()方法创建迭代器时,只会为每个字符串创建一个迭代器的单个实例,你应该跟踪你在迭代器中的位置,所以你必须将arg_num = 0更改为arg_num = i而且你还需要添加break语句。