[Python] Meta Programming 與 __metaclass__, __call__, __new__, __init__, object, type

[注意]

  1. 本文假設你已經寫過一點 Python,並且有基礎的物件導向 (object-oriented, OO) 觀念,至少要知道什麼是 class 跟 instance。
  2. 本文只討論 Python 3,至於 Python 2 那種早該被歷史淘汰的該死東西將完全不去鳥它。
  3. 這篇只是筆記性質的虎頭蛇尾的半廢物文章,寫到後來就忘在硬碟裡,只是今天 [2016-08-28 日 23:55] 不小心把這篇 deply 出來就想說…算了出來就出來吧 _(:3」∠ )_ ,但現在沒那心情所以也懶得仔細再看一次科科,本文最後的 singleton 範例有些東西我自己也搞不清楚了,知道的大大敬請指教 <(_ _)>

要理解 Python 的 Meta programming,必須同時理解 object, type以及 __metaclass__, __call__, __new__, __init__ 的運作機制。

Python 中的 object

  • 首先我們必須先牢牢記住一個重要觀念:Python 是一種物件導向語言;在 Python 裡面,所有你能想得到的東西都是 object
  • 我們可以用 type() 這個內建函數,來得知某樣東西的 type。
    • type() 一個 object 時,會回傳一個東西叫做 Type Object ,你可以用 print() 印出來看看是什麼樣子:
1
2
3
4
5
6
print( type(1) ) # <class 'int'>
print( type(1.1) ) # <class 'float'>
print( type("hello") ) # <class 'str'>
print( type([1,2,3]) ) # <class 'list'>
print( type({1,2,3}) ) # <class 'set'>
print( type({1: 10, 2:20}) ) # <class 'dict'>
  • 上面的例子說明了,像 "hello" 這個字串,在 Python 中其實是 str 這個 class 的 instance。
  • 連函數(function)也是一個 object:

Read More

「逃離伊甸園」的結局

這篇漫畫非常非常有意思,但看完以後去 Google 了一下,發現大家對這部的結局罵翻天…但我個人是很滿意這個收尾,看完後欲罷不能趕緊找這作者的其他作品來看。

以下嚴重劇透,沒看過這部的請自主避難(但我非常推薦去讀一下這部),謝謝。

Read More

Lisp 中的 Symbol 與 Atom 差別在哪

初學 Lisp 時一直搞不懂 atom 跟 symbol 到底差在哪…不對那個時候連 symbol 是三小都搞不清楚。不過不才在下最近突然理解了 Lisp 裡面的 Symbol 跟 Atom 的意義,所以來解釋一下。

此篇假設你已經理解 Lisp 中的 cons 與 list

atom

atom 這個詞本來的意思就是指不可分割的東西、最小的單位,而 Lisp 中,只有 cons (或者用其他語言使用者比較可以理解的詞,叫做 list) 是可以分割的。例如最基本的 operator 之一。

Read More

搞懂 Lexical scoping 與 Dynamic scoping 的差異與用途

「阿母啊!我終於搞懂 Lexical Scoping 是啥啦~!」

Lexical scoping 與 Dynamic scoping 我以前一直嘗試想要弄懂但卻一直搞不懂,就算一時看懂了也不知這到底在幹麻或能幹麻所以每次看完就忘記(用不到的東西都記不住的啦),但最近因為 Lisp 寫得比較熟悉了,開始狂用 functional programming 的一些技巧後才終於了解其中奧妙,尤其是 lexical scoping 的好用之處,以及為什麼王垠會幹譙 dynamic scoping。希望這篇筆記可以幫助到一些跟我一樣搞不清楚狀況的人搞懂並記住兩者差異,尤其是如何善用 lexical scoping 的特性做一些神奇的功能。

  • 採用 lexical scoping 的語言:JavaScript、Scheme、Common Lisp
  • 採用 dynamic scoping 的語言:Emacs Lisp(預設行為)、Lisp Machine Lisp。

關於 Emacs Lisp 的 Lexical Scoping

Emacs Lisp 在 Emacs 24.3 開始也支援 lexical scoping 了,然而它預設仍然是 dynamic scoping。如果需要使用 lexical scoping。必須在你寫的 el 檔案的第一行內加入這句咒語「-*- lexical-binding: t; -*-」,例如:

1
;;; hexo.el --- Major mode & tools for Hexo -*- lexical-binding: t; -*-

這句咒語的作用技術上來說,Emacs 的設計是:當 lexical-binding 這個 buffer local 變數的值是 non-nil 時,該 el 檔案/buffer 就會以 lexical scoping 去 interpret Emacs Lisp。而-*- lexical-binding: t; -*-的作用其實就是自動在你打開這個檔案、或者 interpreter 在 eval 這個檔案時自動幫你(setq-local lexical-binding t)而已。

要記住用身體記住最快,所以如果可以建議以下整個自己開個 Emacs 跑一遍。以下會拿 Emacs Lisp 當例子,而不是大家常用的 Scheme 或 JS。為什麼呢~~~因為 Emacs Lisp 是目前我所知道最詭異的語言,竟然同時存在 dynamic scoping 跟 lexical scoping 的執行方式……總而言之,寫 Emacs Lisp 你可以自己決定要用哪一種 scoping,這種情況拿來實驗 scoping 是非常容易理解的,因為你可以拿一樣的 code 放在不同的 scoping 下實際執行比較看看。

下面會大量用到 lambda。如果你不熟悉 Lisp 裡的 lambda ,請先讀過這篇Lisp 裡面的 Lambda 到底是什麼?

先從例子來談談 Dynamic Scoping

我們先用 Emacs 建立一個新檔案 test.el,注意行首不要加上 -*- lexical-binding: t; -*- ,這樣該檔案寫的 Emacs Lisp 都會是預設的 dynamic scoping。

首先,我們先寫出兩個非常簡單、功能完全相同的函數(只是一個有名字、一個是 lambda 匿名函數),這兩個函數都一樣接受 1 個參數 x,並回傳 (* x 3) 的值:

1
2
3
4
5
6
;; 匿名函數
(lambda (x) (* x 3))
;; 有名字的函數
(defun triple (x)
(* x 3))

Read More