Lua元表使用

lua中每個(gè)值都可以擁有一個(gè)元表黎泣,元表是一個(gè)普通的lua table仰禀,定義了原始值在特定操作下的行為。

  • setmetatable新荤、getmetatable就可以設(shè)置元表和獲取元表:
local t = {}
local mt = {}
setmetatable(t, mt)
getmetatable(t)
  • 改變?cè)碇刑囟ǖ膋ey凡橱,來改變?cè)贾档膶?duì)應(yīng)行為小作,這個(gè)key對(duì)應(yīng)的value(table或function)叫”元方法“。

通過例子可以更清楚的理解元表\元方法的含義:

1. __call 元方法

-- 嘗試調(diào)用一個(gè)非函數(shù)類型值

local A = {}
local metaA = {
    __call = function(...)
        print("call A with args : ", ...)
    end
}

setmetatable(A, metaA)

A("string", 1)  -- output: call A with args: table:0x2814cd640 string 1

當(dāng)調(diào)用A(A是一個(gè)table)時(shí)稼钩,會(huì)找A的元表中的__call元方法顾稀,A作為第一個(gè)參數(shù),調(diào)用A的參數(shù)列表隨后坝撑。
也可以用”:“定義__call元方法静秆,傳入的第一個(gè)參數(shù)A 則就是self :

-- 嘗試調(diào)用一個(gè)非函數(shù)類型值

local A = {}
local metaA = {}

function metaA:__call(...)
    print("call A with self : ", self)
    print("with args : ", ...)
end

setmetatable(A, metaA)

A("string", 1)  
-- output: call A with self: table:0x2814cd640
--         with args: string 1

tips:
(1) __call元方法可以用來實(shí)現(xiàn)類似構(gòu)造方法的調(diào)用形式粮揉。
(2) 在調(diào)用某個(gè)值a前不確定它是否是function時(shí),除了判斷 type(a) == "function" 外抚笔,還有可能a不是function 但a的元表__call是function扶认,也是可以成功調(diào)用的。


2. __index 元方法

-- 嘗試訪問一個(gè)不存在的key
local A = {}
print( A.a )    -- output: nil

-- 1.__index是一個(gè)table
local metaA = {
    __index = {
    a = "123"
    }
}
setmetatable(A, metaA)
print( A.a )    
-- output: 123

-- 2.__index是一個(gè)function
local metaA2 = {
    __index = function(t, k)
        print("try to get key : "..k.." from : ",t)
    end
}
setmetatable(A, metaA2)
print( "A.a is : ", A.a )   
-- output: try to get key : a from table: 0x281495640
--         A.a is : nil  (這里由于__index并沒有返回任何值殊橙,所以打印 nil )

lua中查找表元素的過程:
1.在表中查找辐宾,如果找到,返回該元素膨蛮,找不到則繼續(xù)
2.判斷該表是否有元表叠纹,如果沒有元表,返回 nil敞葛,有元表則繼續(xù)誉察。
3.判斷元表有沒有 __index 方法,如果 __index 方法為 nil制肮,則返回 nil冒窍;如果 __index 方法是一個(gè)表,則重復(fù) 1豺鼻、2、3款慨;如果 __index 方法是一個(gè)函數(shù)儒飒,則返回該函數(shù)的返回值(傳入查找的表和key)。


3. __newindex 元方法
__newindex 元方法用來對(duì)表更新檩奠,當(dāng)你給表的一個(gè)不存在的key賦值桩了,解釋器就會(huì)查找__newindex 元方法:

-- 嘗試給一個(gè)不存在的 key 賦值
local A = {}
A.a = "123"
print("A.a is ", A.a)
-- output: A.a is 123

local metaA = {
    __newindex = function(t, k, v)
        print("try to set value:", v, " for key:", k, " for ", t)
    end
}
setmetatable(A, metaA)
A.a = "456"
print("A.a is ", A.a)
-- output: A.a is "456"
-- 此時(shí) A 已經(jīng)包含 key "a", 所以直接改變 A.a 而沒有走元表的__newindex

A.a = nil
A.a = "789"
print("A.a is ", A.a)
-- output: try to set value: 789 for key: a for table: 0x2814aec00
--         A.a is nil
-- 這里由于已經(jīng)將 A.a 置空,再次賦值時(shí)沒有這個(gè) key 所以走了元表的__newindex埠戳,元方法只進(jìn)行了打印并沒有任何操作井誉,所以打印 A.a 仍是nil

