查看: 33|回复: 0

lua元表

[复制链接]

47

主题

53

回帖

1551

积分

金牌会员

积分
1551
发表于 2026-5-25 16:29:08 | 显示全部楼层 |阅读模式
这是 Lua 中最核心也最灵活的特性之一 —— 元表本质是给普通 table 附加 “自定义行为” 的特殊 table,比如让 table 支持加减运算、自定义索引查找规则等
  • 元表(Metatable):一个特殊的 table,用来定义另一个 table(被元表修饰的 table 称为 “原表”)的特殊行为;
  • 元方法(Metamethod):元表中以 __ 开头的键(比如 __add、__index),对应原表的自定义行为(比如 __add 定义加法运算);
  • 核心函数
    • setmetatable(t, mt):给 table t 设置元表 mt(返回 t);
    • getmetatable(t):获取 table t 的元表(返回元表或 nil)。

__index(索引查找)
当访问原表中不存在的键时,Lua 会去元表的 __index 中查找,这是 Lua 实现继承、默认值的核心。
__index 可以是 table(优先查找该 table 的键),也可以是函数(自定义查找逻辑)。

Lua复制代码
-- 示例1:__index 为 table(继承的底层逻辑)local proto = {name = "默认名称", age = 18}  -- 原型 tablelocal obj = {gender = "男"}                  -- 原表-- 给 obj 设置元表,__index 指向 protosetmetatable(obj, {__index = proto})-- 访问 obj 不存在的键,自动去 proto 找print(obj.name)  -- 输出:默认名称(obj 无 name,从 proto 取)print(obj.age)   -- 输出:18(同理)print(obj.gender)-- 输出:男(obj 自身有,直接取)-- 示例2:__index 为函数(自定义查找逻辑)local t = {}setmetatable(t, {    __index = function(table, key)  -- table=原表,key=访问的键        return "键 " .. key .. " 不存在,返回默认值"    end})print(t.abc)  -- 输出:键 abc 不存在,返回默认值__newindex(索引赋值)
当给原表中不存在的键赋值时,Lua 会调用元表的 __newindex,而非直接给原表添加键。
  • 用法和 __index 类似,可用于限制赋值、记录赋值日志、重定向赋值目标。

Lua复制代码
-- 示例:禁止给原表添加新键local t = {name = "张三"}setmetatable(t, {    __newindex = function(table, key, value)        print("赋值操作:表 =", table, "键 =", key, "值 =", value)        -- 使用 rawset 绕开元方法,直接给原表赋值(避免递归触发 __newindex)        rawset(table, key, value)    end})t.age = 20  -- 报错:禁止给表添加新键:age(t 无 age 键,触发 __newindex)t.name = "李四"  -- 正常执行(t 有 name 键,不触发 __newindex)__add/__sub/__mul 等
通过元方法自定义原表的算术运算,比如让两个 table 支持加法。常用算术元方法:__add(+)、__sub(-)、__mul(*)、__div(/)、__eq(==)。

Lua复制代码
-- 示例:让 table 表示向量,支持加法运算local vec1 = {x = 1, y = 2}local vec2 = {x = 3, y = 4}-- 定义元表(包含 __add 元方法)local vec_mt = {    __add = function(a, b)  -- a=左操作数,b=右操作数        return {x = a.x + b.x, y = a.y + b.y}    end}-- 给两个向量设置元表setmetatable(vec1, vec_mt)setmetatable(vec2, vec_mt)-- 向量加法(触发 __add)local vec3 = vec1 + vec2print(vec3.x, vec3.y)  -- 输出:4 6__call(把 table 当作函数调用)
当原表被当作函数调用时(比如 t()),触发 __call 元方法,可实现 “可调用的对象”。

Lua复制代码
local t = {name = "Lua"}setmetatable(t, {    __call = function(table, ...)  -- table=原表,...=调用时的参数        print("调用了 table,参数:", ...)        print("table 的 name:", table.name)    end})-- 把 table 当作函数调用(触发 __call)t(10, 20)-- 输出:-- 调用了 table,参数:10 20-- table 的 name:Lua__tostring(自定义打印格式)
当用 print() 打印原表时,触发 __tostring 元方法,替代默认的 table: 0xXXXXXXX 格式。

Lua复制代码
local person = {name = "张三", age = 20}setmetatable(person, {    __tostring = function(table)        return "Person: " .. table.name .. "(" .. table.age .. "岁)"    end})print(person)  -- 输出:Person: 张三(20岁)(而非默认的内存地址)









您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表