python--函数之-生成器与生成器表达式

时间:2021-05-02 18:49:41

   生成器

       生成器的实质就是迭代器.

      生成器的特点和迭代器的特点一样,取值方式和迭代器一样( _next_() ).

     生成器一般由生成器函数或者生成器表达式来创建.

    其实就是手写的迭代器.

一.生成器函数:

         1.生成器函数:

def func():
    print("111")
    yield 222
g = func()
print(g)

结果:
<generator object func at 0x00000000024958E0>            #生成器

          由于函数中存在了 yield ,那么这个函数就是一个生成器函数,这个时候在执行这个函数的时候,就不在是函数的执行了,而是获得这个生成器.

def func():
    print("111")
    yield 222

g = func()       #这个时候函数不会执行,而是获得生成器.
ret = g.__next__()    #这个时候函数才会执行,yield的作业和return一样,也是返回数据.
print(ret)                   


结果:
111
222

                         所以,我们可以执行 _next_ 来执行生成器.

      2.yield 与 return 的区别

          yield 是分段执行一个函数, 但是return 是直接停止执行函数.

def func():
    print("111")
    yield 222
    print("333")
    yield 444
gener = func()
ret = gener.__next__()
print(ret)
ret1 = gener.__next__()
print(ret1)
ret2 = gener.__next__()   #最后一个 yield 执行完毕,再次 _next_() 会报错,也就是说和return无关了. print(ret2)

结果
Traceback (most recent call last): 111
  File "E:/wanchengdezuoye/复习/复习.py", line 1052, in <module>
222
    ret2 = gener.__next__()
333
StopIteration
444

                               所以说,当程序运行完最后一个yield ,那么后面继续进行 _next_(),程序会报错.

       3.生成器的作用:

           比如: 你比较喜欢吃苹果,特别喜欢吃,那么如果你一次性的将你这一年的苹果都买了,那么问题来了,你的苹果放在那里,占用内存.

def func():
    li = []
    for i in range(1,500):
        li.append("买了"+str(i)+"苹果")
    return li
ret = func()
print(ret)

结果:
['买了1苹果', '买了2苹果', '买了3苹果', '买了4苹果', '买了5苹果', 
'买了6苹果',.............,'买了499苹果']

                            但是如果你想吃的时候,想吃一个我去买一个,想吃就买,这样就可以既省内存,还可以保鲜.

def func():
    for i in range(1,500):
        yield "买了"+str(i)+"苹果"
ret = func()
print(ret.__next__())
print(ret.__next__())
print(ret.__next__())
print(ret.__next__())

结果:
买了1苹果
买了2苹果
买了3苹果
买了4苹果

                                                总结:第一种是直接一次性全部拿出来,会很占用内存.

                                                      第二种使用生成器,一次就一个,用多少拿多少,节省内存.

      4.生成器的  send() 方法:

          send() 和 _next_() 的区别:

                     ①.一样都可以让生成器执行下一个yield.

                   ②.send() 可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候不能使用 send() [因为send不能给最后一个yield发送值

def func():
    print("我喜欢吃什么?") #执行的ret1
   a = yield "包子"
    print(a)
   b = yield "馒头"           #a = 金玉满堂,馒头,执行ret2
    print(b)
    c = yield "韭菜盒子"      #b = 乱炖,韭菜盒子,执行ret3
    print(c)
    yield "OVER"            #c = 煎饼.执行ret4   send不能给最后一个yield发送值.
gen = func()
ret1 = gen.__next__()
print(ret1)
ret2 = gen.send("金玉满堂")
print(ret2)
ret3 = gen.send("乱炖")
print(ret3)
ret4 = gen.send("煎饼")
print(ret4)

结果:
我喜欢吃什么?
包子
金玉满堂
馒头
乱炖
韭菜盒子
煎饼
OVER

     二.推导式

             1.列表推导式:

                         例如:给出一个列表,通过循环,向列表中添加1-13

li = []
for i in range(1,14):
    li.append(i)
print(li)

结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

   用列表推导式:

li = [i for i in range(1,14)]
print(li)

结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

         列表推导式语法:  [结果  for循环  判断]

例如:从1-25的树中找出能被2整数的数,添加到列表中.

li = [i for i in range(1,26) if i % 2 == 0]
print(li)

结果:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]


