openresty开发系列22--lua的元表
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方法是一個表,則重復1、2、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
注意:冒號的操作,只有當變量是類對象時才需要。
轉載于:https://www.cnblogs.com/reblue520/p/11433952.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的openresty开发系列22--lua的元表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: openresty开发系列21--lua
- 下一篇: openresty开发系列23--lua