元表

Lua语言中的每种类型的值都有一套可预见的操作集合。例如,我们可以将数字相加,可以连接字符串,还可以在表中插入键值对等,但是我们无法将两个表相加,无法对函数作比较,也无法调用一个字符串,除非使用元表。

元表可以修改一个值在面对一个未知操作时的行为。例如,假设a和b都是表,那么可以通过元表定义Lua语言如何计算表达式a+b。当Lua语言试图将两个表相加时,它会先检查两者之一是否有元表(metatable)且该元表中是否有__add字段。如果Lua语言找到了该字段,就调用该字段对应的值,即所谓的元方法(metamethod)(是一个函数)。

Lua语言中的每一个值都可以有元表。每一个表和用户数据类型都具有各自独立的元表,而其他类型的值则共享对应类型所属的同一个元表。

获取元表

获取元表使用getmetatable()方法

t = {}
print(getmetatable(t)) --> nil

设置元表

可以使用函数setmetatable来设置或修改任意表的元表

t1 = {}
setmetatable(t,t1)
print(getmetatable(t) == t1) --> true

我们只能为表设置元表;如果要为其他类型的值设置元表,则必须通过C代码或调试库完成。字符串标准库为所有的字符串都设罝了同一个元表,而其他类型在默认情况中都没有元表:

print(getmetatable("hello world"))   --> table: 0000022E8BA91B40
print(getmetatable(123)) --> nil
print(getmetatable(print)) --> nil

为两个表添加算术运算功能

下面展示怎么为两个表添加 “+” 功能

-- 定义两个表 t 和 t1
t = {}
t1 = {}

-- 向表中添加变量并赋值
t.a = 123
t1.a = 4

-- 向两个表添加函数
t.func = function ()
   return 1
end

t1.func = function ()
   return 2
end

-- 定义元表
mt = {}

-- 分别为两个表设置元表
setmetatable(t,mt)
setmetatable(t1,mt)

--为元表定于__add函数
--a b 分别代表执行加法的表,这里指代t和t1
mt.__add = function (a,b)
   print(a.func() + b.func())   --> 3
   return a.a + b.a
end

print(t + t1) --> 127

Lua语言会按照如下步骤来查找元方法:

  • 如果第一个值有元表且元表中存在所需的元方法,那么Lua语言就使用这个元方法,与第二个值无关

  • 如果第二个值有元表且元表中存在所需的元方法,Lua语言就使用这个元方法

  • 否则,Lua语言就抛出异常。

因此 在执行最后一行 t + t1的时候,会检查元表中是否存在 t1 中是否存在 __add 方法,如果存在,则调用该元方法,否则查找 t2,如果还是不存在,将会抛出异常。因此上面的代码中,这行代码 setmetatable(t1,mt) 可以删除,因为始终会执行 t 中的方法。例如我们修改上面代码

-- 定义第二个元表
mt1 = {}

-- 注释t的元表,为t1添加新定义的元表
--setmetatable(t,mt)
setmetatable(t1,mt1)

mt1.__add = function()
   print("this is mt1 add")
end

print(t + t1)  -->   调用mt1.__add方法,执行方法中的print语句
 -->   打印nil,因为mt1.__addm

除了 __add 外,还有下面这些键值定义:

描述
__add 改变加法操作符的行为。
__sub 改变减法操作符的行为。
__mul 改变乘法操作符的行为。
__div 改变除法操作符的行为。
__mod 改变模除操作符的行为。
__unm 改变一元减操作符的行为。
__concat 改变连接操作符的行为。
__eq 改变等于操作符的行为。
__lt 改变小于操作符的行为。
__le 改变小于等于操作符的行为。

表相关的元方法

__index元方法

当我们访问表中一个不存在的字段时,得到的结果会是nil,这是正确的,但不是完整的真相。实际上,这些访问会引发解释器查找一个名为 __index 的元方法。如果没有这个元方法,那么像一般情况下一样,结果就是nil;否则,则由这个元方法来提供最终结果。

mt = {x = 5}    --定义一个元表,里面拥有一个字段x

w = {}

w = setmetatable(w,mt) --将mt设置为w的元表

mt.__index = mt --设置元表的__index值

print(w.x) -- 5
print(w.y) -- nil

--将mt的__index元方法设置为函数
mt.__index = function(_,key)
   return mt[key]
end

print(w.x) --w中没有x字段,所以调用函数 __index,传入的参数为function(w,x),所以得到的值为mt["x"] = 5
 --也就是以表和键为参数调用该函数,并返回该函数的返回值

Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:

  • 在表中查找,如果找到,返回该元素,找不到则继续

  • 判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。

  • 判断元表有没有 index 方法,如果 index 方法为 nil,则返回 nil;如果 index 方法是一个表,则重复 1、2、3;如果 index 方法是一个函数,Lua会以表和键为参数调用该函数,并返回该函数的返回值。

如果我们希望在访问一个表时不调用__index元方法,那么可以使用函数rawget,它在不考虑元表的情况下对表进行简单的访问,定义为:

rawget (table, index)

在不触发任何元方法的情况下 获取 table[index] 的值。 table 必须是一张表; index 可以是任何值。

接着上面的代码,添加一些内容

print(rawget(w,"x"))    -- 忽略元表中的值,x在w表中不存在,所以输出为 nil
w.x = "hhh"
print(rawget(w,"x"))    -- 输出 hhh

__newindex元方法

元方法__newindex__index类似,不同之处在于前者用于表的更新而后者用于表的查询。当对一个表中不存在的索引赋值时,解释器就会查找__newindex元方法:如果这个元方法存在,那么解释器就调用它而不执行赋值。

mt = {x = 5,y = 6}

w = {}

w = setmetatable(w,mt)
mt.__newindex = mt

