Lua 元表,元方法
元表与元方法的概念
Lua中每个值都可具有元表。元表是普通的Lua表,定义了原始值在某些特定操作下 的行为。
例如,当table作为加法的操作数时,Lua检查其“元表”中的“__add”字段是否有 个函数。如果有,Lua调用它执行加法。我们称“元表”中的“键(如__add)”为事件(event),称“值”为元方法(metamethod)。前述例子中的事件(event)是"__add",元方法(metamethod)是执行加法的函数。
又例如,使用元表我们可以定义Lua如何计算两个table的相加操作a+b。当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add "等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。
自己的理解:可以理解为“元方法(metamethod)”只存在与“元表”中。或者认为在元表中,元方法才有意义。
Lua创建新的table的同时不会创建元表,但是可以使用“setmetatable”函数为table设置原表
当你通过键来访问table的时候,如果这个键没有值,那么Lua就会寻找该table的元表(metatable)(假定有metatable)中的__index键。如果__index 包含一个表格,Lua会在表格中查找对应的键。如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。__index元方法查看表中元素是否存在,如果不存在,返回结构为nil;如果存在则由__index返回结果。
元方法
常用元方法
- __add:加法
- __sub:减法
- __mul:乘法
- __div:除法
- __unm:相反数
- __mod:取模
- __pow:乘幂
- __index:
- __newindex:
- 等
关系类的元方法
除了加法减法这些算术类的操作之外,大于小于等这些关系类的操作也是有元方法的:
- __eq:等于
- __lt:小于
- __le:小于等于
如果对两个具备不同“元表”的值进行这些比较操作,就会报错,一定要注意,这和加减法的规则不一样。
测试用例
local mtab1 = {}
mtab1.Name = "mtab1 name"
-- 元方法:加法
mtab1.__add = function(t1, t2)local val = "mtab1.__add"-- print(val)return val
end
-- 元方法:减法
mtab1.__sub = function(t1, t2)local val = "mtab1.__sub"-- print(val)return val
end
-- 元方法:小于
mtab1.__lt = function(t1, t2)local val = "mtab1.__lt"print("call metamethod:", val)math.randomseed(os.time())local rval = math.random(1, 10)-- for i = 1, 10 do-- rval = math.random(1, 10)-- endreturn rval < 5
endlocal mtab2 = {}
mtab2.Name = "mtab2 name"
-- 元方法:加法
mtab2.__add = function(t1, t2)local val = "mtab2.__add"-- print(val)return val
end
-- 元方法:减法
mtab2.__sub = function(t1, t2)local val = "mtab2.__sub"-- print(val)return val
endlocal tab1 = {}
local tab2 = {}
local tab3 = {}
local tab4 = {}
-- 元方法:加法,如果没有将 tab4 这是为其它 table 的元表,好像没有意义!!
tab4.__add = function(t1, t2)local val = "tab4.__add"-- print(val)return val
end--此 table 没有元表
local tab5_nomet = {}
-- 元方法:加法
tab5_nomet.__add = function(t1, t2)local val = "tab5_nomet.__add"-- print(val)return val
endlocal tab6 = {}
local tab7 = {}--此 table 没有元表
local tab8_nomet = {}-- 为 table 表设置 元表
setmetatable(tab1, mtab1)
setmetatable(tab2, mtab1)
setmetatable(tab3, mtab2)
setmetatable(tab4, {__index = mtab1})
setmetatable(tab6, tab5_nomet)
setmetatable(tab7, tab4)-------------------------------------------------------- 输出table表的地址
print(tab1, tab2, tab3, tab4, mtab1, mtab2)
-- 输出tabled表的原表地址
-- getmetatable(tab1)与 getmetatable(tab2)的值相等,是因为 tab1 与 tab2 的 元表 都是 mtab1
print(getmetatable(tab1),getmetatable(tab2),getmetatable(tab3),getmetatable(tab4),getmetatable(mtab1),getmetatable(mtab2)
)-- 输出表 key=Name 的值
print("tab1.Name=",tab1.Name,",tab2.Name=",tab2.Name,",tab3.Name=",tab3.Name,",tab4.Name=",tab4.Name,",mtab1.Name=",mtab1.Name,",mtab2.Name=",mtab2.Name
)
------------------------------------------------------------------------------------------------------------
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 都没有元表。所以,报错:attempt to perform arithmetic on local 'mtab1' (a table value)
-- print("mtab1 + mtab2 =",mtab1 + mtab2)
------------------------------------------------------
-- 相同元表:执行元表中的元方法
-- 输出:tab1 + tab2 = mtab1.__add
print("tab1 + tab2 =", tab1 + tab2)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab1 + tab3 = mtab1.__add
print("tab1 + tab3 =", tab1 + tab3)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab1 + tab4 = mtab1.__add
print("tab1 + tab4 =", tab1 + tab4)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab1 + tab5_nomet = mtab1.__add
print("tab1 + tab5_nomet =", tab1 + tab5_nomet)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab5_nomet + tab1 = mtab1.__add
print("tab5_nomet + tab1 =", tab5_nomet + tab1)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab4 + tab1 = mtab1.__add
print("tab4 + tab1 =", tab4 + tab1)
------------------------------------------------------
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- tab4有原表,但是没有__add元方法,tab5_nomet也没有__add元方法。所以,报错:attempt to perform arithmetic on local 'tab4' (a table value)
-- print("tab4 + tab5_nomet =",tab4 + tab5_nomet)-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- tab4有原表,但是没有__add元方法,tab5_nomet也没有__add元方法。所以,报错:attempt to perform arithmetic on local 'tab5_nomet' (a table value)
-- print("tab5_nomet + tab4 =", tab5_nomet + tab4)
------------------------------------------------------
-- 输出:tab5_nomet + tab6 = tab5_nomet.__add
print("tab5_nomet + tab6 =", tab5_nomet + tab6)
-- 输出:tab7 + tab8_nomet = tab4.__add
print("tab7 + tab8_nomet =", tab7 + tab8_nomet)
-- 输出:tab7 + tab4 = tab4.__add
print("tab7 + tab4 =", tab7 + tab8_nomet)
-------------------------------------------------------- 输出:tab1 < tab2 = boolean结果
print("tab1 < tab2 =", tab1 < tab2)
-- 如果对两个具备不同“元表”的值进行这些比较操作,就会报错,一定要注意,这和加减法的规则不一样。
-- 所以,报错:attempt to compare two table values
-- print("tab1 < tab3 =", tab1 < tab3)
print("----------------")
测试用例输出结果
----------------
table: 00C5C9A8 table: 00C5C8E0 table: 00C5C958 table: 00C5C9F8 table: 00C5C9D0 table: 00C5C7F0
table: 00C5C9D0 table: 00C5C9D0 table: 00C5C7F0 table: 00C5C8B8 nil nil
tab1.Name= nil ,tab2.Name= nil ,tab3.Name= nil ,tab4.Name= mtab1 name ,mtab1.Name= mtab1 name ,mtab2.Name= mtab2 name
tab1 + tab2 = mtab1.__add
tab1 + tab3 = mtab1.__add
tab1 + tab4 = mtab1.__add
tab1 + tab5_nomet = mtab1.__add
tab5_nomet + tab1 = mtab1.__add
tab4 + tab1 = mtab1.__add
tab5_nomet + tab6 = tab5_nomet.__add
tab7 + tab8_nomet = tab4.__add
tab7 + tab4 = tab4.__add
call metamethod: mtab1.__lt
tab1 < tab2 = true
----------------
相关文章:
Lua 元表,元方法
元表与元方法的概念 Lua中每个值都可具有元表。元表是普通的Lua表,定义了原始值在某些特定操作下 的行为。 例如,当table作为加法的操作数时,Lua检查其“元表”中的“__add”字段是否有 个函数。如果有,Lua调用它执行加法。我们称“元表”中的“键(如__add)”为事件(event),称…...

