python中的生成器
2022/1/13 20:04:26
本文主要是介绍python中的生成器,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1 生成器简介
在Python中构建迭代器必须实现一个带有 iter() 和 next() 方法的类,跟踪内部状态,并在没有返回值时引发 StopIteration。
Python 生成器是创建迭代器的一种简单方法。
简单地说,生成器是一个函数,它返回一个对象(迭代器),可以迭代(一次一个值)。
2 创建生成器
在Python中创建生成器相当简单。它与定义普通函数一样简单,但使用yield语句而不是return语句。
如果一个函数至少包含一条yield语句(它可能包含其他yield或return语句),那么它将成为一个生成器函数。
yield和return都将从函数中返回一些值。不同之处在于,return语句完全终止函数,而yield语句则暂停函数保存其所有状态,然后在后续调用中继续执行。
3 生成器函数和普通函数之间的区别
下面是生成器函数与普通函数的区别:
- 生成器函数包含一个或多个 yield 语句;
- 调用时,它返回一个对象(迭代器),但不会立即开始执行;
- 像 iter() 和 next() 这样的方法是自动实现的。因此,可以使用 next() 遍历这些项;
- 一旦该函数yield,该函数将暂停,并将控制权转移给调用者;
- 局部变量及其状态在连续调用之间会被记住;
- 最后,当函数终止时,StopIteration 会在进一步调用时自动引发。
下面是一个例子来说明上面提到的所有要点。有一个名为 my_gen() 的生成器函数,它有几个 yield 语句。
# A simple generator function def my_gen(): n = 1 print('This is printed first') # Generator function contains yield statements yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n
运行:
>>> # It returns an object but does not start execution immediately. >>> a = my_gen() >>> # We can iterate through the items using next(). >>> next(a) This is printed first 1 >>> # Once the function yields, the function is paused and the control is transferred to the caller. >>> # Local variables and theirs states are remembered between successive calls. >>> next(a) This is printed second 2 >>> next(a) This is printed at last 3 >>> # Finally, when the function terminates, StopIteration is raised automatically on further calls. >>> next(a) Traceback (most recent call last): ... StopIteration >>> next(a) Traceback (most recent call last): ... StopIteration
在上面的例子中有趣的是,在每次调用之间记住变量n 的值。
这与普通函数不同,当生成函数产生时,局部变量不会被销毁。而且,生成器对象只能迭代一次。
要重新启动该进程,需要使用 a = my_gen() 之类的东西创建另一个生成器对象。
最后要注意的是,可以直接使用 for循环 的生成器。
这是因为 for循环 接受一个迭代器,并使用 next()函数 对其进行迭代。当 StopIteration 被引发时,它会自动结束。
# A simple generator function def my_gen(): n = 1 print('This is printed first') # Generator function contains yield statements yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n # Using for loop for item in my_gen(): print(item)
输出:
This is printed first 1 This is printed second 2 This is printed at last 3
4 带有循环的生成器
通常,生成器函数是用一个具有适当终止条件的循环来实现的。
看一个生成器的例子,它可以反转一个字符串
def rev_str(my_str): length = len(my_str) for i in range(length - 1, -1, -1): yield my_str[i] # For loop to reverse the string for char in rev_str("hello"): print(char)
输出:
o l l e h
在这个例子中,使用了 range()函数 来使用 for循环 逆序获取索引。
注:这个生成器函数不仅可以处理字符串,还可以处理其他类型的可迭代对象,如list、tuple等。
5 生成器表达式
使用生成器表达式可以轻松地动态创建简单的生成器。它使得构建生成器变得容易。
与创建匿名函数的lambda函数类似,生成器表达式也创建匿名生成器函数。
生成器表达式的语法类似于Python中的列表推导式。但是方括号换成了圆括号。
列表推导式和生成器表达式之间的主要区别是:
- 列表推导式生成整个列表,而生成器表达式每次生成一项。
- 生成器表达式具有惰性执行(只在请求时生成项)。
因此,生成器表达式比等效的列表推导式的内存效率高得多。
# Initialize the list my_list = [1, 3, 6, 10] # square each term using list comprehension list_ = [x**2 for x in my_list] # same thing can be done using a generator expression # generator expressions are surrounded by parenthesis () generator = (x**2 for x in my_list) print(list_) print(generator)
输出:
[1, 9, 36, 100] <generator object <genexpr> at 0x7f5d4eb4bf50>
上面可以看到,生成器表达式并没有立即生成所需的结果。相反,它返回一个生成器对象,该对象只根据需要生成项。
下面是如何从生成器中获取项:
# Initialize the list my_list = [1, 3, 6, 10] a = (x**2 for x in my_list) print(next(a)) print(next(a)) print(next(a)) print(next(a)) next(a)
输出:
1 9 36 100 Traceback (most recent call last): File "<string>", line 15, in <module> StopIteration
生成器表达式可以用作函数参数。这样使用时,可以省略圆括号。
>>> sum(x**2 for x in my_list) 146 >>> max(x**2 for x in my_list) 100
6 生成器的使用
生成器如此强大的原因:
1. 容易实现
与迭代器类相比,生成器可以以一种清晰和简洁的方式实现。下面是一个使用迭代器类实现幂次为2的序列的示例:
class PowTwo: def __init__(self, max=0): self.n = 0 self.max = max def __iter__(self): return self def __next__(self): if self.n > self.max: raise StopIteration result = 2 ** self.n self.n += 1 return result
上面的程序冗长且令人困惑。现在,用一个生成器函数来做同样的事情。
def PowTwoGen(max=0): n = 0 while n < max: yield 2 ** n n += 1
因为生成器可以自动跟踪细节,所以实现更加简洁明了。
2. 节约内存
返回序列的普通函数将在返回结果之前在内存中创建整个序列。如果序列中的条目数量非常大,那么这就有点过分了。
这种序列的生成器实现是内存友好的,是首选的,因为它一次只产生一个项。
3. 代表无限数据流
生成器是代表无限数据流的优秀媒介。无限的数据流不能存储在内存中,而且由于生成器每次只能产生一个项,所以它们可以表示无限的数据流。
下面的生成器函数可以生成所有的偶数(至少在理论上)。
def all_even(): n = 0 while True: yield n n += 2
4. 管道生成器
可以使用多个生成器对一系列操作进行流水线处理。
假设有一个生成器可以生成斐波那契数列中的数字。我们有另一个平方数生成器。
如果想要求出斐波那契数列中数字的平方和,可以通过将生成器函数的输出流水线化来实现。
def fibonacci_numbers(nums): x, y = 0, 1 for _ in range(nums): x, y = y, x+y yield x def square(nums): for num in nums: yield num**2 print(sum(square(fibonacci_numbers(10))))
输出:
4895
这种流水线是高效的,易于阅读的。
参考:programiz
这篇关于python中的生成器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-21Python编程基础教程
- 2024-11-20Python编程基础与实践
- 2024-11-20Python编程基础与高级应用
- 2024-11-19Python 基础编程教程
- 2024-11-19Python基础入门教程
- 2024-11-17在FastAPI项目中添加一个生产级别的数据库——本地环境搭建指南
- 2024-11-16`PyMuPDF4LLM`:提取PDF数据的神器
- 2024-11-16四种数据科学Web界面框架快速对比:Rio、Reflex、Streamlit和Plotly Dash
- 2024-11-14获取参数学习:Python编程入门教程
- 2024-11-14Python编程基础入门