Python 迭代器和生成器

什么时候迭代器?

现在,我们已经获得了一个新线索,有一个叫做“可迭代的”概念

首先,我们从报错来分析,好像之所以1234不可以for循环,是因为它不可迭代。那么如果“可迭代”,就应该可以被for循环了。

这个我们知道呀,字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的

迭代器 = 可迭代对象.__iner__() 或者iter(可迭代对象),自带一个__next__()方法

迭代器的优势:节省内存,效率高,每次只取一个值,而不需要所有的值都取出来之后再进行计算

from collections import Iterable

l = [1, 2, 3, 4]
t = (1, 2, 3, 4)
d = {1: 2, 3: 4}
s = {1, 2, 3, 4}

print(isinstance(l, Iterable))--->True
print(isinstance(t, Iterable))--->True
print(isinstance(d, Iterable))--->True
print(isinstance(s, Iterable))--->True

迭代器的特性:惰性计算,找他next要值才给,不要不给

生成器(generator):自己写的迭代器就是生成器,两种自己写生成器(迭代器)的机制,生成器函数、生成器表达式
生成器函数:
def cloth_g(num):
for i in range(num):
yield 'cloth%s'%i
g = cloth_g(10)
print(next(g))
print(next(g))
1.凡是带有yield关键字的函数,就是一个生成器函数
2.生成器函数的调用不会触发函数的执行,而会返回一个生成器(迭代器)
3.生成器函数的执行需要用next

生成器监听文件输入的例子:
def lister_file():
with open('tmp') as f1:
while True:
for line in f1:
if line.strip():
yield line.strip()
time.sleep(0.1)

g = lister_file()
for line in g:
print(line)

send,在执行next的过程中,传递一个参数给生成器函数的内部;
想生成器中传递值 有一个激活的过程 第一次必须要用next触发这个生成器
def func():
print(1111)
ret1 = yield 1
print(222,'ret1:',ret1)
ret2 = yield 2
print(3333,'ret2:',ret2)
yield 3


g = func()
print(next(g))
print(g.send('alex'))
print(g.send('金老板1'))

计算移动平均值:
def average():
avg = 0
day = 0
sum = 0
while True:
money = yield avg
sum += money
day += 1
avg = sum/day
g = average()
next(g)
print(g.send(200))
print(g.send(500))
print(g.send(800))
 生成器函数: 是python程序员实现迭代器的一种手段
主要特征:在函数体中有yield关键字
调用生成器函数,不会执行这个函数中的代码,只是会获得一个生成器(迭代器)
只有从生成器中取值的时候,才会执行函数内部的代码,且每获取一个数据才得到这个数据的代码
获取数据的方式包括next send for循环(for i in g),数据类型的强制转换列表或者元组(l = list(g))
yield返回值的简单方法,如果本身就是一个可迭代的,且要把可迭代对象中的每个数据返回,可以使用yield from
  def func():
  yield from range(5)
  g = func()
  print(next(g))
  print(next(g))
  print(next(g)) 输入结果为0,1,2
在使用send的时候,在生成器创造出来之后需要进行预激活,这一步可以使用迭代器完成
def init(f):
def inner():
ret = f()
next(ret)#预激活
return ret
return inner

@init
def func():
yield from range(5)

g = func()
print(next(g)) #输出结果为1
生成器表达式:
取出10以内的每个数的平方,添加到列表内
常规操作:
l = []
for i in range(10):
  l.append(i**2)
print(l)

列表推导式和生成器表达式

列表推导式:print([i**2 for i in rang(5)])

推导式的套路


之前我们已经学习了最简单的列表推导式和生成器表达式。但是除此之外,其实还有字典推导式、集合推导式等等。


下面是一个以列表推导式为例的推导式详细格式,同样适用于其他推导式。


variable = [out_exp_res for out_exp in input_list if out_exp == 2]
  out_exp_res:  列表生成元素表达式,可以是有返回值的函数。
  for out_exp in input_list:  迭代input_list将out_exp传入out_exp_res表达式中。
  if out_exp == 2:  根据条件过滤哪些值可以。

例一:30以内所有能被3整除的数 print([i for i in range(30) if i%3 == 0])--->输出结果[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
例二:30以内所有能被3整除的数的平方 print([i**2 for i in range(30) if i%3 == 0])--->输出结果[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]
例三:找到嵌套列表中名字含有两个‘e’的所有名字print(j for i in names for j in i if count('e') == 2)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

print(j for i in names for j in i if j.count('e') == 2)

练习题:

例1:  过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母

names = ['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe']

print([i,upper() for i in names if len(i)>3])

例2:  求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表

[(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1] 

例3:  求M中3,6,9组成的列表M = [[1,2,3],[4,5,6],[7,8,9]]

  l =  [row[2] for row in M] ---->列表推导式 print(l) --->[3, 6, 9]

 g = (row[2] for row in M)---->生成器表达式 print(g)----><generator object <genexpr> at 0x0000000002411BF8>

for i in g:print(i)
生成器的重点在于,有没有执行,一个生成器从头到尾只能取值一次,在不找他要值的时候始终不执行
当他执行的时候,要以执行时候的所有变量值为准。








 

posted @ 2018-04-26 18:13  小A在哪  阅读(151)  评论(0编辑  收藏  举报