Lua 元表及常见元方法

一、什么是元表

Lua 中的 table 使用起来有点像c++中的 map 或者 unordered_map ,都是通过对应的key 获取对应的value。如果访问了表中不存在的key时,就会触发Lua的一种机制,Lua也正是凭借这个机制可以用来模拟类似“继承”的行为,具体可以参考上一篇文章Lua中self 、自索引及其面向对象应用代码示例

元表用来定义一个table在面对未知操作时候的行为,比如,对于a b 两个table,是无法进行相加操作 即:a+b 会报错。
比如:

a, b = {1, 2, 3}, {10, 20}

c = a + b
print(c)      -- 输出的是a的地址

定义了两张表 a 和 b,当Lua执行到 a + b 时就会报错,因为Lua不知道如何将两个table相加。
在这里插入图片描述

元表两个重要的方法
setmetatable(tab, meta_tab)  --设置tab的元表为meta_tab

getmetatable(tab)  -- 获取tab的元表

后续的例子中,会再详细讲解两个方法的用法。

二、元方法

2.1 通过元表实现两个table的相加

定义一个元表meta_ta, 并且实现 __add 方法,然后通过 setmetatable(a, meta_ta) 将meta_ta设置为a的元表 。此时,当Lua 执行到 a+b ,会先检查a 或 b 有没有元表,如果有元表且元表中实现了 __add ,就调用该key对应的值,__add对应的值(函数或者table)就是“元方法”。如果 a 和 b 都有元表会执行a的元表。

a, b = {1, 2, 3}, {10, 20}

-- print(a + b) 

meta_ta = {
    __add = function(t1, t2)
        for k, v in pairs(t2) do
        	-- 将表t2中的元素追加到t1中
            table.insert(t1, v)
        end
        return t1
    end
}

setmetatable(a, meta_ta)

c = a + b
print(c)      -- 输出的是a的地址

for k, v in pairs(c) do
    print(k, v)
end

运行结果:
在这里插入图片描述
运算符相关的元方法除了 __add 还有 减法 乘法 除法 取模 大于 小于 等等

在这里插入图片描述

2.2 Table常用的元方法

__index 元方法

当我们访问表中一个不存在的字段时,得到的结果会是nil。但时,如果为该表设置了元表,访问的时候解释器就会去元表查找一个名为 __index 的元方法,如果没有这个元方法,返回nil,否则,由这个元方法来提供最终结果。

-- 定义表t2
t2 = {id = 1, name = "panda", age = 25}

-- 1. 显式指定meta_t2 为 t2 的元表
-- meta_t2 = {}
-- setmetatable(t2, meta_t2)
-- meta_t2.__index = { addr = "beijing"}

-- 2. 隐式指定元表(匿名元表)
setmetatable(t2, {
    -- __index 是个表
    -- __index = { addr = "beijing"}

    -- __index 是function
    __index = function(t, k)
        print(t, k)  -- 输出 表t的地址   和 key
        t[k] = "深圳"  -- 为 表t 添加kv
        return "北京"
    end,

})

print(t2.name)  -- 输出 panda

print(t2.addr)  -- 输出biao t2 的地址 和 key: addr  及返回值  北京

print(t2.addr)  -- 因为已经成功添加kv 直接输出

运行结果:
在这里插入图片描述
分析:

  • 表 t2 中有name字段,直接返回
  • 表 t2 中没有 addr 字段,就会找 t2 的元表,看元表中有没有__index 字段,找到后调用对应的值。

Lua 读取表中元素的规则如下

  1. 在表中查找,如果找到,返回该元素,找不到则继续
  2. 判断该表是否有元表,如果没有元表,返回 nil,有元表则继续
  3. 判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,Lua会以表和键为参数调用该函数,并返回该函数的返回值

如果我们希望在访问一个表时不调用 __index 元方法,可以使用原生函数 rawget

rawget(t2, addr)   -- 输出 nil

__newindex 元方法
当查询表中一个不存在的字段时,Lua会从该表的元表中的 __index 继续寻找;当对表中一个不存在的字段进行赋值时,就会触发 __newindex 元方法,此时__newindex 的值需要分为两种情况:

(1)__newindex 指向的是一个表:

t3 = {id = 3, name = "lwang", age = 28}

