一、什么是元表

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_tabgetmetatable(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)endreturn t1end
}setmetatable(a, meta_ta)c = a + b
print(c)      -- 输出的是a的地址for k, v in pairs(c) doprint(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的地址   和 keyt[k] = "深圳"  -- 为 表t 添加kvreturn "北京"end,})print(t2.name)  -- 输出 pandaprint(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)  -- nilprint(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 元方法调用")
endsetmetatable(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) dotable.insert(t1, v)endreturn t1end,-- __tostring 接管本表的返回值,其中 .. 相当于c++中字符串拼接__tostring = function(t)s = ""for k, v in pairs(t) dos = s..v.." "endreturn send
}setmetatable(a, meta_ta)-- 如果不实现 __tostring 方法,直接 print(table) 打印出来是table的地址
print(a)  -- 1 2 3c = 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      trueprint("app close..")

其中,__call 方法的第一个参数为表地址,剩余参数用…表示。

推荐一个零声学院免费教程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:

Lua 元表及常见元方法相关推荐

  1. Lua中强大的元方法__index详解

    今天要来介绍比较好玩的内容--__index元方法 1.我是备胎,记得回头看看 咳咳,相信每一位女生都拥有或者不知不觉中拥有了一些备胎,啊,当然,又或许是成为过别人的备胎. 没有备胎的人,就不是完整的 ...

  2. lua的元表metatable及元方法

    前言 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了"+"符号,就可以进行类的加法运算.在Lu ...

  3. Lua程序设计 | 模块和包、泛型迭代器和for、元表和元方法

    From<Programming in Lua> by Roberto Ierusalimschy 文章目录 模块和包 函数 require 模块重命名 搜索路径 搜索器 Lua语言中编写 ...

  4. Lua 学习元表,元方法

    前言 Lua本身没有面向对象的思想,但是可以根据表.元表.元方法来靠近它 一.元表与元方法的概念 Lua中每个值都可具有元表.元表是普通的Lua表,定义了原始值在某些特定操作下 的行为.例如,当tab ...

  5. lua学习笔记之元表和元方法

    元表允许当遇到未知操作时,改变值的行为.例如,使用元表,可以定义表a与表b的关系运算a+b.当lua尝试两个表相加时,会检查是否其中一个有元表并且元表是否有__add字段. 元表在面向对象的术语中是一 ...

  6. lua元表和元方法 《lua程序设计》 13章 读书笔记

    lua中每个值都有一个元表,talble和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表.lua在创建table时不会创建元表. t = {} print(getmet ...

  7. lua 元表/元方法

    在lua中,每个值都有其对应的操作,比如数值型有加减乘除等操作,字符串型有连接截取等操作,那么这些操作(加减乘除,连接截取等)定义在什么地方呢?定义在这些值默认的元表中,如果想修改这些操作(比如加法) ...

  8. Lua语言编程学习之路02----第13章 元表与元方法

    前言   在Lua中我们无法直接对两个table进行相加,无法对函数进行比较,也无法调用一个函数. 于是Lua可以通过修改一个值的行为,使其在面对一个非预定义的操作时执行一个自己实现的操作.比如两个t ...

  9. 【Lua进阶系列】lua元方法

                        [Lua进阶系列]之Lua元方法案例+字段     大家好,我是Lampard~~     欢迎来到Lua进阶系列的博客 前文再续,书接上一回.今天和大家讲解一 ...

最新文章

  1. java arraylist优点_Java中各种集合的特点总结
  2. 社交网络图挖掘5--图的邻居性质
  3. springmvc静态资源拦截与访问
  4. java去除不为null,java – 选择特定字段不为NULL的所有记录
  5. Google、MS和BAT教给我的面试真谛
  6. Qt之QtCreator Qt5示例丢失解决方案
  7. java实现Beta函数
  8. Linux操作系统的VI命令
  9. how to set up github blog
  10. java名字自动生成_Java名字生成器
  11. repast HPC初探(运行其自带例子过程中出现的问题)
  12. bochs上网镜像怎么上网_【bochs win10镜像可上网版】bochs win10镜像img下载 完整版-趣致软件园...
  13. Northwind数据库练习及参考答案
  14. 网络分析仪测试线损_网络分析仪测试天线隔离度
  15. ASO关键词排名的三种优化方式,aso关键词排名优化是
  16. 计算机科学与技术影视,计算机科学与技术专业--水墨的影视艺术语言的研究
  17. ownCloud问题处理server replied 423 Locked to
  18. 第164篇,陌生人和贵人(扶摇生财思维)
  19. centos8调整分辨率
  20. 【Linux】gdb调试器的使用

热门文章

  1. 新的开始 new start
  2. 影响软件质量的因素有哪些?
  3. xp,windows2003卸载Windows PowerShell 1.0
  4. Winmgmt.exe错误解决办法
  5. flash进行上传使用什么协议?
  6. 消息框:服务器正在运行中 OLE解决办法
  7. 数据分析模型,你会用多少种?建议你用这28种商业模型和方法武装自己
  8. 那些让人心痛的句子 还是记忆
  9. JavaScript:断点调试
  10. Unix环境编程中的apue.h和err_quit、err_sys问题