Python 教程中的 Yield:生成器和 Yield 与 Return 示例

Python yield 是什么?

Python 中的 yield 关键字的工作方式类似于 return,唯一的

区别在于,它不是返回一个值,而是将一个生成器对象返回给调用者。

当调用一个函数,并且执行流遇到函数中的 yield 关键字时,函数执行将在该行停止,并将生成器对象返回给调用者。

语法

yield expression

描述

Python yield 返回一个生成器对象。生成器是特殊的函数,必须迭代才能获得值。

yield 关键字将给定的表达式转换为生成器函数,该函数返回一个生成器对象。要获取对象的值,必须对其进行迭代才能读取给 yield 的值。

示例:Yield 方法

这是一个简单的 yield 示例。testyield() 函数包含一个 yield 关键字,值为“Welcome to Guru99 Python Tutorials”。当调用函数时,输出将被打印,并且它会返回一个生成器对象而不是实际值。

def testyield():
  yield "Welcome to Guru99 Python Tutorials"
output = testyield()
print(output)

输出

<generator object testyield at 0x00000028265EB9A8>

输出的是一个生成器对象,其中包含我们赋给 yield 的值。

但是我们没有在输出中看到我们赋给 yield 的消息!

要打印赋给 yield 的消息,您需要迭代生成器对象,如下面的示例所示。

def testyield():
  yield "Welcome to Guru99 Python Tutorials"

output = testyield()
for i in output:
    print(i)

输出

Welcome to Guru99 Python Tutorials

Python 中的生成器是什么?

生成器是返回可迭代生成器对象的函数。生成器对象中的值是一次获取一个,而不是一次性获取整个列表,因此要获取实际值,可以使用 for 循环,使用 next() 或 list() 方法。

使用生成器函数

您可以使用生成器函数和生成器表达式来创建生成器。

生成器函数就像一个普通函数,但它没有返回值,而是有一个 yield 关键字。

要创建生成器函数,您需要添加一个 yield 关键字。以下示例显示了如何创建生成器函数。

def generator():
    yield "H"
    yield "E"
    yield "L"
    yield "L"
    yield "O"

test = generator()
for i in test:
    print(i)

输出

H
E
L
L
O

普通函数与生成器函数的区别。

让我们了解生成器函数与普通函数的区别。

有两个函数 normal_test() 和 generator_test()。

这两个函数都应该返回字符串“Hello World”。normal_test() 使用 return,而 generator_test() 使用 yield。

# Normal function
def normal_test():
    return "Hello World"
	
#Generator function
def generator_test():
	yield "Hello World"
print(normal_test()) #call to normal function
print(generator_test()) # call to generator function

输出

Hello World
<generator object generator_test at 0x00000012F2F5BA20>

输出显示,当您调用普通函数 normal_test() 时,它会返回 Hello World 字符串。对于带有 yield 关键字的生成器函数,它返回 <generator object generator_test at 0x00000012F2F5BA20> 而不是字符串。

这是生成器函数和普通函数之间的主要区别。现在,要从生成器对象中获取值,我们需要在 for 循环中使用该对象,或者使用 next() 方法,或者使用 list()。

print(next(generator_test()))  # will output Hello World

普通函数与生成器函数还有另一个区别,即当您调用普通函数时,执行将开始,并在遇到 return 时停止,然后将该值返回给调用者。因此,当执行开始时,您无法在中间停止普通函数,它只会停止在遇到 return 关键字时。

但是对于生成器函数,一旦执行开始,当它遇到第一个 yield 时,它会停止执行,并将生成器对象返回给调用者。您可以使用生成器对象来获取值,还可以根据需要暂停和恢复执行。

如何从生成器读取值?

您可以使用 list()、for 循环和 next() 方法从生成器对象读取值。

使用: list()

列表是一个可迭代对象,其元素包含在括号内。对生成器对象使用 list() 将获得生成器拥有的所有值。

def even_numbers(n):
    for x in range(n):
       if (x%2==0): 
           yield x       
num = even_numbers(10)
print(list(num))

输出

[0, 2, 4, 6, 8]

使用: for-in

在示例中,有一个名为 even_numbers() 的函数,它将为您提供 n 定义的所有偶数。对 even_numbers() 函数的调用将返回一个生成器对象,该对象在 for 循环中使用。

示例

def even_numbers(n):
    for x in range(n):
       if (x%2==0): 
           yield x       
num = even_numbers(10)
for i in num:
    print(i)

输出

