Web F2E 看 Python Syntax

Bruce Eckel's keynote,

雖然主業是 Web Front End,不過其實要搞好 Front End,後端也不可不知,所以我工作內容其實也寫 Python 寫了不少,最近終於可以跟 Flake8 相安無事,所以想來記錄一些對我來說很有趣的 Python Syntax,不全是喜歡的就是了,以下內容以 2.7 為主。

內建支援 String Formatting

第一個我覺得很棒的是,Python 內建有 String Formatting Operations 可以用,超方便的,所以我只要寫:

'%d: The answer to the ultimate question of life, the universe and everything' % 42

就可以把 42 填進去字串裡面了,雖然我第一次看到放最後面還以為是什麼奇妙的註解符號;傳統的 formatting 用來做翻譯字串就會發現,如果有多個變數,它們的順序在不同的語言可能有不同,傳統的 formatting 只能處理固定順序,不適合這種情景,這時候還有新的format()可以用,幫變數命名好、然後丟參數進去就可以了,例如:

'{author} wrote {name}'.format(author='JRR', name='TLOR')

雖然 Python 的 string format 很好用,不過文件寫得太高深了,所以還有人做了 pyformat.info 這個站,收集了不少實用的範例幫助大家理解,而且仔細看過之後發現舊的格式也可以用 dict 格式丟命名變數進去。

Multiline String

多行字串也是我蠻喜歡的,像是要弄 template 的時候就很方便,JavaScript 一直到 ES6 的 tempalte string 才算是有內建,Python 就用三個引號框起來就可以了:

template_string = """<div>
                       Wow
                     </div>"""

不過很理所當然的,那些為了縮排所填入的空白,就都是真的字串內容,所以Wow前面就是有 23 個空白字元,如果字串在 class 或是 function 定義裡面,那空白就會更多,在一些使用情境下,空白數量是影響很大的,所以就會有到底該怎樣排的問題,StackOverflow 上就可以找到相關的問題,還好我處理 HTML template 的話,影響不大,當然結果會造成一些多餘的資料傳輸啦。

Keyword Arguments

很久以前我寫過一篇 options object 的文章,為的就是處理參數太多造成程式碼不好解讀的問題,沒想到 Python 可以在呼叫函數的時候,傳入參數的名稱,例如以下的函數:

def func(a, b, c, d):
    return a + b + c + d

呼叫的時候可以分別說明每個參數的 key 和 value 對應關係:

func(a=1, b=2, c=3, d=4)

而且也可以混用:

func(1, 2, c=3, d=4)

覺得這語法真是領先超多,當然 ECMAScript 現在可以用 destructing assignment 的語法做到類似效果,不過我覺得還是有些差距。

而針對 Keyword Argument 其實還有特殊的 syntax 是**kwargs,其實我一開始是先看到這個語法的,想說 Python 怎麼有個很像 C++ 指標的東西,看了許久,某天終於會意到 kw 是 keyword 的意思,然後才終於理解是怎麼回事,後來查資料才知道還有 *args,現在的 ECMAScript 的話可以用...spread operator 做到。

Circular Dependency

恩,可以做到循環相依,第一次看到真是覺得不可思議,不過後來慢慢瞭解限制,大概也知道怎麼實際上是如何跑的了。

Ternary Operator

三元運算,Python 的語法真的是比較特別一點,其它語言比較常看到的是用?,不過 Python 是用後置的if else

reality = True if isReal else False

其實我還蠻喜歡後置的if語句,第一次看到這種寫法是在 CoffeeScript,我很常用在一些參數特殊狀況的處理,一樣 CoffeeScript,不用後置if的時候:

filename = file.name

if file.hash
  filename = filename + '-' + file.hash
  
if file.ext
  filename = filename + '.' + file.ext

用了後置if的話可以寫成:

filename = file.name

filename = filename + '-' + file.hash if file.hash

filename = filename + '.' + file.ext if file.ext

看起來整齊許多,視覺上(?)少了一層縮排,不過 Python 的三元運算,和 CoffeeScript 的後置if語法是不一樣的東西,雖然可以用來做類似的事情,但是因為他是三元運算,所以一定要提供else區段:

