发电量仅为发电机所需的量

发布于 2024-09-06 03:59:01 字数 953 浏览 8 评论 0原文

我希望仅根据需要的物品数量从生成器中获得收益。

在以下代码中

a, b, c = itertools.count()

,我收到此异常:

ValueError: too many values to unpack

我已经看到了几个相关问题,但是我对生成器的剩余项目兴趣为零,我只希望收到我要求的数量,而不提供该数量前进。

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

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

Update0

为了帮助理解我认为应该可能的近似行为,这里有一个代码示例:

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

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

亚历克斯·马泰利的答案向我建议字节操作 UNPACK_SEQUENCE 负责限制。我不明白为什么这个操作应该要求生成的序列的长度也必须完全匹配。

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

I wish to yield from a generator only as many items are required.

In the following code

a, b, c = itertools.count()

I receive this exception:

ValueError: too many values to unpack

I've seen several related questions, however I have zero interest in the remaining items from the generator, I only wish to receive as many as I ask for, without providing that quantity in advance.

It seems to me that Python determines the number of items you want, but then proceeds to try to read and store more than that number (generating ValueError).

How can I yield only as many items as I require, without passing in how many items I want?

Update0

To aid in understanding the approximate behaviour I believe should be possible, here's a code sample:

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

If you can improve this code please do.

Alex Martelli's answer suggests to me the byte op UNPACK_SEQUENCE is responsible for the limitation. I don't see why this operation should require that generated sequences must also exactly match in length.

Note that Python 3 has different unpack syntaxes which probably invalidate technical discussion in this question.

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

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

发布评论

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

评论(4

债姬 2024-09-13 03:59:01

您需要深入的字节码破解 - 您的请求无法在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 字节码“快速加载;调用函数”序列(就在 unpack-sequence 字节码之前)必须变成这种 5 字节码序列,并且之前有一个 limit 的加载全局字节码原始序列和 load-const;在其之后调用函数 序列。

您当然可以在装饰器中实现此字节码修改。

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

这对于生产用途来说值得吗?当然,绝对不是——为了一个微小的“语法糖”改进而付出的工作量是荒谬的。然而,它可能是一个有启发性的项目,可以帮助你掌握字节码黑客、ast 黑客和其他黑魔法,你可能永远不需要这些技巧,但当你想要超越单纯的语言向导的语言时,知道这些技巧肯定很酷。世界级大师的——事实上,我怀疑那些有动力成为大师的人通常是无法不屈服于这种“语言内部机制”的迷恋的人......以及那些实际上达到崇高水平的人是足够聪明的人,他们意识到这种努力“只是玩”,并将其作为业余活动来追求(精神上相当于举重,就像数独或填字游戏; -) 不让他们干扰重要的任务(通过部署可靠、清晰、简单、性能良好、经过充分测试、记录良好的代码为用户提供价值,通常没有哪怕是最轻微的提示黑魔法的作用;-)。

You need a deep bytecode hack - what you request cannot be done at Python source code level, but (if you're willing to commit to a specific version and release of Python) may be doable by post-processing the bytecode after Python has compiled it. Consider, e.g.:

>>> 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        
>>> 

As you see, the UNPACK_SEQUENCE 3 bytecode occurs without the iterator someit being given the least indication of it (the iterator has already been called!) -- so you must prefix it in the bytecode with a "get exactly N bytes" operation, e.g.:

>>> 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        

where limit is your own function, easily implemented (e.g via itertools.slice). So, the original 2-bytecode "load fast; call function" sequence (just before the unpack-sequence bytecode) must become this kind of 5-bytecode sequence, with a load-global bytecode for limit before the original sequence, and the load-const; call function sequence after it.

You can of course implement this bytecode munging in a decorator.

Alternatively (for generality) you could work on altering the function's original source, e.g. via parsing and alteration of the AST, and recompiling to byte code (but that does require the source to be available at decoration time, of course).

Is this worth it for production use? Absolutely not, of course -- ridiculous amount of work for a tiny "syntax sugar" improvement. However, it can be an instructive project to pursue to gain mastery in bytecode hacking, ast hacking, and other black magic tricks that you will probably never need but are surely cool to know when you want to move beyond the language of mere language wizard to that of world-class guru -- indeed I suspect that those who are motivated to become gurus are typically people who can't fail to yield to the fascination of such "language internals' mechanics"... and those who actually make it to that exalted level are the subset wise enough to realize such endeavors are "just play", and pursue them as a spare-time activity (mental equivalent of weight lifting, much like, say, sudoku or crosswords;-) without letting them interfere with the important tasks (delivering value to users by deploying solid, clear, simple, well-performing, well-tested, well-documented code, most often without even the slightest hint of black magic to it;-).

尾戒 2024-09-13 03:59:01

您需要确保双方的物品数量匹配。一种方法是使用 itertools 模块中的 islice

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

You need to make sure that the number of items on both sides matches. One way is to use islice from the itertools module:

from itertools import islice
a, b, c = islice(itertools.count(), 3)
我是男神闪亮亮 2024-09-13 03:59:01

Python 不会按照你想要的方式工作。在任何作业中,例如

a, b, c = itertools.count()

首先评估右侧,在左侧之前。
除非你告诉它,否则右侧无法知道左侧有多少项。

Python does not work the way you desire. In any assignment, like

a, b, c = itertools.count()

the right-hand side is evaluated first, before the left-hand side.
The right-hand side can not know how many items are on the left-hand side unless you tell it.

自我难过 2024-09-13 03:59:01

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

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

或者更简单:

a,b,c = range(3)

Or use a list comprehension, since you know the desired number of items:

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

or even simpler:

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