JS数组和Python列表都是各自语言里最常用的数据结构,表面看都是“一串值的集合”,但真正用起来会发现,两者的脾气差得不是一星半点。写前端的人转到Python写业务逻辑,或者在Python后端顺手写几行前端,最容易踩的坑往往就藏在这些“看起来一样”的操作里——排序结果不对、删除元素漏删、拷贝之后改了原数据、大列表操作卡成狗,追根溯源都是这两种类型在设计哲学上的差异。
这篇就按我自己的使用经验,从类型体系、创建方式、操作方法、切片语法、内存模型、拷贝与迭代这六个维度,把JS数组和Python列表的核心差异一次说清楚。不管是刚从JS转Python、还是从Python转JS,或者干脆两边都得写的全栈,这篇都值得收藏当个对照手册用。
1. 类型体系:JS数组能塞“万物”,Python列表也没差,但底层逻辑完全不同
1.1 JS数组:高密度还是稀疏,直接影响性能
JS数组最大的特点是它可以不是真正意义上的“数组”。V8引擎在底层做了大量的优化,当数组里存放的元素类型一致时(比如全是整数、全是浮点数、全是字符串),V8会把它优化成一块连续的内存空间,也就是“快数组”(fast elements),访问速度极快,接近C语言数组的水平。
但只要你往这个数组里塞进一个不同类型的数据,比如从全是数字的数组里加一个字符串进去,V8只能把底层结构降级成“字典模式”(dictionary elements),也就是把数组变成了一个以索引为键的哈希表。这时候数组的访问速度会急剧下降,而且一旦降级,几乎不可能自动恢复。
举个实际例子:
const arr = [1, 2, 3, 4, 5]; // 此时底层是连续内存的快数组 arr.push(6); // 突然添加一个不同类型 arr.push('hello'); // 底层结构降级为字典模式,性能下降一个量级这就是为什么在某些高性能场景下,会看到有人用const arr = new Array(1000).fill(0)预先填充固定类型的数据,就是为了让引擎从一开始就保持快数组模式,避免中途降级。而JS数组的稀疏性也是一个特点,const a = []; a[100] = 1,中间那些索引会被引擎特殊处理成“空洞”,访问时返回undefined,并不会真的为你保留中间的内存空间。这种语义在你遍历的时候会搞出很多意想不到的问题,后面细说。
1.2 Python列表:引用数组,装的都是对象的引用
Python列表的底层实现是PyObject**,一个指针数组,每一项都指向实际的Python对象。换句话说,Python列表本身不存数据,存的是数据的内存地址。这就决定了:
第一,Python列表天然可以装不同类型的数据,因为每个元素都是一个指向任意对象的指针。比如:
lst = [1, 'hello', 3.14, [1, 2], {'a': 1}]第二,Python列表并没有JS那样的“快数组/慢数组”概念,它始终是动态数组(dynamic array),需要扩容时进行整体搬迁,而访问任一元素都是先寻址再解引用,开销固定但相对JS的优化后快数组要慢一些。
第三,由于存的是引用,Python列表在拷贝时的“浅拷贝”问题会比JS更隐蔽、更容易踩坑。比如lst2 = lst1,你改lst2的某个可变元素,lst1也跟着变——这在JS里一样存在,但Python里由于函数传参都是传对象的引用,这个坑会以另一种形式爆发,后面专门讲。
从语义上讲,Python列表更像是“序列容器”,而JS数组在语义上更复杂,它既可以当栈用(pop/push)、当队列用(shift/unshift),还可以当迭代器、当稀疏映射,甚至在某些老代码里被当集合用。这种“一专多能”是JS的福气,也是它的负担。
2. 创建与初始化:看似一样的方括号,坑全藏在细节里
2.1 JS数组的创建:字面量、构造器与空洞
JS数组最推荐的创建方式是字面量:
const arr = [1, 2, 3];但有几种创建方式特别容易踩坑:
用Array构造器创建:
const a = new Array(3); // [空 × 3] 不是 [3] const b = new Array(3, 3); // [3, 3]new Array(3)创建的不是含数字3的数组,而是一个包含3个空洞的数组。这个数组在实际使用中行为诡异:
const a = new Array(3); a.map(x => 1); // [空 × 3],map不会遍历空洞 a.forEach(x => console.log(x)); // 什么也不打印 a[0] === undefined; // true,但和直接赋值的undefined有区别实战中我几乎不用new Array()这种方式创建数组,除非是new Array(100).fill(0)这种需要预分配的场景。注意,fill一用,空洞就被填上了,行为就正常了。
用Array.from和展开操作符:
const arr1 = Array.from({length: 3}, (_, i) => i); // [0, 1, 2] const arr2 = [...new Set([1, 2, 2, 3])]; // 去重这两个是我日常高频使用的。Array.from第二个参数是map函数,非常方便,而展开操作符在合并数组、复制数组时离不开。
2.2 Python列表的创建:推导式才是灵魂
Python列表的创建同样用方括号:
lst = [1, 2, 3]但Python有一个列表推导式(list comprehension),这个语法强悍到用久了再写JS会觉得难受:
# 生成0到9的平方 squares = [x * x for x in range(10)] # 带条件过滤 evens = [x for x in range(20) if x % 2 == 0] # 双重循环 pairs = [(x, y) for x in range(3) for y in range(3) if x != y]JS虽然也有Array.from和map/filter链式调用,但列表推导式的可读性和效率在简单场景下确实更胜一筹。比如你写JS:
const squares = Array.from({length: 10}, (_, i) => i * i); // 或者 const squares = [...Array(10).keys()].map(x => x * x);这两种写法都远不如Python的[x * x for x in range(10)]来得直观。
还有一个很基础但新手经常搞混的点:Python的range()返回的不是列表,而是一个迭代器对象。你要真想得到一个列表,必须用list(range(10))。这在JS里没有直接对应物,Array(10)创造的是空洞数组,[...Array(10).keys()]才能拿到[0,1,2,...,9],这个对应关系建议背下来。
2.3 初始化多维数组的对比
这是另一个高频踩坑点。
JS里初始化二维数组:
// 错误示范 const matrix = new Array(3).fill([]); matrix[0].push(1); // 这下三个子数组都多了个1 // 因为fill填的是同一个数组引用 // 正确做法 const matrix = Array.from({length: 3}, () => []); // 或者 const matrix = [...Array(3)].map(() => []);Python里初始化二维列表:
# 错误示范 matrix = [[]] * 3 matrix[0].append(1) # 同样三个子列表都变 # 正确做法 matrix = [[] for _ in range(3)]细品一下,JS的fill([])、Python的[[]] * 3,犯的都是同一个错——对可变对象的引用做了复制而不是创建新对象。这个坑两边都有,但表现形式又相似,所以跨语言开发时特别容易麻痹大意。
3. 增删改查:方法命名背后的设计哲学差异
3.1 JS数组的“万金油”方法
JS数组的方法非常“花”,或者说“冗余”,同样一个功能往往有至少两种写法。比如:
- 尾部增删:
push/pop - 头部增删:
unshift/shift - 任意位置增删:
splice - 连接:
concat - 截取:
slice
其中最容易出错的莫过于splice和slice。这俩在面试题里的出现频率极高,因为它们名字相似但行为完全不同:
const arr = [1, 2, 3, 4, 5]; // slice:不修改原数组,返回一个从start到end(不含)的浅拷贝 const newArr = arr.slice(1, 3); // [2, 3] // splice:修改原数组,从start开始,删除deleteCount个元素,可插入新元素 const removed = arr.splice(1, 2, 'a'); // arr变成[1, 'a', 4, 5],removed是[2, 3]我自己的记忆方法:slice里的s和string一样,都不改变原字符串/数组;splice里的p和push一样,都会改变原数组。用多了就条件反射了,但新手确实容易混。
JS的push返回值是新数组的长度,而Python列表的append返回的是None。这个差异在你写链式调用的时候会直接爆雷。比如Python:
lst = [1, 2, 3] lst.append(4).append(5) # AttributeError: 'NoneType' object has no attribute 'append'JS可以这样链:
const arr = [1, 2, 3]; arr.push(4).push(5); // 这里也会报错,因为push返回数字不是数组对,这里我纠正一下:JS的push返回的是数字(新长度),同样不能链式调用。但JS的map/filter/reduce这类方法可以链式,而Python列表却没有这类方法,要靠推导式+内置函数来组合。这是两种语言在“函数式编程”支持上的一个显著差异。
3.2 Python列表的“直白命名”与内置函数霸权
Python列表的方法数量比JS数组少得多,命名更直白:
append(element):尾部添加extend(iterable):扩展,相当于JS的push(...arr2)insert(index, element):插入remove(value):按值删除第一个匹配项pop(index):按索引弹出,默认弹出最后一个clear():清空index(value):按值找索引count(value):统计出现次数
注意,Python列表没有直接按值查找并返回索引的“一键方法”之外的函数,但有lst.index(x)。问题是,如果x不存在于列表中,index()会抛出ValueError,而不是返回-1。这和JS数组的indexOf返回-1的语义完全不同,跨语言时容易惯性写错:
lst = [1, 2, 3] idx = lst.index(4) # ValueError: 4 is not in list所以Python老手一般先判断:
if 4 in lst: idx = lst.index(4)这里的in操作符对列表是线性扫描(O(n)),对小列表无所谓,大列表就要考虑用set了。
另外,Python的删除有“按值删”和“按索引删”两个独立方法,分别是remove和pop。JS的splice一个方法全能干,但需要传入索引。从可读性上讲,Python的remove(4)比JS的splice(indexOf(4), 1)直观得多;从灵活性上讲,JS的splice又强得多,可以同时完成删除和插入。
这里给一个常用对照表,建议跨语言开发时贴在旁边:
| 操作场景 | JS 写法 | Python 写法 |
|---|---|---|
| 尾部添加一个元素 | arr.push(x) | lst.append(x) |
| 尾部添加多个元素 | arr.push(...newItems) | lst.extend(newItems) |
| 尾部弹出 | arr.pop() | lst.pop() |
| 头部添加 | arr.unshift(x) | lst.insert(0, x) |
| 头部弹出 | arr.shift() | lst.pop(0) |
| 按值删除第一个 | const i = arr.indexOf(x); arr.splice(i, 1) | lst.remove(x) |
| 按索引删除 | arr.splice(i, 1) | lst.pop(i)/del lst[i] |
| 删除一段 | arr.splice(start, count) | del lst[start:start+count] |
| 判断是否存在 | arr.includes(x) | x in lst |
| 找索引 | arr.indexOf(x) | lst.index(x)(不存在会抛异常) |
4. 切片与索引:Python的“杀手锏”,JS只能干瞪眼
4.1 JS的slice方法:功能有限
JS数组有slice方法,能截取一段:
const arr = [0, 1, 2, 3, 4, 5]; const sub = arr.slice(2, 5); // [2, 3, 4]但JS的slice不支持负步长,也不支持负索引在任意位置灵活使用(虽然支持slice(-3)表示最后三个,但也仅此而已)。JS里要实现“从索引1开始,每隔一个取一个”,你得:
const arr = [0, 1, 2, 3, 4, 5]; const result = arr.filter((_, i) => i % 2 === 1); // 或者 const result = []; for (let i = 1; i < arr.length; i += 2) { result.push(arr[i]); }再比如反转数组,Python一行lst[::-1],JS就只能是arr.slice().reverse()或者[...arr].reverse()。很绕。
4.2 Python的切片语法:一种写进骨髓的便利
Python的切片是列表最让我欲罢不能的特性之一,语法是lst[start:stop:step],三个参数都可以省略,而且支持负索引和负步长:
lst = [0, 1, 2, 3, 4, 5] lst[1:4] # [1, 2, 3],不包含索引4 lst[:3] # [0, 1, 2] lst[3:] # [3, 4, 5] lst[:] # 全部(浅拷贝) lst[::2] # [0, 2, 4],步长为2 lst[::-1] # [5, 4, 3, 2, 1, 0],反转 lst[-3:] # [3, 4, 5],最后三个 lst[5:1:-1] # [5, 4, 3, 2],从索引5到索引2(反向)这套语法的强大之处在于它不只是“读”,还可以“写”:
lst = [0, 1, 2, 3, 4, 5] lst[1:4] = [9, 8, 7] # lst 变成 [0, 9, 8, 7, 4, 5] lst[::2] = [100, 200, 300] # 因为0、2、4三个位置被替换,lst 变成 [100, 9, 200, 7, 300, 5]这种“切片赋值”的能力在JS里完全没有对应语法。你在JS里要实现“把数组的一段替换成另一个数组”,只能splice:
const arr = [0, 1, 2, 3, 4, 5]; arr.splice(1, 3, 9, 8, 7); // [0, 9, 8, 7, 4, 5]效果类似,但可读性、灵活度差很远。
实操建议:Python切片中的step可以为负,但start和stop不写全时,负步长的默认边界是倒着走到尽头。lst[::-1]是最常见的反转写法,但注意它会生成一个新列表,如果只是为了反转后遍历,可以用reversed(lst)省一份内存。
4.3 索引越界的处理差异
这个细节看起来小,但跨语言时真的很容易出事:
JS数组索引越界时返回undefined,不报错:
const arr = [1, 2, 3]; console.log(arr[5]); // undefinedPython列表索引越界时直接抛IndexError:
lst = [1, 2, 3] lst[5] # IndexError: list index out of rangeJS的容错性更强,但代价是很多bug被静默吞掉——你拿到的undefined可能是越界,也可能是数组里真的存了undefined,排查起来很费劲。Python的这种“暴力报错”短期内让人头疼,但长期看能帮你更早暴露问题。我个人偏好Python的这种严格性,写代码时边界条件想得更清楚。
5. 内存模型与性能:都是动态数组,但差距远比你想象的大
5.1 JS数组的底层双层表示
前面提过,V8对JS数组做了“快数组/慢数组”的分类。快数组在内存中是连续的,索引访问是O(1)的;慢数组退化为基于哈希表的字典模式,虽然也是O(1),但常数项大得多,而且占用内存高很多。
还有一个细节:JS数组的长度是32位整数的范围,最大是2^32 - 1(约42.9亿)。虽然实际浏览器基本到不了这个上限,但了解这个限制有助于理解为什么某些极端场景下JS数组的行为很奇怪。
V8优化数组的一个常见手段是**元素种类(element kind)**的自动跟踪,比如让数组一直保持“PACKED_SMI_ELEMENTS”这种最优化形态,只存放小整数。但只要你操作不当,添加了浮点数或者字符串、或者产生了空洞,元素种类就会降级,性能随之衰退。所以写高性能JS代码时,保持数组元素类型一致、避免空洞,是非常实际的优化方向。
拿一个真实的案例分析:我处理过一个前端列表筛选功能,用户上传几千条数据,页面上需要反复过滤、排序。最初用arr.filter(...)返回新数组后一次次传递,几轮下来上千条数据的操作也能明显感觉到卡顿。后来优化为:用类型一致的数组、避免不必要的中间数组分配、使用索引复用而不是反复filter,性能立刻提升了一个量级。JS数组的性能不是恒定的,关键看你怎么“哄”V8开心。
5.2 Python列表的动态扩容与引用开销
Python列表的底层是PyListObject,它是一个动态数组,维护着三个关键值:
ob_item:指向实际元素数组的指针allocated:当前分配的内存容量ob_size:实际元素个数
当ob_size接近allocated时,Python会触发扩容,典型的扩容策略大约是原来容量的1.125倍(不同版本略有差异),并整体申请新内存、迁移元素。这意味着频繁append会有弹性的性能损耗,但均摊下来依然是O(1)的。
Python列表每个元素都是指针,占用8字节(64位系统)。如果你的列表里存了100万个整数,那么光是指针数组就占8MB,再加上每个整数对象(Python的int是满功能对象,约28字节)……注意,小整数在Python里会被缓存,但大整数每个都是独立对象。所以同样的100万个整数,Python列表的内存占用远高于JS数组(如果JS数组保持SMI元素的话)。这个差异在数据处理时尤其明显,也是为什么大数组场景很多人会转向array模块或numpy的原因。
比如:
import sys lst = list(range(1000000)) sys.getsizeof(lst) # 约8000056字节(指针数组) # 而每个int对象还要额外占28字节左右,所以总内存远超这个数 # 用numpy: import numpy as np arr = np.arange(1000000, dtype=np.int64) arr.nbytes # 8000000字节,且没有额外的对象头结论很直白:Python列表本质上是“对象指针的动态数组”,不是紧凑的数据存储容器。要处理大量数值数据,该用numpy用numpy,别拿列表死扛。
5.3 遍历性能的直觉
JS数组用for循环、for...of、forEach、map等,性能排序大致是:
for循环 >for...of>forEach/map(现代引擎下差距在缩小)
Python列表的迭代则统一用for item in lst,很简洁,但在需要索引时要用enumerate。Python显式用for i in range(len(lst))访问是纯Python循环,性能很慢,数据量大时会被numpy或推导式完爆。
Python里有一个性能常识:列表推导式通常比相同逻辑的for循环快,因为底层的LIST_APPEND字节码经过优化,减少了append方法的属性查找开销。比如:
lst = [] for i in range(1000): lst.append(i * i) # 推导式版本(更快) lst = [i * i for i in range(1000)]JS里对应的则是map尾调用链通常能和for循环打平,个别情况甚至更快(因为V8能对数据流做优化)。所以跨语言时的性能直觉要纠正:Python你用推导式、用生成器、用map/filter搭配内置函数,会比手写for循环好得多;JS则要回归标记位变量+缓存length的循环才有极致性能。
6. 迭代、拷贝与函数式操作:细节魔鬼都在这里
6.1 迭代中的删除陷阱
这是一个史诗级的跨语言共同坑,但表现形态完全不同。
JS中,在forEach里删除元素:
const arr = [1, 2, 3, 4, 5]; arr.forEach((item, i) => { if (item === 3) { arr.splice(i, 1); // 看似删掉了3 } }); console.log(arr); // [1, 2, 4, 5] —— 看似正常,但如果是相邻元素会出问题更常见的坑是:
const arr = [1, 2, 2, 3]; arr.forEach((item, i) => { if (item === 2) { arr.splice(i, 1); } }); console.log(arr); // [1, 2, 3] —— 删了一个2,但另一个2漏过了问题在于splice会让后序元素索引前移,而forEach继续按原索引+1遍历,于是跳过了紧跟其后的元素。
Python中同样有类似的坑:
lst = [1, 2, 2, 3] for item in lst: if item == 2: lst.remove(item) # 结果是 [1, 2, 3],同样漏删了一个2更严重的是,Python在迭代列表的同时修改列表长度,有时候会直接得到反直觉的结果,甚至在某些版本里可能导致意想不到的行为。安全做法是遍历一个副本:
lst = [1, 2, 2, 3] for item in lst[:]: if item == 2: lst.remove(item)或者用列表推导式重建一个列表:
lst = [x for x in lst if x != 2]JS里也可以用filter重建,或者反向遍历删除:
arr = arr.filter(x => x !== 2); // 或者反向遍历 for (let i = arr.length - 1; i >= 0; i--) { if (arr[i] === 2) arr.splice(i, 1); }实操心得:但凡需要对数组/列表做“边遍历边删除”,我的第一选择都是生成新结构代替原地修改(filter/推导式),这样最安全、最易读,代价只是多一次内存分配。数据量大到无法承受这份拷贝时,再考虑反向遍历。
6.2 拷贝:浅拷贝、深拷贝,两个语言都没省心
JS数组的浅拷贝方式:
const copy1 = arr.slice(); const copy2 = [...arr]; const copy3 = Array.from(arr);Python列表的浅拷贝方式:
copy1 = lst[:] copy2 = lst.copy() copy3 = list(lst)注意,浅拷贝后,如果数组/列表里的元素是可变对象(嵌套数组/列表、对象),那么拷贝后的容器和原容器共享这些可变元素。修改内层对象会“互穿”:
const arr = [[1, 2], [3, 4]]; const copy = arr.slice(); copy[0].push(99); console.log(arr); // [[1, 2, 99], [3, 4]]lst = [[1, 2], [3, 4]] copy = lst[:] copy[0].append(99) print(lst) # [[1, 2, 99], [3, 4]]深拷贝才是真麻烦。JS有structuredClone是原生较推荐的方案:
const deepCopy = structuredClone(arr);或者JSON.parse(JSON.stringify(arr))——注意它对函数、undefined、NaN、Date等特殊值会有丢失或转变,只能用于纯数据场景。
Python有copy.deepcopy:
import copy deep_copy = copy.deepcopy(lst)但deepcopy非常慢,而且复杂对象之间容易递归成环,不是万无一失。跨语言感受到的都是同一个真理:在逻辑上优先避免深拷贝需求,数据只读、使用不可变对象、存flat数据,比什么拷贝魔法都高效。
6.3 函数式操作:map、filter、reduce 的异同
JS数组的map、filter、reduce是每个前端的基本功,能链式调用,但每次调用都生成一个新数组,链太长会有中间对象开销。
Python没有这些方法,但内置了map()、filter()、functools.reduce()函数,而且map、filter返回的是惰性迭代器,不是列表。这有个很大的影响:你打印map对象时,看到的是一串内存地址而不是结果,很多人第一次用就懵了:
nums = [1, 2, 3] result = map(lambda x: x * 2, nums) print(result) # <map object at 0x...> list(result) # [2, 4, 6]JS和Python在“函数式编程”这件事上的哲学差异很明显:JS把map/filter/reduce作为数组的方法,内建在语言使用习惯里;Python则把它们作为全局函数,且默认惰性求值,搭配生成器表达式和推导式用。实际开发中,Python我更习惯直接用推导式,而不是map(lambda...)——推导式的可读性和速度都好于lambda结合map。只有在已经有现成函数、不需要lambda时,map才更干净:
import math nums = [1, 2, 3] # 推导式 squares = [math.sqrt(x) for x in nums] # map + 现成函数 squares2 = list(map(math.sqrt, nums))JS方面,链式调用时要保持每个回调函数的返回逻辑清晰,否则难以调试:
const result = arr .filter(x => x > 0) .map(x => x * 2) .reduce((sum, x) => sum + x, 0);6.4 排序:一个专门大坑
JS的sort()默认把元素转为字符串后按字典序比较,这是新手最大的雷:
const arr = [1, 10, 2, 21]; arr.sort(); // [1, 10, 2, 21],你没看错,字典序必须传入比较器:
arr.sort((a, b) => a - b); // [1, 2, 10, 21] 升序 arr.sort((a, b) => b - a); // 降序而且,JS的sort是原地排序,会修改原数组。如果要保留原数组,得先拷贝:
const sorted = [...arr].sort((a, b) => a - b);Python的sorted()则不同,它返回新列表,不修改原列表:
lst = [1, 10, 2, 21] new_lst = sorted(lst) # [1, 2, 10, 21] print(lst) # [1, 10, 2, 21] 原列表不变如果非要原地排序,Python用lst.sort(),这也是一个方法一个函数的区别。
另外,Python的sorted支持key参数,用起来非常顺手:
people = [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}] sorted(people, key=lambda p: p['age'])JS的sort里等价的写法是传入比较器:
people.sort((a, b) => a.age - b.age);两种都能用,但key参数有个额外好处:Python会先为每个元素计算一次key并缓存,然后比较缓存值,对大对象列表来说这比JS的比较器方法(每个元素可能被比较多次)更高效。数据量大的时候这个差异非常明显。
实操总结:跨语言切换的避坑清单
最后把自己的心得整理成一份清单,都是我真正踩过坑或者亲眼见证别人踩坑后的总结:
- 删除元素:JS用
filter重建、Python用推导式重建,比原地删除安全一整个量级。真的需要原地删,JS反向遍历,Python也反向遍历。 - 拷贝:在写任何需要保留“原数组”的功能时,先想清楚你要的是浅拷贝还是深拷贝。JS的
slice/[...arr]、Python的[:]/copy()都只是浅拷贝,嵌套对象要小心。 - 排序:JS排序必须传比较器;Python用
sorted返回新列表、sort原地修改,别搞反。 - 索引查找:JS的
indexOf找不到返回-1;Python的index()找不到抛ValueError。 - 判断包含:JS用
includes、Python用in。记住是**O(n)**的线性查找,频繁查找用Set。 - 切片:Python的切片是杀手级特性,但注意切片返回新列表(部分场景支持
memoryview,但那不是列表)。JS没有对应的步长语法,只能写循环或链式调用。 - 性能:JS数组保持元素类型一致,避免空洞,才能让V8维持在快数组模式。Python列表如果只存数值,数据量大时考虑
array.array或numpy。
我在实际工作中有一个很深的体会:跨语言切换时,最坑的从来不是某个具体的API记不住,而是上一门语言形成的惯性和直觉带到了新语言里。看到切片不会往负步长想,看到remove不会假设它按值删,看到sort不会下意识检查它是否原地……这些都是需要用一段时间的刻意练习来纠正的。
建议所有从JS转Python或从Python转JS的朋友,都拿同一个任务(比如把一组对象按某个字段排序、过滤并分页)在两种语言里各写一遍,感受一遍思路差异。等你能不看文档就写出两种语言“地道”的列表/数组操作时,基本就完成跨语言思维的切换了。
全文完。感谢读到这里的你,希望对你有用。