Lua元表理解
之前接觸lua時看到了元表的使用,但是一直一知半解,藉此機會對自己的理解做一下總結,方便日後回顧
元表本質上來說是一種用來存放元方法的table。我們可以通過對應的key來得到value值,作用就是修改一個值的行為(更確切的說,這是元方法的能力),需要注意的是,這種修改會覆蓋掉原本該值可能存在的相應的預定義行為。
1. lua中的每個值都可以有一個元表,只是table和userdata可以有各自獨立的元表,而其他型別的值則共享其型別所屬的單一元表。
lua程式碼中只能設定table的元表,至於其他型別值的元表只能通過C程式碼設定。
多個table可以共享一個通用的元表,但是每個table只能擁有一個元表。
我們稱元表中的鍵為事件(event),稱值為元方法(metamethod)。前述例子中的事件是"add",元方法是執行加法的函式。
可通過函式getmetatable查詢任何值的元表。
可通過函式setmetatable替換表的元表
lua查詢表中的元素時規則如下:
1.在表中查詢,如果找到,返回該元素,找不到則繼續
2.判斷該表是否有元表,如果沒有元表,返回nil,有元表則繼續
3.判斷元表有沒有__index方法,如果__index方法為nil,則返回nil;如果__index方法是一個表,則重複1、2、3;如果__index方法是一個函式,則返回該函式的返回值
例如下面程式碼
father = { house=1 } son = { car=1 } setmetatable(son, father) --把son的metatable設定為father print(son.house)
返回為nil,如果改一下
father = {
house=1
}
son = {
car=1
}
setmetatable(son, father) --把son的metatable設定為father
father.__index = father
print(son.house)
返回為1,這是因為在son表中沒有house欄位,雖然father是son的元表,但是father沒有元方法,即元方法沒有指向father自己,因此找不到house欄位,這也就解釋通了為什麼會經常在lua中這麼寫了
到這裡,我們對元表有了最基本的認識,它更像是一個備用的查詢表,如果a的元表是b,在a中查詢不到的東西會嘗試從b中繼續找,當然,前提是b設定了元方法
__index元方法:
上面我們說道在b中查詢a中不存在的值時,需要給b的元方法賦值,不然仍然會像上面的例子返回為nil,因此,元方法在這裡像是一個a開啟b表查詢入口的鑰匙,我們順著上面的邏輯再理一下整個查詢思路:
我們在訪問son.house時,son中沒有house這個成員,但Lua接著發現son有元表father,於是此時father被當做元表來查詢,此時,Lua並不是直接在father中找名為house的成員,而是呼叫father的__index方法,如果__index方法為nil,則返回nil,如果是一個表(上例中father的__index方法等於自己,就是這種情況),那麼就到__index方法所指的這個表中查詢名為house的成員,於是,最終找到了house成員。
注:__index方法除了可以是一個表,還可以是一個函式,如果是一個函式,__index方法被呼叫時將返回該函式的返回值
下面引用一個菜鳥教程的例子,很能說明問題
對指定的表設定元表:
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 設為 mytable 的元表
以上程式碼也可以直接寫成一行:
mytable = setmetatable({},{})
以下為返回物件元表:
getmetatable(mytable) -- 這回返回mymetatable
如果__index包含一個函式的話,Lua就會呼叫那個函式,table和鍵會作為引數傳遞給函式。
__index 元方法查看錶中元素是否存在,如果不存在,返回結果為 nil;如果存在則由 __index 返回結果。
mytable = setmetatable({key1 = "value1"}, {
__index = function(mytable, key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
})
print(mytable.key1,mytable.key2)
例項輸出結果為: value1 metatablevalue
例項解析:
-
mytable 表賦值為 {key1 = "value1"}。
-
mytable 設定了元表,元方法為 __index。
-
在mytable表中查詢 key1,如果找到,返回該元素,找不到則繼續。
-
在mytable表中查詢 key2,如果找到,返回 metatablevalue,找不到則繼續。
-
判斷元表有沒有__index方法,如果__index方法是一個函式,則呼叫該函式。
-
元方法中檢視是否傳入 "key2" 鍵的引數(mytable.key2已設定),如果傳入 "key2" 引數返回 "metatablevalue",否則返回 mytable 對應的鍵值。
我們可以將以上程式碼簡單寫成:
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)