在 Lua 裡,metatables 的存在是非常重要的,它提供了高度靈活性供程式設計師實現較良好的程式架構來進行後續的開發,例如:OO 就是由 metatables 裡面的 __index 來實現。
但對我來說,這概念不是很好懂,而且應用上有點 magic。
在講 metatables 之前,還是要先聊聊 Lua 裡面的 table。table 可以存放各種資料型態(除了 nil),因此一份 table 裡面有五花八門的資料是可能的。但不管是什麼資料,都一定會有一個 key 來做對應,例如:
t = {name = 'goodman', age = 29, 123, 456, 789}
for key, value in pairs(t) do
print(key, value)
end
-- output is :
-- 1 123
-- 2 456
-- 3 789
-- name goodman
-- age 29
了解 table 裡面的 key 是什麼之後,再來聊聊 metatables。
每一個 table 都可以當成別人的 metatable,因此 metatable 也只是一個普通的 table 而已。在每一個 table 裡,都有一些特殊的 key,不過平時將這些 key 賦值也是沒有任何特殊意義的。但當 table 本身成為別人的 metatable 時,這些特殊的 key 開始影響另一個 table,舉例來說:table A 為 table B 的 metatable,則 table A 裡面的特殊 key 影響到的是 table B 的行為。
每一個特殊 key 對應到某種操作或行為,在 Lua 5.0 Reference Manual 裡稱做 event (其實非常貼切),在這邊拿最常見的 __index 作為例子。
__index 會影響到的行為是:從 table 中取值的動作。(嚴格說起來,應該是取「不存在的值」的動作)
而 metatable 中的 __index 的內容,又會因為型態的不同,而有兩種不同的處理。
欲看所有 key 的細節,請參考Lua 5.0 Reference Manual # 2.8
這是 metatable.__index 型態為 function 的例子
首先先建立一個 table (等等要拿來當作 metatable 的 table),並在 __index 這個特殊的 key 上填入一個 function。
-- 許多網路範例中,都會取名叫 mt(MetaTable),在這邊也不失流行叫做 mt
mt = {}
-- 當取值時,table 會來詢問 metatable 是否有宣告過如何處理,這個範例是一個 function print 因此會到兩個參數 table, key
mt.__index = print
接著建立 table t,並將其 metatable 設置為 mt
t = {}
setmetatable(t, mt)
此時對 table t 做取值的動作(如下圖),這邊可以看到 function print 收到了兩筆參數,分別是 table 本身以及 key,因此印出了「table: 0055D250 a」以及「table: 0055D250 b」的內容。
這是 metatable.__index 型態為 table 的例子
首先先建立一個 table (等等要拿來當作 metatable 的 table),並在 __index 這個特殊的 key 上填入一個 table。
-- 某個存在的 table
sometable = {a = 1, b = 2}
mt = {}
mt.__index = sometable
接著建立 table t,並將其 metatable 設置為 mt
t = {}
setmetatable(t, mt)
此時對 table t 做取值的動作(如下圖),這邊可以看到 table t 只是把 key 原封不動轉去問 sometable 而已
重點整理
- metatable 也是個普通的 table
- 每個 table 內都有一些特殊的 key (如 __index, __add…etc)
- 這些特殊 key 不影響 table 本身,而是影響將其當為 metatable 的 table
- __index 會影響到的行為是:從 table 中取值的動作。(嚴格說起來,應該是取「不存在的值」的動作)
- __index 會依照內容的型態,對取值的動作有不同的處理。
參考連結
沒有留言:
張貼留言