Python 嵌套生成器

发布于 2024-11-16 07:54:01 字数 1373 浏览 2 评论 0原文

我试图在Python 2.7.1上实现itertools.izip的反向功能。问题是我发现了一个问题,但我没有解释。 解决方案 1,iunzip_v1 完美运行。但解决方案 2. iunzip_v2 无法按预期工作。到目前为止,我还没有找到任何关于这个问题的相关信息,并且阅读了有关生成器的 PEP,听起来它应该可以工作,但事实并非如此。

import itertools
from operator import itemgetter

def iunzip_v1(iterable):
    _tmp, iterable = itertools.tee(iterable, 2)
    iters = itertools.tee(iterable, len(_tmp.next()))
    return tuple(itertools.imap(itemgetter(i), it) for i, it in enumerate(iters))

def iunzip_v2(iterable):
    _tmp, iterable = itertools.tee(iterable, 2)
    iters = itertools.tee(iterable, len(_tmp.next()))
    return tuple((elem[i] for elem in it) for i, it in enumerate(iters))

结果:

In [17]: l
Out[17]: [(0, 0, 0), (1, 2, 3), (2, 4, 6), (3, 6, 9), (4, 8, 12)]

In [18]: map(list, iunzip.iunzip_v1(l))
Out[18]: [[0, 1, 2, 3, 4], [0, 2, 4, 6, 8], [0, 3, 6, 9, 12]]

In [19]: map(list, iunzip.iunzip_v2(l))
Out[19]: [[0, 3, 6, 9, 12], [0, 3, 6, 9, 12], [0, 3, 6, 9, 12]]

似乎 iunzip_v2 使用的是最后一个值,因此生成器在第一个生成器内创建时不会保留该值。 我错过了一些东西,但我不知道是什么。

如果有什么可以澄清我的情况,请提前致谢。

更新: 我在这里找到了解释 PEP-289 ,我第一次阅读是在 PEP-255。 我试图实现的解决方案是一种懒惰的解决方案,因此:

  zip(*iter) or izip(*...)

对我不起作用,因为 *arg 扩展了参数列表。

I was trying to implement the reverse function of itertools.izip on Python 2.7.1. The thing is that I find a problem, and I don't have an explantion.
Solution 1, iunzip_v1 works perfectly. But solution 2. iunzip_v2, doesn't works as expected. Til now, I haven't found any relevant information about this problem, and reading the PEP about generators, it sound it should work, but it doesn't.

import itertools
from operator import itemgetter

def iunzip_v1(iterable):
    _tmp, iterable = itertools.tee(iterable, 2)
    iters = itertools.tee(iterable, len(_tmp.next()))
    return tuple(itertools.imap(itemgetter(i), it) for i, it in enumerate(iters))

def iunzip_v2(iterable):
    _tmp, iterable = itertools.tee(iterable, 2)
    iters = itertools.tee(iterable, len(_tmp.next()))
    return tuple((elem[i] for elem in it) for i, it in enumerate(iters))

result:

In [17]: l
Out[17]: [(0, 0, 0), (1, 2, 3), (2, 4, 6), (3, 6, 9), (4, 8, 12)]

In [18]: map(list, iunzip.iunzip_v1(l))
Out[18]: [[0, 1, 2, 3, 4], [0, 2, 4, 6, 8], [0, 3, 6, 9, 12]]

In [19]: map(list, iunzip.iunzip_v2(l))
Out[19]: [[0, 3, 6, 9, 12], [0, 3, 6, 9, 12], [0, 3, 6, 9, 12]]

Seems that iunzip_v2 is using the last value, so the generators aren't keeping the value while they are created inside the first generator.
I'm missing something and I don't know what is.

Thanks in advance if something can clarify me this situation.

UPDATE:
I've found the explanation here PEP-289, my first read was at PEP-255.
The solution I'm trying to implement is a lazy one, so:

  zip(*iter) or izip(*...)

doesn't work for me, because *arg expand the argument list.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦巷 2024-11-23 07:54:01

你正在以一种疯狂的方式重新发明轮子。 izip 是它自己的逆:

>>> list(izip(*izip(range(10), range(10))))
[(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)]

但这并不能完全回答你的问题,不是吗?

嵌套生成器的问题是范围界定问题,这是因为最里面的生成器在最外面的生成器运行之前不会被使用:

def iunzip_v2(iterable):
    _tmp, iterable = itertools.tee(iterable, 2)
    iters = itertools.tee(iterable, len(_tmp.next()))
    return tuple((elem[i] for elem in it) for i, it in enumerate(iters))

在这里,您生成三个生成器,每个生成器使用相同的变量。不会创建此变量的副本。然后,tuple 耗尽最外层的生成器,创建一个生成器元组:

>>> iunzip_v2((range(3), range(3)))
(<generator object <genexpr> at 0x1004d4a50>, <generator object <genexpr> at 0x1004d4aa0>, <generator object <genexpr> at 0x1004d4af0>)

此时,每个生成器都将为它的每个元素执行 elem[i] 。由于所有三个生成器的 i 现在都等于 3,因此每次都会获得最后一个元素。

第一个版本起作用的原因是 itemgetter(i) 是一个闭包,有自己的作用域——所以每次它返回一个函数时,它都会生成一个新的作用域,其中 的值>i 不会改变。

You're reinventing the wheel in a crazy way. izip is its own inverse:

>>> list(izip(*izip(range(10), range(10))))
[(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)]

But that doesn't quite answer your question, does it?

The problem with your nested generators is a scoping problem that happens because the innermost generators don't get used until the outermost generator has already run:

def iunzip_v2(iterable):
    _tmp, iterable = itertools.tee(iterable, 2)
    iters = itertools.tee(iterable, len(_tmp.next()))
    return tuple((elem[i] for elem in it) for i, it in enumerate(iters))

Here, you generate three generators, each of which uses the same variable, i. Copies of this variable are not made. Then, tuple exhausts the outermost generator, creating a tuple of generators:

>>> iunzip_v2((range(3), range(3)))
(<generator object <genexpr> at 0x1004d4a50>, <generator object <genexpr> at 0x1004d4aa0>, <generator object <genexpr> at 0x1004d4af0>)

At this point, each of these generators will execute elem[i] for each element of it. And since i is now equal to 3 for all three generators, you get the last element each time.

The reason the first version works is that itemgetter(i) is a closure, with its own scope -- so every time it returns a function, it generates a new scope, within which the value of i does not change.

时光无声 2024-11-23 07:54:01

好吧,这有点棘手。当您使用像 i 这样的名称时,它所代表的值将在运行时查找。在此代码中:

return tuple((elem[i] for elem in it) for i, it in enumerate(iters))

您返回多个生成器 (其中 elem[i] 表示 elem),并且每个生成器都使用相同的名称 i。当函数返回时,tuple( .. for i in .. ) 中的循环已结束,i 已设置为其最终值 (3代码> 在你的例子中)。一旦您将这些生成器评估为列表,它们都会创建相同的值,因为它们使用相同的 i

顺便提一句:

unzip = lambda zipped: zip(*zipped) 

Ok this is a bit tricky. When you use a name like i the value it stands for is looked up just during runtime. In this code:

return tuple((elem[i] for elem in it) for i, it in enumerate(iters))

you return a number of generators, (elem[i] for elem in it) and each of them uses the same name i. When the function returns, the loop in tuple( .. for i in .. ) has ended and i has been set to it's final value (3 in your example). Once you evaluate these generators to lists, they all create the same values because they are using the same i.

Btw:

unzip = lambda zipped: zip(*zipped) 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文