Lua元方法

这一章主要记录介绍 元表元方法 和使用,如果对 元表 不熟悉的同学可以闪现Lua元表

元方法 是元表中的某些字段,定义了表在特定操作下的行为。例如,当表进行加法运算时,Lua会检查元表中的__add字段,并使用它定义的行为。

1. 常见元方法

元方法 作用
__index 在表中访问不存在的键时触发。
__newindex 在表中对不存在的键赋值时触发。
__add 重载加法运算符 + ,如对两个表进行 **+**时触发。
__sub 重载减法运算符 - ,如对两个表进行 **-**时触发。
__mul 重载乘法运算符 * ,如对两个表进行 * 时触发。
__div 重载除法运算符 / ,如对两个表进行 / 时触发。
__mod 重载取模运算符 % ,如对两个表进行 **%**时触发。
__pow 重载幂运算符**^** ,如对两个表进行**^**时触发。
__concat 重载字符串连接运算符 .. ,如对两个表进行 .. 时触发。
__eq 重载相等比较运算符 == ,如对两个表进行 == 时触发。
__lt 重载小于比较运算符 < ,如对两个表进行 < 时触发。
__le 重载小于等于比较运算符 <= ,如对两个表进行 <= 时触发。
__call 将表当作函数调用时触发。
__tostring 定义 tostring() 函数的输出行为。
__metatable 设置元表的保护,外部无法获取或修改元表。

以上包含了所有的元方法,下面会针对每一个展开记录。

2. __index

  • 功能:

    • 在访问表中不存在的键时触发。

    • 用于提供默认值,实现继承等。

  • 参数:

    • table:原始表(正在访问的表)。

    • key:访问的键名。

  • 返回值:返回键对应的值。

  • 如果 __index 元方法是一个表,则从该表中查找键的值。

  • 如果 __index 元方法是一个函数,则会调用该函数并返回结果。

  • 示例:返回默认值

Lua 复制代码
local t = {}
local mt = {
    __index = function(table, key)
        return "default value for " .. key
    end
}
setmetatable(t, mt)

print(t.foo) -- 输出 "default value for foo"
  • 示例:实现简单继承
Lua 复制代码
local Parent = {name = "Parent"}
local Child = {}
setmetatable(Child, {__index = Parent})

print(Child.name) -- 输出 "Parent"

3. __newindex

  • 功能:

    • 在为表中不存在的键赋值时触发。
  • 参数:

    • table:原始表(正在访问的表)。

    • key:赋值的键名。

    • value:赋值的值。

  • 返回值:无返回值。

  • __newindex 元方法必须是一个函数,不能是表这是Lua的设计特点与 __index 明确区分。

  • 在表中赋值键时,如果元表中定义了 __newindex ,会调用该元方法不会直接修改原始表。

  • 示例:

Lua 复制代码
local t = {}
local mt = {
    __newindex = function(_, key, value)
        print("Setting " .. key .. " = " .. value)
    end
}
setmetatable(t, mt)

t.foo = "bar" -- 输出 "Setting foo = bar"

4. __add

  • 功能:

    • 定义 + 运算符的行为。
  • 参数:

    • a:第一个操作数,可以是表、数字或其他 Lua 数据类型。

    • b:第二个操作数,可以是表、数字或其他 Lua 数据类型。

  • 返回值:加法运算结果(类型任意)。

  • 当尝试对两个表使用 + 运算符时,Lua会调用元表的 __add 方法。

  • 示例:加法运算

Lua 复制代码
local t1 = {value = 10}
local t2 = {value = 20}
local mt = {
    __add = function(a, b)
        return {value = a.value + b.value}
    end
}
setmetatable(t1, mt)
setmetatable(t2, mt)

local t3 = t1 + t2
print(t3.value) -- 输出 30

5. __sub

  • 功能:

    • 定义 - 运算符的行为。
  • 参数:

    • a:第一个操作数,可以是表、数字或其他 Lua 数据类型。

    • b:第二个操作数,可以是表、数字或其他 Lua 数据类型。

  • 返回值:减法运算结果(类型任意)。

  • 当尝试对两个表使用 - 运算符时,Lua会调用元表的 __sub 方法。

  • 示例:减法运算

Lua 复制代码
local t1 = {value = 10}
local t2 = {value = 5}
local mt = {
    __sub = function(a, b)
        return {value = a.value - b.value}
    end
}
setmetatable(t1, mt)
setmetatable(t2, mt)

local t3 = t1 - t2
print(t3.value) -- 输出 5

6. __mul

  • 功能:

    • 定义 * 运算符的行为。
  • 参数:

    • a:第一个操作数,可以是表、数字或其他 Lua 数据类型。

    • b:第二个操作数,可以是表、数字或其他 Lua 数据类型。

  • 返回值:乘法运算结果(类型任意)。

  • 当尝试对两个表使用 * 运算符时,Lua会调用元表的 __mul 方法。

  • 示例:乘法运算

Lua 复制代码
local t1 = {value = 4}
local t2 = {value = 5}
local mt = {
    __mul = function(a, b)
        return {value = a.value * b.value}
    end
}
setmetatable(t1, mt)
setmetatable(t2, mt)

local t3 = t1 * t2
print(t3.value) -- 输出 20

7. __div

  • 功能:

    • 定义 / 运算符的行为。
  • 参数:

    • a:第一个操作数,可以是表、数字或其他 Lua 数据类型。

    • b:第二个操作数,可以是表、数字或其他 Lua 数据类型。

  • 返回值:除法运算结果(类型任意)。

  • 当尝试对两个表使用 / 运算符时,Lua会调用元表的 __div 方法。

  • 示例:除法运算

