0.3 容器与数据结构

概述

你已经熟悉 JS 里的 ArrayObjectMapSet,以及 map / filter / reduceslice、解构展开这些利器。Python 有几乎一一对应的数据结构,但有几个关键差异:list(可变数组)、tuple(不可变数组)、dict(对象/映射)、set(集合)。这一章把容器、推导式、切片、解构全部对照清楚。

JS/TS ↔ Python 对照表

概念

JavaScript

Python

说明

数组

Array[...]

list[...]

可变、有序

不可变数组

Object.freeze(arr) / ReadonlyArray

tuple(...)

创建后不可修改

对象/映射

Object / Map

dict{...}

键值对

集合

Set

set{...}set()

无序、去重

长度

arr.length

len(lst)

函数而非属性

访问元素

arr[i]

lst[i]

相同,支持负数下标

末尾追加

arr.push(x)

lst.append(x)

注意方法名

末尾弹出

arr.pop()

lst.pop()

相同

首部插入

arr.unshift(x)

lst.insert(0, x)

Python 无 unshift

首部移除

arr.shift()

lst.pop(0) / deque.popleft

无 shift

切片

arr.slice(1, 3)

lst[1:3]

语法不同

拼接

arr.concat(b) / [...a, ...b]

lst + b / [*a, *b]

+ 即可拼接

展开

...arr

*lst(list)**d(dict)

星号语法

解构

const [a, b] = arr

a, b = lst

Python 不需要中括号

查找

arr.includes(x)

x in lst

in 运算符

索引查找

arr.indexOf(x)

lst.index(x)

找不到抛异常

计数

arr.filter(...).length

lst.count(x)

直接有方法

排序

arr.sort()(原地)

lst.sort() / sorted(lst)

sorted 返回新列表

反转

arr.reverse()

lst.reverse() / reversed(lst)

原地 / 迭代器

map/filter

arr.map(fn) / arr.filter(fn)

推导式 / map() / filter()

推导式更常用

reduce

arr.reduce(fn, init)

functools.reduce()

需 import

遍历带索引

arr.forEach((x, i) => ...)

for i, x in enumerate(lst):

enumerate

字典键存在

key in obj / map.has(key)

key in d

in

字典取值

obj[key]

d[key] / d.get(key, 默认)

get 更安全

字典键列表

Object.keys(obj)

d.keys() / list(d)

返回视图

合并对象

{...a, ...b} / Object.assign

{**a, **b} / a | b

Python 3.9+ 支持 |

核心概念

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 的 ReadonlyArrayObject.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

Python

是否包含

arr.includes(x)

x in lst

求和

arr.reduce((a, b) => a + b)

sum(lst)

最大/最小

Math.max(...arr)

max(lst) / min(lst)

去重

[...new Set(arr)]

list(set(arr))

连接为字符串

arr.join(",")

",".join(lst)

拆分字符串

str.split(",")

str.split(",")

合并两列表

[...a, ...b]

a + b

代码示例

综合例子:处理一组用户数据

// 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),不要在循环里 +=

前端开发者常见陷阱

  1. len() 是函数不是属性len(lst) 而非 lst.length

  2. append 不是 pushlst.append(x);也别把 add 用错(set.addlist.append)。

  3. 切片是"左闭右开"lst[1:3] 不含索引 3,和 slice 一致但要记牢 lst[-1] 是最后一个元素。

  4. 访问越界抛异常lst[10]IndexError,不像 JS 返回 undefined

  5. {} 是 dict 不是 set:空集合要 set()

  6. in 的语义x in lst 判断元素,key in dict 判断键,别混淆。

  7. index 找不到抛异常lst.index(x) 找不到会 ValueError,先 in 判断。

  8. 解构不需要括号a, b = lst,写成 [a, b] = lst 也行但不符合习惯。

  9. map/filter 返回迭代器:Python 内置 map/filter 返回惰性迭代器,需要 list(...) 才能打印/复用。

  10. 元组单元素要逗号(1,) 才是元组,(1) 是数字。

总结

  • list = Arraytuple = ReadonlyArraydict = Object/Mapset = Set

  • 推导式 [f(x) for x in xs if cond] 替代 map + filter

  • 切片 lst[start:stop:step]slice 更强大,支持步长和反转。

  • 解构用 a, b = lst,展开用 *(列表)和 **(字典)。

  • len()inenumeratesum/max/min 是高频内置工具,值得记牢。