推导式
很多循环干的是同一件事:先建一个空列表,走过一串东西,每个东西往里 append 一个值。Python 有一种专门的短写法,叫推导式。先看长的写法:
1loads = [3, 8, 2, 6]2doubled = []3for ore in loads:4 doubled.append(ore * 2)5print(doubled)
[6, 16, 4, 12]
把循环改成一行
同一个列表,用列表推导式来写:
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() 也可以。
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,只有通过检查的东西才放进新列表,其他的都跳过。
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 只留下偶数 2、4、6,留下的每一个再算 n * 10。
用 if-else 选值
想每个都留下,只是给每个选一个值,就在最前面用条件表达式 a if 条件 else b:
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]]) 放在最前面的 if-else 不会去掉任何东西,只是选一个值。结果是 [4, 0, 7, 0],还是四个数。
字典推导式
换成花括号,最前面写一对 键: 值,得到的就是字典,不是列表。zip() 和 .items() 在这里也和 for 循环里一样用。
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,余数都有哪些:
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,或者每个东西要做好几步; - 推导式长到要读两遍才看得懂。
下面这个循环又要显示、又要中途停下、还要累加,就该保持循环的样子:
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 空车,停
短而清楚才是好。推导式不比循环清楚的话,就写循环。
练一练
写好代码,点检查。提示随便看,实在想不出来再看答案。每道题都能一行写完。
偶数的平方
✓把空列表换成推导式,装下 numbers 里每个偶数的平方。
[4, 16, 36, 64, 100]
1numbers = list(range(1, 11))2squares = []3print(squares)
亮还是灭
✓把油量列表变成一个字的列表:油量大于 0 就是 "亮",否则是 "灭"。
['亮', '灭', '亮', '灭', '亮']
1oil = [5, 0, 2, 0, 7]2states = [level for level in oil]3print(states)
补货单
✓做一个补货用的字典:库存不到 10 的东西,各要再进几个才够 10。
{'铜': 7, '金': 10}1stock = {"铁": 14, "铜": 3, "煤": 22, "金": 0}2order = {}3print(order)
这一课学会了
[表达式 for x in 一串东西]一行搭出新列表,不用先建空列表再在循环里append。if放在最后是过滤;a if 条件 else b放在最前是给每个东西选值。{键: 值 for ...}搭出字典;{值 for ...}搭出集合。- 推导式用来搭容器。要显示、要
break、每个东西要做好几步,就用普通循环。 - 推导式读起来费劲,就说明循环更合适。