理解python生成器和iterable参数时出现问题

2024-09-29 23:15:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我应该编写一个生成器,给定一系列iterable参数,从第一个参数生成第一个元素,从第二个参数生成第一个元素,从第三个元素生成第一个元素,从第一个参数生成第二个元素,依此类推。在

所以

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

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

^{pr2}$

这是我的发电机:

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


Tags: in元素for参数argargsiterablenum
3条回答

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

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语句。在

在您的代码中有几个地方可以改进。导致你的问题的是所有这些问题中最错误的——你实际上是在你的每个参数上迭代几次而实际上在每次传递中对中间值什么都不做。在

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

尽管这在任何情况下都是对资源的巨大浪费,但它也不能与迭代器一起工作(这是您使用hide函数得到的结果),因为它们在迭代其元素一次之后就耗尽了——这与序列相反——可以迭代——然后再重复几个关系(比如你用来测试的字符串)

(另一个错误是将10硬编码为您有过的最长序列值-在Python中迭代生成器和序列-不管它们的大小)

无论如何,解决这个问题的方法是确保只迭代每个参数一次-内置的zip可以这样做-或者对于您的用例,itertools.zip_longest(Python 2.x中的izip_longest)可以在一个for结构中从args中检索所需的值:

from itertools import izip_longest
def alternate(*args):
    sentinel = object()
    for values in izip_longest(*args, fillvalue=sentinel):
         for value in values:
              if value is not sentinel:
                   yield value

像这样的方法很有效:

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值。只要一个迭代器返回一个sentinel,那么我们就需要继续下去,直到它耗尽为止。如果sentinel没有从next返回,那么该值是正确的,我们需要生成它。在

请注意,如果iterable的数量很大,则此实现是次优的(最好将iterable存储在支持O(1)删除的数据结构中,并在检测到iterable耗尽时立即将其删除,因此可以使用collections.OrderedDict,但我将把它留给感兴趣的读者作为练习)。在


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

^{pr2}$

这里,我返回一个生成器表达式。。。这与编写一个生成器函数略有不同,但实际上没有多大区别:-)。再一次,如果有很多iterable,并且其中一个iterable比其他的长很多(考虑这样的情况,长度为1的iterable为100个,长度为101的iterable,则效率为101*101步,而实际上应该能够在101*2+1步中完成迭代)。在

相关问题 更多 >

    热门问题