关于python:只从生成器中获得所需的数量

Yield only as many are required from a generator

我希望从一台发电机上让步,只要有多少项目是必需的。

在以下代码中

1
a, b, c = itertools.count()

我收到这个例外:

1
ValueError: too many values to unpack

我已经看到几个相关的问题,但是我对发电机的剩余项目没有兴趣,我只希望收到我要求的数量,而不预先提供数量。

在我看来,python决定了您想要的项目的数量,但随后继续尝试读取和存储超过这个数量的项目(生成ValueError)。

我怎样才能只生产我所需要的数量的物品,而不传递我想要的数量?

更新0

为了帮助理解我认为可能的近似行为,下面是一个代码示例:

1
2
3
4
5
6
7
def unpack_seq(ctx, items, seq):
    for name in items:
        setattr(ctx, name, seq.next())

import itertools, sys
unpack_seq(sys.modules[__name__], ["a","b","c"], itertools.count())
print a, b, c

如果您可以改进此代码,请这样做。

亚历克斯·马泰利的回答告诉我,字节op UNPACK_SEQUENCE负责限制。我不明白为什么这个操作需要生成的序列在长度上也必须完全匹配。

注意,python 3有不同的解包语法,这可能使本问题中的技术讨论无效。


您需要一个深入的字节码黑客——您请求的不能在Python源代码级别上完成,但是(如果您愿意提交到特定的Python版本和发行版),在Python编译后,通过对字节码进行后处理可能是可行的。考虑,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> def f(someit):
...   a, b, c = someit()
...
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (someit)
              3 CALL_FUNCTION            0
              6 UNPACK_SEQUENCE          3
              9 STORE_FAST               1 (a)
             12 STORE_FAST               2 (b)
             15 STORE_FAST               3 (c)
             18 LOAD_CONST               0 (None)
             21 RETURN_VALUE        
>>>

如您所见,出现UNPACK_SEQUENCE 3字节码时,迭代器someit的指示最少(迭代器已经被调用!)--因此,您必须在字节码中为其加前缀,并执行"get-exactly n bytes"操作,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> def g(someit):
...   a, b, c = limit(someit(), 3)
...
>>> dis.dis(g)
  2           0 LOAD_GLOBAL              0 (limit)
              3 LOAD_FAST                0 (someit)
              6 CALL_FUNCTION            0
              9 LOAD_CONST               1 (3)
             12 CALL_FUNCTION            2
             15 UNPACK_SEQUENCE          3
             18 STORE_FAST               1 (a)
             21 STORE_FAST               2 (b)
             24 STORE_FAST               3 (c)
             27 LOAD_CONST               0 (None)
             30 RETURN_VALUE

其中limit是您自己的功能,易于实现(例如通过itertools.slice实现)。因此,原来的2字节码"快速加载;调用函数"序列(就在解包序列字节码之前)必须成为这种5字节码序列,在原来的序列之前为limit,之后为load-const; call function序列。

当然,您可以在修饰符中实现这个字节码。

或者(为了通用性)您可以修改函数的原始源代码,例如通过解析和修改ast,然后重新编译为字节代码(当然,这需要源代码在修饰时可用)。

这值得用于生产吗?当然,绝对不是——为小小的"语法糖"改进做了大量的工作。然而,要想掌握字节码黑客、AST黑客和其他你可能永远不需要的黑魔法把戏,这可能是一个很有指导意义的项目,但当你想从单纯的语言巫师的语言发展到世界级的古鲁语时,这无疑是一个很酷的主意——我怀疑那些有动机成为古鲁语的人我们通常都是那些不能不屈服于这种"语言内部"机制的魅力的人……而那些真正达到这一高度的人则是足够聪明的一个子集,他们意识到这些努力是"只是玩",并把它们作为业余活动(精神上相当于举重,比如数独或纵横字谜;-)而不让它们干扰重要的任务(通过部署坚实的、清晰的、清晰的r,简单、性能良好、测试良好、文档记录良好的代码,大多数情况下甚至没有任何黑色魔力的提示;-)。


你需要确保两边的物品数量相匹配。一种方法是使用ITertools模块中的islice:

1
2
from itertools import islice
a, b, c = islice(itertools.count(), 3)


Python的工作方式不符合你的要求。在任何任务中,比如

1
a, b, c = itertools.count()

首先评估右侧,然后评估左侧。右手边不知道左手边有多少东西,除非你说出来。


或者使用列表理解,因为您知道所需的项目数:

1
2
ic = itertools.count()
a,b,c = [ic.next() for i in range(3)]

或者更简单:

1
a,b,c = range(3)