目录
1.创建生成器
创建生成器的两种主要方法:生成器函数、生成器表达式。
生成器函数的外观和行为与常规函数类似,但有一个定义特征。生成器函数使用Python yield关键字而不是return。
#无限序列
def infinite_sequence():
num=0
while true:
yield num
num +=1
>>>g=infinite_sequence()
>>>next(g)
0
生成器表达式类似于列表推导,可以仅用几行代码即可快速创建生成器对象,两者外观基本相同,列表推导使用方括号构建列表,生成器表达式使用括号创建生成器表达式。
>>> lc=[x**2 for x in range(5)]
>>> lc
[0, 1, 4, 9, 16]
>>> gc=(x**2 for x in range(5))
>>> next(gc)
0
2.分析生成器性能
内存使用情况:
>>> l=[x for x in range(10000)]
>>> g=(x for x in range(10000))
>>> import sys
>>> sys.getsizeof(l)
87624
>>> sys.getsizeof(g)
120
>>> print(87624/120)
730.2
列表推导返回完整列表,而生成器表达式返回生成器,列表比生成器对象大700多倍!
程序执行时间:
>>> cProfile.run('sum([i * 2 for i in range(10000)])')
5 function calls in 0.001 seconds
...
>>> cProfile.run('sum((i * 2 for i in range(10000)))')
10005 function calls in 0.002 seconds
...
可以看到列表推导中所有值的求和大约花费了生成器中求和的三分之一时间。
如果速度是个问题,而内存不是,那么列表推导可能是更好的工具。
3.Python yield语句
总体而言,yield
是一个相当简单的语句。它的主要工作是以类似于return
语句的方式控制生成器函数的流。
当您调用生成器函数或使用生成器表达式时,将返回一个称为生成器的特殊迭代器。您可以将此生成器分配给变量以使用它。当您在生成器上调用特殊方法时,例如next()
,函数中的代码将执行到yield
。
执行到Python yield语句时,程序将暂停函数执行,并将产生的值返回给调用方。(相反,return将
完全停止执行功能。)当函数挂起时,保存该函数的状态。这包括生成器本地的所有变量绑定,指令指针,内部堆栈以及任何异常处理。
这样,无论何时调用生成器的方法之一,都可以恢复函数执行。这样,在yield后
所有函数求值都恢复正常。
像迭代器一样,生成器也会耗尽(除非是无限的),耗尽时抛出Stopinteration异常。
>>> def gr():
for x in range(3):
yield x
>>> g=gr()
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
>>> next(g)
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
next(g)
StopIteration
4.生成器的方法
生成器方法有.__next()__、.send()、.throw()、.close(),用于控制生成器功能的执行。
generator.__next()__通常通过for
循环或内置next()
函数隐式调用,用于开始执行生成器函数或在最近暂停执行的yield表达式处继续执行。
generator.send(value),生成器在最近暂停执行的yield表达式处继续执行,并向生成器函数“发送”一个值,即value参数成为当前yield表达式的结果,如下例中的res=yield 1。
send()方法返回生成器生成的下一个值,如果生成器退出而不产生另一个值,则引发停止迭代。如果用send()来启动生成器时,必须以None作为参数调用它,因为没有可接收该值的yield表达式。
>>> def foo():
while True:
res=yield 1
print(res)
>>> gr=foo()
>>> next(gr)
1
>>> gr.send(0)
0
1
generator.
throw
(type[, value[, traceback]]) ,在生成器暂停时引发类型为type的异常,并返回生成器函数生成的下一个值。
>>> def foo():
x=0
while True:
yield x
x +=1
>>> for i in gr:
print(i)
if i>=5:
gr.throw(ValueError('value most less than 5'))
0
1
2
3
4
5
Traceback (most recent call last):
File "<pyshell#33>", line 4, in <module>
gr.throw(ValueError('value most less than 5'))
File "<pyshell#26>", line 4, in foo
yield x
ValueError: value most less than 5
generator.close(),
停止生成器。当控制无限序列发生器时,这尤其方便。例如上面的代码用.close()
替代.throw()
来停止迭代
>>> for i in gr_close:
print(i)
if i>=5:
gr_close.close()
0
1
2
3
4
5
5.使用生成器创建数据管道
数据管道可以将代码串在一起以处理大型数据集或数据流,而无需占用计算机的内存。假设您有一个大型CSV文件,它是美国一些创业公司的融资轮次和融资金额,以下其中部分(第一行是列名):
permalink,company,numEmps,category,city,state,fundedDate,raisedAmt,raisedCurrency,round
digg,Digg,60,web,San Francisco,CA,1-Dec-06,8500000,USD,b
digg,Digg,60,web,San Francisco,CA,1-Oct-05,2800000,USD,a
facebook,Facebook,450,web,Palo Alto,CA,1-Sep-04,500000,USD,angel
facebook,Facebook,450,web,Palo Alto,CA,1-May-05,12700000,USD,a
photobucket,Photobucket,60,web,Palo Alto,CA,1-Mar-05,3000000,USD,a
用数据通道来计算这些数据中A轮融资公司的融资金额之和:
file_name = "techcrunch.csv"
lines = (line for line in open(file_name))
list_line = (s.rstrip().split(",") for s in lines)
cols = next(list_line)
company_dicts = (dict(zip(cols, data)) for data in list_line)
funding = (
int(company_dict["raisedAmt"])
for company_dict in company_dicts
if company_dict["round"] == "A" or "a"
)
total_series_a = sum(funding)
print(f"Total series A fundraising: ${total_series_a}")
list_line是迭代返回列表的生成器,clos是原数据的列名,company_dicts是迭代返回字典的生成器,funding是用生成器表达式创建的生成器,并做了筛选,最后sum()对funding求得总和