元表与元方法

      •  简介
      •  正文
      •  元表
      •  元方法
        •  表相关常用的元方法
          •  __index
          •  __newindex
          •  __len
          •  __call
        •  算术及关系运算 元方法
          •  __add
          •  __eq
          •  __len
          •  __unm
          •  综合案例
        •  库定义元方法
          •  __tostring
          •  __pairs

简介

  在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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

元方法

表相关常用的元方法
__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
越界了
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
__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) -- 不会输出任何值
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
__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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
__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]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
算术及关系运算 元方法
元方法对应算术操作
__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])
--]]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
库定义元方法
__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)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
__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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.