python从零开始--14 列表生成式和生成器

本文详细介绍了Python中列表生成式的使用方法及其优势,并深入探讨了生成器的两种创建方式——基于列表生成式和基于函数。通过实例展示了如何利用生成器节省内存资源,以及如何改造普通函数为生成器。

  列表生成式:

  用遍历的方式生成的list,比如:

my_list1 = []
for x in range(1, 20):
    my_list1.append(x * x + 2)
print(my_list1)

  能用遍历生成的list, 可以用简化的方式实现上面的代码,但从易读性的角度看,还是上面的写法好:

my_list2 = [x*x + 2 for x in range(1, 20)] # for前面的 x*x + 2就是计算的语句, for后面的语句就是循环迭代
print(my_list2)

  两层循环也可以用上面的方式实现,这种简化写法就是“列表生成式”

my_list3 = [m - n for m in range(1,20) for n in range(2,5)]
print(my_list3)
[3, 6, 11, 18, 27, 38, 51, 66, 83, 102, 123, 146, 171, 198, 227, 258, 291, 326, 363]
[3, 6, 11, 18, 27, 38, 51, 66, 83, 102, 123, 146, 171, 198, 227, 258, 291, 326, 363]
[-1, -2, -3, 0, -1, -2, 1, 0, -1, 2, 1, 0, 3, 2, 1, 4, 3, 2, 5, 4, 3, 6, 5, 4, 7, 6, 5, 8, 7, 6, 9, 8, 7, 10, 9, 8, 11, 10, 9, 12, 11, 10, 13, 12, 11, 14, 13, 12, 15, 14, 13, 16, 15, 14, 17, 16, 15]

 对字典、字符串的遍历也可,用来写生成式:

student_dict = {"张三": 15, "李四": 14, "王五": 16}
#  k + "年龄为:" + str(v) 用来拼接字符串,v是int类型,所以先转换为字符串
print([k + "年龄为:" + str(v) for k, v in student_dict.items()])
['李四年龄为:14', '张三年龄为:15', '王五年龄为:16']

  生成器(基于列表生成式的生成器):

  列表生成式会一次性生成全部的元素计算出来并存储到内存,在列表规模比较大的时候会占用较多的内存;采用生成器,则是用到一个元素,才会去计算一个元素,节约了内存消耗。

  生成器的创建,将上面的生成式的[]改成(),就变成了一个生成器,如下方代码;因为gen1创建出来后,并没有存储的值,所以打印后展现的是一个生成器对象地址。 要访问它的值,需要用到next()函数,每用next()调用生成器一次,生成器就生成一个元素,并且下一行要执行的代码的位置会被记录,直到在此调用next();再次调用next(),生成器函数会从它上次离开的地方开始。

gen1 = (x*x + 2 for x in range(1, 20))
print(gen1)

i = 0
while i < 10:
    print(next(gen1))
    i += 1
D:\pythonProjects\venv\Scripts\python.exe D:/pythonProjects/100Prac/55.py
<generator object <genexpr> at 0x0000000000B65150>
3
6
11
18
27
38
51
66
83
102

  这种生成还可以用for循环访问:

gen1 = (x*x + 2 for x in range(1, 5))
print(gen1)

for i in gen1:
    print(i)
D:\pythonProjects\venv\Scripts\python.exe D:/pythonProjects/100Prac/55.py
<generator object <genexpr> at 0x0000000001135150>
3
6
11
18

  生成器(基于函数的生成器):

  还有一种生成器的创建方法,就是基于改造函数。我尝试了一下,将改造要点列举在下面:

  1. 将函数返回值从return变为yield。

       2. 遇到要改造的函数中有循环,需要将其改造为无限循环

       3. 调用的方式与函数也不一样,要先创建生成器对象,再进行调用 (因为只有有一个明确的对象后,才能记录代码执行到什么位置了)

  下面改造下面的普通函数来说明:

def fib(max):
    n, a, b = 0, 0, 1
    fib_list = []
    while n < max:
        fib_list.append(b)
        a = b
        b = a + b
        n += 1
    return fib_list

for i in fib(4):
    print(i)
D:\pythonProjects\venv\Scripts\python.exe D:/pythonProjects/100Prac/56-1.py
1
2
4
8

  step1: 首先将return改造为yield,因为上面的函数是一次性计算了所有需要的b,然后返回一个b值的列表。这个对于生成器而言就不合适,生成器的本意是,你调用我一次,我返回一个b值,你再调用一次,我在继续运行代码,返回下一个b值。所以需要将列表去掉,将返回的内容直接指定为b

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a = b
        b = a + b
        n += 1

for i in fib(4):
    print(i)

  step2: 将循环改为无限循环,原因是为了避免“StopIteration”异常,因为next()函数现在还并有智能到如同 for..in..,知道什么时候该停下来,一但调用超出循环范围,程序就会异常。 改为无限循环后,就不会出现异常了。

def fib():
    a, b = 0, 1
    while True:
        yield b
        a = b
        b = a + b

for i in fib(4):
    print(i)

  step 3:  函数已经改造完毕,接着调用也需要改造,关键是要创建生成器对象,再用next()调用对象。

def fib():
    a, b = 0, 1
    while True:
        yield b
        a = b
        b = a + b

f = fib()
for i in range(1, 5):
    print(next(f))

运行结果如下:

D:\pythonProjects\venv\Scripts\python.exe D:/pythonProjects/100Prac/56.py
1
2
4
8


例子三: 杨辉三角

def triangles():
    L = [1]
    while True:
        yield L
        L = [1] + [L[i] + L[i+1] for i in range(len(L)-1)] + [1]

a = triangles()
for i in range(10):
    print(next(a))
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]


   



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值