Lua元表使用

lua中每个值都可以拥有一个元表,元表是一个普通的lua table,定义了原始值在特定操作下的行为。

  • setmetatable、getmetatable就可以设置元表和获取元表:
local t = {}
local mt = {}
setmetatable(t, mt)
getmetatable(t)
  • 改变元表中特定的key,来改变原始值的对应行为,这个key对应的value(table或function)叫”元方法“。

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

1. __call 元方法

-- 尝试调用一个非函数类型值

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

当调用A(A是一个table)时,会找A的元表中的__call元方法,A作为第一个参数,调用A的参数列表随后。
也可以用”:“定义__call元方法,传入的第一个参数A 则就是self :

-- 尝试调用一个非函数类型值

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元方法可以用来实现类似构造方法的调用形式。
(2) 在调用某个值a前不确定它是否是function时,除了判断 type(a) == "function" 外,还有可能a不是function 但a的元表__call是function,也是可以成功调用的。


2. __index 元方法

-- 尝试访问一个不存在的key
local A = {}
print( A.a )    -- output: nil

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

-- 2.__index是一个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.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值(传入查找的表和key)。


3. __newindex 元方法
__newindex 元方法用来对表更新,当你给表的一个不存在的key赋值,解释器就会查找__newindex 元方法:

-- 尝试给一个不存在的 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"
-- 此时 A 已经包含 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
-- 这里由于已经将 A.a 置空,再次赋值时没有这个 key 所以走了元表的__newindex,元方法只进行了打印并没有任何操作,所以打印 A.a 仍是nil

给table的某个key赋值时,只有当表中不存在这个key时才会找元表的__newindex方法,传入表t、要赋值的key和value,当表中已经存在这个key时则会直接赋值。

利用__index和__newindex可以实现一些有意思的功能,比如:

  • 实现类似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"  
  • 将一个表作为另一个表的元表的__index,从而实现表继承关系:Lua实现继承
  • 将__index 和 __newindex作为get、set方法:Lua实现KVO

4. 运算符方法
运算符方法用来定义table的运算操作,类似C++中的运算符重载

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中所有的运算符元方法: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回收,通过设置元表的__mode实现,__mode是一个字符串,如果包含"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:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容