python (生成器,生成推导式)

一. 生成器

  生成器的实质就是迭代器, 在python中有三种方式获得生成器

    1. 生成器函数

    2. 各种推导式实现生成器

    3. 数据转换也可以获得生成器

def func():
    print("111")
    return 222
ret = func()
print(ret)

#结果:
#    111
#    222

  将函数中的return 换成 yield就是生成器

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

#结果:
#<generator object func at 0x0000025256283A40>

  以上两段代码执行的结果不一样. 因为下面的代码有yield,所以这个函数就是一个生成器函数.这个时候再执行这个函数,就不在是函数的执行了.而是获取这个生成器.

  所以要想运行这个函数,就要执行__next__()函数来执行函数

def func():
    print("111")
    yield 222
gener = func()  # 这个时候函数不会执行. 而是获取到⽣生成器
ret = gener.__next__()  # 这个时候函数才会执行. yield的作⽤和return⼀样. 也是返回数据
print(ret)

#结果:
#     111
#     222

  可以看到:return和yield执行的效果是一样. 但是二者是有区别的,yield是分段执行一个函数,return是直接停止执行函数

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

#结果:
#111
#222
#333
#444
#Traceback (most recent call last):
# File "D:/pycharm项目/week3/day3/函数生成器.py", line 103, in #<module>
#    ret3 = gener.__next__()  # 最后⼀一个yield执⾏行行完毕. 再次#__next__()程序报错, 也就是 说. 和return⽆无关了了.
#StopIteration

  当程序运行完后⼀个yield. 那么后面继续进行__next__()程序会报错. 

  生成器作用:

def cloth():
    lst = []
    for i in range(0, 100):
        lst.append("衣服"+str(i))
    return lst
cl = cloth()
print(cl)

#结果:
#['衣服0', '衣服1', '衣服2', '衣服3', '衣服4', '衣服5', '衣服6', '衣服7', '衣服8', '衣服9', '衣服10', '衣服11', '衣服12', '衣服13', '衣服14', '衣服15', '衣服16', '衣服17', '衣服18', '衣服19', '衣服20', '衣服21', '衣服22', '衣服23', '衣服24', '衣服25', '衣服26', '衣服27', '衣服28', '衣服29', '衣服30', '衣服31', '衣服32', '衣服33', '衣服34', '衣服35', '衣服36', '衣服37', '衣服38', '衣服39', '衣服40', '衣服41', '衣服42', '衣服43', '衣服44', '衣服45', '衣服46', '衣服47', '衣服48', '衣服49', '衣服50', '衣服51', '衣服52', '衣服53', '衣服54', '衣服55', '衣服56', '衣服57', '衣服58', '衣服59', '衣服60', '衣服61', '衣服62', '衣服63', '衣服64', '衣服65', '衣服66', '衣服67', '衣服68', '衣服69', '衣服70', '衣服71', '衣服72', '衣服73', '衣服74', '衣服75', '衣服76', '衣服77', '衣服78', '衣服79', '衣服80', '衣服81', '衣服82', '衣服83', '衣服84', '衣服85', '衣服86', '衣服87', '衣服88', '衣服89', '衣服90', '衣服91', '衣服92', '衣服93', '衣服94', '衣服95', '衣服96', '衣服97', '衣服98', '衣服99']



def cloth():
    for i in range(0, 10000):
        yield "衣服"+str(i)
cl = cloth()
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())

#结果:
#    衣服0
#    衣服1
#    衣服2
#    衣服3

  区别: 第一种是将循环的内容直接全部拿出来,很占用内存. 第二种使用生成器,一个__next__函数只拿出一个,之后循环停止在当前位置,下一个再取值再从停止位置继续向前取值.

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)

#结果:
#    我吃什么啊
#    馒头
#    a= 胡辣汤
#    大饼
#    b= 狗粮
#    韭菜盒子
#    c= 猫粮
#    GAME OVER

  send和__next__()区别: 1. send和next()都是让生成器向下走⼀次 2. send可以给上⼀个yield的位置传递值, 不能给后⼀个yield发送值. 在第⼀次执行生成器代码的时候不能使用send()

  生成器可以使用for循环来循环获取内部的元素:

def func():
    print(111)
    yield 222
    print(333)
    yield 444
    print(555)
    yield 666
gen = func()
for i in gen:
    print(i)

#结果:
#    111
#    222
#    333
#    444
#    555
#    666

二. 列表推导式

  列表推导式:最终给的是列表

  语法:  [最终结果(变量) for 变量 in 可迭代对象]

lst = []
for i in range(1,15):
    lst.append("python%s" % i)
print(lst)
#结果:
#['python1', 'python2', 'python3', 'python4', 'python5', 'python6', 'python7', 'python8', 'python9', 'python10', 'python11', 'python12', 'python13', 'python14']


lst = ["python%s" % i for i in range(1,15)]  
print(lst)              
#结果:
#['python1', 'python2', 'python3', 'python4', 'python5', 'python6', 'python7', 'python8', 'python9', 'python10', 'python11', 'python12', 'python13', 'python14']

   列表推导式是通过⼀行来构建你要的列表, 列表推导式看起来代码简单. 但是出现错误之后很难排查. 

  ⽣生成器表达式和列表推导式的语法基本上是⼀样的. 只是把[]替换成() 

g = (i for i in range(10))
print(g)    #打印结果是一个生成器
#结果:
#<generator object <genexpr> at 0x0000029147073A40>


gen = ("麻xx我第%s次爱你" % i for i in  range(10))
for i in  gen:    #遍历生成器内容
    print(i)
#结果:
#麻xx我第0次爱你
#麻xx我第1次爱你
#麻xx我第2次爱你
#麻xx我第3次爱你
#麻xx我第4次爱你
#麻xx我第5次爱你
#麻xx我第6次爱你
#麻xx我第7次爱你
#麻xx我第8次爱你
#麻xx我第9次爱你      

  生成器表达式可以进行筛选:

lst = [i for i in range(1,101) if i % 3 == 0]
print(lst)
#结果:
#[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]

lst = [i*i for i in range(1,100) if i % 3 ==0]
print(lst)
#结果:
#[9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304, 2601, 2916, 3249, 3600, 3969, 4356, 4761, 5184, 5625, 6084, 6561, 7056, 7569, 8100, 8649, 9216, 9801]

找出下列名字中带有两个'e'的.
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,
'Joe'],['Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' , 'Eva']]
lst = [ii for i in names for ii in i if ii.count('e') ==2]
print(lst)
#结果:
#['Jefferson', 'Wesley', 'Steven', 'Jennifer']

  生成器表达式和列表推导式的区别:

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

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

  生成器的惰性机制:生成器只有在访问的时候才取值. 说白了就是找他要他才给值,不要的话,他是不会执行的

  总结:

    推导式有列表推导式,字典推导式,集合推导式,没有元组推导式

    生成器表达式:(结果 for 变量 in 可迭代对象 if 条件筛选)

    生成器表达式可以直接获取到生成器对象,生成器可以直接进行for循环. 生成器具有惰性机制

原文地址:https://www.cnblogs.com/dong-/p/9330727.html