共计 3465 个字符,预计需要花费 9 分钟才能阅读完成。
写这篇文章的目的是想记录下 NodeJs(后面简称 node)与 python 的使用对比,希望看完之后大家对 node 跟 python 有个基本的认识。
(本文使用的 node 版本为 v12.14.0,python 为 v3.8.3。)
简介
node 是一个基于 Chrome V8 引擎的 JavaScript(简称 js)运行时。简单的说就是通过 v8 引擎 (由 c ++ 编写) 解释并执行 js 代码,然后就能运行在服务器上。
python 则是一门面向对象的解释型编程语言,目前最广泛的 python 解释器是 CPython,就是通过 C 语言把 python 代码编译成字节码然后在虚拟机上运行。
node 适用于前端代码的打包发布、服务端开发跟桌面端应用开发等。而 python 则适合科学计算、数据分析、自动化运维等场景。
数据结构
node 的数组对应 python 的列表,都可以存放多种不同类型的数据。node 对象则对应 python 的字典,都是使用 key-value 的形式。set 结构也是类似的概念,都是没有重复元素的集合。node 没有 python 中的元组类型,但是可以通过 Object.freeze 实现类似的效果。
node
let list = [1,2,3]
list.push(4) // list [1,2,3,4]
list.splice(2,1) // list [1,2,4]
list.concat([5,6]) // [1,2,4,5,6]
list.slice(1) // [2,4]
let [a,b,c] = list // a=1 b=2 c=4
let tuple = [1,2]
Object.freeze(tuple)
tuple[0] = 3 // tuple [1,2]
// 一般可以使用 for、forEach、for...of 进行遍历
list.forEach((item)=>{console.log(item)}) //1 2 4
python
list = [1,2,3]
list.append(4) # list [1,2,3,4]
del list[2] # list [1,2,4]
list + [5,6] # [1,2,4,5,6]
list[1:] # [2,4]
a,b,c = list # a=1 b=2 c=4
tuple = (1,2)
tuple[0] = 3 # 报错 tuple (1,2)
#通过 for in 遍历
for item in list:
print(item) # 1 2 4
变量与作用域
node
node 中全局变量在 global 对象上定义,可以在多个模块中访问。模块中声明变量可以通过 var、let 和 const,其中 let 跟 const 在代码块 (if、for 等) 内无法被外面的方法访问,而 var 可以。除了块级作用域外,还有函数作用域,函数作用域内的变量想被函数外访问则需要通过闭包。另外每个 js 文件就是一个模块,而模块最终会被一个匿名函数包裹(exports 跟 module 就是匿名函数里的参数),所以模块里的变量也是局部变量。
// 代码块内的变量在代码块外面访问不了
{
let a = 1
const PI = 3.14 //const 用来定义常量
}
console.log(a, PI) // 无法访问代码块内的变量,会报错
// 通过闭包访问函数内的变量
function wrap(){
let n = 0
function inside(){
n = n + 1
return n
}
return inside
}
let count = wrap()
count() // 1
count() // 2
python
python 没有像 node 这样的 global 对象,多个模块想要共享一个变量只能通过引入同一个模块的方式获取共享变量。python 变量定义直接使用赋值的方式即可:value = 1
。python 中只有模块、类和函数会引入新的作用域,代码块不会引入新的作用域。python 的闭包跟 node 有个明显的不同,假设函数 inside 在函数 wrap 内,内层函数 inside 想修改函数 wrap 的变量需要通过 nonlocal 关键字,而 node 可以直接使用函数 wrap 的变量。
# globalValue.py 存放全局变量的模块
global_list = []
#p1.py
from py2 import *
import globalValue
if __name__ == '__main__': # 表示如果主入口是 p1.py 文件
do_something()
print('py1.py')
print(globalVal.global_list)
#p2.py
import globalVal
def do_something():
globalVal.global_list.append('a')
print('py2.py')
print(globalVal.global_list)
#-------------------------------------
def wrap():
n = 0
def inside():
nonlocal n # 需要使用 nonlocal 关键字
n = n + 1
return n
return inside
count = wrap()
count() # 1
count() # 2
模块
node
导入模块通过 require 方法。
导出模块可以使用 exports 跟 module.exports,而 require 最终使用的是 module.exports 对象。需要注意的是 exports 跟 module.exports 虽然使用的是同一个内存地址,但如果对 exports 赋值了引用类型的值,那么就等于给 exports 使用了新的内存地址,使用 require 方法时就无法获取到 exports 的值。
通过文件夹中的 package.json 来表示包(package),package.json 包含了包的描述信息、依赖项、运行命令等。
// a.js 跟 main.js 在同一个目录
exports = function name_a(){console.log(1)
}
// main.js
let a = require('./a')
console.log(a) // 输出{} 如果改成 module.exports 则输出 [Function: name_a]
python
导入模块通过 import 或 from package_name import module_name 导入。模块导入还分为绝对导入跟相对导入,绝对导入要求使用导入模块的完整路径。相对导入则不需要。
导出模块并不需要特定语法,查看模块的变量可以通过 dir 方法。
python 通过__init__.py 文件来表示当前目录是 package,当有外部 import 时,就执行里面的函数(pyton3.3 后可以不用添加__init__.py 文件)。__init__.py 可以修改模块的引入方式。
# 绝对导入
from package.module import function
#相对导入
from . import module
#查看当前模块含有的变量
dir()
#__init__.py
from package.module import function # 在导入的模块可以用 function()。__all__ = ['module'] # 导入模块用 from package import * 后就能直接使用 module 里面的变量跟方法。
性能
在性能方面 python 比 node 差了不少,且不说 CPython,同样有 JIT 的 pypy 也比 node 慢(用 pypy3 计算斐波那契数列,在 n =40 时大概比 node 慢了 1 秒多)。另外一般想提升多核 CPU 的利用率是通过开启多线程,但 python 的多线程由于 CPython 的 GIL(全局解释器锁),一个 CPU 同一个时刻只能执行一个线程,所以在计算密集型任务通过开启多进程来优化。而 node 的 worker_thread 模块没有这个问题。
function fib(n) {if (n === 0) {return 0} else if (n === 1) {return 1} else {return fib(n - 1) + fib(n - 2)
}
}
console.time('fib')
fib(40)
console.timeEnd('fib')
//python 也是同样采用递归的方式,代码就省略了,大家可以在自己的电脑上试试。
总结
node 跟 python 在各自领域都有不错的发展。对 node 来说前端打包构建、reactvue 的同构应用这些场景很难被替代(采用 js 的好处)。而 Deno 如果在性能方面没有高过 node 很多的话也是不太可能取代 node 的,语言的生态是很重要的。python 则由于有良好的开发效率、强大的库生态;并且随着近几年机器学习的热潮,python 的语言热度一直保持在前几名。