openresty開發系列22--lua的元表
舉個例子,在 Lua table 中咱們能夠訪問對應的key來獲得value值,可是卻沒法對兩個 table 進行操做。
那如何計算兩個table的相加操做a+b?
local t1 = {1,2,3}
local t2 = {4,5,6}
local t3 = t1 + t2 ----> {1,2,3,4,5,6}
相似java的一些操做重載
這種相似的需求,lua 提供了元表(Metatable)和元方法,容許咱們改變table的行爲,每一個行爲關聯了對應的元方法。
1)setmetatable(table,metatable): 對指定table設置元表(metatable),
若是元表(metatable)中存在__metatable鍵值,setmetatable會失敗 。
2)getmetatable(table): 返回對象的元表(metatable)。
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 設爲 mytable 的元表
等價於:
mytable = setmetatable({},{})
返回對象元表:
getmetatable(mytable) -- 返回mymetatable
元方法的命名都是以 __ 兩個下劃線開頭。
一)__index 元方法
對錶讀取索引一個元方法
這是 metatable 最經常使用的鍵。
當你經過鍵來訪問 table 的時候,若是這個鍵沒有值,那麼Lua就會尋找該table的metatable(假定有metatable)
中的__index元方法。若是__index是一個表,Lua會在表格中查找相應的鍵。
local t = {} -- 普通表t爲空
local other = {foo = 2} -- 元表 中有foo值
setmetatable(t,{__index = other}) -- 把 other 設爲 t 的元表__index
print(t.foo); ---輸出 2
print(t.bar); ---輸出nil
t.foo爲何會輸出2,就是由於咱們重寫了__index索引的重載,lua在執行中若是t中沒有foo,
就會在他的元表中__index中去找,__index等於other,就輸出2。
----------------
若是咱們把t設置一下foo的值爲3,在看看結果
local t = {foo = 3 } -- 普通表t爲空
local other = {foo = 2} -- 元表 中有foo值
setmetatable(t,{__index = other}) -- 把 other 設爲 t 的元表__index
print(t.foo); ---輸出 3
print(t.bar); ---輸出nil
---------------------------------------------
若是__index賦值爲一個函數的話,Lua就會調用那個函數,table和鍵會做爲參數傳遞給函數。
__index 元方法查看錶中元素是否存在,若是不存在,返回結果爲 nil;若是存在則由 __index 返回結果。
local t = {key1 = "value1" }
local function metatable(mytable,key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
setmetatable(t,{__index = metatable})
print(t.key1);
print(t.key2);
print(t.key3);
分析:
print(t.key1); ---這個輸出value1 ,是由於t表中有此key
print(t.key2); ---這個輸出metatablevalue,是由於t表中沒有此key,就會調用t的元表中的元方法__index,
---這是__index是個函數,就會執行這個函數,傳t表和key值這兩個參數到此函數,
---函數體中判斷有此key2 就輸出metatablevalue;
print(t.key3); ---這個輸出nil,是由於t表沒有,元方法__index函數中 對key3返回nil值
--------------------------------------
總結:lua對錶索引取值的步驟
Lua查找一個表元素時的規則,其實就是以下3個步驟:
1.在表中查找,若是找到,返回該元素,找不到則繼續步驟2
2.判斷該表是否有元表,若是沒有元表,返回nil,有元表則繼續步驟3。
3.判斷元表有沒有__index元方法,若是__index方法爲nil,則返回nil;
若是__index方法是一個表,則重複一、二、3;
若是__index方法是一個函數,則執行該函數,獲得返回值。
二)__newindex 元方法
__newindex 元方法用來對錶更新,__index則用來對錶訪問 。
當你給表進行索引進行賦值,但此索引不存在;lua會查找是否有元表,有元表就會查找__newindex 元方法是否存在:
若是存在則調用__newindex的值進行執行操做,但不會對原表進行賦值操做。
如下實例演示了 __newindex 元方法的應用:
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print("mytable.key1=",mytable.key1)
mytable.newkey = "新值2"
print("mytable.newkey=",mytable.newkey)
print("mymetatable.newkey=",mymetatable.newkey)
mytable.key1 = "新值1"
print("mytable.key1=",mytable.key1)
print("mymetatable.key1=",mymetatable.key1)
以上實例中表設置了元方法 __newindex
在對新索引鍵(newkey)賦值時(mytable.newkey = "新值2"),會調用元方法,而對mytable原表不進行賦值。
而對mytable已存在的索引鍵(key1),則會進行賦值,而不調用元方法 __newindex。
----------------------------
若是咱們要對原來的table進行賦值,那咱們就能夠用rawset;;__newindex函數會傳三個參數,
mytable = setmetatable({key1 = "value1"}, {
__newindex = function(t,k,v) ---第一個參數爲table,第二個參數爲key,第三個參數爲value
rawset(t,k,v);
end
})
mytable.key1 = "new value"
mytable.key2 = 4
print("mytable.key1=",mytable.key1)
print("mytable.key2=",mytable.key2)
key2本來是再也不mytable表中的,經過元方法__newindex中函數使用了rawset,就能夠對原table進行賦值。
三)爲表添加操做符「+」
咱們這裏定義「+」這元方法,把它定義爲兩個table相連
如
t1={1,2,3}
t2={4,5,6}
t1 + t2 相加的結果,咱們想獲得的是 {1,2,3,4,5,6}
那咱們如何寫元表?
「+」對應的元方法爲__add
local function add(mytable,newtable)
local num = table.maxn(newtable)
for i = 1, num do
table.insert(mytable,newtable[i])
end
return mytable
end
local t1 = {1,2,3}
local t2 = {4,5,6}
setmetatable(t1,{__add = add})
t1 = t1 + t2
for k,v in ipairs(t1) do
print("key=",k," value=",v)
end
這樣咱們就實現了兩個table相加
如下是咱們的操做符對應關係
模式 描述
__add 對應的運算符 '+'.
__sub 對應的運算符 '-'.
__mul 對應的運算符 '*'.
__div 對應的運算符 '/'.
__mod 對應的運算符 '%'.
__unm 對應的運算符 '-'.
__concat 對應的運算符 '..'.
__eq 對應的運算符 '=='.
__lt 對應的運算符 '<'.
__le 對應的運算符 '<='.
四)__call元方法
__call元方法在 Lua 調用一個值時調用。如下實例演示了計算兩個表中全部值相加的和:
相似的 t();相似函數調用
local function call(mytable,newtable)
local sum = 0
local i
for i = 1, table.maxn(mytable) do
sum = sum + mytable[i]
end
for i = 1, table.maxn(newtable) do
sum = sum + newtable[i]
end
return sum
end
local t1 = {1,2,3}
local t2 = {4,5,6}
setmetatable(t1,{__call = call})
local sum = t1(t2)
print(sum)
五)__tostring 元方法
__tostring 元方法用於修改表的輸出行爲。如下實例咱們自定義了表的輸出內容,把表中全部的元素相加輸出:
local t1 = {1,2,3}
setmetatable(t1,{
__tostring = function(mytable)
local sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "all value sum =" .. sum
end
})
print(t1) ----print方法會調用table的tostring元方法
到此咱們的元表 和 元方法 就講完了,這個是須要你們本身動手去測試體驗的。要有領悟能力
六)點號與冒號操做符的區別
local str = "abcde"
print("case 1:", str:sub(1, 2))
print("case 2:", str.sub(str, 1, 2))
執行結果
case 1: ab
case 2: ab
冒號操做會帶入一個 self 參數,用來表明 本身。而點號操做,只是 內容 的展開。
在函數定義時,使用冒號將默認接收一個 self 參數,而使用點號則須要顯式傳入 self 參數。
obj = { x = 20 }
function obj:fun1()
print(self.x)
end
等價於
obj = { x = 20 }
function obj.fun1(self)
print(self.x)
end
注意:冒號的操做,只有當變量是類對象時才須要。
java