【问题标题】:Python function to return a list or work as a generator?Python函数返回列表或作为生成器工作?
【发布时间】:2014-01-05 07:13:30
【问题描述】:

我正在尝试创建一个返回对象或用作生成器的函数。

这是一个坏主意,因为作为最佳实践,您希望函数可靠地返回相同类型的值,但为了科学的利益...

我使用的是 Python 2,因此 range 返回一个列表,而 xrange 是一个可迭代对象(有趣的是,它还提供了一个 __len__)。

def xr(start, stop=None, step=1, gen=True):
    if stop is None:
        start, stop = 0, start
    if gen == True:
        for i in xrange(start, stop, step):
            yield i
    else:
        return range(start, stop, step)

我得到这个错误:

  File "<stdin>", line 8
SyntaxError: 'return' with argument inside generator

问题:

为什么(除了明显的“你不能在一个函数中同时拥有 yield 和 return”,假设这是正确的)它会这样做?查看我的代码,并不清楚为什么这样做会不好。

我将如何尝试解决这个问题?我知道我可以返回 xrange 而不是从 xrange 中产生每个项目,因此我可以返回在另一个函数中创建的生成器,但是有更好的方法吗?

【问题讨论】:

    标签: python generator


    【解决方案1】:

    generator expression怎么样?

    >>> def xr(start, stop=None, step=1, gen=True):
    ...     if stop is None:
    ...         start, stop = 0, start
    ...     if gen == True:
    ...         return (i for i in xrange(start, stop, step)) # <----
    ...     else:
    ...         return range(start, stop, step)
    ...
    >>> xr(2, gen=False)
    [0, 1]
    >>> xr(2, gen=True)
    <generator object <genexpr> at 0x0000000002C1C828>
    >>> list(xr(2, gen=True))
    [0, 1]
    

    顺便说一句,我宁愿只定义一个生成器函数。如果需要列表,请使用list(xr(..))

    更新 或者,您可以使用iter(xrange(start, stop, step)) 代替@DSM 注释的生成器表达式。见Built-in functions -- iter

    【讨论】:

    • (i for i in xrange(start, stop, step)) 可以写成iter(xrange(start, stop, step))
    • @DSM,感谢您的评论。我根据你更新了答案。
    • 但是函数本身并不是一个生成器——它返回一个生成器。
    • @volcano:这篇文章并没有要求它成为生成器,而是“作为生成器工作”(在 Pythonic 的鸭子类型视图上就是这样无论如何都很重要)。
    • @volcano:是的,确实如此,因为当你调用它时gen=True 你得到的是一个生成器。
    【解决方案2】:

    falsetru 为您提供了一种方法来获得一个返回生成器或列表的函数。我正在回答你的另一个问题,即为什么你不能在同一个函数中有 return 和 yield。

    当您调用生成器函数时,它实际上并不会立即执行任何操作(请参阅this question)。它不执行函数体,而是等到您开始对其进行迭代(或在其上调用next)。因此,Python一开始就必须知道函数是否是生成器函数,当你调用它时,才能知道是否运行函数体。

    然后让它返回一些值是没有意义的,因为如果它是一个生成器函数,它返回的是生成器(即你迭代的东西)。如果您可以在生成器函数中返回,则在调用该函数时将无法到达,因此该函数必须在稍后的某个时间点“自发地”返回一个值(当到达 return 语句时生成器被消耗),这要么与当时产生一个值相同,要么是一些奇怪和令人困惑的东西。

    我同意 falsetru 的观点,即拥有一个有时返回生成器、有时返回列表的函数可能不是一个好主意。如果你想要一个列表,只需在生成器上调用list

    【讨论】:

    • 我同意,这不是最佳做法。以上是我在开始之前有一半预期会失败的实验的结果。但这就是学习,现在我总是认为它会失败。你有更规范的来源可以推荐我吗?
    • @AaronHall:生成器的基本描述在PEP 255PEP 342
    【解决方案3】:

    您不能同时拥有这两者,因为解释器 - 当它遇到关键字 yield - 将该函数视为生成器函数。显然,您不能在生成器函数中使用 return 语句。 附加点-您不直接使用生成器函数,而是使用它们来创建(我很想说实例化)生成器。 简单的例子

    def infinite():
        cnt = 1
        while True:
            yield cnt
            cnt += 1
    
    infinite_gen = infinite()
    

    infinite 是生成器函数,infinite_gen 是生成器

    【讨论】:

      猜你喜欢
      • 2014-03-17
      • 1970-01-01
      • 2017-06-10
      • 1970-01-01
      • 1970-01-01
      • 2017-03-17
      • 2018-05-20
      • 1970-01-01
      • 2013-07-24
      相关资源
      最近更新 更多