Python 迭代器和生成器和推导式

阅读目录

      • 迭代器
      • 生成器
      • 推导式(生成式/解析式)
      • 易错题/难题

迭代器

  • 引入
    dir([1,2].iter())是列表迭代器中实现的所有方法,
    dir([1,2])是列表中实现的所有方法,
    都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合,
    然后取差集。
# print(dir([1,2].__iter__()))
# print(dir([1,2]))
print(set(dir([1,2].__iter__()))-set(dir([1,2])))

# 结果:
{
     '__length_hint__', '__next__', '__setstate__'}
# 看到在列表迭代器中多了三个方法,那么这三个方法都分别做了什么事呢?
iter_l = [1,2,3,4,5,6].__iter__()

print(iter_l.__length_hint__()) # 获取迭代器中元素的长度

print('*',iter_l.__setstate__(4)) # 根据索引值指定从哪里开始迭代

print('**',iter_l.__next__()) # 一个一个的取值
print('***',iter_l.__next__())
-------

s = 123
for i in s:
     print(i)
print(dir(str)) #  dir查看xx类型的数据可以执行哪些方法, __iter__  iterable
print(dir(list)) # __iter__
print(dir(int)) # 没有__iter__

# 所有的带__iter__可以使用for循环的, 可迭代对象

# 可迭代对象可以使用 __iter__()来获取到迭代器

# 迭代器里面有__next__()

s = "周杰伦喜欢林俊杰"
it = s.__iter__() # 获取迭代器
print(dir(it)) # 迭代器里,也有__iter__ 还有__next__

# 特点
1. 只能向前.
2. 几乎不占用内存, 节省内存(生成器)
3. for循环
4. 惰性机制 (面试题,难度系数比较高)

print(it.__next__())# 周
print(it.__next__())# 杰
print(it.__next__())# 伦
print(it.__next__())# 喜
print(it.__next__())# 欢
print(it.__next__())# 林
print(it.__next__())# 俊
print(it.__next__())# 杰

# 迭代器模拟for循环的内部机制

lst = ["周杰伦", "林俊杰", "汪峰", "姚明","潘长江"]

it=lst.__iter__() # 获取迭代器
while True:
     try:
          content=it.__next__()
          print(content)
     except StopIteration:
          break
# 使用while循环实现了原本for循环做的事,是从it(迭代器)里取值
# 迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法

# 判断一个对象是否是迭代器

lst = ["肖战为什么这么帅","我不信","你们信吗?"]
it = lst.__iter__()

# 偏方
print("__iter__" in dir(it))
print("__next__" in dir(it))
可以通过dir来判断数据是否是可迭代的, 以及数据是否是迭代器

# 官方方案
from collections import Iterable  # 可迭代对象
from collections import Iterator  # 迭代器

print(isinstance(lst, Iterable)) # isinstance 是否是个实例
print(isinstance(lst, Iterator))

print(isinstance(it, Iterable))
print(isinstance(it, Iterator))

# range() 不是一个迭代器
print('__next__' in dir(range(12)))  # 查看'__next__'是不是在range()方法执行之后内部是否有__next__
print('__iter__' in dir(range(12)))  # 查看'__iter__'是不是在range()方法执行之后内部是否有__next__

from collections import Iterator
print(isinstance(range(100000000),Iterator))  
# 验证range执行之后得到的结果不是一个迭代器


# 惰性机制
lst = ["赵四","花生哥, 越来越皮", "天台见"]
it = lst.__iter__()

# it.__next__()
# it.__next__()
# list(参数)把参数进行循环迭代
s = list(it) # 在list中.一定存在for. 一定__next__(),可以一次性遍历,装入内容
print(s) #["赵四","花生哥, 越来越皮", "天台见"]
  • 迭代器存在的必要(为什么要使用for 循环):
l=[1,2,3]

index=0
while index < len(l):
    print(l[index])
    index+=1
# 上述列表,通过索引就能取出所有的值,那么为什么还需要用for
  • 序列类型字符串,列表,元组都有下标,可以用上述的方式访问,
  • 但非序列类型像字典,集合,文件对象无法使用;
    for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法;
    即在遍历之前,先调用对象的__iter__方法将其转换成一个迭代器;
    然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通过for循环来遍历了,
    而且效果也确实如此,这就是无所不能的for循环。

生成器

def func():
     print("娃哈哈")
     yield 1 # return和yield都可以返回数据
     print("呵呵呵")


gen = func() # 不会执行你的函数. 拿到的是生成器


# 函数中如果有 yield 这个函数就是生成器函数 
# 生成器函数() 获取的是生成器,这个时候不执行函数

yield: 相当于return 可以返回数据. 但是yield不会彻底中断函数. 而是分段执行函数.

gen.__next__() 执行函数. 执行到下一个yield(找到下一个yield为止,等待下一次执行)
gen.__next__() 继续执行函数到下一个yield.