print(mt.y)   -- 6
w.y = 123    -- __newindex中包含y字段,所以为mt.y赋值,而不进行自身赋值
print(w.y) --nil
print(mt.y) --123

如果我们想跳过原函数为它赋值,可以使用rawset方法

rawset (table, index, value)

在不触发任何元方法的情况下 将 table[index] 设为 valuetable 必须是一张表, index 可以是 nil 与 NaN 之外的任何值。 value 可以是任何 Lua 值。

在上面的基础上添加下面代码

rawset(w,"y",456)
print(w.y) -- 456
print(mt.y) -- 123

一文读懂Lua元表的更多相关文章

  1. 一文读懂HTTP/2及HTTP/3特性

    摘要: 学习 HTTP/2 与 HTTP/3. 前言 HTTP/2 相比于 HTTP/1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何 ...

  2. 一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现

    一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现 导读:近日,马云.马化腾.李彦宏等互联网大佬纷纷亮相2018世界人工智能大会,并登台演讲.关于人工智能的现状与未来,他们提出了各自的观点,也引 ...

  3. 一文读懂高性能网络编程中的I/O模型

    1.前言 随着互联网的发展,面对海量用户高并发业务,传统的阻塞式的服务端架构模式已经无能为力.本文(和下篇<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>)旨在为大家提供有用的 ...

  4. 从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路

    本文原作者阮一峰,作者博客:ruanyifeng.com. 1.引言 HTTP 协议是最重要的互联网基础协议之一,它从最初的仅为浏览网页的目的进化到现在,已经是短连接通信的事实工业标准,最新版本 HT ...

  5. 一文读懂 深度强化学习算法 A3C (Actor-Critic Algorithm)

    一文读懂 深度强化学习算法 A3C (Actor-Critic Algorithm) 2017-12-25  16:29:19   对于 A3C 算法感觉自己总是一知半解,现将其梳理一下,记录在此,也 ...

  6. [转帖]MerkleDAG全面解析 一文读懂什么是默克尔有向无环图

    MerkleDAG全面解析 一文读懂什么是默克尔有向无环图 2018-08-16 15:58区块链/技术 MerkleDAG作为IPFS的核心数据结构,它融合了Merkle Tree和DAG的优点,今 ...

  7. [转帖]一文读懂 HTTP/2

    一文读懂 HTTP/2 http://support.upyun.com/hc/kb/article/1048799/ 又小拍 • 发表于:2017年05月18日 15:34:45 • 更新于:201 ...

  8. [转帖]从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路

    从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路   http://www.52im.net/thread-1709-1-2.html     本文原作者阮一峰,作者博客:r ...

  9. 一文读懂HDMI和VGA接口针脚定义

    一文读懂HDMI和VGA接口针脚定义 摘自:http://www.elecfans.com/yuanqijian/jiekou/20180423666604.html   HDMI概述 HDMI是高清 ...

随机推荐

  1. 大数据学习(14)—— HBase进阶

    HBase读写流程 在网上找了一张图,这个画的比较简单,就拿这个图来说吧. 写流程 1.当Client发起一个Put请求时,首先访问Zookeeper获取hbase:meta表. 2.从hbase:m ...

  2. [CTF] CTF入门指南

    CTF入门指南 何为CTF ? CTF(Capture The Flag)夺旗比赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式.CTF起源于1996年DEFCON全球黑客大会 ...

  3. input输入框只能输入正数和小数(保留小数点后两位)

    1.限制只能输入正数和小数保留小数点后两位 1 <input type="number" id="txtNum" /> 2 3 <script ...

  4. 寄生线虫免疫学研究新路径!华中农业大学胡敏团队报道寄生线虫N-糖基化修饰图谱

    N-糖基化修饰是真核生物中一种重要的蛋白质翻译后修饰,在许多生物学过程中起着关键作用,包括蛋白质折叠.受体-配体相互作用.免疫应答和疾病发病机制等.近年来,高精度质谱技术的出现促进了糖组和糖蛋白质组的 ...

  5. Android开发如何准备技术面试(含Android面试押题)

    今年毋庸置疑是找工作的寒冬,每一个出来找工作的同学都是值得尊敬的.现在找工作,虽然略难,但是反过来看也会逼迫我们成为更加优秀的自己. 但是不管是旺季还是寒冬,有一些优秀的同学找工作还是挺顺利的.所以说 ...

  6. 关于Feign、Jackson、RabbitMQ、Jrebel插件的开发中遇到的问题

    在工作实际开发中需要开发一个消息模块对外提供统一的接口feign调用提供消息加载到MQ队列的服务,采用泛型的形式. 刚开始搭建好之后,正好需要做一个全局的日志添加到zuul网关中,通过网关feign ...

  7. WPF 线程开启等待动画

    public static Dictionary<string, object> Dic = new Dictionary<string, object>();//定义一个字典 ...

  8. MOOC大学计算机课程推荐

    转自:https://zhuanlan.zhihu.com/p/30659834  这个是大佬总结的, 大学计算机课程 以下课程是我在MOOC上找到的所有我认为讲的好的. 主要是中国大学MOOC,学堂 ...

  9. 基于RT1052 Aworks 内存扩容记录(一)

    本文主要是通过迁移的思维,记录本人初次使用周立功的Aworks框架进行BSP开发 1. 首先阅读原理图 内存容量由32M扩容至64M. 2. 再则比较两颗芯片的参数 通过比较32M和64M SDRAM ...

  10. 007 PCI总线的桥与配置(二)

    一.PCI桥与PCI设备的配置空间 PCI设备都有独立的配置空间,HOST主桥通过配置读写总线事务访问这段空间.PCI总线规定了三种类型的PCI配置空间,分别是PCI Agent设备使用的配置空间,P ...