0.3 容器与数据结构
概述
你已经熟悉 JS 里的 Array、Object、Map、Set,以及 map / filter / reduce、slice、解构展开这些利器。Python 有几乎一一对应的数据结构,但有几个关键差异:list(可变数组)、tuple(不可变数组)、dict(对象/映射)、set(集合)。这一章把容器、推导式、切片、解构全部对照清楚。
JS/TS ↔ Python 对照表
核心概念
1. list vs Array
Python 的 list 对应 JS 的 Array,语法几乎一致,方法名略有差异:
const arr = [1, 2, 3];
arr.push(4);
arr[0];
arr.length; // 4
arr.slice(1, 3); // [2, 3]
arr = [1, 2, 3]
arr.append(4) # push → append
arr[0]
len(arr) # 4,length 是函数 len()
arr[1:3] # [2, 3],slice → 切片
访问越界:JS 返回
undefined,Python 直接抛IndexError。负数下标:
arr[-1]是最后一个元素(JS 的arr.at(-1))。
2. tuple vs 只读/冻结数组
Python 的 tuple(元组)是不可变的有序序列,类似 TypeScript 的 ReadonlyArray 或 Object.freeze 的数组:
// TS:只读数组
const point: readonly [number, number] = [3, 4];
// point[0] = 5; // 编译报错
point = (3, 4) # 圆括号创建 tuple
# point[0] = 5 # 运行时报 TypeError,不可修改
创建单元素元组要加逗号:
(1,),否则(1)只是数字 1。元组常用于:函数返回多个值、作为字典键、表示"不会变的一组数据"。
3. dict vs Object / Map
Python 的 dict 同时扮演 JS 的 Object(字面量)和 Map(任意类型键)角色:
const obj = { name: "Alice", age: 30 };
obj.name;
const m = new Map();
m.set("a", 1);
d = {"name": "Alice", "age": 30}
d["name"] # 用中括号访问,不是点号
d.get("name") # 安全的取值,键不存在返回 None
d.get("x", "默认值") # 指定默认值
Python 的
dict键可以是任意可哈希类型(字符串、数字、元组),比 JS 的Object键只能字符串更接近Map。访问不存在的键会抛
KeyError,用.get()或in判断。Python 3.7+ 的
dict保证插入顺序(和 JS 对象类似)。
4. set vs Set
const s = new Set([1, 2, 2, 3]); // {1, 2, 3}
s.has(1);
s = {1, 2, 2, 3} # {1, 2, 3}
1 in s # has → in
s.add(4) # add 同款
注意:空集合必须用
set(),因为{}是空字典。
5. 列表推导式 vs map/filter
Python 的**列表推导式(comprehension)**是 map / filter 的语法糖,写法更 Pythonic:
// JS
const doubled = [1, 2, 3].map(x => x * 2); // [2, 4, 6]
const evens = [1, 2, 3, 4].filter(x => x % 2 === 0); // [2, 4]
# Python:推导式
doubled = [x * 2 for x in [1, 2, 3]] # [2, 4, 6]
evens = [x for x in [1, 2, 3, 4] if x % 2 == 0] # [2, 4]
// JS:链式 map + filter
const result = [1, 2, 3, 4]
.map(x => x * 2)
.filter(x => x > 4); // [6, 8]
# Python:嵌套推导
result = [x * 2 for x in [1, 2, 3, 4] if x * 2 > 4] # [6, 8]
还有字典推导
{k: v for ...}和集合推导{x for ...}。
6. 切片 slice vs Array.prototype.slice
arr.slice(1, 3); // 从索引 1 到 2(不含 3)
arr.slice(2); // 从 2 到末尾
arr.slice(-2); // 最后两个
lst[1:3] # 从索引 1 到 2(不含 3)
lst[2:] # 从 2 到末尾
lst[-2:] # 最后两个
lst[::2] # 步长 2,隔一个取一个
lst[::-1] # 反转整个列表
Python 切片支持第三个参数步长
lst[start:stop:step],lst[::-1]反转是 JS 没有的简洁写法。
7. 解构与展开 * / ** vs …
// JS:解构
const [a, b] = [1, 2];
const { name, ...rest } = obj;
const combined = [...arr1, ...arr2];
# Python:解构
a, b = [1, 2] # 无需中括号
name, *rest = arr # * 收集剩余(list)
combined = [*arr1, *arr2] # * 展开 list
merged = {**d1, **d2} # ** 展开 dict
Python 的
*对应 JS 的...(用于 list/参数),**对应展开对象。函数参数里的
*args/**kwargs详见第 0.4 章。
8. enumerate 遍历
for (const [i, x] of arr.entries()) {
console.log(i, x);
}
for i, x in enumerate(lst):
print(i, x)
9. 常见内置方法对照
代码示例
综合例子:处理一组用户数据
// JS
const users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 17 },
{ name: "Carol", age: 25 },
];
const adults = users
.filter(u => u.age >= 18)
.map(u => u.name);
console.log(adults); // ["Alice", "Carol"]
# Python
users = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 17},
{"name": "Carol", "age": 25},
]
adults = [u["name"] for u in users if u["age"] >= 18]
print(adults) # ["Alice", "Carol"]
去重与排序
// JS
const unique = [...new Set([3, 1, 2, 1, 3])]; // [3, 1, 2]
unique.sort((a, b) => a - b); // [1, 2, 3]
# Python
unique = sorted(set([3, 1, 2, 1, 3])) # [1, 2, 3],一步搞定
最佳实践
遍历优先用
for x in lst或推导式,别用 C 风格的for (i=0; ...)。需要"带索引"用
enumerate,需要"两列表配对"用zip。取不存在的字典键用
d.get(key, 默认)而非d[key],避免KeyError。不可变数据用
tuple,体现"这组值不会变"的意图(类似as const/readonly)。用推导式代替
map/filter,代码更易读。拼接字符串用
",".join(items),不要在循环里+=。
前端开发者常见陷阱
len()是函数不是属性:len(lst)而非lst.length。append不是push:lst.append(x);也别把add用错(set.add,list.append)。切片是"左闭右开":
lst[1:3]不含索引 3,和slice一致但要记牢lst[-1]是最后一个元素。访问越界抛异常:
lst[10]会IndexError,不像 JS 返回undefined。空
{}是 dict 不是 set:空集合要set()。in的语义:x in lst判断元素,key in dict判断键,别混淆。index找不到抛异常:lst.index(x)找不到会ValueError,先in判断。解构不需要括号:
a, b = lst,写成[a, b] = lst也行但不符合习惯。map/filter返回迭代器:Python 内置map/filter返回惰性迭代器,需要list(...)才能打印/复用。元组单元素要逗号:
(1,)才是元组,(1)是数字。
总结
list=Array,tuple=ReadonlyArray,dict=Object/Map,set=Set。推导式
[f(x) for x in xs if cond]替代map+filter。切片
lst[start:stop:step]比slice更强大,支持步长和反转。解构用
a, b = lst,展开用*(列表)和**(字典)。len()、in、enumerate、sum/max/min是高频内置工具,值得记牢。
0.3-容器与数据结构
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法