元表:对table操作进行扩展的表,元表里面填的是元方法,通过setmetatable(mytable,mymetatable)函数操作可以把表和元表进行绑定,这样表(mytable)就有了你自定义的功能
例如可以自定义下面俩个功能,示例在下面有说明:
设置元表函数:
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
可以直接写成一行
mytable = setmetatable({},{})
返回对象元表
getmetatable(mytable) -- 这回返回mymetatable
当访问的键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。
1.如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
mytable = {"C#","PHP","Java","Python","Lua"} --新建一个表
--元表
--查找不存在的key时调用__index方法
--匿名函数的第一个参数是mytable,参数二是mytable的key
mymetatable = {
__index = function(mytable,key)
if key >= 10 then
return "More than 10"
end
end
}
mytable =setmetatable(mytable,mymetatable) --设置元表
print(mytable[5])
print(mytable[8])
print(mytable[10])
输出结果
Lua
nil
More than 10
如果__index包含一个表(newtable)的话,当查找的值mytable不存在时会在另一个表(newtable)里面找,若都没有就返回nil
--新建一个表
mytable = {"C#","PHP","Java","Python","Lua"}
--新建另一个表
newtable = { }
newtable[7] = "html"
newtable[8] = "css"
newtable[9] = "ph"
--直接把newtable赋值给__index
mymetatable = {
__index = newtable
}
--设置元表
mytable =setmetatable(mytable,mymetatable)
print(mytable[5])
print(mytable[8])
print(mytable[10])
输出结果
Lua
css
nil
Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:
1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。
__newindex 元方法用来对表更新,__index则用来对表访问 。
当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
mytable = {"C#","PHP","Java","Python"}
mymetatable = {
__newindex = function(mytable,key,value)
print("更新了key为:"..key.."value为:"..value)
--添加新的key-value(这里注意:如果直接写 mytable[kye] = value 相当于在表里面又添加了一对键值对,导致重复调用,Lua提供rawset来添加新的值)
rawset (mytable,key,value)
end
}
mytable =setmetatable(mytable,mymetatable)
--修改已有的值
mytable[1] = "Lua"
--添加新的值
mytable[5] = "ABC"
print(mytable[1])
print(mytable[5])
输出结果
更新了key为:5value为:ABC
Lua
ABC
如果__newtable包含的是一个表(othertable)的话那么新添加的数据都会存放在(othertable)里面
mytable = {"C#","PHP","Java","Python"}
othertable = {}
mymetatable = {
__newindex = othertable
}
mytable =setmetatable(mytable,mymetatable)
mytable[1] = "Lua"
mytable[5] = "ABC"
print(mytable[1])
print(mytable[5])
print(othertable[5])
输出结果
Lua
nil
ABC
mytable = {"C#","PHP","Java","Python"}
--元表
mymetatable = {
__add = function(tab,newtab)
--获取tab的最大key
local maxIndex = 0;
for k,v in pairs(tab) do
if(k>maxIndex) then
maxIndex = k
end
end
--把表二的值添加到表一里面
for k,v in pairs(newtab) do
maxIndex = maxIndex+1
table.insert(tab,maxIndex,v)
end
return tab
end
}
--设置元表
mytable = setmetatable(mytable,mymetatable)
--新建一个表
newtable= {"ABC","Lua","C++"}
--俩表相加
mytable= mytable+newtable
--遍历相加后的结果
for k,v in pairs(mytable) do
print(k,v)
end
输出结果:
1 C#
2 PHP
3 Java
4 Python
5 ABC
6 Lua
7 C++
补充:
当有俩个table相加的时候,只要其中一个有定义元表即可,相加的顺序可以颠倒如上面的例子,mytable+newtable和newtable+mytable都可以执行只是输出顺序不同而已
当定义了__call元方法时,table可以当成函数来使用可以传递(多个)参数,返回参数
mytable = {"C#","PHP","Java","Python"}
mymetatable = {
--第一个参数是mytable表,其他的是函数的参数,参数可以是表
__call = function(tab,arg1,arg2)
print(arg1.." - " ..arg2)
return arg1*arg2,arg1/arg2
end
}
--设置元表
mytable = setmetatable(mytable,mymetatable)
a , b = mytable(10,5)
print(a)
print(b)
输出结果
10 - 5
50
2
mytable = {"C#","PHP","Java","Python"}
--输出mytable所有值value
mymetatable = {
__tostring = function(mytable)
local str = ""
for k,v in pairs(mytable) do
str = str..v.."-"
end
return str
end
}
mytable = setmetatable(mytable,mymetatable)
print(mytable)
执行输出结果为:
C#-PHP-Java-Python-
用逗号进行分割就行
mytable = {}
--元方法
mymetatable = {
__index = function(mytable,key)
--body
end,
__tostring = function(mytable)
--body
end
-- ... ...
}
mytable = setmetatable(mytable,mymetatable)