lua元表详解

元表的作用

元表是用来定义对table或userdata操作方式的表

举个例子

local t1 = {1}
local t2 = {2}
local t3 = t1 + t2

我们直接对两个table执行+运算,会报错

lua: /usercode/file.lua:3: attempt to perform arithmetic on local ‘t1‘ (a table value)

因为程序不知道如何对两个表执行+运行,这时候就需要通过元表来定义如何执行t1的+运算,有点类似于c语言中的运算符重载。

local mt = {}
--定义mt.__add元方法(其实就是元表中一个特殊的索引值)为将两个表的元素合并后返回一个新表
mt.__add = function(t1,t2)
    local temp = {}
    for _,v in pairs(t1) do
        table.insert(temp,v)
    end
    for _,v in pairs(t2) do
        table.insert(temp,v)
    end
    return temp
end
local t1 = {1,2,3}
local t2 = {2}
--设置t1的元表为mt
setmetatable(t1,mt)

local t3 = t1 + t2
--输出t3
local st = "{"
for _,v in pairs(t3) do
    st = st..v..", "
end
st = st.."}"
print(st)

结果为:

{1, 2, 3, 2, }

因为程序在执行t1+t2的时候,会去调用t1的元表mt的__add元方法进行计算。

具体的过程是:

1.查看t1是否有元表,若有,则查看t1的元表是否有__add元方法,若有则调用。

2.查看t2是否有元表,若有,则查看t2的元表是否有__add元方法,若有则调用。

3.若都没有则会报错。

所以说,我们通过定义了t1元表的__add元方法,达到了让两个表通过+号来相加的效果

元表的元方法

函数 描述
__add 运算符 +
__sub 运算符 -
__mul 运算符 *
__ div 运算符 /
__mod 运算符 %
__unm 运算符 -(取反)
__concat 运算符 ..
__eq 运算符 ==
__lt 运算符 <
__le 运算符 <=
__call 当函数调用
__tostring 转化为字符串
__index 调用一个索引
__newindex 给一个索引赋值

由于那几个运算符使用类似,所以就不单独说明了,接下来说 __call, __tostring, __index, __newindex四个元方法。

__call

__call可以让table当做一个函数来使用。

local mt = {}
--__call的第一参数是表自己
mt.__call = function(mytable,...)
    --输出所有参数
    for _,v in ipairs{...} do
        print(v)
    end
end

t = {}
setmetatable(t,mt)
--将t当作一个函数调用
t(1,2,3)

结果:

1
2
3

__tostring

__tostring可以修改table转化为字符串的行为

local mt = {}
--参数是表自己
mt.__tostring = function(t)
    local s = "{"
    for i,v in ipairs(t) do
        if i > 1 then
            s = s..", "
        end
        s = s..v
    end
    s = s .."}"
    return s
end

t = {1,2,3}
--直接输出t
print(t)
--将t的元表设为mt
setmetatable(t,mt)
--输出t
print(t)

结果:

table: 0x14e2050
{1, 2, 3}

__index

调用table的一个不存在的索引时,会使用到元表的__index元方法,和前几个元方法不同,__index可以是一个函数也可是一个table。

作为函数:

将表和索引作为参数传入__index元方法,return一个返回值

local mt = {}
--第一个参数是表自己,第二个参数是调用的索引
mt.__index = function(t,key)
    return "it is "..key
end

t = {1,2,3}
--输出未定义的key索引,输出为nil
print(t.key)
setmetatable(t,mt)
--设置元表后输出未定义的key索引,调用元表的__index函数,返回"it is key"输出
print(t.key)

结果:

nil
it is key

作为table:

查找__index元方法表,若有该索引,则返回该索引对应的值,否则返回nil

local mt = {}
mt.__index = {key = "it is key"}

t = {1,2,3}
--输出未定义的key索引,输出为nil
print(t.key)
setmetatable(t,mt)
--输出表中未定义,但元表的__index中定义的key索引时,输出__index中的key索引值"it is key"
print(t.key)
--输出表中未定义,但元表的__index中也未定义的值时,输出为nil
print(t.key2)

结果:

nil
it is key
nil

__newindex

当为table中一个不存在的索引赋值时,会去调用元表中的__newindex元方法

作为函数

__newindex是一个函数时会将赋值语句中的表、索引、赋的值当作参数去调用。不对表进行改变

local mt = {}
--第一个参数时表自己,第二个参数是索引,第三个参数是赋的值
mt.__newindex = function(t,index,value)
    print("index is "..index)
    print("value is "..value)
end

t = {key = "it is key"}
setmetatable(t,mt)
--输出表中已有索引key的值
print(t.key)
--为表中不存在的newKey索引赋值,调用了元表的__newIndex元方法,输出了参数信息
t.newKey = 10
--表中的newKey索引值还是空,上面看着是一个赋值操作,其实只是调用了__newIndex元方法,并没有对t中的元素进行改动
print(t.newKey)

结果:

it is key
index is newKey
value is 10
nil

作为table

__newindex是一个table时,为t中不存在的索引赋值会将该索引和值赋到__newindex所指向的表中,不对原来的表进行改变。

local mt = {}
--将__newindex元方法设置为一个空表newTable
local newTable = {}
mt.__newindex = newTable
t = {}
setmetatable(t,mt)
print(t.newKey,newTable.newKey)
--对t中不存在的索引进行负值时,由于t的元表中的__newindex元方法指向了一个表,所以并没有对t中的索引进行赋值操作将,而是将__newindex所指向的newTable的newKey索引赋值为了"it is newKey"
t.newKey = "it is newKey"
print(t.newKey,newTable.newKey)

结果:

nil nil
nil it is newKey

rawget 和 rawset