filename = filename + '.' + file.ext if file.ext else filename

就比較不喜歡這樣就是了。

Tuple

Python 的 List 資料型態可以比做 JS 的 Array、Dict 可以比做 Object,兩種資料型態分別是使用中括號和大括號,不過在 Python 語言裡,還有一種用小括號的 Tuple 資料型態。

Tuple 資料型態似乎還蠻少見的,我第一次聽到這個名詞的時候是在學校學資料庫系統的時候,一筆資料稱為一個 Tuple,不知道為什麼印象很深,然後第一次看到使用 tuple 的程式碼自然是不太理解,不過還算直觀看的懂,後來不知道為什麼查到這種語法其實是一種資料型態叫 Tuple 的,意義上和資料庫系統的 Tuple 感覺還蠻像的,理解這是個資料型態之後用起來覺得順手很多,而且 Python 還蠻自由,很多地方和 List 都可以用一樣的操作,像是in運算,或是作為 function 的多個回傳值(多回傳值的函數也蠻方便的)。

in 運算

上面提到的in運算,用來判斷一個 List 或 Tuple 是否包含特定元素:

if target.stat in ('ACTIVE', 'PREMIUM')
    ok()

對於常在古早 JS 開發的人,真的是超羨慕的,可能有人說可以用indexOf做,雖然 JS String 的 indexOf 很早就有了,但是 Array 的 indexOf 卻是到 ES5.1 才正式進標準,IE9 之前的都不支援,所以要用他來判斷一個元素是否在一個陣列內,首先要確定你不支援 IE8 之前的瀏覽器,不過就算支援,其實程式碼也沒in運算來的漂亮,後來 ES2015 有個比較好一點的 Array.includes 可以用就是了。

Dict

Dict 可以比做 JS 的 Object 比較好理解,對於這個我不能適應的有兩個地方,一是 Dict 不是 class,所以不能直接用.取屬性,一定要用[]或是內建的get(),再來就是用[]取屬性的時候,一定要 key 存在,用到不存在的 key 就會噴錯誤,如果一定要這樣操作就要改用get(),get 還有一個特點是可以給 default 值,如果是複雜的結構,想要一口氣很深入就可以寫成:

data.get('attr1', {}).get('attr2', {}).get('attr3', None)

實在是有點難看,CoffeeScript 是有 Existential Operator 可以做這種多階層的取值:

data.attr1?.attr2?.attr3?

在 TC39 的草案也有類似的 Optional Chainging,這兩樣都是上一篇文章有提到的東西。

Unix Timestamp

內建的 datetime 似乎沒有支援直接輸出 Unix Timestamp,是說目前有需要都用 Pendulum,還蠻好用的,API 介面也蠻直接,也有完整的時區、Period、Duration 等觀念。

PEP8, Flake8

文章一開始提到的 Flake8 把好幾個 code checker 包進去,包括了官方的 PEP8、PyFlakes、pycodestyle 等,我用 Vim 的 Syntastic 都有支援,只要有安裝就會偵測到執行檔,然後就可以用來檢查了,一開始裝起來的時候就和第一次用 JSLint 一樣傷感情,不過兩個月過後到是還蠻適應的,其中比較和以往習慣不一樣的就是 function 參數的值,不論是定義時的 default value 還是呼叫時的 keyword argument,=的左右兩邊都是不加空白的,例如:

def hello(name='John'):
  return 'Hello ' + name

hello(name='Hancock')

另外就是特殊情況需要循環相依,或是 import 但是不使用時,會需要關閉一些檢查,可以在該行末端加上註解關閉特定項目:

import pages  # noqa: F401

錯誤的編號可以參考 Flake8 的文件

Python Enhancement Proposals (PEPs)

之前在研究 Joda Time 的時候,發現 Java 有個 JSR (Java Specification Requests),在找 Python 的 coding style 的時候則是發現了 PEPs (Python Enhancement Proposals),不過 ECMAScript 目前是沒有類似的、完整的收集並編目各個 Proposal 文件的地方,甚至連語言本身的官網都沒有啊...XDrz