说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
在构造列表、集合、字典时,Python提供了名为“显示”的特殊句法,用方括号和花括号将容器的元素包裹起来,在括号里支持两种形式:一种是显式地写元素,并用逗号分隔;另一种是写for语句来迭代生成元素,可以有多个for子句和if子句。这是一个非常方便、紧凑地定义列表的方式,可以大幅减少代码量。
以下是推导式构造列表的示例:
# 将一个可迭代的对象展开形成一个列表
[i for i in range(5)]
# [0, 1, 2, 3, 4]
# 可以处理结果
['第'+str(i) for i in range(5)]
# ['第0', '第1', '第2', '第3', '第4']
# 可以进行条件筛选, 实现取偶数
[i for i in range(5) if i%2==0]
# 拆开字符, 过滤空格,全变成大写
[i.upper() for i in 'Hello world' if i != ' ']
# ['H', 'E', 'L', 'L', 'O', 'W', 'O', 'R', 'L', 'D']
# 对于复杂逻辑可以分行编写
[i.upper()
for i in 'Hello world'
if i != ' '
]
# 条件分支
data= ['good','bad','bad','good','bad']
[1 if x == 'good' else 0 for x in data]
# [1, 0, 0, 1, 0]
以下是集合的推导式:
{i**2 for i in range(1,4)}
# {1, 4, 9}
my_list = [1, 2, 3]
{i**2 for i in my_list}
# {1, 4, 9}
以下是利用推导式生成新的字典:
d = {i: i*10 for i in range(1,5)}
# {1: 10, 2: 20, 3: 30, 4: 40}
# 键值互换
d = {'name': 'Tom', 'age': 18, 'height': 180}
{v:k for k,v in d.items()}
# {'Tom': 'name', 18: 'age', 180: 'height'}
如果要将两个序列对齐为对应的键值,可以借助Python内置函数zip():
names = ('Tom', 'Lily', 'Lucy')
ages = [18, 19, 20]
# 对齐
list(zip(names, ages))
# [('Tom', 18), ('Lily', 19), ('Lucy', 20)]
{
name:age
for name,age in zip(names, ages)
}
# {'Tom': 18, 'Lily': 19, 'Lucy': 20}
以下是一个有多个for语句的示例,生成了一个排列组合数列:
# 生成排列组合数
[
(i,j)
for i in range(10)
for j in range(5)
if i > 6
]
'''
[(7, 0),
(7, 1),
(7, 2),
(7, 3),
(7, 4),
(8, 0),
(8, 1),
(8, 2),
(8, 3),
(8, 4),
(9, 0),
(9, 1),
(9, 2),
(9, 3),
(9, 4)]
'''
可见,如果推导式语句比较复杂,可以将代码物理换行,利用容器中的隐式拼接特性使代码更加易读、易写。
集合和字典的括号都是花括号,但字典必须产生的元素是包含键值的k:v格式,语句执行时会自动认定为字典,而不是集合。
要注意的是,元组没有推导式,在圆括号写类似的代码产生的是一个迭代器,可以参考笔者的新书《Python之光:Python编程入门与实战》。
推导式的结构是一个单独表达式后面加至少一个 for 子句以及零个或更多个 for 或 if 子句。 在这种情况下,新容器的元素产生方式是将每个 for 或 if 子句视为一个代码块,按从左至右的顺序嵌套,然后每次到达最内层代码块时就对表达式进行求值以产生一个元素。
不过,除了最左边 for 子句中的可迭代表达式,推导式是在另一个隐式嵌套的作用域内执行的。 这能确保赋给目标列表的名称不会“泄露”到外层的作用域。
最左边的 for 子句中的可迭代对象表达式会直接在外层作用域中被求值,然后作为一个参数被传给隐式嵌套的作用域。 后续的 for 子句以及最左侧 for 子句中的任何筛选条件不能在外层作用域中被求值,因为它们可能依赖于从最左侧可迭代对象中获得的值。
例如:
[x*y for x in range(10) for y in range(x, x+10)]
示例:
[i for i in range(3)]
# [0, 1, 2]
{i for i in range(3)}
# {0, 1, 2}
{i:i+1 for i in range(3)}
# {0: 1, 1: 2, 2: 3}
为了确保推导式得出的结果总是一个类型正确的容器,在隐式嵌套作用域内禁止使用 yield 和 yield from 表达式。
从 Python 3.6 开始,在 async def 函数中可以使用 async for 子句来迭代 异步迭代器(asynchronous iterator)。 在 async def 函数中构建推导式可以通过在打头的表达式后加上 for 或 async for 子句,也可能包含额外的 for 或 async for 子句,还可能使用 await 表达式。 如果一个推导式包含 async for 子句或者 await 表达式,则被称为 异步推导式。 异步推导式可以暂停执行它所在的协程函数。 另请参阅 PEP 530。
例如:
import asyncio
async def numbers(numbers):
for i in range(numbers):
yield i
await asyncio.sleep(0.5)
[i async for i in numbers(10) if i % 2]
# [1, 3, 5, 7, 9]
# 支持的形式如:
[await fun() for fun in funcs]
{await fun() for fun in funcs}
{fun: await fun() for fun in funcs}
[await fun() for fun in funcs if await smth]
{await fun() for fun in funcs if await smth}
{fun: await fun() for fun in funcs if await smth}
[await fun() async for fun in funcs]
{await fun() async for fun in funcs}
{fun: await fun() async for fun in funcs}
[await fun() async for fun in funcs if await smth]
{await fun() async for fun in funcs if await smth}
{fun: await fun() async for fun in funcs if await smth}
3.6 新版功能: 引入了异步推导式。
在 3.8 版更改: yield 和 yield from 在隐式嵌套的作用域中已被禁用。
更新时间:Sept. 8, 2023, 9:16 p.m. 标签:python 推导式