元表(metatable)
lua中元表是用来存储元方法的表。
我个人理解类似php中集合的概念但功能更多。
元表拥有强大的特性:控制索引和赋值行为,控制算术运算,控制比较操作,控制调用行为,控制长度操作,控制转换行为,控制迭代行为,控制元表的更改。
- 控制索引和赋值行为
__index: 当尝试访问一个表中不存在的键时,Lua 会检查该表的元表是否定义了 __index 属性。如果定义了,则Lua会尝试根据这个属性提供的值来查找键。
__newindex: 当尝试设置一个表中不存在的键时,Lua 会检查该表的元表是否定义了 __newindex 属性。如果定义了,则Lua会根据这个属性提供的值来处理这个赋值操作。 - 控制算术运算
__add, __sub, __mul, __div, __mod, __unm: 这些元方法允许你定义表和其他对象之间的算术运算行为。 - 控制比较操作
__eq, __lt, __le: 这些元方法允许你定义表之间的比较行为。 - 控制调用行为
__call: 允许你将表当作函数来调用。 - 控制长度操作
__len: 允许你定义表的长度(# 操作符)。 - 控制转换行为
__tostring, __concat: 这些元方法允许你在将表转换为字符串或与其他字符串拼接时定义行为。 - 控制迭代行为
__mode: 在Lua 5.2及更高版本中,此元方法允许你指定哪些字段应该使用next函数迭代时可见。 - 控制元表的更改
__metatable: 如果这个元方法存在,那么尝试更改一个表的元表时会调用这个方法。
设置元表演示
--
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
--简写
mytable = setmetatable({},{})
如果元表中有相同元数据,则可以进行加减操作
演示两个表相加操作
-- 自定义计算表中最大键值函数 table_maxn,即返回表最大键值
function table_maxn(t)
local mn = 0
for k, _ in pairs(t) do
if type(k) == "number" and k > mn then
mn = k
end
end
return mn
end
-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
__add = function(mytable, newtable)
local max_key_mytable = table_maxn(mytable)
for i = 1, table_maxn(newtable) do
table.insert(mytable, max_key_mytable + i, newtable[i])
end
return mytable
end
})
secondtable = {4, 5, 6}
mytable = mytable + secondtable
for k, v in ipairs(mytable) do
print(k, v)
end
--输出结果
1 1
2 2
3 3
4 4
5 5
6 6
其他:拓展展示使用元表来拓展表的功能
-- 创建一个表
local myTable = { name = "example", value = 42 }
-- 创建一个元表
local myMeta = {
__index = {
description = "This is an example table."
},
__tostring = function(t)
return string.format("Name: %s, Value: %d", t.name, t.value)
end,
__call = function(t, arg1, arg2)
print("Called with:", arg1, arg2)
end
}
-- 给myTable设置元表
setmetatable(myTable, myMeta)
-- 访问不存在的键
print(myTable.description) -- 输出: This is an example table.
-- 打印表
print(tostring(myTable)) -- 输出: Name: example, Value: 42
-- 调用表
myTable("Hello", "World") -- 输出: Called with: Hello World
在这个例子中,我们定义了一个元表 myMeta,它包含了几个元方法来改变 myTable 的行为。通过设置 myTable 的元表为 myMeta,我们可以扩展 myTable 的功能,如定义其描述、改变其字符串表示形式,以及使其可调用。
元表使得Lua中的表可以非常灵活,并且可以用来模拟面向对象编程中的类和继承的概念。通过适当的元表设计,你可以创建非常强大的数据结构和模式。
在Lua中,__index
和 __newindex
是元表(metatable)中的两个特殊方法,用于实现表的继承和动态扩展功能。
当一个表作为另一个表的元表时,这两个方法就变得非常重要。
__index元方法
__index是一个索引器,当尝试访问一个表中不存在的键时,Lua会检查该表的元表是否定义了__index属性。
如果定义了,则Lua会尝试根据这个属性提供的值来查找键。
- 如果 __index 是一个表,Lua会在那个表中查找键。
- 如果 __index 是一个函数,Lua会调用该函数,并将表以及键作为参数传递给这个函数。
示例
local parent = { a = 1, b = 2 }
local child = {}
-- 设置parent为child的元表
setmetatable(child, { __index = parent })
print(child.a) -- 输出: 1
print(child.b) -- 输出: 2
Lua 查找一个表元素时的规则:
- 在表中查找,如果找到,返回该元素,找不到则继续
- 判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
- 判断元表有没有__index方法,如果__index方法为nil,则返回nil;
如果__index方法是一个表,则重复1、2、3;
如果__index方法是一个函数,则返回该函数的返回值。
__newindex元方法
__newindex与__index类似,但它是在尝试设置一个表中不存在的键时触发。
如果设置了__newindex,Lua会根据它的值决定如何处理这个赋值操作。
- 如果 __newindex是一个表,Lua会尝试在那个表中设置键的值。
- 如果 __newindex是一个函数,Lua会调用该函数,并将表、键和要设置的值作为参数传递给这个函数。
示例:假设我们想要记录每次对 child 表进行赋值的操作
local parent = { a = 1, b = 2 }
local meta = {
__newindex = function(t, key, value)
print("Setting " .. key .. " to " .. value)
t[key] = value
end
}
setmetatable(parent, meta)
parent.c = 3 -- 输出: Setting c to 3
print(parent.c) -- 输出: 3
--在这个例子中,当我们尝试给parent表添加一个新的键c时,
--__newindex定义的函数被调用,并且打印出一条消息,同时仍然允许赋值操作。
总结:
- __index 用于处理读取不存在的键时的行为。
- __newindex 用于处理写入不存在的键时的行为。
评论