def order():
     lst = []
     for i in range(10000):
          lst.append("衣服"+str(i))
     return lst

li = order()
print(li)

def order():
     for i in range(10000):
          yield "衣服"+str(i)

g = order() # 获取生成器
mingwei = g.__next__()
print(mingwei)
zhaoyining = g.__next__()
print(zhaoyining)
  • send() 和__next__()是一样的;
    send() 可以执行到下一个yield,也可以给上一个yield位置传值
def func():
     print("我是第一个段")
     a = yield 123
     print(a) # 执行第一个.__next__()时,这里是没执行的
     print("周杰伦是第二段")
     b = yield 456
     print(b) # ??
     print("林俊杰是第三段")
     c = yield 789
     print(c)
     print("李宗盛是最后一个段")
     yield 79  # 最后收尾一定是yield
     # 只要记住 是停在 yield的位置,send 返回也是在 yield的位置

g = func()
print(g.__next__()) # 如果要用send()开头的话,必须设置为None
# 也可以写成:print(g.send(None))
print(g.send("煎饼果子"))
print(g.send("韭菜盒子"))
print(g.send("锅包肉"))  

# 输出结果:
"""
我是第一个段
123
煎饼果子
周杰伦是第二段
456
韭菜盒子
林俊杰是第三段
789
锅包肉
李宗盛是最后一个段
79
"""

def eat():
     print("我吃什么啊")
     a =  yield  "馒头"
     print("a=",a)
     b =  yield  "鸡蛋灌饼"
     print("b=",b)
     c =  yield  "韭菜盒子"
     print("c=",c)
     yield  "GAME OVER"

gen = eat() # 获取生成器

ret1 = gen. __next__()
print(ret1) # 馒头
ret2 = gen.send("胡辣汤")
print(ret2)
ret3 = gen.send("狗粮")
print(ret3)
ret4 = gen.send( "猫粮")
print(ret4)
>>>计算移动平均值(1)
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count


g_avg = averager()
next(g_avg)
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
>>>计算移动平均值(2)_预激协程的装饰器
def init(func):  # 在调用被装饰生成器函数的时候首先用next激活生成器
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        next(g)
        return g
    return inner

@init
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count


g_avg = averager()
# next(g_avg)   在装饰器中执行了next方法
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
# 生成器可以用for循环
def func():
     yield 1
     yield 13
     yield 26
     yield 88
     yield 46
     yield 100


for i in func():# 这个不是函数了,是生成器了;for的内部一定有__next__()
     print(i)
'''
1
13
26
88
46
100
'''
print(list(func())) # list内部都有__next__() 
#[1, 13, 26, 88, 46, 100]

# 直接取出生成器函数里的值
def func():
     print(111)
     yield 222
a=func() # 生成器
print(a) # 生成器地址
print(list(a)) # 输出结果 111 [222]
# print(a.__next__()) 111 222
# print(next(a)) 111 222

# print(list(func()))
# 这里的list 可以改成 set、tuple不能使用dict,用str则还是生成器
  • 生成器监听文件输入的例子:
import time


def tail(filename):
    f = open(filename)
    f.seek(0, 2) # 从文件末尾算起
    while True:
        line = f.readline()  # 读取文件中新的文本行
        if not line:
            time.sleep(0.1)
            continue
        yield line

tail_g = tail('tmp')
for line in tail_g:
    print(line)
  • yield from
def gen1():
    for c in 'AB':
        yield c
    for i in range(3):
        yield i

print(list(gen1()))

def gen2():
    yield from 'AB'
    yield from range(3)

print(list(gen2()))

yield from

推导式(生成式/解析式)

  • 推导式: 用一句话来生成一个列表
  • 列表推导式
lst = []
for i in range(1, 16):
     lst.append("python"+str(i))
print(lst)

# 列表推导式:
lst = ["python"+str(j) for j in range(1,16)]
print(lst)

# 语法:  [结果 for循环 判断]
lis=[i for i in range(10)if i%2==1]
print(lis)

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

>>>寻找名字中带有两个e的人的名字
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,'Joe'],
     [ 'Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry']]


# lis=[]
# for el in names:
#      for i in el:
#          if i.count("e")>=2:
#               lis.append(i)
# print(lis)

lst=[name for el in names for name in el if name.count("e")>=2]
print(lst)
  • 字典推导式
>>>给一个列表[11,22,33,44] 需要变成 字典{
     0: 11, 1: 22, 2: 33, 3: 44}
li=[11,22,33,44]
dic={
     }
for i in range(len(li)):
    for j in range(len(li)):
        if i==j:
            dic[i]=li[i]
print(dic)

# 推导式做法:
dic={
     i:li[i] for i in range(len(li)) if i<2}
