【问题标题】:how do you chain iterables?你如何链接迭代?
【发布时间】:2021-03-06 01:43:52
【问题描述】:

我有两个可迭代的对象并想要链接它们,即将它们连接起来。具体来说,可迭代的对象返回一个迭代器,这可以重复。这两个迭代器的连接应该是一个迭代器,每次从输入中获取两个迭代器并返回连接的迭代器。我对 Python 还是很陌生,发现它非常微妙而且非常困难——不像每个人都告诉我的“简单”语言。但我原以为会有一种简单的方法来完成这项特定任务。来自 itertools 的注释链不起作用:

from itertools import chain

def i():
    n = 0
    while n < 2:
        yield n
        n = n+1

def j():
    m = 0
    while m < 3:
        yield m
        m = m+1

print("iterating i")        
for x in i():
    print(x)
    
print("iterating i again")   
for x in i():
    print(x)
    
k = chain(i(),j())

print("iterating k")    
for x in k:
    print(x)
    
print("iterating k again")   
for y in k:
    print(y)
print("but it's empty :(")

给予

iterating i
0
1
iterating i again
0
1
iterating k
0
1
0
1
2
iterating k again
but it's empty :(

这里的链似乎对提供迭代器的迭代器进行操作,但我想链接两个提供迭代器的迭代器。

对初始 cmets 的回应:

我不认为这是关于生成器的问题:我只是用生成器来说明。

有些人说可迭代的东西已经用完了。但据我了解,这是不对的:iterable 总是可以创建新的迭代器。您可以看到 i() 可以重复。一个特定的迭代器已用尽。这个问题是关于iterator和iterable的区别。

最后,我当然可以将数据保存在一个列表中,并在该列表上进行多次迭代。但我想让你想象一下,2 和 3 太大了,这是不切实际的。

提前致谢:)

【问题讨论】:

  • 第二次用尽了可迭代对象
  • 它不仅仅是一个可迭代的,它是一个 ganarator...你最好看看它的具体用例..
  • 不...我会在问题中添加一些内容以明确我想要什么。
  • Python 会将任何带有yield 表达式的函数视为生成器函数,并且此生成器是一次性迭代器。用你自己的话You can see that i() can be repeated,这是因为你从函数i 生成了一个全新的迭代器。要证明这确实是一次性迭代器,请尝试以下操作:f = i() 现在循环两次,第二次循环不会产生任何结果。

标签: python iterable


【解决方案1】:

这就是生成器的工作方式。用外行的话来说,你用过一次,它就空了。这个想法不是使用新内存来存储东西,而是在运行中创建东西

当您执行k = chain(i(),j()) 时,您将创建生成器对象k,并在第一个for x in k 循环中将其清空。现在,当您尝试在 for y in k 循环中循环相同的 k 时,迭代器 k 中没有任何东西可以迭代

在您的第一个for x in i() 循环中,您调用i() 并在那里创建一个新的生成器以进行循环。在for y in i() 中再次执行此操作,您正在循环一个新的生成器;不是你以前用过的那个

如果你想多次使用k,你可以创建一个列表而不是生成器

k = list(chain(i(), j()))

编辑:如果您不想要列表,您只需使用for x in chain(i(), j())for y in chain(i(), j()) 而不是for .. in k。这将在每个 for 循环中创建一个新的生成器对象。 k = chain(i(), j()) 只创建了一个生成器,您尝试使用它两次

【讨论】:

  • 不是很有帮助。因为 i() 可以创建一个新的迭代器(正如你指出的那样!)我想要的是一个版本的链,它返回一个每次都创建一个新迭代器的对象。换句话说,我想链接两个可迭代对象。我知道迭代器是如何工作的。
  • 我认为您还没有完全掌握迭代器的概念。请查看编辑
  • 谢谢。我看到了编辑。我知道发生了什么事。我只是不想要这种行为我想要一些其他行为!
  • 那我还没有完全理解你的问题。您每次都想要for .. in k 形式的for 循环吗?也许您想要一个返回生成器def k(): return chain(i(), j()) 的函数? (那你就可以for .. in k())
  • 我想要一个迭代器上的元链,而不是迭代器上的链
【解决方案2】:

就像我说的,Python 很微妙。而且似乎真的很难解释我想要什么,更不用说去做了!

我现在有一些代码可以 做我想做的事。它不是特别漂亮。我已经删除了发电机上的东西,因为这似乎是一个红鲱鱼。如果这真的是在 Python 中最简单的方法,那么它就是我问题的答案。但是外面的人可能知道更简单的方法。

from itertools import chain

# make iterators without using generators
class iterthing:
    def __init__( self , n ):
        self._max = n
    def __iter__( self ):
        return iter(range(self._max))
    
i = iterthing(2)
j = iterthing(3)

print("iterating i")        
for x in i:
    print(x)

print("iterating j")        
for x in j:
    print(x)
    
print("iterating i again")   
for x in i:
    print(x)

# join two iterables
class metachain:
    def __init__( self, i , j ):
        self._first = i
        self._second = j
    def __iter__( self ):
        return chain( iter(self._first), iter(self._second) )
    
k = metachain(i,j)

print("iterating k")    
for x in k:
    print(x)
    
print("iterating k again")   
for y in k:
    print(y)
print("this time it repeats as required with no lists stored in memory")

【讨论】:

    【解决方案3】:

    您可以通过定义自己的需要函数的链来做一些魔术:

    def mychain(f1,f2):
      return itertools.chain(f1(),f2())
    

    然后使用functools.partial() 创建您的k

    k=functools.partial(mychain,i,j)
    
    for x in k():
      print(x);
    print("again")
    for x in k():
      print(x)
    

    (当然你可以把它改成*args,只是这个想法可能用2个参数更易读)

    【讨论】:

    • 谢谢!这是一个有用的建议,并给了我一些其他选择。我不知道 functools.partial,它非常好!
    猜你喜欢
    • 2018-04-16
    • 2018-05-06
    • 2010-09-24
    • 2020-01-20
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多