推导式、生成式与生成器

逸兴
逸兴
逸兴
57
文章
25
评论
2025-01-2915:56:43推导式、生成式与生成器已关闭评论 4438字阅读14分47秒

推导式

概述

Python中的推导式是一种快速、简洁的数据结构创建方式,不需要手动创建数据结构中的每一个元素,类似于给出一个规律,python会根据这个规律自动填充数据结构。支持有列表推导式字典推导式集合推导式

列表推导式

嵌套if语句

创建一个包含1-10中的所有偶数的列表,常规的做法可能是:

# 先创建一个空列表
even_numbers = []

# 然后遍历1-101,找到其中的偶数
for x in range(1, 11):
    if x & 1 == 0:
        # 最后将找到的偶数添加到列表中
        even_numbers.append(x)

print(even_numbers)

# 输出结果
[2, 4, 6, 8, 10]

推导式依然遵循这个计算逻辑,但是写法不同,更为简介:

even_numbers2 = [x for x in range(1, 11) if x & 1 == 0]
print(even_numbers2)

# 输出结果
[2, 4, 6, 8, 10]

其计算过程依然是先遍历range(1-11),然后每次遍历的值都会和1进行按位与,如果是0,就将值也就是x放入列表中,也就是推导式开头的x

另外,还可以对取得的值x再进行处理,比如每个值都加2

even_numbers3 = [x+2 for x in range(1, 11) if x & 1 == 0]
print(even_numbers3)

# 输出结果
[4, 6, 8, 10, 12]

嵌套循环

Python中也运行对推导式进行循环嵌套

比如:计算99乘法表

multiplication_table = [f{x} x {y} = {x * y} for x in range(1, 10) for y in range(x, 10)]
print(multiplication_table)

# 输出结果
['1 x 1 = 1', '1 x 2 = 2', '1 x 3 = 3', '1 x 4 = 4', '1 x 5 = 5', '1 x 6 = 6', '1 x 7 = 7', '1 x 8 = 8', '1 x 9 = 9', '2 x 2 = 4', '2 x 3 = 6', '2 x 4 = 8', '2 x 5 = 10', '2 x 6 = 12', '2 x 7 = 14', '2 x 8 = 16', '2 x 9 = 18', '3 x 3 = 9', '3 x 4 = 12', '3 x 5 = 15', '3 x 6 = 18', '3 x 7 = 21', '3 x 8 = 24', '3 x 9 = 27', '4 x 4 = 16', '4 x 5 = 20', '4 x 6 = 24', '4 x 7 = 28', '4 x 8 = 32', '4 x 9 = 36', '5 x 5 = 25', '5 x 6 = 30', '5 x 7 = 35', '5 x 8 = 40', '5 x 9 = 45', '6 x 6 = 36', '6 x 7 = 42', '6 x 8 = 48', '6 x 9 = 54', '7 x 7 = 49', '7 x 8 = 56', '7 x 9 = 63', '8 x 8 = 64', '8 x 9 = 72', '9 x 9 = 81']

这里的循环嵌套就相到于:

multiplication_table2 = []
for x in range(1, 10):
    for y in range(x, 10):
        multiplication_table2.append(f{x} * {y} = {x * y})
print(multiplication_table2)

此外,推导式还支持字典、集合

字典推导式

{} 表示字典,其中写推导式

创建一个字典,key是1-5的数字,值是key的平方

squares_dict = {x: x**2 for x in range(1, 5)}
print(squares_dict)

# 输出结果
{1: 1, 2: 4, 3: 9, 4: 16}

字典推导式中不适合嵌套循环,因为字典的key是唯一的。

集合推导式

例如:提取字符串中的唯一字符

unique_chars = {char for char in hello world}
print(unique_chars)

# 输出结果
{'h', 'r', 'd', 'w', 'o', 'e', 'l', ' '}

元组()没有推导式,()用于生成式。

生成式

如果说推导式是根据规律自动填充数据,那么生成式则是规律本身,因为它是惰性的不会一次性生成所有元素,而是按需生成,每执行一次则生成一个元素。生成式本身是一个可迭代对象。

语法:

(表达式 for 变量in 可迭代对象 if 条件)

( )表示生成式, 如果换成 [ ]则表示列表推导式。

# 生成 1~10 的平方,但不会一次性存入列表
squares_gen = (x**2 for x in range(1, 10))

print(squares_gen)  # 输出的是生成器对象

# 输出结果
<generator object=""> at 0x102273bc0></generator>

next() 迭代取值

next() 函数接收一个生成器对象,每执行一次则返回迭代器的下一个元素对象。

# 生成 1~10 的平方,但不会一次性存入列表
squares_gen = (x**2 for x in range(1, 10))

print(squares_gen)  # 输出的是生成器对象

for i in range(1, 3):
    print(f第{i}次: {next(squares_gen)})

需要注意的是 生成式只能被遍历一次,当遍历结束后生成式就会耗尽,无法再次使用。等待被 垃圾回收(Garbage Collection),或者手动删除引用后销毁。

生成式更节省内存

