各语言特有 · 40
Python 风格的代码
推导式、带步长的切片、解包、enumerate 和 zip、in、f 字符串,以及三个陷阱。
本单元适用于 Python。
其他语言用循环或库方法做同样的事(JavaScript map、filter,Java 流,C# LINQ,C、C++、Craykon 则是循环加 push_back、添加)。
Python 有许多简写,能把好几行循环缩成一行。
下面的示例依次用到推导式、切片、解包、enumerate、zip 和 f 字符串,最后展示两个不报错却给出错误结果的陷阱。
nums = list(map(int, input().split()))words = input().split() odd_squares = [x * x for x in nums if x % 2 == 1]print(*odd_squares)print({x % 3 for x in nums})print({w: len(w) for w in words}) print(*nums[::-1])print(*nums[1:6:2])print(words[0][::-1]) first, *rest = numsa, b = first, rest[-1]a, b = b, aprint(a, b, len(rest)) for i, w in enumerate(words, start=1): print(f"{i}. {w:<6}|")print(*(f"{w}={n}" for w, n in zip(words, nums)))print(9 in nums, "lo" in words[0]) gen = (x for x in nums)print(sum(gen), sum(gen)) grid = [[0] * 3] * 2grid[0][0] = 1print(grid)grid = [[0] * 3 for _ in range(2)]grid[0][0] = 1print(grid)3 1 4 1 5 9 2 6 hello world codes
9 1 1 25 81
{0, 1, 2}
{'hello': 5, 'world': 5, 'codes': 5}
6 2 9 5 1 4 1 3
1 1 9
olleh
6 3 7
1. hello |
2. world |
3. codes |
hello=3 world=1 codes=4
True True
31 0
[[1, 0, 0], [1, 0, 0]]
[[1, 0, 0], [0, 0, 0]]
要点
- 推导式(comprehension)用一行循环生成新列表。
[x * x for x in nums if x % 2 == 1]的意思是“挑出奇数并求平方的列表”,先建空列表再append的四行循环就变成了这一行。 - 只要换一下括号,同样的形状就能生成集合
{x % 3 for x in nums}或字典{w: len(w) for w in words}。 - 切片
a[开始:结束:步长]不包括结束位置,省略的部分默认为开头、末尾和 1。nums[1:6:2]取第 1、3、5 个,nums[::-1]是倒过来的新列表,"hello"[::-1]是"olleh"。 - 超出范围的切片不会报错,只返回现有的部分(
nums[5:100])。
负数下标从后往前数,所以a[-1]是最后一个。 - 解包:
a, b = b, a不用临时变量就能交换,first, *rest = nums把第一个值和其余部分的列表分开。
需要下标用enumerate(words, start=1),并排遍历两个列表用zip(words, nums)(在较短的那个结束时停下)。 in判断是否包含。
列表从前往后逐个查找,比较慢;集合和字典几乎立刻就能找到。
在字符串上,"lo" in "hello"查找的是子串。
各语言的写法
| Python | f 字符串 f"{i}. {w:<6}|" 在花括号里写表达式,: 后面写格式(<6 左对齐占 6 格,.2f 保留两位小数)。f"{x=}" 会连名字一起输出成 x=3,调试时很方便。很少用 range(len(...)) 遍历下标,带步长的切片和 [::-1] 是 Python 独有的简写 |
|---|
常见错误
默认值用了 []
def add(x, bucket=[]):
bucket.append(x)
return bucket
add(1); print(add(2))→ [1, 2] —— [] 只在定义函数时创建一次,每次调用用的都是同一个列表
写成 bucket=None,在函数里用 if bucket is None: bucket = [] 新建列表。
生成器用了两次
gen = (x for x in nums)
print(sum(gen), sum(gen))→ 31 0 —— 圆括号 ( ) 生成的不是列表,而是用一次就空了的生成器
要用两次,就用方括号 [ ] 生成列表。
用 [[0] * 3] * 2 建二维列表
grid = [[0] * 3] * 2
grid[0][0] = 1
print(grid)→ [[1, 0, 0], [1, 0, 0]] —— 外层的 * 2 不会复制内层列表,而是把同一个列表放了两次
用推导式逐行新建:[[0] * 3 for _ in range(2)](像 [0] * 3 这样乘数字没问题)。
各语言的差异
用乘法(
需要多个列表就用推导式,默认值请用
用乘法(
* m)生成的二维列表和 =[] 默认值都会不报错地共用同一个列表,导致意想不到的地方被改掉。需要多个列表就用推导式,默认值请用
None。