目錄
- TL; DR
- python調javascript
- javascript調python
- 原理
- 解析副語言
- 優點
- 缺點
首先要明白的是,javascript和python都是解釋型語言,它們的運作是需要具體的runtime的。
- Python: 我們最常安裝的Python其實是cpython,它有一個基于C的解釋器。除此之外還有像pypy這種解釋器,等等。基本上,不使用cpython作為python的runtime的最大問題就是通過pypi安裝的那些外來包,甚至有一些cpython自己的原生包(像
這種)都用不了。collections
- JavaScript: 常見的運作引擎有google的V8,Mozilla的SpiderMonkey等等,這些引擎可以把JavaScript代碼轉換成機器碼執行。基于這些基礎的運作引擎,我們可以開發支援JS的浏覽器(比如Chrome的JS運作引擎就是V8);也可以開發功能更多的JS運作環境,比如Node.js,相當于我們不需要一個浏覽器,也可以跑JS代碼。有了Node.js,JS包管理也變得友善許多,如果我們想把開發好的Node.js包再給浏覽器用,就需要把基于Node.js的源代碼編譯成浏覽器支援的JS代碼。
在本文叙述中,假定:例如,python調用js,python就是主語言,js是副語言
- 主語言: 最終的主程式所用的語言
- 副語言: 不是主語言的另一種語言
适用于:
- 希望自己的項目不要依賴副語言的runtime。換句話說,python調javascript的時候想隻裝一個python,javascript調用python的時候也不想裝python。
- 副語言基本是純代碼,沒有用其他底層實作,也沒有引用太多複雜的包。比如,javascript調python,python用了numpy,打咩;python調用javascript,javascript用了一些C++的Node插件,打咩。
- 主語言和副語言互動很頻繁,且互動的對象很難序列化(比如函數,複雜的類之類的)。
- 如果是python調用javascript,對運作效率不要有太多要求。
有庫!有庫!有庫!
- Js2Py:
pip install js2py
- 優點:
- 目前為止最好的JavaScript to python翻譯器了,除了依賴底層C++實作的包的Node.js包,應該基本都可以翻譯,翻譯得還蠻快。
- 有翻譯成python的中間結果,翻譯完之後以後再import同一個javascript包的話,會直接調用python版本。
- 看起來作者最近還在維護。
- 缺點:
- Javascript部分代碼也比較複雜的時候,會比直接在Node.js裡運作這部分js code慢很多。雖然這個庫裡也實作了一個python的javascript解釋器,能比翻譯器快,但目前它實作的版本裡隻支援運作一段給定的javascript代碼,兩種語言的互動部分的優勢就沒了,期待作者後面加更多新feature吧。
- 把它放到自己的應用場景的時候,總有一些小問題,可能需要稍微修改源碼優化。比如:
- 對Node.js包的安裝-翻譯-調用這部分的pipeline做得不是特别好,影響程式效率。比如說,它翻譯之前要先把基于ES6的包用
轉成ES5,但像babel
這樣的依賴項被安裝在了臨時目錄裡,導緻每次打開python程序執行翻譯的時候都要安裝一次。不過,同一個python程序裡翻譯多次不會重複安裝,多個python程序裡多次引用同一個包也不會,後面幾次直接會導入翻譯好的python版本。babel
-
翻譯之後隻會列印console.log(a, b, c)
,可能需要改一下a
裡console的實作。js2py
- 對Node.js包的安裝-翻譯-調用這部分的pipeline做得不是特别好,影響程式效率。比如說,它翻譯之前要先把基于ES6的包用
- 優點:
- Brython:是python3的javascript解釋器,主要目的是讓浏覽器可以跑python,沒太用過。
- PScript: 把python代碼翻譯成javascript代碼,但正如其包名所說,隻能翻譯純python的簡單腳本(換句話說,隻能翻譯python的一個子集)。
- Transcrypt:python to javascript翻譯器,也隻能翻譯一個子集。
- 優點: 功能比較全的javascript to python翻譯器。
- 雖然隻能翻譯python語言的一個子集,但功能比
還是要全不少的。PScript
- 翻譯的時候還有許多宏可以決定要如何翻譯。
- 雖然隻能翻譯python語言的一個子集,但功能比
- 缺點:在我删光了我的python代碼裡對
的依賴後,發現numpy
包也翻譯不了,遂放棄此路。collections
- 優點: 功能比較全的javascript to python翻譯器。
由于python和javascript都不是強類型的語言,是以一定程度上也是可以互譯的。其次,由于它們都屬于解釋型語言,是以也可以用主語言實作一個副語言的解釋器,最後統一在主語言的runtime下運作。更深入的原理涉及一些編譯原理的知識,咱也不太懂,就隻按自己的粗淺了解簡單講一下,不詳細展開了,有不對的地方歡迎指正。
這種打不過就加入(……)的方式有兩種實作方法,一種是直接翻譯成主語言,另一種是用主語言寫一個副語言的解釋器。翻譯和解釋的差別在哪兒呢?簡單舉個例子,假如你是個中文母語者:
- 翻譯: 有人跟你說“Go and fetch a book”(副語言),你先在腦中翻譯成中文(主語言):“去拿本書”,哦……懂了(解析主語言),然後去拿書了(在主語言runtime下執行指令)
- 解釋: 有人跟你說“Go and fetch a book”(副語言),你直接懂了(解析副語言),然後就去拿書了(在主語言runtime下運作)。(這不是我們學外語的理想狀态嘛!)
順便一提,在這個例子裡,編譯型語言就像是,有人早上跟你說“Go and fetch a book”,經過漫長的訓練(編譯和連結),然後你就變成了一個隻會拿書的機器(可執行檔案),這一天裡别人隻要叫你(運作Run),你就會自動去拿書,不需要再跟你說“Go and fetch a book”(源代碼)。
不管是翻譯還是解釋,首先的第一步應該都是要解析副語言。一般情況下可以選擇解析出副語言的抽象文法樹(AST),然後根據抽象文法樹決定如何翻譯,或者如何執行程式。
以副語言為js,即python調用javascript為例。
題外話: javascript有個專門的包esprima.js專門解析js code的AST。Js2Py的作者把它人工翻譯成了Python(強!):pyjsparser,成功為js翻譯成python打好了基石。
這是
pyjsparser
解析出來的一個AST:
>>> from pyjsparser import parse
>>> parse('const abc = "Hello!"\nconst c = abc')
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "abc"
},
"init": {
"type": "Literal",
"value": "Hello!",
"raw": "\"Hello!\""
}
}
],
"kind": "const"
},
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "c"
},
"init": {
"type": "Identifier",
"name": "abc"
}
}
],
"kind": "const"
}
]
}
我們就可以周遊這個AST,然後智能地翻譯出等價的python程式。
比如第一層是程式層,
body
隻有一個元素,說明程式隻有兩行指令,都是定義變量(
"VariableDeclaration"
)。
那麼怎麼定義的呢?從
declarations
可知,每個指令隻定義了一個變量,且類型都是
const
常量(說明後面不允許改變值)。
我們知道定義變量需要知道:(1)變量的名字(2)有無初始值。
第一個變量的名字(
id
)是
abc
,有初始值(
init
),是一個字面量(
Literal
),轉成python的字面量之後值在
value
字段裡,聲明時的原始代碼在
raw
裡。舉個例子,可以簡單地翻譯為
abc = Constant("Hello")
,其中
Constant
是特地為JS定義的常量類型。第二個變量的名字(
id
c
init
),是通過變量定義的,變量的辨別符(
Identifier
)為
abc
。同理,也可以翻譯為
c = Constant(abc)
。
如果是解釋器的話,最後python中隻要有2個常量,名字分别是
abc
和
c
,且值都為
"Hello"
就行,不需要特地關心如何得到這個結果的,甚至于可能,我也不需要實際執行兩次指派。
當然實際使用的時候,翻譯和解釋的實作可能比這複雜得多,但基本都有前人已經做過相關的工作了。
- 隻需要裝主語言的runtime。對于想把自己的項目作為主語言的插件分發,不要求目标機器一定得有副語言runtime的情況比較适用。舉例子的話,比如想直接線上嘗試python,可以用python到js的翻譯庫或者解釋器,這樣可以不需要裝python也可以運作python,對于把python嵌入浏覽器可能有不錯的效果。
- 無縫互動!因為最後都在一個體系裡了,再也不用為了傳遞函數和複雜類對象而煩惱。
- 副語言能支援的包有限。比如python轉成js的時候,甚至很多原生包(如
都用不了,更别說collections
之類的了);js轉python還好,大概隻要不是底層由C++實作的Node.js包的話,應該都可以翻譯成功。這基本是通過這種原理javascript調用python的瓶頸。numpy
- 兩種語言運作效率不同。由于Javascript運作速度比python快多了,如果是python調用javascript,在整個項目javascript比重很大,且運作效率又很重要的時候,會慢到不能接受(……),個人覺得這是python通過該原理調用javascript的最大瓶頸。