有时候我们希望直接改动或获取表中的值时,就需要rawget和rawset方法了。

rawget可以让你直接获取到表中索引的实际值,而不通过元表的__index元方法。

local mt = {}
mt.__index = {key = "it is key"}
t = {}
setmetatable(t,mt)
print(t.key)
--通过rawget直接获取t中的key索引
print(rawget(t,"key"))

结果:

it is key
nil

rawset可以让你直接为表中索引的赋值,而不通过元表的__newindex元方法。

local mt = {}
local newTable = {}
mt.__newindex = newTable
t = {}
setmetatable(t,mt)
print(t.newKey,newTable.newKey)
--通过rawset直接向t的newKey索引赋值
rawset(t,"newKey","it is newKey")
print(t.newKey,newTable.newKey)

结果:

nil nil
it is newKey    nil

元表的使用场景

作为table的元表

通过为table设置元表可以在lua中实现面向对象编程。

作为userdata的元表

通过对userdata和元表可以实现在lua中对c中的结构进行面向对象式的访问。

原文地址:https://www.cnblogs.com/blueberryzzz/p/8947446.html

时间: 2024-08-27 11:53:03

lua元表详解的相关文章

lua学习笔记16:table元表详解

一 table本质 Lua中table本质实际上是个类似HashMap东西. 其元素是很多的Key-Value对,类似iOS中的字典NSDictionary. 如果尝试访问了一个表中并不存在的元素时,就会触发Lua的一套查找机制. lua"面向对象"就是凭借这个机制实现的. 示例: local tab = {} print(tab.key) 输出:nil 因为tab中没有任何元素,当然视图访问其key元素时就会找不到,所以返回nil. 二 元表metatable 元表,即元素列表,是t

Lua中的模块(module)和包(package)详解1

这篇文章主要介绍了Lua中的模块(module)和包(package)详解,本文讲解了require函数.写一个模块.package.loaded.module函数等内容,需要的朋友可以参考下 前言 从Lua5.1版本开始,就对模块和包添加了新的支持,可是使用require和module来定义和使用模块和包.require用于使用模块,module用于创建模块.简单的说,一个模块就是一个程序库,可以通过require来加载.然后便得到了一个全局变量,表示一个table.这个table就像是一个命

Lua中的模块(module)和包(package)详解

这篇文章主要介绍了Lua中的模块(module)和包(package)详解,本文讲解了require函数.写一个模块.package.loaded.module函数等内容,需要的朋友可以参考下 前言 从Lua5.1版本开始,就对模块和包添加了新的支持,可是使用require和module来定义和使用模块和包.require用于使用模块,module用于创建模块.简单的说,一个模块就是一个程序库,可以通过require来加载.然后便得到了一个全局变量,表示一个table.这个table就像是一个命

Lua的协程和协程库详解

我们首先介绍一下什么是协程.然后详细介绍一下coroutine库,然后介绍一下协程的简单用法,最后介绍一下协程的复杂用法. 一.协程是什么? (1)线程 首先复习一下多线程.我们都知道线程——Thread.每一个线程都代表一个执行序列. 当我们在程序中创建多线程的时候,看起来,同一时刻多个线程是同时执行的,不过实质上多个线程是并发的,因为只有一个CPU,所以实质上同一个时刻只有一个线程在执行. 在一个时间片内执行哪个线程是不确定的,我们可以控制线程的优先级,不过真正的线程调度由CPU的调度决定.

详解如何让EditPlus支持LUA教程

如何让EditPlus支持LUA教程是本文要介绍的内容,这次主要介绍一下学习Lua之前的准备工作.关于在EditPlus中实现lua的安装,具体内容来看本文详解. (1) 下载Lua安装包,最新版本是lua-5.1.3. 它的体积很小,只有210K,http://luaforge.net/frs/?group_id=377 这是WINDOWS版本. 附带了编辑器,连环境变量都一次搞定了. (2)安装lua包 (3)安装EditPlus编辑器: EditPlus 简单又好用, 我自己喜欢汉化版 h

新书《Nginx实战:基于Lua语言的配置、开发与架构详解》开始发售

新书<Nginx实战:基于Lua语言的配置.开发与架构详解>开始发售https://item.jd.com/12487157.html#none <Nginx实战:基于Lua语言的配置.开发与架构详解>主要讲解了Nginx在反向代理和应用开发中的作用,阅读本书可以了解Nginx在互联网开发中扮演的多个角色,充分利用这些角色的各项功能有助于提升服务的整体性能.<Nginx实战:基于Lua语言的配置.开发与架构详解>所介绍的大部分功能是通过Nginx+Lua进行开发和配置的

(转)Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解

这是第二篇 Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程 这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结果,被移除首页了!前面第一篇的内容是这篇的基础,前面那些不弄好,下面的商品信息你是请求不到的,这点需要大家特别注意...有需要前面提到的内容的孩子可以点击链接进去自己看看!! 这篇就具体的总结我们Lua和OC交互的内容以及内购具体的代码以及结果的测试说明: 内购部分OC的代码实现 先自己总结一下整个

cocos2dx+lua注册事件函数详解

coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 registerScriptTapHandler 注册点击事件 registerScriptHandler 注册基本事件 包括 触屏 层的进入 退出 事件 registerScriptKeypadHandler 注册键盘事件 registerScriptAccelerateHandler 注册加速事件 registerScriptTouchHandler 详解(可以设置单点或多点) ? 1 2

cocos2dx+lua注册事件函数详解 事件

coocs2dx 版本 3.1.1 registerScriptTouchHandler             注册触屏事件 registerScriptTapHandler                  注册点击事件 registerScriptHandler                         注册基本事件 包括 触屏 层的进入 退出 事件 registerScriptKeypadHandler           注册键盘事件 registerScriptAccelera