例如2;找出names中带有两个e的人
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,'Joe'],
[ 'Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' , 1]]
lst = [name for line in names for i in line if type(name) == str and name.count("e") == 2]
print(lst)

结果:
['Jefferson', 'Wesley', 'Steven', 'Jennifer']

      2.字典推导式:

                  语法: {k:v  for循环  条件筛选}

    例如:

例1 :将[11,22,33,44] =>变成 {0:11,1:22,2:33,3:44}

算法:
dic = {}
li = [11,22,33,44]
for i in range(len(li)):
    dic[i] = li[i]
print(dic)


字典推导式:
li = [11,22,33,44]
dic = {i:li[i] for i in range(len(li))}
print(dic)

结果:
{0: 11, 1: 22, 2: 33, 3: 44}

例2 :将dic = {"jj": "林俊杰", "jay": "周杰伦", "zs": "赵四", "ln":"刘能"}中的键和值对调.
dic = {"jj": "林俊杰", "jay": "周杰伦", "zs": "赵四", "ln":"刘能"}
dic2 = {v:k for k,v in dic.items()}
print(dic2)

结果:
{'林俊杰': 'jj', '周杰伦': 'jay', '赵四': 'zs', '刘能': 'ln'}

  3.集合推导式:

                    语法:  {结果  for循环  条件筛选}

li = [1,2,5,4,6,4,6,8,9,9,]
set = {i for i in li}
print(set)

结果:
{1, 2, 4, 5, 6, 8, 9}

    三.生成器表达式:

               语法: (结果  for循环  条件筛选)          (注意:元组是没有推导式的!!!!!!!!)

       生成器表达式可以直接获取生成器对象,生成器对象可以直接进行for循环.

用生成器表达式获取1-100内能被3整除的数

s = (i for i in range(1,100) if i % 3 == 0)
for el in s:
    print(el)

结果:
3
6
9
12
.......
99

         1. 生成器表达式 与 列表推导式 的区别:

                 ①.列表推导式比较耗内存,一次性加载.生成器表达式几乎不占用内存,使用的时候才分配和使用内存.

                ②.得到的值不一样,列表推导式得到的是一个列表,生成器表达式获取的是一个生成器.

      比如: 同样一筐鸡蛋,列表推导式:直接拿一筐鸡蛋,--生成器表达式:拿到一只老母鸡,需要鸡蛋的时候就下个鸡蛋.

         2.生成器的懒惰机制:生成器只有在访问的时候才取值.        

def func():
    print(111)
    yield 222
    yield 333

g = func()                #生成器 g
g1 = (i for i in g)       #生成器
#g3 = func()
#g2 = (i for i in g3) #这样,g2会有结果,因为g3获取数据的时候,func()被执行. g2
= (i for i in g1) #生成器 print(list(g)) #[222,333]源头,获取g的数据,这是func()被执行,打印[222,333],从源头将数据拿走 print(list(g1)) #[]执行到g1的时候,源头的数据已经被 g 拿走了,源头没有数据了 print(list(g2)) #[]这里也没有值了. 结果: 111 [222, 333] [] []

    ********面试题*******(有坑)

def add(a,b):                #求和
    return a + b

def test():               #生成器函数  # 
    for r_i in range(4):  #0-3  0,1,2,3
        yield r_i
g = test()                #获取生成器

# n = 1
# n = 10
# n = 5                        #g = 0,1,2,3     =        i = 0,1,2,3
# (add(n,i) for i in (add(n,i) for i in (add(n,i) for i in g)))       #n=5 ********
   #15,16,17,18      #10,11,12,13        # 5,6,7,8
for n in [1,10,5]:
    g = (add(n,i) for i in g)

print(list(g))          #到最后往里面放数据******

结果:
[15,16,17,18]