Python 生成器

什么是 Python 生成器?

Python 生成器是返回遍历对象的函数,用于创建迭代器。它一次遍历整个项目。生成器也可以是语法类似于 Python 中列表推导的表达式。

在 Python 中创建迭代有很大的复杂性;我们需要实现 __iter__()__next__() 方法来跟踪内部状态。

创建迭代器是一个漫长的过程。这就是为什么生成器在简化这一过程中起着至关重要的作用。如果迭代中没有发现值,则引发停止迭代异常。

如何用 Python 创建生成器函数?

用 Python 创建一个生成器非常简单。类似于 def 关键字定义的正常函数,使用 yield 关键字代替 return。或者我们可以说,如果任何函数的主体包含一个 yield 语句,那么它就会自动成为一个生成函数。考虑以下示例:


def simple():
for i in range(10):
if(i%2==0):
yield i

#Successive Function call using for loop
for i in simple():
print(i)

输出:

0
2
4
6
8

yield vs return

yield 语句负责控制生成器功能的流程。它通过保存所有状态暂停函数执行,并向调用者让步。稍后,当调用一个连续的函数时,它将恢复执行。我们可以在生成器函数中使用多重 yield语句。

return 语句返回一个值,终止整个函数,函数中只能使用一个 return 语句。

多重 yield

我们可以在生成器函数中使用多重 yield语句。考虑下面的例子。


def multiple_yield():
str1 = "First String"
yield str1

str2 = "Second string"
yield str2

str3 = "Third String"
yield str3
obj = multiple_yield()
print(next(obj))
print(next(obj))
print(next(obj))

输出:

First String
Second string
Third String

生成器函数和普通函数的区别

  • 普通函数只包含一个 L 返回语句,而生成器函数可以包含一个或多个返回语句。

  • 当调用生成器函数时,正常函数会立即暂停,并将控制权转移给调用方。

  • 局部变量及其状态在连续调用之间被记住。

  • 当函数终止时,会自动引发 StopIteration 异常。

生成器表达式

我们可以轻松创建生成器表达式,而无需使用用户定义的函数。它与创建匿名函数的 lambda 函数相同;生成器的表达式创建匿名生成器函数。

生成器表达式的表示类似于 Python 列表推导。唯一不同的是方括号被圆括号代替。列表推导计算整个列表,而生成器表达式一次计算一个项目。

考虑以下示例:


list = [1,2,3,4,5,6,7]

# List Comprehension
z = [x**3 for x in list]

# Generator expression
a = (x**3 for x in list)

print(a)
print(z)

输出:

<generator object <genexpr> at 0x01BA3CD8>
[1, 8, 27, 64, 125, 216, 343]

在上面的程序中,列表推导返回了元素立方体的列表,而生成器表达式返回了计算值的引用。我们也可以在生成器对象上调用 next() ,而不是对循环应用**。让我们考虑另一个例子:**


list = [1,2,3,4,5,6]

z = (x**3 for x in list)

print(next(z))

print(next(z))

print(next(z))

print(next(z))

输出:

1
8
27
64

注意:-当我们调用 next()时,Python 会对我们作为参数传递的函数调用 next()函数。

在上面的程序中,我们使用了 next() 函数,该函数返回列表的下一项。

**示例:**编写一个程序,使用生成器打印给定数字的表格。


def table(n):
for i in range(1,11):
yield n*i
i = i+1

for i in table(15):
print(i)

输出:

15
30
45
60
75
90
105
120
135
150

在上面的例子中,生成器函数使用 for循环进行迭代。

生成器的优点

生成器有各种各样的优点。下面给出了几个例子:

1.易于实现

与迭代器相比,生成器很容易实现。在迭代器中,我们必须实现**__ITER __()__next__()**函数。

2.内存高效

对于大量序列,生成器具有内存效率。normal 函数返回一个列表序列,在返回结果之前在内存中创建一个完整的序列,但是 generator 函数计算值并暂停它们的执行。它继续连续呼叫。无限序列生成器是内存优化的一个很好的例子。下面我们用 sys.getsizeof() 函数来讨论一下。


import sys
# List comprehension
nums_squared_list = [i * 2 for i in range(1000)]
print(sys.getsizeof("Memory in Bytes:"nums_squared_list))
# Generator Expression
nums_squared_gc = (i ** 2 for i in range(1000))
print(sys.getsizeof("Memory in Bytes:", nums_squared_gc))

输出:

Memory in Bytes: 4508
Memory in Bytes: 56

我们可以从上面的输出中观察到,列表推导使用了 4508 字节的内存,而生成器表达式使用了 56 字节的内存。这意味着生成器对象比列表压缩效率高得多。

3.Pipelining with Generators

数据管道提供了在不使用额外计算机内存的情况下处理大型数据集或数据流的工具。

假设我们有一个著名餐厅的日志文件。日志文件中有一列(4 列)记录了每小时售出的汉堡数量,我们想对其求和,找出 4 年内售出的汉堡总数。在这种情况下,生成器可以生成包含一系列操作的管道。下面是它的代码:


with open('sells.log') as file:
burger_col = (line[3] for line in file) per_hour = (int(x) for x in burger_col if x != 'N/A')
print("Total burgers sold = ",sum(per_hour))

4.Generate Infinite Sequence

生成器可以生产无限多的物品。内存中不能包含无限序列,由于生成器一次只能生成一个项目,请考虑以下示例:


def infinite_sequence():
num = 0
while True:
yield num
num += 1

for i in infinite_sequence():
print(i)

输出:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.........
..........
315
316
317
Traceback (most recent call last):
File "C:\Users\DEVANSH SHARMA\Desktop\generator.py", line 33, in <module>
print(i)
KeyboardInterrupt

在本教程中,我们已经了解了 Python 生成器。

原文:https://www.javatpoint.com/python-generators