【问题标题】:Python Yield Statement does not appear to continue where it left offPython Yield Statement 似乎没有从中断的地方继续
【发布时间】:2011-06-10 02:05:25
【问题描述】:

我一定忽略了显而易见的事情,但我终其一生都无法弄清楚为什么这个 yield 语句没有不断地给我一个比前一个晚 15 分钟的新日期时间值。 gettime 函数的行为更像是一个“返回”而不是“产生”的函数。

import datetime

#function that continually adds 15 minutes to a datetime object
def gettime(caldate):
    while True:
        yield caldate
        caldate += datetime.timedelta(minutes=15)

#initialize a datetime object
nextdate = datetime.datetime(2011, 8, 22, 11,0,0,0)

#call gettime function 25 times.
for i in range(0,25):
    print gettime(nextdate).next()


#output feels like it should be a series of incrementing datetime values 15 minutes apart.
#in actuality, the same result namely:

#2011-08-22 11:00:00

#happens 25 times.

【问题讨论】:

  • 代码中的第一条注释具有误导性。由于您使用关键字yield 这个def 块神奇地变成了一个生成器而不是一个函数,所以您可能希望将第一个单词更改为“生成器”

标签: python yield


【解决方案1】:

这是因为你每次都调用生成器,重新启动它。

这是一个固定版本:

dates = gettime(nextdate)
for i in range(0, 25):
    print dates.next()   # note that you're not initializing it each time here
                         # just calling next()

这给了我:

2011-08-22 11:00:00
2011-08-22 11:15:00
2011-08-22 11:30:00
2011-08-22 11:45:00
...etc.

要记住的重要一点是 yields 实际上返回一个生成器的函数,正如您在查看我的 dates 对象时看到的那样:

>>> dates
<generator object gettime at 0x02A05710>

这是您可以反复调用next() 以获取下一个值的方法。每次执行循环时,您都在创建一个全新的生成器并从中获取下一个(在本例中为第一个)值。

【讨论】:

  • .next() 已弃用(在 Python3 中变为 .__next__())。如果您的 Python 版本足够新以支持它(2.6+),您应该更喜欢使用next(dates)
  • 你甚至不需要next。只需迭代它,直到获得足够多的数据。
  • @gnibbler 是的,虽然我只是尽可能少地修改原件以显示真正的问题在哪里
【解决方案2】:

Daniel 已经指出,您每次都在通过循环创建一个新的生成器。循环一个生成器或让另一个生成器使用它比每次都显式调用next() 更为常见。

这里是你如何循环你的生成器的 islice()。

from itertools import islice
import datetime

#generator that continually adds 15 minutes to a datetime object
def gettime(caldate):
    while True:
        yield caldate
        caldate += datetime.timedelta(minutes=15)

#initialize a datetime object
nextdate = datetime.datetime(2011, 8, 22, 11,0,0,0)

#call gettime function 25 times.
for the_date in islice(gettime(nextdate),0,25):
    print the_date

如果您愿意,也可以将其简化为生成器表达式

from itertools import islice, count
import datetime

#initialize a datetime object
nextdate = datetime.datetime(2011, 8, 22, 11,0,0,0)

#generator expression that continually adds 15 minutes to a datetime object
gettime = (nextdate+datetime.timedelta(minutes=15*i) for i in count())

#call gettime function 25 times.
for the_date in islice(gettime,0,25):
    print the_date

【讨论】:

  • 这与 OP 有什么关系?
  • @Winston,这里如何展示如何循环生成器 not 相关。虽然有时需要next(),但循环生成器是使用它们的常用方法,有助于避免函数/生成器之间的混淆
  • 哦,我现在明白了。对不起。显然我不能撤回反对票。但是,您仍然没有解释他实际上错在哪里。
  • @Winston,很公平。我不想重复 Daniel 的回答,但我觉得指出正确使用生成器可以使您的代码更简单、更易于阅读很重要。我认为通过“常规方式”编写代码,函数和生成器之间的混淆不太可能发生。
【解决方案3】:

使用打印函数:

print(*[i[0] for i in zip(gettime(nextdate), range(25))], sep='\n')

但您可能只想要列表。

【讨论】:

    猜你喜欢
    • 2019-06-14
    • 2011-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-16
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多