15.元表与元方法
元表,即 Lua 中普通 table 的元数据表,而元方法则是元表中定义的普通表的默认行为。
Lua 中的每个普通 table 都可为其定义一个元表,用于扩展该普通 table 的行为功能。例如,
对于 table 与数值相加的行为,Lua 中是没有定义的,但用户可通过为其指定元表来扩展这
种行为;再如,用户访问不存在的 table 元素,Lua 默认返回的是 nil,但用户可能并不知道
发生了什么。此时可以通过为该 table 指定元表来扩展该行为:给用户提示信息,并返回用
户指定的值。
(1) 重要函数
元表中有两个重要函数:
setmetatable(table,metatable):将 metatable 指定为普通表 table 的元表。
getmetatable(table):获取指定普通表 table 的元表。
(2) _ index 元方法
当用户在对 table 进行读取访问时,如果访问的数组索引或 key 不存在,那么系统就会
自动调用元表的 index 元方法。该重写的方法可以是一个函数,也可以是另一个表。如果
重写的 _index 元方法是函数,且有返回值,则直接返回;如果没有返回值,则返回 nil。
__index元方法是一个函数如下:
--元表与元方法 t1 = {"a", age = 23,"b","c",name = "王五", "d",100} --定义一个元表 meta = {} --关联原始表和元表 setmetatable(t1,meta) function meta.__index(tab,key) print("key "..key.." 在原始表中不存在") end print(t1[99]) print(t1[100]) //输出结果如下: key 99 在原始表中不存在 nil key 100 在原始表中不存在 nil
__index元方法是另一个表如下
t1 = {"a", age = 23,"b","c",name = "王五", "d",100} --定义一个元表 meta = {} --定义一个元表 meta = {} --定义另一个普通表 other = {} other[99] = 999 other[100] = 1000 --关联原始表和元表 setmetatable(t1,meta) meta.__index = other print(t1[99]) print(t1[100]) //输出结果如下: 999 1000
3.当用户为 table 中一个不存在的索引或 key 赋值时,就会自动调用元表的_ newindex 元
方法。该重写的方法可以是一个函数,也可以是另一个表。如果重写的 _newindex 元方法
是函数,且有返回值,则直接返回;如果没有返回值,则返回 nil。
__newindex是函数,如下:
t1 = {"a", age = 23,"b","c",name = "王五", "d",100} --元表 meta = {}; setmetatable(t1,meta) meta.__newindex = function(tab,key,value) print("写 "..key.." value不存在") --新值写入原始表 rawset(tab,key,value) end t1[100] = 1900 t1.x = "xxx" print (t1[100]) print (t1.x) //输出结果如下: 写 100 value不存在 写 x value不存在 1900 xxx
__newindex是另一个表,如下:
t1 = {"a", age = 23,"b","c",name = "王五", "d",100} meat = {} other = {} setmetatable(t1,meat) meat.__newindex = other --此时,other的作用是,暂存新增加的数据 t1.x = "xxx369" print(t1.x) print(other.x) //输出结果如下: nil xxx369
4.运算符元方法
如果要为一个表扩展加号(+)、减号(-)、等于(==)、小于(<)等运算功能,则可重写相应的
元方法。
例如,如果要为一个 table 扩展加号(+)运算功能,则可重写该 table 元表的_ add 元方
法,而具体的运算规则,则是定义在该重写的元方法中的。这样,当一个 table 在进行加法
(+)运算时,就会自动调用其元表的 _add 元方法。
t1 = {"a", age = 23,"b","c",name = "王五", "d",100} meta = { __add = function(tab,num) for k,v in pairs(tab) do if (type(v) == "number") then tab[k] = v + num elseif (type(v) == "string" and type(k) ~= "string") then tab[k] = tab[k]..".xxx" end end return tab end } setmetatable(t1,meta) t2 = t1 + 3 for k,v in pairs(t2) do print(k,v) end //输出结果如下: 1 a.xxx 2 b.xxx 3 c.xxx 4 d.xxx 5 103 age 26 name 王五
5._tostring元方法
直接输出一个 table,其输出的内容为类型与 table 的存放地址。如果想让其输出 table
中的内容,可重写 _tostring 元方法。
t1 = {"a", age = 23,"b","c",name = "王五", "d",100} meta = {} setmetatable(t1,meta) meta.__tostring = function (tab) str = "" for k, v in pairs(tab) do str = str..k..": "..v.."\n" end return str end print(t1) //输出结果如下: 1: a 2: b 3: c 4: d 5: 100 age: 23 name: 王五
6._call元方法
当将一个 table 以函数形式来使用时,系统会自动调用重写的 _call 元方法。该用法主
要是可以简化对 table 的相关操作,将对 table 的操作与函数直接相结合。
t1 = {"a", age = 23,"b","c",name = "王五", "d",100} meta = {} setmetatable(t1,meta) function meta.__call(tab,num,str) for k,v in pairs(tab) do if (type(v) == "string") then tab[k] = tab[k]..str else tab[k] = v + num end end return tab end t1(1,"xx") for k,v in pairs(t1) do print(k, v) end //输出结果如下: 1 axx 2 bxx 3 cxx 4 dxx 5 101 age 24 name 王五xx
7.元表单独定义
为了便于管理与复用,可以将元素单独定义为一个文件。该文件中仅可定义一个元表,
且一般文件名与元表名称相同。
若一个文件要使用其它文件中定义的元表,只需使用 require “元表文件名”即可将元表
导入使用。
如果用户想扩展该元表而又不想修改元表文件,则可在用户自己文件中重写其相应功能
的元方法即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