最近由于工作的需要開始開發一些python的東西,由于之前一直在使用javascript,是以會不自覺的使用一些javascript的概念,文法什麼的,經常掉到坑裡。我覺得對于從javascript轉到python,有必要總結一下它們之間的差異。
基本概念
python和javascript都是腳本語言,是以它們有很多共同的特性,都需要解釋器來運作,都是動态類型,都支援自動記憶體管理,都可以調用eval()來執行腳本等等腳本語言所共有的特性。
然而它們也有很大的差別,javascript這設計之初是一種用戶端的腳本語言,主要應用于浏覽器,它的文法主要借鑒了c,而python由于其“優雅”,“明确”,“簡單”的設計而廣受歡迎,被應用于教育,科學計算,web開發等不同的場景中。
程式設計範式
python和javascript都支援多種不同的程式設計範式,在面向對象的程式設計上面,它們有很大的差別。javascript的面向對象是基于原型(prototype)的, 對象的繼承是由原型(也是對象)建立出來的,由原型對象建立出來的對象繼承了原型鍊上的方法。而python則是中規中矩的基于類(class)的繼承,并天然的支援多态(polymophine)。
因為是基于對象的繼承,在javascript中,我們沒有辦法使用類成員empcount,隻好聲明了一個全局變量,當然實際開發中我們會用更合适的scope。注意javascript建立對象需要使用new關鍵字,而python不需要。
除了原生的基于原型的繼承,還有很多利用閉包或者原型來模拟類繼承的javascript oo工具,因為不是語言本身的屬性,我們就不讨論了。
線程模型
在javascript的世界中是沒有多線程的概念的,并發使用過使用事件驅動的方式來進行的, 所有的javascript程式都運作在一個線程中。在html5中引入web worker可以并發的處理任務,但沒有改變javascript單線程的限制。
python通過thread包支援多線程。
不可改變類型 (immutable type)
在python中,有的資料類型是不可改變的,也就意味着這種類型的資料不能被修改,所有的修改都會傳回新的對象。而在javascript中所有的資料類型都是可以改變的。python引入不可改變類型我認為是為了支援線程安全,而因為javascript是單線程模型,是以沒有必要引入不可改變類型。
當然在javascript可以定義一個對象的屬性為隻讀。
在ecmascript5的支援中,也可以調用object的freeze方法來是對象變得不可修改。
object.freeze(obj)
資料類型
javascript的資料類型比較簡單,有object、string、boolean、number、null和undefined,總共六種
python中一切均為對象,像module、function、class等等都是。
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),正負無窮大(+/-infiity)。
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值。
空值
javascript定義了兩種空值。 undefined表示變量沒有被初始化,null表示變量已經初始化但是值為空。
python中不存在未初始化的值,如果一個變量值為空,python使用none來表示。
javascript中變量的聲明和初始化
在如上的代碼中v1是全局變量,未初始化,值為undefined;v2是全局變量,初始化為空值;v3為局部未初始化變量,v4是局部初始化為空值的變量;v5是局部已初始化為一個字元處的變量。
python中變量的聲明和初始化
python中的變量聲明和初始化就簡單了許多。當在python中通路一個不存在的變量時,會抛出nameerror的異常。當通路對象或者字典的值不存在的時候,會抛出attributeerror或者keyerror。是以判斷一個值是否存在在javascript和python中需要不一樣的方式。
javascript中檢查某變量的存在性:
注意使用!v來檢查v是否初始化是有歧義的因為有許多種情況!v都會傳回true
python中檢查某變量的存在性:
在python中也可以通過檢查變量是不是存在于局部locals()或者全局globals()來判斷是否存在該變量。
類型檢查
javascript可以通過typeof來獲得某個變量的類型:
typeof in javascript 的例子:
要非常小心的使用typeof,從上面的例子你可以看到,typeof null居然是object。因為javscript的弱類型特性,想要獲得更實際的類型,還需要結合使用instanceof,constructor等概念。具體請參考這篇文章
python提供内置方法type來獲得資料的類型。
同時也可以通過isinstance()來判斷類的類型
但是注意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風格
從以上的兩個代碼的例子可以看出,python确實非常簡潔。
作用範圍和包管理
javascript的作用域是由方法function來定義的,也就是說同一個方法内部擁有相同的作用域。這個嚴重差別與c語言使用{}來定義的作用域。closure是javascript最有用的一個特性。
python的作用域是由module,function,class來定義的。
python的import可以很好的管理依賴和作用域,而javascript沒有原生的包管理機制,需要借助amd來異步的加載依賴的js檔案,requirejs是一個常用的工具。
指派邏輯操作符
javascript使用=指派,擁有判斷相等(==)和全等(===)兩種相等的判斷。其它的邏輯運算符有&& 和||,和c語言類似。
python中沒有全等,或和與使用的時and 和 or,更接近自然語言。python中沒有三元運算符 a :b ?c,通常的寫法是
因為這樣寫有一定的缺陷,也可以寫作
python對指派操作的一個重要的改進是不允許指派操作傳回指派的結果,這樣做的好處是避免出現在應該使用相等判斷的時候錯誤的使用了指派操作。因為這兩個操作符實在太像了,而且從自然語言上來說它們也沒有差別。
++運算符
python不支援++運算符,沒錯你再也不需要根據++符号在變量的左右位置來思考到底是先加一再指派呢還是先指派再加一。
連續指派
函數參數
python的函數參數支援命名參數和可選參數(提供預設值),使用起來很友善,javascript不支援可選參數和預設值(可以通過對arguments的解析來支援)
其它
立即調用函數表達式 (iife)
javascript的一個友善的特性是可以立即調用一個剛剛聲明的匿名函數。也有人稱之為自調用匿名函數。
下面的代碼是一個module模式的例子,使用閉包來儲存狀态實作良好的封裝。這樣的代碼可以用在無需重用的場合。
python沒有相應的支援。
生成器和疊代器(generators & iterator)
在我接觸到的python代碼中,大量的使用這樣的生成器的模式。
python生成器的例子
javascript1.7中引入了一些列的新特性,其中就包括生成器和疊代器。然而大部分的浏覽器除了mozilla(mozilla基本上是在自己玩,下一代的javascript标準應該是ecmascript5)都不支援這些特性
javascript1.7 疊代器和生成器的例子。
清單(字典、集合)映射表達式 (list、dict、set comprehension)
python的映射表達式可以非常友善的幫助使用者構造清單、字典、集合等内置資料類型。
下面是清單映射表達式使用的例子:
lamda表達式是一種匿名函數,基于著名的λ演算。許多語言諸如c#,java都提供了對lamda的支援。pyhton就是其中之一。javascript沒有提供原生的lamda支援。但是有第三方的lamda包。
裝飾器(decorators)
decorator是一種設計模式,大部分語言都可以支援這樣的模式,python提供了原生的對該模式的支援,算是一種對程式員的便利把。
decorator的用法如下。