python迭代器-生成器-列表推导式-生成器表达式-day11-12

生成器Generator
    生成器函数
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束
    def func():
        print('hello')
        yield 1
    g = func()      #调用生成器函数
    print(g)        #g是一个generator生成器
结果:
    <generator object func at 0x000001F22C0D0EB8>
#调用就不会执行这个函数,而是返回一个生成器
    print(dir(g))     #g是一个迭代器,所以所有的生成器都是迭代器
    a = g.__next__()
    print(a)
结果:
    hello
    1

事例二:next本质就是调用__next__
    def genrator_fun1():            #1 #5
        a = 1                       #6
        print('现在定义了a变量')       #7
        yield a                     #8 #10
        b = 2                       #11
        print('现在又定义了b变量')  #12
        yield b                     #13
    g1 = genrator_fun1()            #2
    #打印g1可以发现g1就是一个生成器
    print('g1 : ',g1)               #3
    print(next(g1))                 #4
    print(next(g1))                 #9
结果:
    g1 :  <generator object genrator_fun1 at 0x00000195435B0EB8>
    现在定义了a变量
    1
    现在又定义了b变量
    2
yield关键字的特点: 可以记录当前函数中执行的位置,下一次继续执行
next和yield是一对搭档 : next开始函数的执行 yield停止函数的执行
生成器函数最大的特点:
调用之后不执行,需要next来触发这个函数继续向下执行
一个函数中可以有多个yield
    def func():
        yield 1     #停止符,也是一个记录符(到这里num1就执行完了返回结果给num1)
        print(123)      #接着上次的代码开始执行
        yield 2         #遇到yield又停止了,返回结果给num2
        for i in range(2):
            print(i)
        yield 3
    g = func()
    num1 = g.__next__()
    print(num1)
    num2 = g.__next__() #num1函数执行完后就开始执行num2
    print(num2)
    num3 = g.__next__()
    print(num3)
结果
    1
    123
    2
    0
    1
    3

事例二
    def f():
        print(111)
        yield 1
        print(222)
        yield 3
        print(333)
        yield 5
    f1 = f()
    for i in f1:
        print('*'*i)

    # num1 = next(f1)
    # num2 = next(f1)
    # num3 = next(f1)
    # print(num1,num2,num3)
结果
    111
    *
    222
    ***
    333
    *****

执行效率对比,明显yield一次取10个数比下边这个一次生成所有数据,然后再从所有的数据中判断取值效率要高的多
    def func1():
        for i in range(10000000):
            yield (i+1)%10
    g = func1()
    for i in range(10):
        print(g.__next__())
----------------------------
    def func2():
        new_l = []
        for i in range(10000000):
            new_l.append((i+1)%10)
        return new_l
    ret = func2()
    count = 0
    for i in ret:
        if count < 10:
            print(i)
        else:
            break
        count+= 1
    iter_ret = ret.__iter__()
    for i in range(10):
        iter_ret.__next__()

#做2000001件衣服,前两次每次输出1件,后边一次输出80件
    def get_cloth():
        for i in range(1,2000001):
            yield '第%s件衣服'%i
    g = get_cloth()
    print(g.__next__())     #这是我已经执行过的第1件衣服
    print(g.__next__())     #这是我已经执行过的第2件衣服
    for i in range(80):
        print(g.__next__()) #下面我开始一次要80件就从3件开始执行82结束

# 监听文件的输入,对于文件中随时输入的内容进行自动化分析/自动化展示
def get_line(f):
    f = open('f',encoding='utf-8')
    while True:
        line = f.readline().strip()
        if not line:
            continue
        yield line
line_g = get_line(file)
for line in line_g:
    print(line.split(','))

总结
可迭代对象:
    拥有__iter__方法
    特点:惰性运算
    例如:range(),str,list,tuple,dict,set

迭代器Iterator:
  拥有__iter__方法和__next__方法
  例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o

生成器Generator:
  本质:迭代器,所以拥有__iter__方法和__next__方法
  特点:惰性运算,开发者自定义
使用生成器的优点:
1.延迟计算,一次返回一个结果。不会一次生成所有的结果,这对于大数据量处理,将会非常有用
2.提高代码可读性

send 获取下一个值的效果和next基本一致
只是在获取下一个值的时候,给上一yield的位置传递一个数据
send使用的注意事项
    第一次使用生成器的时候,是用next获取下一个值
    最后一个yield不能接受外部的值
    def f():
        print(111)
        # smell = yield 1   #ret把000传递给yield位置上即smell=000
        # print(smell)
        yield 1             #ret的yield返回值1
        print(222)
        smell2 = yield 2
        print(smell2)       #这个是最后一个yield不接受外部的参数所以没有返回值
        yield 3
    g = f()
    print(g.__next__())
    ret = g.send('000')     #传一个值到生成器里
    print(ret)
    ret2 = g.send('ccc')
    print(ret2)
结果
    111
    1
    222
    2
    ccc
    3
如果启用上边的两行结果
    111
    1
    000
    1
    222
    2


yield from生成器函数调用从中取值?
    def func():
        l1 = [1,2,3]
        s2 = 'abc'
        l1.extend(s2)
        print(l1)
        for i in l1:
            yield i
    g = func()
    for i in g:
        print(i)
结果:
    [1, 2, 3, 'a', 'b', 'c']
    1
    2
    3
    a
    b
    c

