在Lua中,Metatable元表如何操作?
Lua中的Metatable(元表)是一个强大的特性,它允许我们改变表(table)的行为。下面是对Lua中的Metatable元表的详细介绍,包括语法规则和示例。

1.Metatable介绍
Metatable是一个普通的Lua表,它用于定义原始值在特定操作下的行为。每个表都可以有一个元表,这个元表通过特殊的键(以双下划线__开头)来定义元方法(metamethods),这些元方法可以响应不同的事件。Metatable可以控制对象在算术操作、顺序比较、连接、长度操作和索引时的行为。
为什么需要元表?为了给用户提供一种机制来定义非预定义的操作行为。例如,两个表不能直接相加,但是通过元表我们可以定义__add元方法来实现这一点。
什么是元方法?元表通过特殊的键来定义元方法,这些键通常以双下划线__开头。例如,__index用于定义当访问表中不存在的键时的行为,__newindex用于定义当对表中不存在的键进行赋值时的行为,__add用于定义两个表相加的操作等。
2.设置与获取元表
Metatable有两个很重要的函数用于处理元表,具体介绍见下表。
| 函数 | 作用 |
| setmetatable(table, metatable) | 为表设置元表,其中table是要设置元表的表,metatable是元表。需要注意的是,如果元表中已存在 字段,就不能再用setmetatable()修改该表的元表了。 |
| getmetatable(table) | 获取表的元表,其中table是要获取元表的表。 |
以下示例演示了如何对指定的表设置元表。
-- table_setget_test.lua脚本文件
local mytable = {}
print(getmetatable(mytable)) -- 输出nil,表示当前没有元表local mymetatable = {}
setmetatable(mytable, mymetatable)
assert(getmetatable(mytable) == mymetatable) -- 确认mytable现在有关联的元表mymetatable
3.常用元方法
3.1 __index元方法
__index元方法表示,当访问一个不存在于表中的键时触发。它可以是另一个表或是一个函数。如果是表,Lua会在那个表中查找;如果是函数,则调用它并将原来的表和缺失的键作为参数传递。
我们可以使用lua命令进入交互模式来查看指定键的信息。
-- indext_key_test.lua脚本文件
local userInfo = {}
local user = {name="Tom", gender="男", age=24, phone="17858802222"}user_info = setmetatable(userInfo, {__index = user})print(userInfo.name)
print(userInfo.email)
执行以上脚本代码,程序输出结果如下。
Tom
nil
__index元方法查看表中元素是否存在,如果不存在,返回结果为nil;如果存在则由__index返回结果。示例代码见下。
-- index_function_test.lua脚本文件
local mytable = setmetatable({key1 = "value1"}, {__index = function(mytable, key)if key == "key2" thenreturn "value2"elsereturn nilendend})print(mytable.key1, mytable.key2)
执行以上脚本代码,程序输出结果如下。
value1 value2
对上述示例做如下的解析:
- mytable表赋值为{key1 = "value1"}。
- mytable设置了元表,元方法为
__index。 - 在mytable表中查找"key1",如果找到,返回该键的值"value1",找不到则继续。
- 在mytable表中查找"key2",如果找到,返回该键的值"value2",找不到则继续。
- 判断元表有没有
__index方法,如果__index方法是一个函数,则调用该函数。 - 元方法中查看是否传入"key2"键的参数(mytable.key2已设置),如果传入"key2"参数返回"value2",否则返回nil。
Lua查找一个表元素时的规则,可以总结成如下的三个步骤。
- 在表中查找,如果找到,返回该元素,找不到则继续。
- 判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
- 判断元表有没有
__index元方法,如果__index元方法为nil,则返回nil;如果__index元方法是一个表,则重复1、2、3步骤;如果__index元方法是一个函数,则返回该函数的返回值。
3.2 __newindex元方法
在Lua编程语言中,__newindex是一个元方法(metamethod),它允许你自定义对表(table)中不存在的键进行赋值时的行为。当尝试给一个表中的非现有字段赋值,并且该表有一个带有__newindex元方法的元表(metatable)时,Lua不会直接设置这个新字段,而是调用__newindex元方法。
__newindex的行为取决于它是如何定义的:
- 如果
__newindex是一个函数,那么它将接收三个参数:事件发生时的表本身(或其代理)、被赋值的键、以及被赋值的值。你可以在这个函数内部定义任何逻辑来处理赋值操作。 - 如果
__newindex是一个表,那么Lua将在这个表中创建一个新的条目,而不是在原始表中创建。这可以用来实现继承或重定向赋值。
下面是一个简单的例子,展示了如何使用__newindex来拦截对表的新索引赋值。
-- newindex_test.lua脚本文件
-- 创建一个普通的表
local myTable = {}-- 创建一个带有__newindex元方法的元表
local metaTable = {__newindex = function(tbl, key, value)print("Setting " .. tostring(key) .. " to " .. tostring(value))rawset(tbl, key, value) -- 使用rawset来避免递归调用__newindexend
}-- 将元表应用到普通表上
setmetatable(myTable, metaTable)-- 现在当我们尝试为myTable中不存在的键赋值时
myTable.x = 10
-- 我们会看到输出: Setting x to 10-- 已经存在的键仍然可以直接赋值
myTable.x = 20 -- 这里不会触发__newindex因为键已经存在
在这个例子中,当你尝试为myTable设置一个新的键时,__newindex元方法会被调用,并打印出正在设置的键和值。对于已经存在的键,直接赋值不会触发__newindex元方法。如果你想要对所有赋值都应用自定义行为,你需要更复杂的逻辑来检查键是否已经存在于表中。
3.3 __tostring元方法
在Lua中,__tostring元方法允许你自定义当尝试将一个表转换为字符串时的行为。通常情况下,当你对一个表使用tostring函数时,如果没有指定__tostring元方法,它会返回类似table: 0x地址的默认字符串表示,其中的地址是该表在内存中的位置。然而,通过设置__tostring元方法,你可以让Lua在转换时返回更友好的、自定义的字符串表示。
__tostring元方法用于修改表的输出行为。以下示例我们自定义了表的输出内容。
-- tostring_test.lua脚本文件
-- 创建一个普通的表,用于存储一些数据
local data = { name = "Alice", age = 30 }-- 定义元表,并添加__tostring元方法
local mt = {__tostring = function(tbl)-- 自定义输出格式return string.format("Name: %s, Age: %d", tbl.name, tbl.age)end
}-- 将元表应用到数据表上
setmetatable(data, mt)-- 使用tostring函数来获取表的字符串表示
print(tostring(data)) -- 输出:Name: Alice, Age: 30-- 直接打印表也会调用__tostring元方法
print(data) -- 输出:Name: Alice, Age: 30
执行以上脚本代码,程序输出结果如下。
Name: Alice, Age: 30
Name: Alice, Age: 30
在这个例子中,我们创建了一个名为data的普通表,并为其指定了一个包含__tostring元方法的元表 mt。当我们使用tostring或者直接打印这个表的时候,Lua会调用__tostring方法并按照我们自定义的方式输出表的内容。这种方式可以使得调试信息更加清晰,或者让日志记录更为友好。
3.4 __call元方法
在Lua中,__call元方法允许将表(table)当作函数来调用。当一个表被调用(就像调用函数一样,使用表名加上括号和参数,例如myTable()语法)时,如果这个表的元表(metatable)中定义了__call元方法,Lua就会调用这个元方法,并将表本身以及任何传递给它的参数作为参数传递给__call。
这特别有用当你想创建一种“可调用”的对象时,比如闭包、类的实例化构造器、或者任何你想要通过函数调用语法来触发行为的地方。
以下是一个简单的例子,展示如何使用__call元方法来创建一个可调用的表。
-- call_test.lua脚本文件
-- 创建一个简单的表
local greet = {}-- 定义元表,并添加__call元方法
local mt = {__call = function(tbl, name)-- 当表被调用时返回问候语return "Hello, " .. tostring(name) .. "!"end
}-- 将元表应用到greet表上
setmetatable(greet, mt)-- 现在可以像调用函数一样调用greet表
print(greet("World")) -- 输出:Hello, World!
print(greet("Lua")) -- 输出:Hello, Lua!
执行以上脚本代码,程序输出结果如下。
Hello, World!
Hello, Lua!
通过以上内容的学习,我们知道元表可以很好的简化我们的代码功能,所以了解Lua的元表,可以让我们写出更加简单优秀的Lua代码。
4.其他元方法
表中对应的操作列表如下(需要注意的是,__是两个下划线)。例如,__add键包含在元表中,并进行相加操作。
| 模式 | 描述 |
| __add | 加,对应算数运算符'+',接收两个操作数作为参数,并返回结果。 |
| __sub | 减,对应算数运算符'-',接收两个操作数作为参数,并返回结果。 |
| __mul | 乘,对应算数运算符'*',接收两个操作数作为参数,并返回结果。 |
| __div | 除,对应算数运算符'/',接收两个操作数作为参数,并返回结果。 |
| __mod | 取模,对应算数运算符'%',接收两个操作数作为参数,并返回结果。 |
| __pow | 幂运算,对应算数运算符'^',接收两个操作数作为参数,并返回结果。 |
| __unm | 定义了一元减法(取负)的行为。 |
| __concat | 定义字符串连接操作符'..'的行为。 |
| __eq | 定义等于'=='比较操作符的行为。 |
| __lt | 定义小于'<'比较操作符的行为。 |
| __le | 定义小于等于'<='比较操作符的行为。 |
| __len | 定义长度操作符'#'的行为。 |
| __gc | 对于用户数据类型(userdata),可以定义垃圾回收期间的动作。 |
下面是一个简单的例子,展示如何使用__add元方法来定义两个表的加法操作。假设我们有两个表,每个表包含一个字段value,我们希望对这些表的value字段进行加法运算。
-- add_test.lua脚本文件
-- 定义两个表
local table1 = {value = 10}
local table2 = {value = 20}-- 定义元表,其中包含__add元方法
local mt = {__add = function(a, b)-- 创建一个新表,其value字段是两个表value字段的和return {value = a.value + b.value}end
}-- 将元表设置为这两个表的元表
setmetatable(table1, mt)
setmetatable(table2, mt) -- 通常只需设置一个参与运算的表的元表,但为展示效果,这里都设置了-- 执行加法操作
local result = table1 + table2-- 输出结果
print(result.value) -- 输出:30
执行以上脚本代码,程序输出结果如下。
30
5.总结
Lua的元表(Metatable)机制提供了对表行为的深度定制能力,允许开发者定义非预定义操作的行为。通过将一个普通的Lua表作为元表关联到另一个表上,可以利用一系列以双下划线开头的特殊键(元方法),来响应如算术运算、比较、索引访问等事件。例如,`__add`元方法可以让两个表相加,而`__index`和`__newindex`则分别控制读取和设置不存在键的行为。此外,还有诸如`__tostring`用于自定义表转字符串表示,以及`__call`使得表能像函数一样被调用。
设置与获取元表的功能由`setmetatable()`和`getmetatable()`两个内置函数提供。当访问或修改表中不存在的键时,Lua会检查表是否有关联的元表,并根据其中定义的元方法执行相应逻辑。这不仅增加了语言的灵活性,也使得实现继承、代理模式等高级特性成为可能。元表是Lua语言的一个强大工具,极大地扩展了表这一核心数据结构的功能性,让开发者能够编写出更加简洁且高效的代码。
相关文章:
在Lua中,Metatable元表如何操作?
Lua中的Metatable(元表)是一个强大的特性,它允许我们改变表(table)的行为。下面是对Lua中的Metatable元表的详细介绍,包括语法规则和示例。 1.Metatable介绍 Metatable是一个普通的Lua表,它用于…...
4D LUT: Learnable Context-Aware 4D LookupTable for Image Enhancement
摘要:图像增强旨在通过修饰色彩和色调来提高照片的审美视觉质量,是专业数码摄影的必备技术。 近年来,基于深度学习的图像增强算法取得了可喜的性能并越来越受欢迎。 然而,典型的努力尝试为所有像素的颜色转换构建统一的增强器。 它…...
瑞芯微rk3568平台 openwrt系统适配ffmpeg硬件解码(rkmpp)
瑞芯微rk3568平台 openwrt系统适配ffmpeg硬件解码(rkmpp) RK3568及rkmpp介绍编译安装mpp获取源码交叉编译安装 libdrmlibdrm-2.4.89 make 方式编译(cannot find -lcairo, 不推荐)下载源码编译编译错误: multiple definition of `nouveau debug‘错误cannot find -lcairo:…...
使用SuperMap制作地形图的详细教程
一、数据准备 本示例以山东为例,演示如何通过SuperMap iDesktopX制作一个好看的地形图。所有数据均来源于互联网公开数据,如有自己项目真实数据,可直接跳过数据下载进入下一步。 本示例所需数据包括: 数据类别 数据类型 DEM数据…...
PHP Array:精通数组操作
PHP Array:精通数组操作 PHP,作为一门流行的服务器端编程语言,提供了强大的数组处理能力。数组是PHP中非常灵活和强大的数据结构,它可以存储多个相同类型的值。在PHP中,数组不仅可以存储数字,还可以存储字…...
【使用命令配置java环境变量永久生效与脚本切换jdk版本】
java配置环境变量命令与脚本切换jdk版本 新建用户环境变量永久生效 setx JAVA8_HOME "D:\Java\jdk8" setx JAVA17_HOME "d:\Java\jdk-17" setx JAVA_HOME %JAVA8_HOME% setx CLASSPATH ".;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;"…...
STM32-笔记32-ESP8266作为服务端
esp8266作为服务器的时候,这时候网络助手以客户端的模式连接到esp8266,其中IP地址写的是esp8266作为服务器时的IP地址,可以使用ATCIFSR查询esp8266的ip地址,端口号默认写333。 当esp8266作为服务器的时候,需要完成哪些…...
RAG(Retrieval-Augmented Generation,检索增强生成)流程
目录 一、知识文档的准备二、OCR转换三、分词处理四、创建向量数据库五、初始化语言聊天模型1.prompt2.检索链3.对话 完整代码 知识文档的准备:首先需要准备知识文档,这些文档可以是多种格式,如Word、TXT、PDF等。使用文档加载器或多模态模型…...
【Python学习(六)——While、for、循环控制、指数爆炸】
Python学习(六)——While、for、循环控制、指数爆炸 本文介绍了While、for、循环控制、指数爆炸,仅作为本人学习时记录,感兴趣的初学者可以一起看看,欢迎评论区讨论,一起加油鸭~~~ 心中默念:Py…...
解释一下:运放的输入失调电流
输入失调电流 首先看基础部分:这就是同相比例放大器 按照理论计算,输入VIN=0时,输出VOUT应为0,对吧 仿真与理论差距较大,有200多毫伏的偏差,这就是输入偏置电流IBIAS引起的,接着看它的定义 同向和反向输入电流的平均值,也就是Ib1、Ib2求平均,即(Ib1+Ib2)/2 按照下面…...
力扣hot100——二分查找
35. 搜索插入位置 class Solution { public:int searchInsert(vector<int>& a, int x) {if (a[0] > x) return 0;int l 0, r a.size() - 1;while (l < r) {int mid (l r 1) / 2;if (a[mid] < x) l mid;else r mid - 1;}if (a[l] x) return l;else …...
PHP 使用集合 处理复杂数据 提升开发效率
在 PHP 中,集合(Collections)通常是通过数组或专门的集合类来实现的。 集合(Collection)是一种高级的数据结构,可以提供比普通数组更强大的操作和功能,特别是当你需要更复杂的数据处理时。 La…...
Unity 对Sprite或者UI使用模板测试扣洞
新建两个材质球: 选择如下材质 设置如下参数: 扣洞图片或者扣洞UI的材质球 Sprite或者UI的材质球 新建一个单独Hole的canvas,将SortOrder设置为0,并将原UI的canvans的SortOrder设置为1 对2DSprite则需要调整下方的参数 hole的O…...
unity学习3:如何从github下载开源的unity项目
目录 1 网上别人提供的一些github的unity项目 2 如何下载github上的开源项目呢? 2.1.0 下载工具 2.1.1 下载方法1 2.1.2 下载方法2(适合内部项目) 2.1.3 第1个项目 和第4项目 的比较 第1个项目 第2个项目 第3个项目 2.1.4 下载方法…...
PHP后执行php.exe -v命令报错并给出解决方案
文章目录 一、执行php.exe -v命令报错解决方案 一、执行php.exe -v命令报错 -PHP Warning: ‘C:\windows\SYSTEM32\VCRUNTIME140.dll’ 14.38 is not compatible with this PHP build linked with 14.41 in Unknown on line 0 解决方案 当使用PHP8.4.1时遇到VCRUNTIME140.dll…...
CDP集群安全指南-动态数据加密
[〇]关于本文 集群的动态数据加密主要指的是加密通过网络协议传输的数据,防止数据在传输的过程中被窃取。由于大数据涉及的主机及服务众多。你需要更具集群的实际环境来评估需要为哪些环节实施动态加密。 这里介绍一种通过Cloudera Manager 的Auto-TLS功能来为整个…...
【shell编程】报错信息:Undefined Variable(包含6种解决方法)
大家好,我是摇光~ 当Shell脚本报错“Undefined Variable”时,是未定义变量的意思。 以下是对每个可能原因及其对应详细解决方案的详细解释: 原因1:拼写错误 原因: 脚本中变量名的拼写在使用和定义时不一致。例如&…...
Dubbo扩展点加载机制
加载机制中已经存在的一些关键注解,如SPI、©Adaptive> ©Activateo然后介绍整个加载机制中最核心的ExtensionLoader的工作流程及实现原理。最后介绍扩展中使用的类动态编译的实 现原理。 Java SPI Java 5 中的服务提供商https://docs.oracle.com/jav…...
unity学习7:unity的3D项目的基本操作: 坐标系
目录 学习参考 1 unity的坐标系 1.1 左手坐标系 1.2 左手坐标系和右手坐标系的区别 1.3 坐标系的原点(0,0,0) 2 坐标系下的具体xyz坐标 2.1 position这里的具体xyz坐标值 2.2 父坐标 2.3 世界坐标和相对坐标 2.3.1 世界坐标 2.3.2 相对坐标 2.4 父物体,…...
PyTorch框架——基于深度学习EfficientDeRain神经网络AI去雨滴图像增强系统
第一步:EfficientDeRain介绍 EfficientDeRain 是一个针对单张图像去雨的开源项目,该项目由清华大学的研究团队提出,主要用于处理图像中的雨水干扰,恢复图像的真实场景 核心功能 图像去雨:EfficientDeRain 通过学习像素…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...
