Python之迭代器 生成器

迭代器

1. 可迭代协议和迭代器协议

  迭代器:Iterator      iterable :可迭代的

  可迭代协议:只要对象中包含了__iter__方法,就是可迭代的

  迭代器协议:迭代器中有__next__和__iter__方法

  迭代器实现了能从中一个一个的取值

 查看是否可迭代

from collections.abcimport Iterable    # Iterable检测一个对象是否可迭代
print(isinstance(对象,数据类型/iterable))     #检测对象是否是该数据类型或者是否可迭代

print(dir(对象))    # 可以查看对象可以使用的功能

print(set(dir(对象))-set(dir(对象)))    # 可以求这两个对象之间可使用的功能的差集

 查看是否是迭代器

from collections.abc import Iterator    # Iterator判断是否是迭代器

print(isinstance([1,2,3],Iterator))

 

2. 迭代器

列表本身是可迭代的 但不是迭代器
from collections.abc import Iterable
from collections.abc import Iterator    # 注:python3.7中collections 模块弃用 需从collections.abc模块进行导入


li = [1, 2, 3]

print(isinstance(li, Iterator))     # False  
print(isinstance(li, Iterable))     # True

 列表怎么变成迭代器?

from collections.abc import Iterator


lst_iterator = [1,2,3].__iter__()    # 调用__iter__()方法

print(isinstance(lst_iterator, Iterator))   # True

 Iter() 与 __iter__() 用于产生 iterator(迭代器)
  __iter__ 迭代器协议
    凡是实现__iter__协议的对象,皆是迭代器对象。(next()也得实现,不然没法产生数据)
  Iter()迭代器工厂函数
    凡是有定义有__iter__()函数,或者支持序列访问协议,也就是定义有__getitem__()函数的对象 皆可以通过 iter()工厂函数 产生迭代器(iterable)对象

 

3. 迭代器的取值

lst_iterator = [1,2,3].__iter__()

print(lst_iterator.__next__())
print(lst_iterator.__next__())
print(lst_iterator.__next__())
# 超出范围后报错 StopIteration

 

4. 可迭代对象 迭代器小结

  a. Python语言中可以用for循环遍历的,都是可迭代对象

  b. 可迭代对象包含迭代器

  c. 迭代器的本质:能够对python中的数据类型进行统一的遍历,不需要关心每一个值是什么

  d. 迭代器属于惰性运算,可以节省内存空间

 

5.  迭代器有两种:

  天生的:文件句柄

  后天的:可迭代对象.__iter__()

  文件句柄是一个迭代器,range是一个可迭代对象

 

生成器

1. Gerator   本质就是迭代器,生成器是自己实现

  生成器有两种:生成器函数和生成器表达式

2. 生成器函数

  a. 生成器函数在执行的的时候返回一个生成器

  b. 调用生成器函数时,第一次都是不不执行,而是获取生成器对象

  c. 生成器只能向下取值,不能往回找

  d. 生成器只有在取值的时候,才会执行

def generator_func():
    print(123)
    yield "aaa"
    print(456)
    yield  "bbb"

# 需要先获取生成器 g = generator_func() # g 就是生成器对象 这一步的执行并不会真的执行generator_func 而是由generator_func返回一个生成器对象 print(g) # <generator object generator_func at 0x0000019899601B88> # 使用__next__()方法取值 ret = g.__next__() # ret 是第一个yield返回的值 print(ret) ret1 = g.__next__() print(ret1) # 使用for.. in .. 取值 for i in g: print("i:",i) # 每遍历一次 返回一个yield的值

 

3. yield关键字

  a. 带yield的就是生成器函数

  b. yield不会终止函数

  c. yield后面不加的时候就会是返回None

 

4.  从生成器取值:

  a. __next__ 有几个yield就可以取几次

  b. for循环取值 正常取  for item in generator:

  c. 其他数据类型强制转换 list(generator) 返回一个列表,里面放着生成器的所有内容

# 列表取值
def generator_func(): print(123) yield "aaa" print(456) yield "bbb" g = generator_func() print(list(g)) # ['aaa', 'bbb']

  

def generator_func():
    print(123)
    yield "aaa"    
    print(456)
    yield "bbb"     

# __next__()取值
generator_func().__next__()     # 直接调用不可以,相当于每次都调用一个新的生成器
print(generator_func().__next__)    # aaa

generator_func().__next__()     # 直接调用不可以,相当于每次都调用一个新的生成器
print(generator_func().__next__)    # aaa

# for循环取值
for i in generator_func():      # for 的时候可以直接遍历,因为只调用了一个生成器
    print("i:",i)

 

# for和list取值混用

def func():
    yield 1
    yield 2

g = func()
for i in g:
    print(list(g))      # 这里只会打印一个[2],因为前面for循环的时候就已经拿到了一个1,现在去list(g)只能取到后面的2

  三个取值方法小结:最好使用for循环取值,list强转取值法不推荐,因为无法知道生成器中的数据有多少,双下next取值方法不是很常用,并且用起来比较麻烦。

 

5. yield from

  yield from 后边需要跟可迭代对象

def func():
    yield from [1,2,3]
    print('=============')
    yield from "adfd"


for i in func():
    print(i)

# 结果 会按顺序依次拿到可迭代对象中的元素
'''
1
2
3
=============
a
d
f
d
'''

 