meta_t3 = {
    addr = "beijing"
}
meta_t3.__newindex = meta_t3  

setmetatable(t3, meta_t3)

print(t3.addr)  -- nil

print(meta_t3.addr)  -- beijing
t3.addr = "shenzhen"
print(t3.addr) -- nil  t3 不存在addr字段,找元表meta_t3的__newindex 指向meta_t3表  会为meta_t3的addr赋值,而不进行自身赋值

print(meta_t3.addr) -- shenzhen, 值被t3修改

运行结果:
在这里插入图片描述

(2)__newindex 指向的是函数:

t3 = {id = 3, name = "lwang", age = 28}

meta_t3 = {
    addr = "beijing"
}
meta_t3.__newindex = function()
    print("这里是meta_t3 __newindex 元方法调用")
end

setmetatable(t3, meta_t3)

t3.addr = "shenzhen"  -- 输出:这里是meta_t3 __newindex 元方法调用
print(t3.addr)  -- nil

运行结果:
在这里插入图片描述
__tostring 元方法
用来接管表的返回值,按照我们预定的格式输出,还是以两个表相加为例:

a, b = {1, 2, 3}, {10, 20}

meta_ta = {
    __add = function(t1, t2)
        for k, v in pairs(t2) do
            table.insert(t1, v)
        end
        return t1
    end,

    -- __tostring 接管本表的返回值,其中 .. 相当于c++中字符串拼接
    __tostring = function(t)
        s = ""
        for k, v in pairs(t) do
            s = s..v.." "
        end
        return s
    end
}

setmetatable(a, meta_ta)

-- 如果不实现 __tostring 方法,直接 print(table) 打印出来是table的地址
print(a)  -- 1 2 3

c = a + b
print(c) -- 1 2 3 10 20

运行结果
在这里插入图片描述

__call 和 __gc 元方法
__call 可以让表以类似函数的方式那样调用,比如:tab(arg1, arg2 …)
__gc 有点像c++中析构函数,释放资源

t1 = {10, 20, 30}

meta_t1 = {
    __call = function(t, ...)
        print(t, ...)
    end,

    --元表中用一个以字符串 " __gc " 为索引的域,那么就标记了这个对象需要触发终结器
    __gc = function()
        print("call gc func")
    end
}

setmetatable(t1, meta_t1)

t1("panda", 10, true) -- table: 0xafc780   panda   10      true

print("app close..")

其中,__call 方法的第一个参数为表地址,剩余参数用…表示。
在这里插入图片描述
推荐一个零声学院免费教程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
 本套课程分为基础与中级两部分,分别就lua语言的各方面知识点进行探讨,学习完本套课程,对于后续Xlua(Tolua等框架)技术的学习提供强大的语言技术保证。       本套lua课程采用入门与商业级两种开发IDE进行教学:入门级的SciTE内置IDE与商业级的IDEA lua插件。本套课程学习完毕,对于除了传统手游外,在VR、AR、商业级大型应用程序、嵌入式设备开发等领域都有较强的指导作用。           《lua中级篇》分为:“函数的进阶”、“字符串进阶”、“Table进阶”、“表”、“OOP面向对象”、“协同程序”、“IO操作”、“调试与运行”等八个大的章节,详细深入讲解lua开发的方方面面。        内容包含lua可变参数、闭包、模块、函数尾调用、字符串模式匹配、字符串不变性原理、矩阵、链表、表详解与应用、协同的生命周期与生产消费者问题、lua文件各种读写操作、lua执行外部代码与错误异常处理垃圾收集机制等。       最后,lua中级篇的学习,对于广大学员开发商业级lua热更新技术,具有不可替代的重要作用! 热更新系列(技术含量:中高级):B:《热更新框架设计之Xlua基础视频课程》https://edu.csdn.net/course/detail/27110C:《热更新框架设计之热更流程与热补丁技术》https://edu.csdn.net/course/detail/27118D:《热更新框架设计之客户端热更框架(上)》https://edu.csdn.net/course/detail/27132E:《热更新框架设计之客户端热更框架(中)》https://edu.csdn.net/course/detail/27135F:《热更新框架设计之客户端热更框架(下)》https://edu.csdn.net/course/detail/27136 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值