C# WPF上位机开发(利用tcp/ip网络访问plc)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 c# wpf如果是用来开发非标上位机的,那么和plc的通信肯定是少不了的。而且,大部分plc都支持modbus协议,所以这个…...

Knife4j 接口文档如何设置 Authorization 鉴权参数?
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🌺 仓库主页: Gitee 💫 Github 💫 GitCode 💖 欢迎点赞…...

CentOS 防火墙管理及使用的redis基本常用命令
文章目录 防火墙管理使用systemctl管理防火墙启动、关闭使用firewalld-cmd配置访问防火墙策略firewalld配置文件修改限制来源IP docker使用 redis 防火墙管理 需要关闭防火墙或者开启对应端口 使用systemctl管理防火墙启动、关闭 启动防火墙: systemctl start fi…...

路由器原理
目录 一.路由器 1.路由器的转发原理 2.路由器的工作原理 二.路由表 1.路由表的形成 2.路由表表头含义 直连: 非直连: 静态 静态路由的配置 负载均衡(浮动路由) 默认路由 动态 三.交换与路由对比 一.路由器 1.路由器…...

采埃孚4D成像雷达拆解
1 基本信息 品牌:海外Tier1采埃孚 • 应用:上汽飞凡中高端纯电平台 • 数量:单车2个,安装在前后保内部 • 最远探测距离:350米 拆解来看,4D雷达主要可以分为4个部分,分别为数字接口板及结构件…...