Lua 复制代码
local t1 = {value = 10}
local t2 = {value = 2}
local mt = {
    __div = function(a, b)
        return {value = a.value / b.value}
    end
}
setmetatable(t1, mt)
setmetatable(t2, mt)

local t3 = t1 / t2
print(t3.value) -- 输出 5

8. __mod

  • 功能:

    • 定义 % 运算符的行为。
  • 参数:

    • a:第一个操作数,可以是表、数字或其他 Lua 数据类型。

    • b:第二个操作数,可以是表、数字或其他 Lua 数据类型。

  • 返回值:取模运算结果(类型任意)。

  • 当尝试对两个表使用 / 运算符时,Lua会调用元表的 __div 方法。

  • 示例:取模运算

Lua 复制代码
local t1 = {value = 10}
local t2 = {value = 3}
local mt = {
    __mod = function(a, b)
        return {value = a.value % b.value}
    end
}
setmetatable(t1, mt)
setmetatable(t2, mt)

local t3 = t1 % t2
print(t3.value) -- 输出 1

9. __concat

  • 功能:

    • 定义 .. 运算符的行为。
  • 参数:

    • a:第一个操作表。

    • b:第二个操作表。

  • 返回值:连接结果。

  • 当尝试对两个表使用 .. 运算符时,Lua会调用元表的 __concat 方法。

  • 示例:

Lua 复制代码
local t1 = {name = "Alice"}
local t2 = {name = "Bob"}
local mt = {
    __concat = function(a, b)
        return a.name .. " & " .. b.name
    end
}
setmetatable(t1, mt)
setmetatable(t2, mt)

print(t1 .. t2) -- 输出 "Alice & Bob"

10. __call

  • 功能:

    • 定义表被当作函数调用时的行为。
  • 参数:

    • 调用时传入的参数。
  • 返回值:调用结果。

  • 当表是函数并被调用时,Lua会调用元表的 __call 方法。

  • 示例:

Lua 复制代码
local t = {}
local mt = {
    __call = function(_, arg)
        return "Called with argument: " .. arg
    end
}
setmetatable(t, mt)

print(t("Hello")) -- 输出 "Called with argument: Hello"

11. __tostring

  • 功能:

    • 定义 tostring() 函数的输出行为。
  • 参数:

    • table:原始表。
  • 返回值:返回字符串表示。

  • 当表被放在 tostring() 内使用时,Lua会调用元表的 __tostring 方法。

  • 示例:

Lua 复制代码
local t = {name = "Alice"}
local mt = {
    __tostring = function(table)
        return "Name: " .. table.name
    end
}
setmetatable(t, mt)

print(tostring(t)) -- 输出 "Name: Alice"

12. __metatable

  • 功能:

    • 用于保护表元,防止外部访问或修改元表。
  • 参数:

    • 无参数。
  • 返回值:如果设置了 __metatable 则返回保护值。

  • 提供外部只读访问,防止元表被修改导致意外行为。

  • 在复杂项目中,保护核心逻辑不被破坏。

  • 示例:

Lua 复制代码
local t = {}
local mt = {
    __index = function(_, key)
        return "Default value for " .. key
    end,
    __metatable = "Protected"
}
setmetatable(t, mt)

print(t.foo)           -- 输出 "Default value for foo"
print(getmetatable(t)) -- 输出 "Protected"

-- 尝试修改元表
-- setmetatable(t, nil) -- 报错:cannot change protected metatable

12. 总结

元方法 功能 参数 返回值
__index 访问不存在的键 table,key 键的值
__newindex 拦截不存在的键的赋值 table,key,value
__add 重载加法运算符+ a,b 加法结果
__concat 重载字符串连接运算符.. a,b 连接结果
__call 定义表被调用时的行为 ... 调用结果
__tostring 自定义表的字符串表示 table 字符串
__metatable 保护元表 保护值或nil
  • 通过元方法,可以深度自定义Lua表的行为,从而实现更多高级的功能。
相关推荐
萧瑟余晖22 分钟前
Java深入解析篇三十六之分布式系统详解
java·开发语言·分布式
ZJU_统一阿萨姆1 小时前
【推理优化进阶】调度器的数学内核:排队论、SLO 与在线决策
开发语言·人工智能·语言模型·系统架构·vllm
SomeB1oody1 小时前
【RustyML入门】5.2. 分类指标
开发语言·后端·机器学习·rust·教程
ZJU_统一阿萨姆2 小时前
【推理优化进阶】图编译与运行时:动态形状、CUDA Graph 与内存规划
开发语言·人工智能·语言模型·架构·开源
@呱呱爱学习2 小时前
C++大成之路_ STL容器_ Vector
开发语言·c++
码匠许师傅2 小时前
【C++ 面试真题】19. 聊聊 C++ 的迭代器
开发语言·c++·面试
AI情绪识别开源2 小时前
检信ALLEMOTION VibrationAI 2.4.0 12维度情绪识别开源源代码
开发语言·python
Dovis(誓平步青云)2 小时前
DevEco Studio 6.1.1 Windows 安装实录:从下载校验到首次启动
android·开发语言·数据库·人工智能·windows·harmonyos
qeen872 小时前
【数据结构】红黑树的算法原理解析与实现
开发语言·数据结构·c++·算法·红黑树
Logintern093 小时前
什么时候应该用多进程什么时候用多线程呢?
开发语言·python