哈喽,好久没有做记录了,最近刚好有时间打算整理一些基础常用内容,先做一期关于Lua相关的内容热热身。如果内容有误,欢迎大家指出我会积极做出响应。
在Lua中,元表(metatable) 和 元方法(metamethod) 是一对密切相关的概念,用于扩展表的行为。元表是一个特殊的表,用来定义元方法,而元方法则是元表中的某些特定字段,用于自定义表的操作行为,这一章主要记录元表(metatable)。
1. 什么是Lua
- 在开始介绍元表之前,我们先大致了解Lua是什么。
- Lua 是一种可嵌入、轻量、快速、功能强大的脚本语言。
- Lua 很适合用于配置、脚本化、插件化和快速构造原型的场景。
- Lua 是一门轻量级脚本语言,其核心设计目标之一是简单而高效。
- Lua 体积小、启动速度快,在所有脚本引擎中,Lua 的速可以于说是最快的。
- Lua 运行依托于宿主语言可以是c++,c#,golang等,只需要实现Lua解释器。
2. Lua的元表
Lua中设计 元表的主要原因是为了提供一种灵活的机制。
元表 像是一个"操作指南",里面包含了一系列的解决方案。
- 元表 能够扩展表的行为,而无需改变Lua本身的简单性和动态性。
- 元表 是 Lua 中的普通表,可以附加到另一个表上,用于改变该表的行为。
- 元表 赋予了Lua用户自定义操作的能力,如重载操作符,拦截访问,模拟面向对象编程等。
例如,当对一个表执行特定操作(如算术运算、比较操作或访问不存在的键)时,Lua 会检查是否有 元表 以及是否定义了对应的 元方法。
2.1 设置元表
通过 setmetatable 和 getmetatable 可以设置和获取表的元表。
Lua
local t = {} -- 普通表
local mt = {} -- 元表
setmetatable(t, mt) -- 将元表附加到表 t 上
print(getmetatable(t)) -- 输出元表 mt
-
每个表可以有一个独立的元表。
-
轻量灵活:元表是普通的表,但它的字段有特殊的意义(元方法)。
-
动态行为扩展:元表提供了一种机制,可以在不改变表本身的情况下扩展表的行为。
2.2 原始元表
-
Lua中元表的设置只能针对table,其他类型都不能设置。
-
Lua只有string初始化了元表,而且是针对所有的字符串,其他的类型都为nil。
-
table的原始元表为nil,也就是没有设置元表,只能通过 setmetatable进行设置。
-
多个table可以共享一个table作为元表,也可以使用自己作为自己的元表,它自身也是table。
Lua
print("表的初始值", getmetatable({})) --> 表的初始值 nil
print("整型的初始值", getmetatable(10)) --> 整型的初始值 nil
print("浮点型的初始值", getmetatable(10.0)) --> 浮点型的初始值 nil
--- 通过打印可以看到两个字符串的元表是同一个
print("字符串的初始值", getmetatable("新年快乐")) --> 字符串的初始值 table: 0x600000b14640
print("字符串的初始值", getmetatable("Happy New Year")) --> 字符串的初始值 table: 0x600000b14640
print("布尔型的初始值", getmetatable(true)) --> 布尔型的初始值 nil
print("nil的初始值", getmetatable(nil)) --> nil的初始值 nil
function sayHello() end
print("函数的初始值", getmetatable(sayHello)) --> 函数的初始值 nil
2.3 setmetatable
-
为表设置一个元表。元表是一个特殊的表,可以用来扩展和改变 Lua 表的行为。
-
参数:
-
table:需要设置元表的目标表。
-
matetable:元表,要设置的元表,可以包含元方法(如
__index
、__add
等)。
-
-
返回值:返回被设置元表的表,也就是参数table。
-
给表设置元表
Lua
local t = {} -- 原始表
local mt = {} -- 元表
setmetatable(t, mt) -- 设置元表
print(getmetatable(t) == mt) -- 输出 true
-
设置操作符重载
- 通过设置元表中的元方法,扩展表的行为。
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
-
结合
__index
实现继承setmetatable
常用于为表设置__index
元方法,来实现默认值或继承。
Lua
local defaults = {name = "Default", age = 0}
local t = {}
local mt = {
__index = defaults
}
setmetatable(t, mt)
print(t.name) -- 输出 "Default"
print(t.age) -- 输出 0
-
注意事项
-
只能为表设置元表,如果为非表类型设置元表,将会报错。
-
同一个表只能有一个元表,之后调用 setmetatable会覆盖之前的元表。
-
2.4 getmetatable
-
获取表的元表。
-
如果表没有元表,或元表中设置了保护(
__metatable
),则返回nil
或保护值。
-
参数:
- table:需要获取元表的目标表。
-
返回值:
-
如果表有元表,返回元表。
-
如果没有元表,返回
nil
。 -
如果元表设置了
__metatable
字段,则返回该字段的值,而不是元表本身。
-
-
获取表的元表
Lua
local t = {}
local mt = {}
setmetatable(t, mt)
local retrieved = getmetatable(t)
print(retrieved == mt) -- 输出 true
-
保护元表
通过在元表中设置
__metatable
字段,可以防止外部获取或修改元表。
Lua
local t = {}
local mt = {}
setmetatable(t, mt)
mt.__metatable = "Protected"
print(getmetatable(t)) -- 输出 "Protected"
-- 尝试修改元表会报错
-- setmetatable(t, nil) -- 报错:cannot change protected metatable
2.5 setmetatable
与 getmetatable
的关系
-
setmetatable
用于设置元表。 -
getmetatable
用于获取元表。 -
两者通常一起使用,用于实现自定义行为。例如:
Lua
local t = {}
local mt = {
__index = function(_, key)
return "default value for " .. key
end
}
setmetatable(t, mt)
print(getmetatable(t)) -- 输出元表 mt
print(t.foo) -- 输出 "default value for foo"
以上基本就是Lua元表的相关介绍,下一章我会准备详细记录下与 元表 配合使用的 元方法。如果以上内容有偏差的地方欢迎各位大佬留言指出。