1、生成器
通过一致的方式遍历序列,例如列表中的对象或者文件中的一行行内容,这是python的一个重要特性。这个特性是通过 迭代器协议 来实现的,迭代器协议 是一种令对象可遍历的通用方式。比如,遍历一个字典,获得字典的键:
dict_1 ={'a':1,'b':2,'c':3}
for key in dict_1:
print(key)
a
b
c
当你写下 for key in dict_1 的语句时。python解释器首先尝试根据 dict_1 生成一个迭代器:
dict_2 = iter(dict_1)
dict_2
<dict_keyiterator at 0x24a56959688>
迭代器就是一种用于在上下文中(比如for循环)向python解释器生成对象的对象。大部分以列表或列表型对象为参数的方法都可以接受任意的迭代器对象。包括内建方法比如min、max和sum,以及类型构造函数比如list和tuple:
list(dict_2)
['a', 'b', 'c']
生成器是构造新的可遍历对象的一种非常简洁的方式。普通函数执行并返回单个结果,而生成器则“惰性”地返回一个多结果序列,在每一个元素产生之后暂停,直到下一个请求。如需创建一个生成器,只需要在函数中将返回关键字return替换为yield关键字:
def squares(n = 10):
print('Generating squares from 1 to {0}'.format(n**2))
for i in range(1,n+1):
yield i**2
当你实际调用生成器时,代码并不会立即执行:
gen = squares()
gen
<generator object squares at 0x0000024A56928C78>
直到你请求生成器中的元素时,它才会执行它的代码:
for x in gen:
print(x,end = ' ')
Generating squares from 1 to 100
1 4 9 16 25 36 49 64 81 100
2、生成器表达式
用 生成器表达式 来创建生成器更为简单。生成器表达式与列表、字典、集合的推导式很类似,创建一个生成器表达式,只需要将列表推导式的中括号替换为小括号即可:
gen = (x**2 for x in range(10))
sum(gen)
285
上面的代码与下面更为复杂的生成器是相同的:
def make_gen():
for x in range(10):
yield x**2
sum(make_gen())
285
gen = make_gen()
在很多情况下,生成器表达式可以作为函数参数用于替代列表推导式:
sum(x**2 for x in range(10))
285
dict((i,i**2) for i in range(5))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
3、itertools模块
标准库中的itertools模块是适用于大多数数据算法的生成器集合。例如,groupby可以根据任意的序列和一个函数,通过函数的返回值对序列中连续的元素进行分组:
import itertools
first_letter = lambda x:x[0]
names = ['Alan','Adam','Wes','Will','Albert','Steven']
for letter,names in itertools.groupby(names,first_letter):
print(letter,list(names)) # names is a generator
A ['Alan', 'Adam']
W ['Wes', 'Will']
A ['Albert']
S ['Steven']
下面是一些有用的itertools函数
combinations( iterable, k):根据iterable参数中的所有元素生成一个包含所有可能K-元组的序列,忽略元素的顺序,也不进行替代;
permutations( iterable, k):根据iterable参数中的所有元素按顺序生成包含所有可能K元组的序列;
groupby( iterable, [ , keyfunc]): 根据每一个独一的key生成(key, subiterator)元组;
product( * iterables, repeat = 1): 以元组的形式,根据输入的可遍历对象生成笛卡尔积,与嵌套的for循环类似: