(元表与元方法)
简介
在Lua中,元表(metatable)是一种特殊的表,用于控制其他表的行为。每个表可以关联一个元表,通过设置元表和元方法,可以修改表的一些默认行为。 元方法(metamethod)是一种特殊的函数,用于定义表的一些特殊操作。 元方法通过在元表中定义特定的字段来实现。例如,当表进行加法操作时,Lua会检查表的元表中是否定义了__add字段。如果定义了__add字段,Lua会调用该字段对应的函数来执行加法操作。
正文
元表
只有字符串才有默认的元表,其他类型需要手动添加
任何表都可以作为其他表的元表
---------1.初体验,设置元表,获取元表
t={}
t1={} --元表
print(getmetatable(t))
setmetatable(t,t1) --设置元表
print(getmetatable(t))
print(getmetatable("nihao"))
print(getmetatable("hello"))
print(getmetatable(10))
--输出
nil
table: 00000000006e9df0
table: 00000000006e9ef0
table: 00000000006e9ef0
nil
-----2.获取字符串默认的元表以及里面的元方法
tab = getmetatable("hello")
for index, value in pairs(tab) do
print(index,value) --元表
for key, value in pairs(value) do
print(key,value) --所有元方法
end
end
--输出
__index table: 0000000000ea9f30
rep function: 000000006849d270
format function: 000000006849eb30
char function: 000000006849d730
gsub function: 000000006849fe90
upper function: 000000006849d150
match function: 000000006849fe70
unpack function: 000000006849ddf0
reverse function: 000000006849d1e0
lower function: 000000006849d3d0
byte function: 000000006849f4a0
dump function: 000000006849f300
gmatch function: 000000006849d680
sub function: 000000006849f3a0
pack function: 000000006849e150
packsize function: 000000006849dcb0
find function: 000000006849fe80
len function: 000000006849cf10
元方法
表相关常用的元方法
__index
对应方法是索引符号。例如 a[10] 的 [ ]
如果查找表元素越界,那么就会调用这个元方法
local t = {"hello","ni","www"}
local metable={
__index = function (tab,index)
-- print(index) --这里是输出越界的索引值
if index>#tab then
return "越界了"
end
end
}
setmetatable(t,metable)
print(t[3])
print(t[4])
--输出
www
越界了
__newindex
如果对表新增一个值,那么就会调用这个元方法
1.其中要想再这个元方法里面给表赋值,只能使用rawset方法
2.如果__newindex方法内不做任何操作,则此表变成只读表
---1.常规用法
---只读表
local t = {"hello","ni","www"}
local metable={
__newindex =function (tab,index,value)
end
}
---1.1添加值
local t = {"hello","ni","www"}
local metable={
__newindex =function (tab,index,value)
print("添加了一个新值",index,value)
rawset(tab,index,value) --注意这个方法
end
}
setmetatable(t,metable)
print(t[3])
print(t[4])
t[4] = "aaaa"
print(t[4])
--输出
www
nil
添加了一个新值 4 aaaa
aaaa
---2.使用__newindex 控制添加的值
local myTable = {} -- 创建一个空表
local allowedTypes = {number = true, string = true} -- 只允许添加number和string类型的值
local allowedKeys = {foo = true, bar = true} -- 只允许添加键为foo和bar的值
-- 创建元表,并设置__newindex元方法
local metaTable = {
__newindex = function(tbl, key, value)
if not allowedTypes[type(value)] then -- 检查值的类型
error("Only values of type number or string are allowed.") -- 抛出错误,拒绝添加
end
if not allowedKeys[key] then -- 检查键的合法性
error("Only keys 'foo' and 'bar' are allowed.") -- 抛出错误,拒绝添加
end
rawset(tbl, key, value) -- 符合规则,允许添加
end,
}
setmetatable(myTable, metaTable) -- 将元表设置给表
myTable.foo = 42 -- 允许添加
print(myTable.foo) -- 输出 42
myTable.baz = "test" -- 错误: 只允许键为foo和bar的值
print(myTable.baz) -- 不会输出任何值
__len
对应的是 #tab 取长度
local myTable = {10,5,1,0} -- 创建一个空表
local metaTable = {
__len=function (tab)
local i = 0
for key, value in pairs(tab) do
i=i+1
end
print("len is ",i)
return i
end
}
setmetatable(myTable, metaTable) -- 将元表设置给表
print(#myTable)
--输出
len is 4
4
__call
对应的是小括号符号。 例如 table(arg),这里的( )
local mytable={10,5,0}
local metable ={
__call =function (tab,...)
local str = "["
str =str..table.concat({...},",")
str = str.."]"
for key, value in pairs({...}) do
print(key,value)
end
return str
end
}
setmetatable(mytable,metable)
print(mytable(99,15,16))
--输出
1 99
2 15
3 16
[99,15,16]
算术及关系运算 元方法
元方法 | 对应算术操作 |
---|---|
__add | 对应的运算符 '+'. |
__sub | 对应的运算符 '-'. |
__mul | 对应的运算符 '*'. |
__div | 对应的运算符 '/'. |
__mod | 对应的运算符 '%'. |
__pow | 对应的运算符 '^'幂指数 |
__unm | 对应的运算符 '-'取反 |
__concat | 对应的运算符 '..'连接 |
__eq | 对应的运算符 '=='. |
__lt | 对应的运算符 '<'. |
__le | 对应的运算符 '<='. |
__add
__eq
__len
__unm
综合案例
local myTable = {10,5,1,0} -- 创建一个空表
local metaTable = {
__add=function (tab,newtab)
local res ={}
for i = 1, #tab do
res[#res+1]=tab[i]
end
for i = 1, #newtab do
res[#res+1] = newtab[i]
end
return res
end,
__eq=function (tab,newtab)
if #tab ~= #newtab then
return false
end
for i = 1, #tab do
if tab[i]~=newtab[i] then
print(tab[i],",",newtab[i])
return false
end
end
return true
end,
__len=function (tab)
local i = 0
for key, value in pairs(tab) do
i=i+1
end
print("len is ",i)
return i
end,
__unm =function (tab)
local res ={}
for i = 1, #tab do
res[#res+1] = -tab[i]
end
return res
end
}
setmetatable(myTable, metaTable) -- 将元表设置给表
--[[ --__len
print(#myTable)
--]]
--[[ --__add
local newtab={}
local res = newtab+myTable
for index, value in pairs(res) do
print(index,value)
end
--]]
--[[ __eq
local newtab ={}
local res = newtab == myTable
print(res)
----------------------------
-- setmetatable(newtab,metaTable)
-- local emtab ={}
-- local re = emtab == newtab
-- print(re)
--]]
--[[ --__unm
print(myTable[1])
local ss=-myTable
print(ss[1])
--]]
库定义元方法
__tostring
比如在输出中自动转换为字符串形式就会调用这个
local mytable={10,5,1,0}
local metable={
__tostring =function (tab)
local res="["
res = res .. table.concat(tab,",")
res = res .. "]"
return res
end
}
setmetatable(mytable,metable)
print(mytable)
---------------------------------------
-- 这两个注释切换的时候要等2分钟才能有效
-- function ToStr(tab)
-- local res="["
-- res = res .. table.concat(tab,",")
-- res = res .. "]"
-- return res
-- end
-- local meta ={}
-- setmetatable(mytable,meta)
-- meta.__tostring = ToStr
-- print(mytable)
__pairs
当使用for循环使用 pairs 键值对 遍历表的时候调用该 元方法
-- 定义一个表和一个迭代器函数
local myTable = {10,5,0,1}
local function myPairs()
local i = 0
return function()
i = i + 1
if myTable[i] then
return i, myTable[i]
end
end
end
-- 创建一个元表并设置__pairs字段
local metatable = {
__pairs = function()
return myPairs()
end
}
-- 将元表设置为myTable的元表
setmetatable(myTable, metatable)
-- 使用pairs迭代myTable
for key, value in pairs(myTable) do
print(key, value)
end
--输出
1 10
2 5
3 0
4 1