若依框架springboot——修改前端图片上传样式
简述 使用过若依框架的,一定知道若依前端框架上传图片的样式,是一个正方形加号图片,但是如果你要使用自定义样式呢。 比如将下面这个图进行修改呢 修改后的样式 你可以直接找到element-ui 修改上传图片的组件,也可以加入新的组…...

mysql 数据库 关于库的基本操作
库的操作 如果想到 mysql 客户端当中数据 系统当中的命令的话,直接输入的话,会被认为是 mysql 当中的命令。 所以,在mysql 当中执行系统当中的命令的话,要在系统命令之前带上 ststem ,表示系统命令: 但是…...

【通用】Linux,VSCode,IDEA,Eclipse等资源相对位置
正文 不论是 IDEA、Linux、VSCode、cmd等等吧,都遵循这个规则: 如果以斜杠开头,表示从根开始找: IDEA的根是classpath(classpath就是项目被编译后,位于 target下的 classes文件夹,或者位于ta…...

音视频技术开发周刊 | 323
每周一期,纵览音视频技术领域的干货。 新闻投稿:contributelivevideostack.com。 Meta牵头组建开源「AI复仇者联盟」,AMD等盟友800亿美元力战OpenAI英伟达 超过50家科技大厂名校和机构,共同成立了全新的人工智能联盟。以开源为旗号…...

STM32在CTF中的应用和快速解题
题目给的是bin文件,基本上就是需要我们手动修复的固件逆向。 如果给的是hex文件,我们可能需要使用MKD进行动态调试 主要还是以做题为目的 详细的可以去看文档:https://pdf1.alldatasheet.com/datasheet-pdf/view/201596/STMICROELECTRONIC…...

SaaS 电商设计 (五) 私有化部署-实现 binlog 中间件适配
一、 背景 具体的中间件私有化背景在上文 SaaS 电商设计 (二) 私有化部署-缓存中间件适配 已有做相关介绍.这里具体讨论的场景是通过解析mysql binlog 来实现mysql到其他数据源的同步.具体比如:在电商的解决方案业务流中经常有 ES 的使用场景,用以解决一些复杂的查询和搜索商品…...

Android APP 常见概念与 adb 命令
adb 的概念 adb 即 Android Debug Bridge 。在窗口输入 adb 即可显示帮助文档。adb 实际上就是在后台开启一个 server,会接收 adb 的命令然后帮助管理,控制,查看设备的状态、信息等,是开发、测试 Android 相关程序的最常用手段。…...

菜鸟学习日记(python)——函数
函数是组织好的,用来实现某些功能的代码块,它可以重复使用。 函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()。但我们也可以自己创建函数,这被叫做用户自定义函数。 定义函数 用…...

垃圾回收 (GC) 在 .NET Core 中是如何工作的?
提起GC大家肯定不陌生,但是让大家是说一下GC是怎么运行的,可能大多数人都不太清楚,这也很正常,因为GC这东西在.NET基本不用开发者关注,它是依靠程序自动判断来释放托管堆的,我们基本不需要主动调用Collect(…...

Appium 图像识别技术 OpenCV
在我们做App自动化测试的时候,会发现很多场景下元素没有id、content-desc、text等等属性,并且有可能也会碰到由于开发采用的是自定义View,View中的元素也无法识别到,很多的自动化测试框架对此类场景束手无策。Appium在V1.9.0中有给…...

产品Axure的元组件以及案例
前言 产品<Axure的安装以及组件介绍-CSDN博客经过上文我们可以知道我们Axure是一款适用于网站、移动应用和企业软件的交互式原型设计工具。它可以帮助用户创建高保真的交互式原型,包括线框图、流程图、模型、注释和规格等,以便与客户、开发人…...

智能优化算法应用:基于头脑风暴算法3D无线传感器网络(WSN)覆盖优化 - 附代码
智能优化算法应用:基于头脑风暴算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于头脑风暴算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.头脑风暴算法4.实验参数设定5.算法结果6.…...
flutter Pageview组件
PageView组件说明 组件说明PageView,PageController的源码简单demo 组件说明 属性说明scrollDirection滑动反向 Axis.vertical上下滑动 Axis.horizontal左右滑动reverse是否反转 true从最后一个记0controllerPageController见下文physics滚动方式pageSnapping是否有…...

如何用 Cargo 管理 Rust 工程系列 丙
以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/viSsCaFR2x9hZOvo1PoRqA 添加依赖项 前面已经提到过在 cargo 配置文件 Cargo.toml 中如何手动添加工程依赖项,cargo 同样提供了 add …...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...