首页 > Python资料 博客日记
Python生成器的作用(python生成器和迭代器区别)
2023-07-30 16:57:16Python资料围观307次
在本文中,将学习如何使用Python生成器来创建迭代,了解它与迭代器和常规函数有什么区别,以及为什么要使用它。
在Python中构建迭代器有很多开销; 必须使用__iter__()
和__next__()
方法实现一个类,跟踪内部状态,当没有值被返回时引发StopIteration
异常。
Python生成器是创建迭代器的简单方法。上面提到的所有开销都由Python中的生成器自动处理。
简单来说,生成器是返回一个可以迭代的对象(迭代器)的函数(一次一个值)。
如何在Python中创建生成器?
在Python中创建生成器是相当简单的。 它使用yield
语句而不是return
语句来定义,与正常函数一样简单。
如果函数包含至少一个yield
语句(它可能包含其他yield
或return
语句),那么它将成为一个生成器函数。 yield
和return
都将从函数返回一些值。
不同的是,return
语句完全终止函数,但yield
语句会暂停函数保存其所有状态,并在以后的连续调用中继续执行(有点像线程挂起的意思)。
生成器函数与正常函数的差异
下面列出的是生成器函数与正常函数的区别 -
生成器函数包含一个或多个
yield
语句。当被调用时,它返回一个对象(迭代器),但不会立即开始执行。
__iter__()
和__next__()
之类的方法将自动实现。所以可以使用next()
迭代项目。一旦函数退让(
yields
),该函数将被暂停,并将该控制权交给调用者。局部变量及其状态在连续调用之间被记住。
最后,当函数终止时,
StopIteration
会在进一步的调用时自动引发。
下面的例子用来说明上述所有要点。 我们有一个名为my_gen()
的生成器函数和几个yield
语句。
#!/usr/bin/python3 #coding=utf-8 def my_gen(): n = 1 print('This is printed first, n= ', n) # Generator function contains yield statements yield n n += 1 print('This is printed second, n= ', n) yield n n += 1 print('This is printed at last, n= ', n) yield n
下面给出了交互式运行结果。 在Python shell中运行它们以查看输出 -
>>> # 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, n = 1 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, n = 2 2 >>> next(a) This is printed at last, n = 3 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
被引发时,它会自动结束。 请查看这里了解一个for循环是如何在Python中实际实现的。
# 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
具有循环的Python生成器
上面的例子没有什么用,我们研究它只是为了了解在后台发生了什么。通常,生成器功能用具有适当终止条件的循环实现。
我们举一个反转字符串的生成器的例子 -
length = len(my_str) for i in range(length - 1,-1,-1): yield my_str[i] # For loop to reverse the string # Output: # o # l # l # e # h for char in rev_str("hello"): print(char)def rev_str(my_str):
在这个例子中,使用range()
函数使用for
循环以相反的顺序获取索引。事实证明,这个生成函数不仅可以使用字符串,还可以使用其他类型的列表,元组等迭代。
Python生成器表达式
使用生成器表达式,可以轻松创建简单的生成器。 它使构建生成器变得容易。
与lambda
函数一样创建一个匿名函数,生成器表达式创建一个匿名生成函数。生成器表达式的语法与Python中的列表解析类似。 但方圆[]
替换为圆括号()
。
列表推导和生成器表达式之间的主要区别是:列表推导产生整个列表,生成器表达式一次生成一个项目。
它们是处理方式是懒惰的,只有在被要求时才能生产项目。 因此,生成器表达式的存储器效率高于等效列表的值。
# Initialize the list my_list = [1, 3, 6, 10] # square each term using list comprehension # Output: [1, 9, 36, 100] [x**2 for x in my_list] # same thing can be done using generator expression # Output: <generator object <genexpr> at 0x0000000002EBDAF8> (x**2 for x in my_list)
我们可以看到,生成器表达式没有立即生成所需的结果。 相反,它返回一个发生器对象,并根据需要生成项目。
# Intialize the list my_list = [1, 3, 6, 10] a = (x**2 for x in my_list) # Output: 1 print(next(a)) # Output: 9 print(next(a)) # Output: 36 print(next(a)) # Output: 100 print(next(a)) # Output: StopIteration next(a)
生成器表达式可以在函数内部使用。当以这种方式使用时,圆括号可以丢弃。
>>> sum(x**2 for x in my_list) 146 >>> max(x**2 for x in my_list) 100
为什么在Python中使用生成器?
有几个原因使得生成器成为有吸引力。
1. 容易实现
与其迭代器类相比,发生器可以以清晰简洁的方式实现。 以下是使用迭代器类来实现2
的幂次序的例子。
class PowTwo: def __init__(self, max = 0): self.max = max def __iter__(self): self.n = 0 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.管道生成器
生成器可用于管理一系列操作,下面使用一个例子说明。
假设我们有一个快餐连锁店的日志文件。 日志文件有一列(第4
列),用于跟踪每小时销售的比萨饼数量,我们想算出在5
年内销售的总萨饼数量。
假设一切都是字符串,不可用的数字标记为“N / A
”。 这样做的生成器实现可以如下。
with open('sells.log') as file: pizza_col = (line[3] for line in file) per_hour = (int(x) for x in pizza_col if x != 'N/A') print("Total pizzas sold = ",sum(per_hour))
这种管道的方式是更高效和易于阅读的。
标签:
相关文章
最新发布
- 【Python】selenium安装+Microsoft Edge驱动器下载配置流程
- Python 中自动打开网页并点击[自动化脚本],Selenium
- Anaconda基础使用
- 【Python】成功解决 TypeError: ‘<‘ not supported between instances of ‘str’ and ‘int’
- manim边学边做--三维的点和线
- CPython是最常用的Python解释器之一,也是Python官方实现。它是用C语言编写的,旨在提供一个高效且易于使用的Python解释器。
- Anaconda安装配置Jupyter(2024最新版)
- Python中读取Excel最快的几种方法!
- Python某城市美食商家爬虫数据可视化分析和推荐查询系统毕业设计论文开题报告
- 如何使用 Python 批量检测和转换 JSONL 文件编码为 UTF-8
点击排行
- 版本匹配指南:Numpy版本和Python版本的对应关系
- 版本匹配指南:PyTorch版本、torchvision 版本和Python版本的对应关系
- Python 可视化 web 神器:streamlit、Gradio、dash、nicegui;低代码 Python Web 框架:PyWebIO
- 相关性分析——Pearson相关系数+热力图(附data和Python完整代码)
- Python与PyTorch的版本对应
- Anaconda版本和Python版本对应关系(持续更新...)
- Python pyinstaller打包exe最完整教程
- Could not build wheels for llama-cpp-python, which is required to install pyproject.toml-based proj