Kodewick
第 15 课,共 31 课目录

推导式

第 15 课,共 31 课 · 第 2 部分 · 容器和循环 · 约 20 分钟

很多循环干的是同一件事:先建一个空列表,走过一串东西,每个东西往里 append 一个值。Python 有一种专门的短写法,叫推导式。先看长的写法:

main.py
1loads = [3, 8, 2, 6]2doubled = []3for ore in loads:4    doubled.append(ore * 2)5print(doubled)
输出
[6, 16, 4, 12]

把循环改成一行

同一个列表,用列表推导式来写:

main.py
1loads = [3, 8, 2, 6]2doubled = [ore * 2 for ore in loads]3print(doubled)
输出
[6, 16, 4, 12]

读作「loads 里的每个 ore,都取 ore * 2」。循环里的几样东西都还在,只是换了位置:

部分在循环里在推导式里
要收集什么doubled.append(ore * 2)最前面的 ore * 2
东西从哪来for ore in loads:后面的 for ore in loads
新列表循环前的 doubled = []包住整个式子的方括号

小技巧: 两个例子都点单步看看。循环是一行一行地转;推导式也是每个东西走一步,边走边把列表搭起来。

每个都变一变

最前面可以是任何表达式:算式、字符串方法、f-string 都行。for 后面能放 for 循环能遍历的任何东西,range() 也可以。

main.py
1names = ["ada", "ben", "cleo"]2print([name.title() for name in names])3print([len(name) for name in names])4print([n * n for n in range(1, 6)])5print([f"{n} 号灯" for n in range(3)])
输出
['Ada', 'Ben', 'Cleo']
[3, 3, 4]
[1, 4, 9, 16, 25]
['0 号灯', '1 号灯', '2 号灯']

只留下一部分

最后加一个 if,只有通过检查的东西才放进新列表,其他的都跳过。

main.py
1loads = [3, 8, 2, 6, 11]2big = [ore for ore in loads if ore >= 5]3print(big)4words = ["mine", "ore", "lamp", "warehouse"]5print([w.upper() for w in words if len(w) > 3])
输出
[8, 6, 11]
['MINE', 'LAMP', 'WAREHOUSE']
先猜一猜

这段会显示什么?

1numbers = [1, 2, 3, 4, 5, 6]2print([n * 10 for n in numbers if n % 2 == 0])

用 if-else 选值

想每个都留下,只是给每个选一个值,就在最前面用条件表达式 a if 条件 else b:

main.py
1loads = [3, 8, 2, 6]2sizes = ["大" if ore >= 5 else "小" for ore in loads]3print(sizes)
输出
['小', '大', '小', '大']

if 放的位置不同,作用也不同:

位置写法作用
放在最后[x for x in items if 条件]过滤:列表可能变短
放在最前[a if 条件 else b for x in items]选值:列表长度不变
先猜一猜

显示出来的列表里有几个数?

1print([n if n > 0 else 0 for n in [4, -2, 7, -5]])

字典推导式

换成花括号,最前面写一对 键: 值,得到的就是字典,不是列表。zip() 和 .items() 在这里也和 for 循环里一样用。

main.py
1names = ["ada", "ben", "cleo"]2print({name: len(name) for name in names})3prices = {"铁": 4, "铜": 6, "金": 20}4print({item: price * 2 for item, price in prices.items()})5print({item: price for item, price in prices.items() if price > 5})
输出
{'ada': 3, 'ben': 3, 'cleo': 4}
{'铁': 8, '铜': 12, '金': 40}
{'铜': 6, '金': 20}

集合推导式

花括号里最前面只有一个值,得到的就是集合,重复的会自动去掉。看看 0 到 9 除以 4,余数都有哪些:

main.py
1print({n % 4 for n in range(10)})2words = ["Lamp", "lamp", "ORE", "ore", "Mine"]3print(sorted({w.lower() for w in words}))
输出
{0, 1, 2, 3}
['lamp', 'mine', 'ore']

字符串的集合每次运行,显示的顺序都可能不一样,所以第二行套了一个 sorted(),得到一个顺序固定的列表。

括号最前面得到
[ ... ]一个值列表
{ ... }键: 值字典
{ ... }一个值集合

什么时候不要用

推导式是用来搭一个新容器的。遇到下面这些情况,还是用普通的 for 循环:

  • 循环是去做一件事,比如显示,而不是收集值;
  • 要用 break,或者每个东西要做好几步;
  • 推导式长到要读两遍才看得懂。

下面这个循环又要显示、又要中途停下、还要累加,就该保持循环的样子:

main.py
1loads = [4, 7, 0, 5]2total = 03for ore in loads:4    if ore == 0:5        print("空车,停")6        break7    total += ore8    print(f"卸下 {ore},一共 {total}")
输出
卸下 4,一共 4
卸下 7,一共 11
空车,停

短而清楚才是好。推导式不比循环清楚的话,就写循环。

练一练

写好代码,点检查。提示随便看,实在想不出来再看答案。每道题都能一行写完。

练习 1

偶数的平方

把空列表换成推导式,装下 numbers 里每个偶数的平方。

要显示
[4, 16, 36, 64, 100]
exercise1.py
1numbers = list(range(1, 11))2squares = []3print(squares)
输出
点「运行」或按 Ctrl+Enter
练习 2

亮还是灭

把油量列表变成一个字的列表:油量大于 0 就是 "亮",否则是 "灭"。

要显示
['亮', '灭', '亮', '灭', '亮']
exercise2.py
1oil = [5, 0, 2, 0, 7]2states = [level for level in oil]3print(states)
输出
点「运行」或按 Ctrl+Enter
练习 3

补货单

做一个补货用的字典:库存不到 10 的东西,各要再进几个才够 10。

要显示
{'铜': 7, '金': 10}
exercise3.py
1stock = {"铁": 14, "铜": 3, "煤": 22, "金": 0}2order = {}3print(order)
输出
点「运行」或按 Ctrl+Enter

这一课学会了

  • [表达式 for x in 一串东西] 一行搭出新列表,不用先建空列表再在循环里 append。
  • if 放在最后是过滤;a if 条件 else b 放在最前是给每个东西选值。
  • {键: 值 for ...} 搭出字典;{值 for ...} 搭出集合。
  • 推导式用来搭容器。要显示、要 break、每个东西要做好几步,就用普通循环。
  • 推导式读起来费劲,就说明循环更合适。