迭代器与生成器,及生成器进阶和相关表达式

Posted 小杜要加油

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了迭代器与生成器,及生成器进阶和相关表达式相关的知识,希望对你有一定的参考价值。

一、迭代器(dir()可以查函数具有什么键的功能)

1、可迭代的——iterable(字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。)

可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。

(1)判断是否可迭代的方法:

from collections import Iterable
print(isinstance(aaa,Iterable)                       #输出结果:True
print(isinstance(123,Iterable)                         #输出结果:False
print(isinstance([1,2,3],Iterable)                    #输出结果:True
 print(‘__iter__‘ in dir([1,2,3,4])) # True    可迭代的

(2)可迭代协议

  包含‘__iter__‘方法的数据类型就是可迭代的。输出数据类型所有方法的方式如下:

#实例:求出两个不同数据类型的方法的不同
difference=set(dir([1,2,3]))-set(dir(123))          #dir()方法以列表形式列出数据的所有的方法
print(diffenrence)

2、迭代器——iterator

(1)判断迭代器的方法(迭代器 : __iter__ 和__next__)

from collection import Iterator
iterator_lst=[1,2,3].__iter__()
print(isinstance(itertor_lst,Iterator)               #输出结果为:True
print(isinstance([1,2,3],Iterator)                   #输出结果为:False
print(‘__next__‘ in dir([1,2,3,4])) # False

(2)迭代器协议

  迭代器中有‘__next__‘和‘__iter__‘方法

(3)常见迭代器

  a.先天的:文件句柄;b后天的:从可迭代对象转变,格式为:“可迭代对象.__iter__()”

(4)迭代器取值

lst_iterator=[1,2,3].__iter__()  先有iter
print(lst_iterator.__next__())   才能取值              #输出结果为:1
print(lst_iterator.__next__())                        #输出结果为:2
print(lst_iterator.__next__())                        #输出结果为:3
#超过迭代器中的内容就会报错:stopIterator

(5)优点(迭代器是个好东西)

1、惰性运算   2、从前到后一次去取值,过程不可逆 不可重复   3、节省内存

3、二者的区别

(1)可迭代对象包含迭代器;(2)迭代器=可迭代对象.__iter__();(3)可以对可迭代对象和迭代器进行循环

二、生成器

1、定义:生成器就是迭代器,生成器是我们自己写出来的

2、生成器函数:带有关键字yield/yield from的函数就称为生成器函数

(1)生成器函数在执行时候只返回一个生成器,不执行生成器函数中的内容

(2)从生成器中取值

  a.生成器.__next__():生成器函数中有几个yield,就可以取几次

def generator():
    print(123)
    yield aaa
    print(456)
    yield bbb
g=generator()
print(g.__next__())  # 123 aaa
print(g.__next__())  # 456 bbb
def list():
print(123)
yield ‘aaa‘
print(456)
yield ‘bbb‘
print(list().__next__()) # 123 aaa
print(list().__next__()) # 123 aaa
 注意:前者为创建一个生成器,后者为每次重新创建一个生成器

b、for 循环取值

def func():
    print(123)
    yield aaa
    print(456)
    yield bbb
g=func()
for i in g:
    print(i)
123
aaa
456
bbb
3、用while循环模拟for循环的方式 —— for循环是依赖迭代器的,且我们自己也可以仿照着写
l = [1,2,3,4,5]
l_iter = l.__iter__()  定义
while True:
    try:
        print(l_iter.__next__())
    except StopIteration: # 异常处理机制
        break

实例、

def cloth():
    for i in range(10000):
        yield 衣服%s%i

g = cloth()
for i in range(50):
    print(g.__next__())

for i in range(50):
    print(g.__next__())

   list(生成器函数) 返回一个列表,里面装着生成器中的所有内容

def generator():
    yield aaa
    yield bbb
g=generator()
print(list(g))       # [aaa,bbb]

实例

1、监听器例子

监听器
def tail():
    f = open(文件,r,encoding=utf-8)
    f.seek(0,2)   # 光标移至最后
    while True:
        line = f.readline()
        if line:
            yield line
        import time
        time.sleep(0.1)
g = tail()
for i in g:
    print(i.strip())
生成器监听文件输入的例子

2、计算移动平均值

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

g_avg = averger()
next(g_avg)
print(g_avg.send(10))
print(g_avg.send(100))
print(g_avg.send(20))
print(g_avg.send(30))

三、重要会

def g_func():
 print(aaaaa) # aaaaa
 yield 1 # 返回值 每次只占用一次内存,用完就没了
 print(bbbbb) #bbbbb
 yield 2 # 返回值
 yield 3 # 返回值
 # return [1,2,3] #直接return 是列表,当列表数大的时候会比较占用内存
 # for i in range(20000): #当数量较大时,生成器函数可以这样写、
 # yield i
g = g_func()
# for i in g:
# print(i)
 
print(g.__next__())
print(g.__next__())
print(g.__next__())
def cloth():
 for i in range(10000):
 yield 衣服%s%i
 
g = cloth()
for i in range(50):
 print(g.__next__())
 
for i in range(50):
 print(g.__next__())

一、生成器中的send用法

  send可以把一个值作为信号量传递到生成器函数中,然后和__next__方法一样获取生成器中的值;在生成器执行伊始,只能先使用__next__;二者都是终于yield,而send需要始于一个未被返还的yield处,否则传递的数值将无法被接收。

1、求重复累加平均实例

def average():
    count=0
    toatal=0
    average=0
    while True:
        value=yield average
        toatal+=value
        count+=1
        average=toatal/count
g=average()
print(g.__next__())                           #激活了生成器,返回yield后面的average,为0
print(g.send(10))                             #把10传递给yield前的value,继续执行后面的代码,终于yield后面的average,返回10
print(g.send(20))                             #把20传递给yield前的value,继续执行后面的代码,终于yield后面的average,返回15
print(g.send(30))                             #..........
print(g.send(40))
print(g.send(50))

2、用send传值失败实例

def func():
    print(1)
    yield 2
    print(3)
    value = yield 4
    print(5)
    yield value

g = func()
print(g.__next__())                          #打印出1和返回2,然后在第一个yield处停止等待
print(g.send(88))                            #由于第一个yield处没有未被返回的值,故send传值失败,打印出3和返回4,在第二个yield处停止等待
print(g.__next__())                          #value无值传入,打印出5后在第3个yield处返回none

3、用next激活生成器计算累加平均值

def init(func):                              #在调用被装饰生成器函数的时候首先用next激活生成器
    def inner(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return inner
@init
def average():
    mount=0
    total=0
    average=0
    while True:
        value=yield average
        total+=value
        average=total/mount
g_avg=average()
                                            # next(g_avg)   在装饰器中执行了next方法
print(g_avg.send(10))
print(g_avg.send(20))
print(g_avg.send(30))

二、列表表达式和生成器表达式

1、列表表达式

  简化代码,返回的必须是一个列表

#普通代码
result=[]
for i in [1,2,3]
    result.append[i*i]
print(result)

#列表表达式
print([i*i for i in [1,2,3]])

(1)30内能被3整除的数的实例

print([i for i in range(1,31) if i%3==0])

(2)找到嵌套列表中有两个‘e’的名字

names = [[Tom, Billy, Jefferson, Andrew, Wesley, Steven, Joe], [Alice, Jill, Ana, Wendy, Jennifer, Sherry, Eva]]
print([for list in names for name in list if name.count(e)==2])

 

2、生成器表达式

  把列表解析的[]换成()得到的就是生成器表达式;列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

laomuji=(鸡蛋%s %i for i in range(10))        #生成器表达式
print(laomuji)                                 #返回生成器地址<generator object <genexpr> at 0x000001E17AF23BA0>
print(next(laomuji))                           #输出结果为:鸡蛋0
print(laomuji.__next__())                      #输出结果为:鸡蛋1
print(next(laomuji))                           #输出结果为:鸡蛋2
                                               #next(laomuji)等价于laomuji.__next__()

3、字典推导式

#将字典键与值对调
mcase = {a: 10, b: 34}
print({mcase[k]:k for k in mcase})

4、集合推导式

  自带去重功能,将列表解析式的[]换成{}得到集合推导式

print({i*i for i in [1,-1,2]})    #结果为{1,4}

 

1.把列表解析的[]换成()得到的就是生成器表达式

2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:

sum(x ** 2 for x in range(4))

而不用多此一举的先构造一个列表:

sum([x ** 2 for x in range(4)]) 

生成器面试题

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

g=demo()   # 生成器

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

# print(list(g1))   #[0,1,2,3]
print(list(g2))
def add(n,i):
    return n+i

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

g=tes()
for n in [1,5,10]:
    g=(add(n,i) for i in g)
n=1
g=(add(n,i) for i in tes())  #--->  g=(add(n,i) for i in g) 生成器,没要,不会给值   -->tes() = g
n = 5
g=(add(n,i) for i in (add(n,i) for i in tes()))
n = 10
g=(add(n,i) for i in (add(n,i) for i in (add(n,i) for i in tes())))
# g=(30,31,32,33)
print(list(g))

 

以上是关于迭代器与生成器,及生成器进阶和相关表达式的主要内容,如果未能解决你的问题,请参考以下文章

Python100天学习笔记Day20 迭代器与生成器及 并发编程

Python100天学习笔记Day20 迭代器与生成器及 并发编程

python基础理解迭代器与生成器

Python 迭代器与生成器及装饰器

Python装饰器与GIL锁解释

Python: 迭代器与生成器小结