給table的某個(gè)key賦值時(shí),只有當(dāng)表中不存在這個(gè)key時(shí)才會(huì)找元表的__newindex方法整胃,傳入表t颗圣、要賦值的key和value,當(dāng)表中已經(jīng)存在這個(gè)key時(shí)則會(huì)直接賦值屁使。

利用__index和__newindex可以實(shí)現(xiàn)一些有意思的功能在岂,比如:

  • 實(shí)現(xiàn)類似switch/case中的default語句:
local Score = setmetatable({
    ["A"] = "91~100",
    ["B"] = "81~90",
    ["C"] = "61~80",
    ["D"] = "0~60"
}, {
    __index = function()
        return "error";
    end
})
print(Score.A)      -- "91~100"
print(Score.E)      -- "error"  

4. 運(yùn)算符方法
運(yùn)算符方法用來定義table的運(yùn)算操作,類似C++中的運(yùn)算符重載

local A = {
    a = "a"
}
local B = {
    a = "b"
}
local m = {
    __add = function(t1, t2)
        return {
            a = t1.a .. t2.a
        }
    end
}
setmetatable(A, m)
setmetatable(B, m)
print((A + B).a)    -- output: ab
print((B + A + B).a)    -- output: bab

Lua中所有的運(yùn)算符元方法:https://www.lua.org/manual/5.3/manual.html#2.4

5. 垃圾回收方法

local A = {}
local metaA = {
    __gc = function(...)
        print("receive gc with args : ", ...)
    end
}
setmetatable(A, metaA)
collectgarbage()

ps. lua5.2以上版本


6. 弱引用表
弱引用表允許它的key和value被gc回收酬蹋,通過設(shè)置元表的__mode實(shí)現(xiàn)及老,__mode是一個(gè)字符串抽莱,如果包含"k"則key是弱引用,如果包含"v"則value是弱引用骄恶。

local k = {}
local v = {}
a = {}
a[k] = v
k = nil
v = nil
collectgarbage()
for i, v in pairs(a) do
    print(i, v)
end
-- output: table: 0x282314a40  table: 0x282314840

setmetatable(a, {__mode = "k"})
collectgarbage()
print("weak table:")
for i, v in pairs(a) do
    print(i, v)
end
-- output: weak table:
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末食铐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子叠蝇,更是在濱河造成了極大的恐慌璃岳,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悔捶,死亡現(xiàn)場(chǎng)離奇詭異铃慷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蜕该,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門犁柜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人堂淡,你說我怎么就攤上這事馋缅。” “怎么了绢淀?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵萤悴,是天一觀的道長。 經(jīng)常有香客問我皆的,道長覆履,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任费薄,我火速辦了婚禮硝全,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘楞抡。我一直安慰自己伟众,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布召廷。 她就那樣靜靜地躺著凳厢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柱恤。 梳的紋絲不亂的頭發(fā)上数初,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音梗顺,去河邊找鬼泡孩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛寺谤,可吹牛的內(nèi)容都是我干的仑鸥。 我是一名探鬼主播吮播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼眼俊!你這毒婦竟也來了意狠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤疮胖,失蹤者是張志新(化名)和其女友劉穎环戈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澎灸,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡院塞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了性昭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拦止。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖糜颠,靈堂內(nèi)的尸體忽然破棺而出汹族,到底是詐尸還是另有隱情,我是刑警寧澤其兴,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布顶瞒,位于F島的核電站,受9級(jí)特大地震影響元旬,放射性物質(zhì)發(fā)生泄漏搁拙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一法绵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酪碘,春花似錦朋譬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至探越,卻和暖如春狡赐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钦幔。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工枕屉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鲤氢。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓搀擂,卻偏偏與公主長得像西潘,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哨颂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,763評(píng)論 0 38
  • 前言 元表對(duì)應(yīng)的英文是metatable喷市,元方法是metamethod。我們都知道威恼,在C++中品姓,兩個(gè)類是無法直接相...
    胤醚貔貅閱讀 993評(píng)論 0 2
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程,因...
    小菜c閱讀 6,365評(píng)論 0 17
  • 陪我漸漸老去的 不是某一個(gè)人 而是眾多來自大地的物種 就像那在風(fēng)中搖擺的樹 會(huì)被當(dāng)做木料或者柴火 還是有一顆來替換...
    汐顏成殤閱讀 197評(píng)論 0 0
  • 焉知非魚閱讀 196評(píng)論 0 0