仅按需从生成器中生成值
我希望从一个生成器中只获取所需数量的项目。
在下面的代码中
a, b, c = itertools.count()
我遇到了这个异常:
ValueError: too many values to unpack
我看到过几个相关的问题,但我对生成器中剩下的项目完全不感兴趣,我只想获取我所要求的数量,而不需要提前提供这个数量。
在我看来,Python 会先判断你想要多少个项目,但接着又试图读取并存储超过这个数量的项目(这就导致了 ValueError
)。
我该如何只获取我需要的数量,而不需要提前告诉它我想要多少个呢?
更新0
为了帮助理解我认为应该可以实现的行为,这里有一个代码示例:
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
如果你能改进这段代码,请随意。
Alex Martelli 的回答让我觉得字节操作 UNPACK_SEQUENCE
可能是限制的原因。我不明白为什么这个操作要求生成的序列长度也必须完全匹配。
请注意,Python 3 有不同的解包语法,这可能使得这个问题的技术讨论变得无效。
4 个回答
Python的工作方式可能和你想的不一样。在任何赋值操作中,比如说
a, b, c = itertools.count()
右边的内容会先被计算出来,之后才是左边的部分。右边的内容不知道左边有多少个东西,除非你告诉它。
你需要确保两边的项目数量是相等的。一个方法是使用 itertools模块中的 islice
。
from itertools import islice
a, b, c = islice(itertools.count(), 3)
你需要进行深层的字节码黑客操作——你想要的事情在Python源代码层面上是做不到的,但如果你愿意固定使用某个特定版本的Python,可能可以在Python编译完后对字节码进行后处理。比如说:
>>> 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
并没有得到任何提示(迭代器已经被调用过了!)——所以你必须在字节码中用一个“获取确切N个字节”的操作来前置它,比如:
>>> 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黑客和其他一些黑科技技巧,虽然你可能永远用不上,但知道这些技巧确实很酷。当你想要从一个普通的编程者成长为世界级的高手时,这些知识会让你受益匪浅——我怀疑那些想成为高手的人,通常都是对这种“语言内部机制”充满好奇的人……而那些真正能达到那种高水平的人,往往是那些明白这些努力只是“玩乐”的人,他们把这些当作业余活动来追求(就像举重一样的思维锻炼,类似于数独或填字游戏;-)),而不会让它们干扰到重要的任务(为用户提供价值,部署稳固、清晰、简单、性能良好、经过充分测试和良好文档的代码,通常完全不需要任何黑魔法的痕迹;-)。