事例二
    def func():
        l1 = [1,2,3]
        s2 = 'abc'
        yield from l1   #from循环的意思,相当于for
        yield from s2
    g = func()
    for i in g:
        print(i)
执行结果
    1
    2
    3
    a
    b
    c

事例三
    def func():
        l1 = [1,2,3]
        s2 = 'abc'
        for a in l1:
            yield a
        for j in s2:
            yield  j
    g = func()
    for i in g:
        print(i)

事例四
    def gen2():
        yield from 'AB'
        yield from range(3)
    print(list(gen2()))
等同
    def gen1():
        for c in 'AB':
            yield c
        for i in range(3):
            yield i
    print(list(gen1()))


float 浮点数
    1.2
    2.3333344
    2.56789*10**2 = 256.789
    为什么要叫浮点数 : 因为小数点是浮动的
    浮点数能表示的数学上的数字: 有理数(有限小数 无限循环小数)
    浮点数的小数点是不准确的: 小数的小数位是转换成二进制存储
    如果你创建的变量本身带小数点,那么这个变量的数据类型直接就是浮点数
    所有的除法(除了//)得到的都是小数
浮点数如果太长请转换成字符串存储
a = '1.71264864975975073507505'

除法
python2.x 整数除以整数就会取整(向下取整),有一个数浮点数,就按照浮点数计算
python3.x 所有的除(除了//)的结果都是小数

生成器函数中
send/__next__  生成器函数之外用的
yield/yield from 生成器函数之内用的

next+send == yield
如果函数中的yield要接收参数,那么应该使用send传值
如果函数中的yield不需要接收参数,那么应该使用next即可
生成器和迭代器是一样的,内部的值都只能取一次
从生成器中取值的方式也和迭代器是一样的:
    for
    next/send
    数据类型的强制转换(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))  # 生成器中的数据只能取一次,去完就没有了
结果
[0, 1, 2, 3]
[]

例题二
def demo():
    for i in range(3):
        yield i
g=demo()            #g=0,1,2
g1=(i for i in g)
g2=(i for i in g1)
print(g2.__next__())    #0  #谁先取值就给谁
print(g2.__next__())    #1
print(g1.__next__())    #2,因为g2把前两个数给打印出来了,所以只剩下2,就给了g1
执行结果
0
1
2

列表推导式和生成器表达式
sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以可以直接这样计算一系列值的和
生成器表达式:几乎不占内存
print(sum(x ** 2 for x in range(10000000)))

#而不用多此一举的先构造一个列表:内存占用大,机器容易卡死
#print(sum([x ** 2 for x in range(10000000]))

列表推导式
应用场景:当已经有了一个列表,从这个列表中的每一个元素都需要做某个操作,并且需要将操作的结果放在一个新的列表中,适合使用列表推导式
新的列表 = [每一个元素要做的操作 for 列表中的每一个元素 in 列表]

循环/列表转换成列表推导式
例题一
    l = [1,2,3,4,5,6]
    new_l = []
    for i in l:
        new_l.append(i*i)   #[1,4,9,16,25,36]
    print(new_l)
#列表推导式
    new_l2 = [i*i for i in l]   
    print(new_l2)
等同
    l = [1,2,3,4,5,6]
    print([i*i for i in l])

例题二
    new_lst = []
    for i in range(2,20):
        new_lst.append(i//3)
    print(new_lst)
列表推导式
    lst = [i//3 for i in range(2,20)]
    print(lst)

例题三
#请计算0-100内能被3整除的所有数字
    new_lst = []
    for i in range(0,10):
        if i%3 == 0:
            new_lst.append(i)
    print(new_lst)
列表推导式
    print([i for i in range(0,10) if i%3 == 0])


生成器表达式(的结果是生成器):
    处理比较简单的逻辑
    并且能够以更少的代码来节省时间空间
def func():
    for i in range(101):
        if i%3 == 0:
            yield i
g1 = func()
print(g1.__next__())

生成器表达式
    g = (i for i in range(0,101) if i%3 == 0)
    print(g.__next__())
    print(g.__next__())

#列表推导式
    egg_list=['鸡蛋%s' %i for i in range(10)]
    print(egg_list)
生成器表达式
    egg_list=('鸡蛋%s' %i for i in range(10))
    print(egg_list)
    print(next(egg_list))



# 计算移动平均值的例子(没懂)
#def cal_avg():
#    sum_n = 0
#    count = 0
#    while True:
#        if count:
#            num = yield sum_n/count   # 7/1 num = 9  16/2 = 8  num = 8  24/4 = 8
#        else:
#            num = yield 0    # num = 7
#        sum_n += num         # sum_n = 7 + 9 = 16 +8 = 24 +10
#        count += 1           # count = 1 + 1 = 2 + 1 = 3 + 1
## 7,9,8,10
#g = cal_avg()
#g.__next__()   # 0
#avg1 = g.send(7)
#print(avg1)
#avg2 = g.send(9)
#print(avg2)
#avg3 = g.send(8)
#avg3 = g.send(10)


#def add(n,i):
#    return n+i
#def test():
#    for i in range(4):
#        yield i
#g=test()           #g= 0,1,2,3
#for n in [1,10]:   #n=1,10
#    g=(add(n,i) for i in g) #g= 0,1,2,3所以i=0,1,2,3
#print(list(g))     #因为add=n+i,所以循环第一次就等于0+10,第二次就等于1+10
执行结果
[20, 21, 22, 23]

猜你喜欢

转载自www.cnblogs.com/linuxcbb/p/9910606.html