之前开发过一些 Python 的东西,由于一直在使用 Javascript,所以会不自觉的使用一些 Javascript 的概念,语法什么的,经常掉到坑里。我觉得对于从 Javascript 转到 Python,有必要总结一下它们之间的差异。

基本概念

Python 和 Javascript 都是脚本语言,所以它们有很多共同的特性,都需要解释器来运行,都是动态类型,都支持自动内存管理,都可以调用 eval() 来执行脚本等等脚本语言所共有的特性。
然而它们也有很大的区别,Javascript 这设计之初是一种客户端的脚本语言,主要应用于浏览器,它的语法主要借鉴了 C,而 Python 由于其“优雅”,“明确”,“简单”的设计而广受欢迎,被应用于教育,科学计算,web 开发等不同的场景中。

编程范式

Python 和 Javascript 都支持多种不同的编程范式,在面向对象的编程上面,它们有很大的区别。Javascript 的面向对象是基于原型(prototype)的, 对象的继承是由原型(也是对象)创建出来的,由原型对象创建出来的对象继承了原型链上的方法。而 Python 则是中规中矩的基于类(class)的继承,并天然的支持多态(polymophine)。

OO in Pyhton

class Employee: 'Common base class for all employees' empCount = 0 ##类成员 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary ## 创建实例 ea = Employee("a",1000) eb = Employee("b",2000)
Python

OO in Javascript

//构造函数 function Employee(name, salary) { this.name = name; this.salary = salary; Employee.empCount += 1; } Employee.empCount = 0; Employee.prototype.displayCount = function() { console.log("Total Employee" + Employee.empCount); }; Employee.prototype.displayEmployee = function() { console.log("Name" + this.name + "Salary" + this.salary); }; //创建实例 var ea = new Employee("a", 1000); var eb = new Employee("b", 2000);
JavaScript
因为是基于对象的继承,在 Javascript 中,我们没有办法在构造函数中使用类成员变量,需要在外部定义。Javascript 创建对象需要使用 new 关键字,而 Python 不需要。
除了原生的基于原型的继承,还有很多利用闭包或者原型来模拟类继承的 Javascript OO 工具,因为不是语言本身的属性,我们就不讨论了。
ES6 之后有了 class,JavaScript 的 OO 写起来更像那么一回事了,但 class 只是语法糖,本质仍然是原型。

线程模型

在 Javascript 的世界中是没有多线程的概念的,并发使用过使用事件驱动的方式来进行的, 所有的 JavaScript 程序都运行在一个线程中。在 HTML5 中引入 web worker 可以并发的处理任务,但没有改变 Javascript 单线程的限制。
Python 通过 thread 包支持多线程。

不可改变类型 (immutable type)

在 Python 中,有的数据类型是不可改变的,也就意味着这种类型的数据不能被修改,所有的修改都会返回新的对象。而在 Javascript 中所有的数据类型都是可以改变的。Python 引入不可改变类型我认为是为了支持线程安全,而因为 Javascript 是单线程模型,所以没有必要引入不可改变类型。
当然在 Javascript 可以定义一个对象的属性为只读。
var obj = {}; Object.defineProperty(obj, "prop", { value: "test", writable: false });
JavaScript
或者参考 React 的思想,使用 immutable.js 这样的库。

数据类型

Javascript 的数据类型比较简单,有 object、string、boolean、number、null 和 undefined,总共六种,ES6 又加入了 symbol。
Python 有五个内置的简单数据类型 bool、int、long、float 和 complex,另外还有容器类型,代码类型,内部类型等等。

布尔

Javascript 有 true 和 false。Python 有 True 和 False。它们除了大小写没有什么区别。

字符串

Javascript 采用 UTF16 编码。
Python 使用 ASCII 码。需要调用 encode、decode 来进行编码转换。使用 u 作为前缀可以指定字符串使用 Unicode 编码。

数值

Javascript 中所有的数值类型都是实现为 64 位浮点数。支持 NaN(Not a number),正负无穷大(+/-Infinity)。n
Python 拥有诸多的数值类型,其中的复数类型非常方便,所以在 Python 在科研和教育领域很受欢迎。这应该也是其中一个原因吧。Python 中没有定义 NaN,除零操作会引发异常。

列表