由于生成式不会一次性生成所有数据,而是按需生成,相比推导式更节省内存。

测试:

生成1-10000的序列,检查内存占用

# 推导式
list_squares = [x for x in range(10000)]
print(sys.getsizeof(list_squares), bytes)

# 输出结果
85176 bytes
# 生成式
gen_squares = (x for x in range(10000))
print(sys.getsizeof(gen_squares), bytes)

# 输出结果
104 bytes

生成式与列表、字典、集合的转换

转换为列表,list()函数

gen_even_numbers = (x for x in range(1, 11) if x & 1 == 0)
list_even_numbers = list(gen_even_numbers)

转换为集合,set()函数

unique_chars = (char for char in hello word)
set_unique_chars = set(unique_chars)

转换为字典,dict()函数

square_dict = dict({x: x**2 for x in range(4)})

这里转换的过程就是就生成式迭代的过程,也就意味着生成式已经被消费了,无法继续使用。

# 反转字典
students = {Alice: 85, Bob: 92, Charlie: 78}
reversed_dict = dict((v, k) for k, v in students.items())

生成器

生成器可以看做加强版的生成式,本质上是一个函数,借助 yield 关键字变成生成器,可以按需生成数据节省内存。

与生成式不同的是,它可以实现复杂的逻辑如状态保存、多步计算,并且生成器可以进行复用。

例如:

# 定义生成器gen
def gen():
    for i in range(1, 10):
        if i & 1 == 0:
            yield i

# 赋值给多个对象,实现复用
g = gen()
h = gen()
for x in range(4):
    print(next(g))

for x in range(4):
    print(next(h))

# 输出结果
2
4
6
8 # g耗尽

2
4
6
8 # h耗尽

关于yield

yield 是一个 关键字,用于在 生成器(Generator)暂停函数的执行,并返回一个值,但不会终止函数。当生成器的 next() 方法被调用时,函数会从 yield 语句暂停的地方继续执行。

生成器中可以使用多个yield

示例:

def my_generator():
    print(执行第一步)
    yield 1  # 第一次调用 next(),返回 1 后暂停,等待下次调用
    print(执行第二步)
    yield 2  # 第二次调用 next(),返回 2 后暂停,等待下次调用
    print(执行第三步)
    yield 3  # 第三次调用 next(),返回 3 后暂停,等待下次调用

gen = my_generator()  # 创建生成器对象
print(next(gen))  # 执行到 yield 1
print(next(gen))  # 继续执行到 yield 2
print(next(gen))  # 继续执行到 yield 3
print(next(gen))  # 抛出 StopIteration 异常

生成器暂停后,可以调用 gen.close() 手动终止生成器或者 del gen删除引用。

生成器一旦终止,之后再调用 next()方法,则会抛出 StopIteration 异常。

yield from嵌套生成器

def sub_gen():
    print(in sub 1)
    yield a  # 2
    print(in sub 2)
    yield b  # 3

def main_gen():
    print(in main 1)
    yield A  # 1
    print(in main 2)
    yield from sub_gen() 
    print(in main 3)
    yield B  # 4
g = main_gen()

for i in range(1, 6):
    print(f第{i}次执行, **20)
    print(next(g))

# 执行结果
第1次执行 ********************
in main 1
A
第2次执行 ********************
in main 2
in sub 1
a
第3次执行 ********************
in sub 2
b
第4次执行 ********************
in main 3
B
第5次执行 ********************
StopIteration

yield frommain_gen() 直接返回 sub_gen() 产生的值,同样的遇到 yield 都会暂停执行。




https://www.hugbg.com/archives/3855.html
逸兴
  • 本文由 发表于 2025-01-2915:56:43
  • 除非特殊声明,本站文章均为原创,转载请务必保留本文链接
自动更新SSL证书 默认分类

自动更新SSL证书

现在免费的SSL证书只有三个月有效期,有一个博客和图床都用的ssl证书到期需要重新签发,挺麻烦的。原本想着写个脚本通过阿里云的 OpenAPI 进行证书的签发和部署,但是偶然发现了 ACME 这个项目...
CVE-2024-38077 Windows RDL漏洞检测修复方法(末尾) 默认分类

CVE-2024-38077 Windows RDL漏洞检测修复方法(末尾)

一、漏洞详情 Windows Server是由微软开发的操作系统系列,专为服务器环境设计,用于管理网络、数据存储和应用程序的运行。它为企业和组织提供了稳定、可靠的服务器平台,支持各种规模的网络基础设施...
Django DRF禁用URL末尾斜杆(:) 点点滴滴

Django DRF禁用URL末尾斜杆(:)

一、关于URL末尾斜杆 比如http://127.0.0.1:8000/api/v1/register 和 http://127.0.0.1:8000/api/v1/register/, 这两个是同一...
Mac pip安装mysqlclient报错 默认分类

Mac pip安装mysqlclient报错

问题详情 环境: Mac OS14.4, Python3.10, Django 5.0 mac 上使用docker运行了一个mysql容器, 然后终端中安装了brew install mysql-cl...