Python 列表生成式+生成器
一、列表生成式
1.什么是列表生成器
一种可以便捷地生成列表的表达式,有时候可以替换list.append(变量)
2.需求. 如何将列表中的每个数据都加1
列表:data_list = [1,2,3,4,5]
方法一:思路.通过匿名函数+map进行处理
for i in map(lambda x: x + 1, data_list):
print(i)
输出结果:
2,3,4,5,6
方法二:通过for循环,使用enumerate通过下标取值 +1 进行处理
for index, line in enumerate(data_list):
data_list[index] += 1
print(data_list)
输出结果:
[2, 3, 4, 5, 6]
问题三:通过定义空列表,循环需要 +1 的列表进行i+1。append给新列表,然后赋值给需要+1的列表
date02 = []
for i in data_list:
date02.append(i + 1)
date01 = date02
print(date01)
print(date02)
输出结果:
[2, 3, 4, 5, 6]
[2, 3, 4, 5, 6]
问题四:
date02 = [i+1 for i in data_list]
print(date02)
输出结果:
[2, 3, 4, 5, 6]
拓展
使用三元运算将列表中大于几的数字进行固定加值或乘以几
将列表中大于3后的数字乘以10
date01 = [ i*10 if i > 3 else i for i in data_list]
print(date01)
输出结果:
[1, 2, 3, 40, 50]
二、生成器
2.1 定义
通过列表生成式.我们可以直接创建一个列表. 但是list受到内存的限制. 列表容量肯定是有限的. 而且. 创建一个包含100W个元素的列表. 会占用很大的存储空间.如果我们仅仅需要访问前面的几个元素. 那么后面绝大多数元素占用的空间都浪费掉了.
如果. 列表元素可以按照某种算法推算出来.那么我们是否可以在循环的过程中不断推算出后续的元素?这样就不必创建完整的list,从而节省大量的空间.在python中.这种一边循环一边计算的机制.称为生成器(generator)[也成惰性运算.(用到我就算.没用到我就不算)]
2.2 生成器和列表的区别
列表:
优点:取值快,无须进行运算 缺点:占用空间【浪费空间】
生成器:
优点:节省空间 缺点:取值慢需要进行运算【不支持下标取值,只能从前往后取,无法进行跳着取】
2.3 作用
生成一个推到算法,边运算边生成
2.4 定义语法
创建generator的方法很多.最简单的如下:把一个列表生成式的[]改成(). 就创建了一个generator
列表生成式
L = [x * x for x in range(5)]
print(L)
0 1 4 9 16
生成器( at 0x00000000007E7A40>是生成器内存对象)
L = (x * x for x in range(5))
print(L)
at 0x00000000007E7A40>
2.5 取值
生成器取值可通过for循环进行取值. 也可使用L.__next__()或next(L)来进行取值.
PS:访问顺序只能从头开始取.不能跳着取.【取完值后.内存丢弃generator内存地址.不会进行保存.】
如果使用L.__next__()或next(L)取完值后会报StopIteration.抛异常. So.取值我们一般使用for循环进行取.因为generator也是可迭代对象.
使用for循环取值.如下:
L = (x * x for x in range(5))
for i in L:
print(i)
输出:0 1 4 9 16
使用__next__()或next()取值.如下:【ps:在此提醒.如果取值后再取的话会报StopIteration错误异常】
L = (x * x for x in range(5))
print(L.__next__())
print(L.__next__())
print(L.__next__())
print(next(L))
print(next(L))
输出:0 1 4 9 16
2.6 斐波拉契:
generator非常强大.如果推算的算法比较比较复杂.用类似列表生成式的for循环无法实现的时候.还可以用函数来实现.
著名的斐波拉契数列(Fibonacci).除第一个和第二个数外任意一个数都可由前两个数相加.斐波拉契用列表生成式写不出来.用函数把它打印出来却很容易
如:1 1 2 3 5 8 13 21 55 ...【解释:此处的意思就是. 2等于1 + 1 . 3等于1 + 2 . 5等于2 + 3. 8等于3 + 5. 以此类推】如下代码:
def Fib(num):
"""
:param num:此处num代表求几次
:return: #1 1 2 3 5 8 13 21
"""
count = 0
a, b = 0, 1 # 等于a = 0 ,b = 1
while count < num:
tmp = a
a = b
b = a + tmp
print(a)
count += 1
print("done.....")
Fib(15)
输出:1 1 2 3 5 8 13 21 done....
以上实例可以看出定义了斐波拉契数列的推算规则.可以从第一个元素推算出后续任意元素.这种逻辑很像generator.
上边函数斐波拉契函数和generator仅一步之遥.如何将Fib函数变成generator ?只需把print(a)改为yield b就可以.如下代码:
#可粘贴复制代码运行查看代码
def Fib(num):
"""
:param num:此处num代表求几次
:return:
"""
count = 0
a , b = 0 , 1 # 等于a = 0 ,b = 1
while count < num:
tmp = a
a = b
b = a + tmp
# """此处还可这样写:a,b = b,a+b 和上边的效果一样."""
# print(a) # 此处print(a)是打印第一次循环到这的值.注释后看起来更可观
count += 1
yield a # 返回a. 同时挂起当前这个函数.a返回给了通过__next__()或next()调用当前函数的人
print("done.....")
F = Fib(8) # 生成一个生成器对象. 就是代表推到公式准备好了的意思
print(F.__next__()) # 推算取值. 此处打印的值是yield a的结果.
print(F.__next__()) # 推算取值. 此处打印的值是yield a的结果.
print(next(F)) # 推算取值 print(F) # 查看是否什么类型 输出
执行结果:
1 1 2
函数中加yield是生成器. 如果不加yield则是函数.
yield a 作用:yield 返回a.相当于挂起这个函数.不结束.a返回给了通过__next__()或next()调用当前函数的人.后续调起用__next__()或next()
通过yield实现了函数的中断. 并保存了函数的中间执行状态
return a 作用:return 返回函数运行结果并结束函数
总结:
生成器定义方法:
1.通过L = (x * x for x in range(5))【列表生成式的方法使用()小括号进行定义】
2.复杂的推到算法使用yield进行实现【看以上实例】
2.7 使用yield实现单线程并发【实际是单线程】
import time
def consumer(name):
print("%s 来来来来上狗不理包子了!" % name)
while True:
baozi = yield
print("狗不理[%s]来了. 被[%s].全给吃了! " % (baozi, name))
def producer(name):
c1 = consumer("Harry")
c2 = consumer("ZhanSan")
c1.__next__()
c2.__next__()
print("小二赶紧做包子耶. 都等着呢!")
for i in range(10):
time.sleep(1)
print("好嘞.狗不理包子已做好2个了")
"""
send作用:
相当与__next__(). 并且可以传递数据给yield并且唤醒
而__next__()不能传递特定的值. 只能传递None进去
"""
c1.send(i)
c2.send(i)
# 此处的YuHongLin表示是谁调用的词函数.
producer("Ennnn.....")