Javascript 内置了 array 类型(array 也是 object)
Python 的列表(List)和 Javascript 的 Array 比较接近,而元组(Tuple)可以理解为不可改变的列表。
除了求长度在 Python 中是使用内置方法 len 外,基本上 Javascript 和 Python 都提供了类似的方法来操作列表。Python 中对列表下标的操作非常灵活也非常方便,这是 Javascript 所没有的。例如 l[5:-1],l[:6]等等。

字典、哈希表、对象

Javascript 中大量的使用{}这样的对象字面量来创建对象,这些对象和字典没有什么区别,可以使用[]或者.来访问对象的成员。可以动态的添加,修改和删除成员。可以认为对象就是 Javascript 的字典或者哈希表。对象的 key 必须是字符串。
Python 内置了哈希表(dictS),和 Javascript 不同的是,dictS 可以有各种类型的 key 值。
ES6 引入了 Map、WeakMap、Set、WeakSet。

空值

Javascript 定义了两种空值。 undefined 表示变量没有被初始化,null 表示变量已经初始化但是值为空。
Python 中不存在未初始化的值,如果一个变量值为空,Python 使用 None 来表示。
Javascript 中变量的声明和初始化
v1; v2 = null; var v3; var v4 = null; var v5 = "something";
JavaScript
Python 中变量的声明和初始化
v1 = None v2 = 'someting'
Python
ES6 引入了 const、let。
Javascript 中检查某变量的存在性:
if (!v) { // do something if v does not exist or is null or is false } if (v === undefined) { // do something if v does not initialized }
JavaScript
注意使用!v 来检查 v 是否初始化是有歧义的因为有许多种情况!v 都会返回 true
Python 中检查某变量的存在性:
try: v except NameError ## do something if v does not exist
Python
在 Python 中也可以通过检查变量是不是存在于局部 locals() 或者全局 globals() 来判断是否存在该变量。

类型检查

Javascript 可以通过 typeof 来获得某个变量的类型:
typeof in Javascript 的例子:
typeof 3; // "number" typeof "abc"; // "string" typeof {}; // "object" typeof true; // "boolean" typeof undefined; // "undefined" typeof function() {}; // "function" typeof []; // "object" typeof null; // "object"
JavaScript
要非常小心的使用 typeof,从上面的例子你可以看到,typeof null 居然是 object。因为 javascript 的弱类型特性,想要获得更实际的类型,还需要结合使用 instanceof,constructor 等概念。具体请参考这篇文章
Python 提供内置方法 type 来获得数据的类型。
>>> type([]) is list True >>> type({}) is dict True >>> type('') is str True >>> type(0) is int True
Python
同时也可以通过 isinstance() 来判断类的类型
class A: pass class B(A): pass isinstance(A(), A) # returns True type(A()) == A # returns True isinstance(B(), A) # returns True type(B()) == A # returns False
Python
但是注意 Python 的 class style 发生过一次变化,不是每个版本的 Python 运行上述代码的行为都一样,在 old style 中,所有的实例的 type 都是‘instance’,所以用 type 方法来检查也不是一个好的方法。这一点和 Javascript 很类似。

自动类型转换(隐式类型转换)

当操作不同类型一起进行运算的时候,Javascript 总是尽可能的进行自动的类型转换,这很方便,当然也很容易出错。尤其是在进行数值和字符串操作的时候,一不小心就会出错。我以前经常会计算 SVG 中的各种数值属性,诸如 x,y 坐标之类的,当你一不小心把一个字符串加到数值上的时候,Javascript 会自动转换出一个数值,往往是 NaN,这样 SVG 就完全画不出来啦,因为自动转化是合法的,找到出错的地方也非常困难。
Python 在这一点上就非常的谨慎,一般不会在不同的类型之间做自动的转换。

语法

风格

Python 使用缩进来决定逻辑行的结束非常具有创造性,这也许是 Python 最独特的属性了,当然也有人对此颇具微词,尤其是需要修改重构代码的时候,修改缩进往往会引起不小的麻烦。
Javascript 虽然名字里有 Java,它的风格也有那么一点像 Java,可是它和 Java 就好比雷峰塔和雷锋一样,真的没有半毛钱的关系。到时语法上和 C 比较类似。这里必须要提到的是 coffeescript 作为构建与 Javascript 之上的一种语言,采用了类似 Python 的语法风格,也是用缩进来决定逻辑行。
Python 风格
def func(list): for i in range(0,len(list)): print list[i]
Python
Javascript 风格
function func(list) { for (var i = 0, len = list.length(); i < len; i++) { console.log(list[i]); } }
JavaScript
从以上的两个代码的例子可以看出,Python 确实非常简洁。

作用范围和包管理

Javascript 的作用域是由方法 function 来定义的,也就是说同一个方法内部拥有相同的作用域,即函数作用域。这个严重区别与 C 语言使用{}来定义的作用域。Closure 是 Javascript 最有用的一个特性。
Python 的作用域是由 module,function,class 来定义的。
Python 的 import 可以很好的管理依赖和作用域,而 Javascript 没有原生的包管理机制,需要借助 AMD、CMD 来异步的加载依赖的 js 文件。
ES6 加强了块级作用域,浏览器、node 也开始支持 es module。

赋值逻辑操作符

Javascript 使用=赋值,拥有判断相等(==)和全等(===)两种相等的判断。其它的逻辑运算符有&& 和||,和 C 语言类似。
Python 中没有全等,或和与使用的时 and 和 or,更接近自然语言。Python 中没有三元运算符 A :B ?C,通常的写法是
(A and B) or C
Python
因为这样写有一定的缺陷,也可以写作
B if A else C
Python
Python 对赋值操作的一个重要的改进是不允许赋值操作返回赋值的结果,这样做的好处是避免出现在应该使用相等判断的时候错误的使用了赋值操作。因为这两个操作符实在太像了,而且从自然语言上来说它们也没有区别。

++运算符

Python 不支持++运算符,没错你再也不需要根据++符号在变量的左右位置来思考到底是先加一再赋值呢还是先赋值再加一。

连续赋值

利用元组(tuple),Python 可以一次性的给多个变量赋值
(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
Python

函数参数

Python 的函数参数支持命名参数和可选参数(提供默认值),使用起来很方便,Javascript 以前不支持可选参数和默认值(可以通过对 arguments 的解析来支持)(ES6 已支持)
def info(object, spacing=10, collapse=1):
Python

其它

立即调用函数表达式 (IIFE)

Javascript 的一个方便的特性是可以立即调用一个刚刚声明的匿名函数。也有人称之为自调用匿名函数。
下面的代码是一个 module 模式的例子,使用闭包来保存状态实现良好的封装。这样的代码可以用在无需重用的场合。
var counter = (function() { var i = 0; return { get: function() { return i; }, set: function(val) { i = val; }, increment: function() { return ++i; } }; })();
JavaScript
Python 没有相应的支持。

生成器和迭代器(Generators & Iterator)

在我接触到的 Python 代码中,大量的使用这样的生成器的模式。
Python 生成器的例子
# a generator that yields items instead of returning a list def firstn(n): num = 0 while num < n: yield num num += 1 sum_of_first_n = sum(firstn(1000000))
Python
ES6 中引入了一些列的新特性,其中就包括生成器和迭代器。
Javascript 迭代器和生成器的例子。
function fib() { var i = 0, j = 1; while (true) { yield i; var t = i; i = j; j += t; } }var g = fib(); for (var i = 0; i &lt; 10; i++) { console.log(g.next()); }
JavaScript

列表(字典、集合)映射表达式 (List、Dict、Set Comprehension)

Python 的映射表达式可以非常方便的帮助用户构造列表、字典、集合等内置数据类型。
下面是列表映射表达式使用的例子:
>>> [x + 3 for x in range(4)] [3, 4, 5, 6] >>> {x + 3 for x in range(4)} {3, 4, 5, 6} >>> {x: x + 3 for x in range(4)} {0: 3, 1: 4, 2: 5, 3: 6}
Python
Javascript 曾经有传说要引入,后面没消息了。MDN 上还能搜到
// 非标准!! var numbers = [1, 2, 3, 4]; var doubled = [i * 2 for (i of numbers)];
JavaScript

Lamda 表达式 (Lamda Expression )

Lamda 表达式是一种匿名函数,基于著名的 λ 演算。许多语言诸如 C#,Java 都提供了对 lamda 的支持。Pyhton 就是其中之一。Javascript ES6 已支持箭头函数

装饰器(Decorators)

Decorator 是一种设计模式,大部分语言都可以支持这样的模式,Python 提供了原生的对该模式的支持,算是一种对程序员的便利吧。Javascript ES6 已支持。
更多 decorator 的内容,请参考 https://wiki.python.org/moin/PythonDecorators
本人对 Javascript 和 Python 的认识有限,欢迎大家提出宝贵意见。
React Fiber 漫谈2018-5