print(dic) #最终的结果是要拿到键值对,键值对 分别是列表的索引和索引对应的值
# 字典推导式 
语法:{
     k:v for循环 条件筛选}

>>>将字典的键值对调换顺序
dic = {
     "jj": "林俊杰", "jay": "周杰伦", "zs": "赵四", "ln":"刘能"}
d(dic)={
     v:k for k,v in dic.items()}
print(dic)
  • set集合推导式
li=[1,1,2,3,3,5,6,6]
s={
     el for el in li}#去重
print(s)
  • 生成器表达式 / 推导式
tu = (i for i in range(10)) # 没有元组推导式;这个叫生成器表达式
print(tu) # 生成器
print(tu.__next__())
print(tu.__next__())
...

lst = [i for i in range(10)] # 列表推导式
print(lst)

gen = (i for i in range(10)) # 生成器, 惰性机制

# 使用生成器的优点:
	延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,
		这对于大数据量处理,将会非常有用。
	# 列表解析
	sum([i for i in range(100000000)]) # 内存占用大,机器容易卡死
 
	# 生成器表达式
	sum(i for i in range(100000000)) # 几乎不占内存

易错题/难题

# 生成器函数
def func():
     print(111)
     yield 222

g = func() # 获取生成器

g1 = (i  for i in  g) # 生成器

g2 = (i  for i in  g1) # 生成器

print(list(g)) # ??? [222] 源头.从源头把数据拿走了
print(list(g1))# ??? [] 这里执行的时候. 源头已经没有数据
print(list(g2))# ??? [] 这里也没有值了
# 输出结果:111  [222] [] []

# 变形
def func():
     print(111)
     yield 222
     yield 333

g = func() # 获取生成器
g1 = (i  for i in  g) # 生成器

g3 = func() # 获取生成器,都从生成器函数里拿值
g2 = (i  for i in  g3) # 生成器

print(list(g)) # [222,333] 源头. 从源头把数据拿走了
print(list(g1)) # [] 这里执行的时候. 源头已经没有数据
print(list(g2)) # [222,333]这里有值了,因为g3作为源头是一个新的生成器

# 再变形:
def func():
    print(111)
    yield 222
    yield 333


g = func()  # 获取生成器
g1 = (i for i in g)  # 生成器

g3 = func()  # 获取生成器,都从生成器函数里拿值
g2 = (i for i in g3)  # 生成器

print(g.__next__()) #__next__()只拿yield里的一个,所以是222
print(list(g1)) #是全装,g1 通过g 来拿,但是因为yield里,只剩下一个[333]了
print(list(g2)) #是全装,g2通过 g3来拿,g3会获取生成器,所以能拿到全部的[222,333]
# 输出结果:111 222 [333] 111 [222, 333]


# 生成器问题难题
def add(a, b):
     return a + b

def test():
     for r_i in range(4):
         yield r_i
g = test() # 生成器,没取值,范围0,1,2,3

for n in [2, 10]: # n=2 , n=10
	g = (add(n, i) for i in g)
	print(list(g))   # n循环时,分别代入

# 输出结果为:2,3,4,5 [],前面取完后,后面取不到值

————————
def add(a, b):
     return a + b

def test():
     for r_i in range(4):
         yield r_i
g = test() # 生成器,没取值,范围0,1,2,3

for n in [2, 10]: # n=2 , n=10
	g = (add(n, i) for i in g) 

print(list(g))  # n 循环完,再代入

# 拆分:
n=2
g = (add(n, i) for i in g) # 生成器,没有取值

n=10
g = (add(n, i) for i in g) # 将这个g替换
g =(add(n, i) for i in (add(n, i) for i in g)) 
# 将n=10带入
g =(add(10, i) for i in (add(10, i) for i in0,1,2,3)) 

print(list(g)) # 这个取值了,并且是一次性装入list中
print(list(g)) # [] 取完了
# 输出结果为:[20,21,22,23]

# 总之生成器 执行流程时,只要没到 __next__()和 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))
print(list(g2))
  • 协程应用:grep -rl /dir
    tail&grep
import os

def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

@init
def list_files(target):
    while 1:
        dir_to_search=yield
        for top_dir,dir,files in os.walk(dir_to_search):
            for file in files:
                target.send(os.path.join(top_dir,file))
@init
def opener(target):
    while 1:
        file=yield
        fn=open(file)
        target.send((file,fn))
@init
def cat(target):
    while 1:
        file,fn=yield
        for line in fn:
            target.send((file,line))

@init
def grep(pattern,target):
    while 1:
        file,line=yield
        if pattern in line:
            target.send(file)
@init
def printer():
    while 1:
        file=yield
        if file:
            print(file)

g=list_files(opener(cat(grep('python',printer()))))

g.send('/test1')

你可能感兴趣的:(Python学习,Python,生成器,迭代器,推导式,生成式,生成器难题)