生成器使您可以轻松地在Python中创建迭代,从而减少编写代码。 本教程将向您介绍Python生成器,它们的好处以及它们的工作方式。
基本
生成器是一个返回生成器对象的函数,您可以在该对象上调用next()
方法,这样,每次调用它都会返回一个值或下一个值。 普通的Python函数使用return
关键字返回值,但是生成器使用关键字yield
返回值。 这意味着任何包含yield
语句的Python函数都是生成器函数。
yield
语句通常会暂停该函数并保存局部状态,以便可以从中断处继续恢复。 生成器函数可以具有一个或多个yield
语句。
生成器也是迭代器,但是什么是迭代器? 在我们深入讨论生成器之前,我认为了解迭代器是很重要的,因为它们构成了本次讨论的组成部分。
Python迭代器
Python迭代器只是定义__iter__()
方法的类。 大多数Python对象都是可迭代的,这意味着您可以遍历对象中的每个元素。 Python中可迭代的示例包括字符串,列表,元组,字典和范围。
让我们考虑下面的示例,其中我们遍历颜色列表:
colors= [“red”,”blue”,”yellow”]
def my_funct():
for color in colors:
print color
在幕后, for
语句将在列表对象上调用iter()
。 然后,该函数将返回一个迭代器对象,该对象定义了__next__()
方法,该方法随后将一次访问每种颜色。 当没有更多的颜色时, __next__
stopIteration
将引发一个stopIteration
异常,该异常将反过来通知for
循环终止。
遍历字典
d = {'x': 10, 'y': 20, 'z': 30}
for k,v in d.items():
print k, v
#result
# y 20
# x 10
# z 30
遍历CSV文件中的行
import csv
with open('file.csv', newline='') as File:
reader = csv.reader(File)
for row in reader:
yield row
遍历字符串
my_string = 'Generators'
for string in my_string:
print (string)
#result
# G
# e
# n
# e
# r
# a
# t
# o
# r
# s
使用发电机的好处
让我们讨论使用生成器而不是迭代器的一些好处:
易于实施
在Python中构建迭代器将需要您使用__iter__()
和__next__()
方法实现一个类,并注意可能导致stopIteration
错误的任何错误。
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
如您在上面看到的,实现非常冗长。 所有这些负担由发电机自动处理。
更少的内存消耗
生成器有助于最大程度地减少内存消耗,尤其是在处理大型数据集时,因为生成器一次只会返回一项。
更好的性能和优化
发电机本质上是懒惰的。 这意味着它们仅在需要时才生成值。 与普通迭代器不同,在常规迭代器中,无论是否使用它们都会生成所有值,而生成器只会生成所需的值。 反过来,这将导致您的程序执行更快。
如何在Python中创建生成器
创建一个生成器非常容易。 您需要做的就是编写一个普通函数,但是使用yield
语句而不是return
语句,如下所示。
def gen_function():
yield "python"
当return
语句完全终止一个函数时, yield
只是暂停该函数,直到next()
方法再次调用它为止。
例如,下面的程序同时使用yield
和next()
语句。
def myGenerator(l):
total = 1
for n in l:
yield total
total += n
newGenerator = myGenerator([10,3])
print(next(newGenerator))
print(next(newGenerator))
Python生成器如何工作
让我们看看生成器是如何工作的。 考虑下面的示例。
# generator_example.py
def myGenerator(l):
total = 0
for n in l:
total += n
yield total
newGenerator = myGenerator([10,20,30])
print(next(newGenerator))
print(next(newGenerator))
print(next(newGenerator))
在上面的函数中,我们定义了一个名为myGenerator
的生成器,该生成器将列表l
作为参数。 然后,我们定义一个变量total
并将其赋值为零。 另外,我们遍历列表中的每个元素,然后将其添加到total变量中。
然后,我们实例化newGenerator
并在其上调用next()
方法。 这将运行代码,直到产生total
的第一个值(在这种情况下为0
。 然后,该函数将保留total变量的值,直到下次调用该函数为止。 与普通的return
语句(一次返回所有值)不同,生成器将从中断的地方开始提取。
以下是其余的后续值。
# generator_example.py
def myGenerator(l):
total = 0
for n in l:
yield total
total += n
newGenerator = myGenerator([10,20,30])
print(next(newGenerator))
print(next(newGenerator))
print(next(newGenerator))
# result
# 0
# 10
# 30
如果尝试在函数完成循环后调用该函数,则会收到StopIteration
错误。
next()
方法引发StopIteration
,以表明迭代器不再产生其他项。
0
10
30
Traceback (most recent call last):
File "python", line 15, in <module>
StopIterationNormal function
例子2
在这个例子中,我们展示了如何在一个函数中使用多个yield语句。
# colors.py
def colors():
yield "red"
yield "blue"
yield "green"
next_color =colors()
print(next(next_color))
print(next(next_color))
print(next(next_color))
# result
# red
# blue
# green
普通函数在调用该函数时会返回所有值,而生成器会等待直到再次调用next()
方法。 调用next()
,颜色函数将从其停止处恢复。
结论
生成器的内存使用效率更高,尤其是在处理非常大的列表或大对象时。 这是因为您可以使用yield来处理较小的位,而不是一次将所有数据都存储在内存中。
此外,不要忘记查看我们可以在Envato Market上出售和进行研究的产品 ,也可以使用以下供稿随时提出任何问题并提供宝贵的反馈意见。
此外,如果您感到困惑,在课程部分中会有一门关于Python生成器的很好的课程。
翻译自: https://code.tutsplus.com/tutorials/introduction-to-python-generators--cms-29491