0
2
4
6
8

使用 next()

next() 方法将为您提供列表、数组或对象中的下一个项。一旦列表为空,如果调用 next(),它将返回一个错误,并带有 stopIteration 信号。此错误来自 next(),表示列表中没有更多项了。

def even_numbers(n):
    for x in range(n):
       if (x%2==0): 
           yield x       
num = even_numbers(10)
print(next(num))
print(next(num))
print(next(num))
print(next(num))
print(next(num))
print(next(num))

输出

0
2
4
6
8
Traceback (most recent call last):
  File "main.py", line 11, in <module>
    print(next(num))
StopIteration

生成器是一次性使用的

对于生成器,它们只能使用一次。如果您再次尝试使用它们,它们将是空的。

例如

def even_numbers(n):
    for x in range(n):
       if (x%2==0): 
           yield x       
num = even_numbers(10)
for i in num:
    print(i)

print("\n")
print("Calling the generator again: ", list(num))

输出

0
2
4
6
8
Calling the generator again:  []

如果您希望再次使用输出,您将需要再次调用该函数。

示例:生成器和 yield 用于 Fibonacci 序列

以下示例展示了如何在 Python 中使用生成器和 yield。该示例将生成 Fibonacci 序列。

def getFibonnaciSeries(num):
    c1, c2 = 0, 1
    count = 0
    while count < num:
        yield c1
        c3 = c1 + c2
        c1 = c2
        c2 = c3
        count += 1
fin = getFibonnaciSeries(7)
print(fin)
for i in fin:
    print(i)

输出

<generator object getFibonnaciSeries at 0x0000007F39C8BA20>
0
1
1
2
3
5
8

示例:调用带 yield 的函数

在此示例中,我们将看到如何调用带 yield 的函数。

下面的示例有一个名为 test() 的函数,它返回给定数字的平方。还有一个名为 getSquare() 的函数,它使用带 yield 关键字的 test()。输出会给出给定数字范围的平方值。

def test(n):
    return n*n

def getSquare(n):
    for i in range(n):
        yield test(i)

sq = getSquare(10)
for i in sq:
    print(i)

输出

0
1
4
9
16
25
36
49
64
81

何时在 Python 中使用 Yield 而不是 Return

Python3 Yield 关键字向调用者返回一个生成器,代码的执行仅在生成器被迭代时开始。

函数中的 return 是函数执行的结束,并向调用者返回单个值。

以下是您应该使用 Yield 而不是 Return 的情况

  • 当数据量很大时,请使用 yield 而不是 return
  • 当您希望在大型数据集上提高执行速度时,yield 是最佳选择。
  • 当您想将大量值返回给调用函数时,请使用 yield。
  • Yield 是一种高效的生成大数据或无限数据的方式。

Yield 与 Return

以下是 Yield 和 Return 之间的区别

Yield 退货
Yield 向调用者返回一个生成器对象,代码的执行仅在生成器被迭代时开始。 函数中的 return 是函数执行的结束,并向调用者返回单个值。
当调用函数并遇到 yield 关键字时,函数执行会停止。它将生成器对象返回给调用者。函数执行仅在生成器对象执行时才开始。 当调用函数时,执行开始,如果存在 return 关键字,则将值返回给调用者。函数中的 return 标记着函数执行的结束。
yield 表达式 return 表达式
使用 yield 关键字时,不使用内存。 为返回的值分配内存。
如果您需要处理大量数据,这将非常有用,因为不使用内存。 对于非常小的数据集来说很方便。
如果使用 yield 关键字处理大数据,性能会更好。 如果数据量很大,会消耗大量内存,从而影响性能。
对于大数据,yield 的执行时间更快。 执行时间更长,因为对于大数据集会进行额外的处理,如果数据集很小,它会正常工作。

摘要

  • Python 中的 yield 关键字的工作方式类似于 return,唯一的区别是它不返回一个值,而是将一个生成器函数返回给调用者。
  • 生成器是一种特殊的迭代器,一旦使用,将不再可用。这些值不存储在内存中,只有在调用时才可用。
  • 可以通过 for-in、list() 和 next() 方法从生成器读取值。
  • yield 和 return 的主要区别在于,yield 向调用者返回一个生成器函数,而 return 向调用者返回一个单个值。
  • Yield 不会在内存中存储任何值,其优点在于处理大数据集时非常有用,因为不会存储任何值。
  • 与 return 相比,如果使用 yield 关键字处理大数据,性能会更好。