6. send关键字

  a. send 是往里面传进去参数值,send不返回值的时候和next是一样的,在生成器执行伊始,只能先用next

  b. send传递参数的时候,生成器中必须要有一个未被返回的yield

  c. 函数里有几个yield就有几个next,并且第一个next不能改变,后面的可以改为send,send和text的总和就是yield的个数

def func():
    print(123)
    value = yield 1   # next就是执行到yield 1,当执行send的时候,就从value = send传进来的值开始执行
    print(value)
    print(456)
    yield "***"+value+"***"

g = func()
print(g.__next__())     # 推动func的运行,打印123,直到yield返回一个1,就暂停了
print(g.send("aaa"))    # 接着执行send的时候,把"aaa"传进去,赋值给value后,接着向下执行,打印456,***aaa***

 

def func():
    print(1)
    yield 2     # next的时候就实行到这里,当send88进来的时候,并没有变量去接受88,,
    print(3)
    value = yield 4     # 在这里,并没有send传值进来,所以value打印出来是空的
    print(5)
    yield value

g = func()
print(g.__next__())    # 2
print(g.send(88))  # 4
print(g.__next__())    # None

 

7. 带装饰器的生成器

def wrapper(func):    # 生成器预激装饰器
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)    # g就是一个生成器
        g.__next__()    # 激活生成器
        return g
    return inner

@wrapper
def average_func():
    total = 0
    count = 0
    average = 0
    while True:
        value = yield average
        total += value
        count += 1
        average = total/count


g = average_func()
print(g.send(30))

 

8. 列表表达式,列表推导式

# /的结果是浮点数
# 两个//数字就不是带小数点的

new_l = [i*i for i in [1,3,5]]
print(new_l)

print([i//2 for i in range(0,7,2)])

 

9. 生成器表达式

a = ("egg%d"%i for i in range(10))

# 三种方法从生成器取值:

# 1. __next()
print(a.__next__())

# 2. for 
for i in a:
    print(i)

# 3.  list强转
print(list(a))

 

10.  字典推导式

# 将一个字典的key和value值对换

mcase = {"a":10,"b":20}
new_dic={mcase[k]:k for k in mcase}
print(new_dic)

 

11. 集合推导式 (可以去重)

new_l = {i*i for i in [1,3,5]}
print(new_l)

 

12. 示例

  a. Python代码和列表表达式比较

l = [{'name':'alex','age':80},{'name':'egon','age':40},{'name':'yuan','age':30},{'name':'nezha','age':18}]
new_l = []
for d in l:
    new_l.append(d["name"])
print(new_l)

l = [{'name':'alex','age':80},{'name':'egon','age':40},{'name':'yuan','age':30},{'name':'nezha','age':18}]
print([i["name"] for i in l])
print([i["name"] for i in l if i["age"] > 18])

   b. 生成器表达式

# 30以内能被3整除的数
print([i for i in range(30) if i%3==0])

# 30以内能被3整除的数的平方
print([i*i for i in range(30) if i%3==0])

   c. 生成器取值方法 for list 混用

def demo():
    for i in range(4):
        yield i

g = demo()
g1 = (i for i in g)
g2 = (i for i in g1)
print(list(g1))    # 把g1里的值取出来之后,g1就是空的了
print(list(g2))    # g2就是空的
# 结果
# [0, 1, 2, 3]
# []

   d. 生成器的坑

def add(n,i):
    return n + i

def test():
    for i in range(4): 
        yield i

g = test()
for n in [1,10,5]:

    g = (add(n,i) for i in g)


print(list(g))  # [15, 16, 17, 18]

 解析:

  为什么结果是 [15, 16, 17, 18]???

  按正常逻辑来说,执行过程如下:
  1. n=1 for i in g: i的值 分别是 0 1 2 3,执行完add(n, i), g=(1, 2, 3, 4)
  2. n=10 for i in g: i的值 分别是 1, 2, 3, 4,执行完add(n, i), g=(11, 12, 13, 14)
  3. n=5 for i in g: i的值 分别是 11, 12, 13, 14,执行完add(n, i), g=(16, 17, 18, 19)

  需要注意的点是,for循环中的g是生成器表达式,如果换做列表表达式,那上边的过程就是对的。对于生成器需要注意的点就是,生成器只有在取值的时候才会执行。

  所以我们在最后调用list方法取值时,生成器g还未执行,而此时for循环已经执行完毕,n在内存中最后的值是5。此时 g = (add(n,i) for i in g),这里边的 g 和我们现在找的g 是一样的,所以最终的生成器是 g = (add(n,i) for i in g = (add(n,i) for i in g = (add(n,i) for i in test()))) 。计算结果即为 [15, 16, 17, 18]

小结:

  1. 最常用的是列表推导式, 在工作中,尽量把列表变成生成器表达式。
  2. 尽量让推导式简化操作,增强代码的可读性,如果推导式过于复杂,应该转换成普通的python代码。
  3. 所有的列表推导式都可以转换成生成器表达式,应该多使用生成器表达式,少使用列表表达式。
  4. 在代码里,多层嵌套for循环是禁忌,会大幅度增加代码的复杂度。

 

posted @ 2019-09-02 16:27  chitalu  阅读(209)  评论(0编辑  收藏  举报