Lua 元表及常见元方法

news2024/12/28 3:41:12

一、什么是元表

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 方法的第一个参数为表地址,剩余参数用…表示。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/144287.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

低代码能够为企业带来什么?

目录 1、为企业快速开发应用赋能 2、低成本使用数字化工具 3、满足企业定制化需求 大数据时代的快速发展下,传统的应用开发技术手段渐渐地无法满足企业的高需求。并且,企业想在应用开发的基础上同时实现个性化定制,而传统的技术条件所需要…

Linux 进程概念(一)

目录 一、冯诺伊曼体系结构 二、操作系统(Operator System) 2.1 操作系统如何管理硬件? 2.2 操作系统如何管理软件? 2.3 一张图带你直观了解OS管理过程 三、进程(启示录) 3.1 进程的基本概念 3.1.1 进程PCB 3…

寒假本科创新——机器学习(二)

绪论1.3归纳偏好 一般原则:奥卡姆剃刀 什么样的算法比较好?1.4NFL定理 NFL定理的前提: NFL定理的寓意:1.3归纳偏好 归纳偏好(lnductive Bias): 机器学习算法在学习过程中对某种类型假设的偏好…

sql语句练习2

1、列出至少有一个员工的所有部门编号、名称,并统计出这些部门的平均工资、最低工资、最高工资 做法: 第一步:找出至少有一个员工的部门编号mysql>select deptno,count(empno)from empgroup by deptnohaving count(empno)>0; 第二步: …

node.js快速入门指南

Node.js迅速蹿红,衍生了一个强大的开源社区、支持企业,甚至还拥有属于自己的技术大会。我把这种成功归结于它的简介,高校,同时提高了编程生产力。 Node.js 的前置知识很多,例如以下知识 JavaScriptES6Ajax 还不会的…

Java JVM:虚拟机性能监控、故障处理工具(三)

目录标题一、基础故障处理工具二、可视化故障处理工具三、其他故障相关一、基础故障处理工具 JMC(Java Mission Control)以及JFR(Java Flight Recorder) JMC 从 Java7 以后包含在 JDK 中,直接输入 jmc 就能启动在使用…

JEECGboot数据规则篇

使用 一、功能说明 列表数据权限,主要通过数据权限控制行数据,让不同的人有不同的查看数据规则; 比如: 销售人员只能看自己的数据;销售经理可以看所有下级销售人员的数据;财务只看金额大于5000的数据等等…

js事件高级

文章目录一、注册事件(绑定事件)1、注册事件概述(1)传统注册方式(2)方法监听注册方式2、addEventListener 事件监听方式3、attachEvent 事件监听方式4、注册事件兼容性解决方案二、删除事件(解绑…

C库函数:string.h

string.h C 标准库 – <string.h> | 菜鸟教程 (runoob.com) 1void *memchr(const void *str, int c, size_t n) 在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c&#xff08;一个无符号字符&#xff09;的位置。在这个函数中&#xff0c;可以看到有void …

使用docker安装zabbix监控

官网手册&#xff1a;docker安装zabbix 首先需要安装好docker服务 创建专用于Zabbix组件容器的网络 docker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 zabbix-net启动空的 MySQL 服务器实例 docker run --name mysql-server -t \ //mysql容器…

FFMPEG完美入门资料---002---FFmpeg 支持能力说明

FFMPEG入门资料---001---介绍和参数说明_音视频开发老马的博客-CSDN博客 接着上文写&#xff1a; 2.3.1 FFmpeg 对编码解码器的支持 ffmpeg 支持的编解码器种类共有 280 多种&#xff0c; 涵盖了几乎所有常见音视频编码格式&#xff0c; 能解码几乎所有的音视频&#xff0c; …

月交付破万,长安深蓝成造车新势力“头部玩家”

近日&#xff0c;造车新势力纷纷公布2022年12月交付数据。通过数据&#xff0c;我们发现众多造车新势力都取得了不错的成绩&#xff0c;“头部玩家”的门槛甚至提升至万辆。与此同时&#xff0c;“排位”形势也与往年大相径庭&#xff0c;其中最令人意外的是2022年发布的深蓝品…

ESP8266+MicroPython开发:使用ESP8266+Thonny烧录MicroPython固件

使用ESP8266Thonny烧录MicroPython固件下载固件下载安装Thonny在Thonny烧录固件测试Thonny简单使用下载固件 MicroPython下载网址 根据ESP8266的flash选择&#xff0c;一般选择如图所示 自己随便选择一个版本下载&#xff0c;注意记住自己的下载路径 下载安装Thonny Thonn…

【vue系列-05】vue的生命周期(详解)

深入理解vue的生命周期一&#xff0c;vue的生命周期1&#xff0c;创建流程1.1&#xff0c;beforeCreate1.2&#xff0c;created2&#xff0c;挂载流程2.1&#xff0c;beforeMount2.2&#xff0c;mounted3&#xff0c;更新流程3.1&#xff0c;beforeUpdate3.2&#xff0c;update…

ConvNeXt V2论文翻译:ConvNeXt V2与MAE激情碰撞

文章目录摘要1 简介2 相关工作3 全卷积掩码自编码器4 全局响应归一化5 ImageNet实验6 迁移学习实验7 结论摘要 论文链接&#xff1a;ConvNeXt V2 在改进的架构和更好的表示学习框架的推动下&#xff0c;视觉识别领域在21世纪20年代初实现了快速现代化和性能提升。例如&#xf…

评价类模型(层次分析法与模糊评价模型)

一、评价类模型 综合评价的基本理论和数据预处理&#xff1a; 评价对象评价指标权重系数综合评价模型评价者 二、AHP法—层次分析法 通过打分解决评价类问题&#xff0c;两两比较&#xff0c;推算权重。 %function RIAHPRI(n) %利用MATLAB求随机一致性指标; i0;CI0;Azer…

pyqt5下的QInputDialog跟QFileDialog以及关闭主窗口子窗口自动关闭

QInputDialog 具体的参数可以参考&#xff1a; Qt&#xff1a;30---QInputDialog标准输入对话框_mb6128aabee41d4的技术博客_51CTO博客 官网连接&#xff1a; QInputDialog Class | Qt Widgets 5.15.12 这里只介绍QInputDialog.getText 代码实现&#xff1a; from PyQt5.…

【从零开始学爬虫】采集食品行业最新报价数据

l 采集网站 【场景描述】采集食品行业最新报价数据。 【源网站介绍】中国报告大厅网休闲食品行业数据频道提供休闲食品行业数据信息&#xff0c;在此有大量休闲食品行业数据信息可供选择&#xff0c;是一个可以帮助休闲食品行业了解数据的平台。 【使用工具】前嗅ForeSpider…

hbase2.x orphan regions on filesystem

问题描述&#xff1a;orphan regions on filesystem 可以通过主master web页面的HBCK Report查看 也可以通过hbck2工具查看 # 查看指定表 hbase hbck -j $HBASE_HOME/lib/hbase-hbck2-1.3.0-SNAPSHOT.jar addFsRegionsMissingInMeta default:tableName # 查看命名空间下所有…

Git分支开发中的问题

前言我们在开发中&#xff0c;经常是很多人开发同一份代码&#xff0c;早期没有git工具的时候那可真是噩梦&#xff0c;要复制来复制去&#xff0c;不止繁琐&#xff0c;还容易出错&#xff0c;所以后来涌现了各种代码工具&#xff0c;Svn&#xff0c;Git等等&#xff0c;而Git…