在lua中,每个值都有其对应的操作,比如数值型有加减乘除等操作,字符串型有连接截取等操作,那么这些操作(加减乘除,连接截取等)定义在什么地方呢?定义在这些值默认的元表中,如果想修改这些操作(比如加法)就得修改其默认的元表,这样一来整型值所有的加法操作都被修改了。
table和userdata型没有默认元表,所以我们可以很容易修改其操作行为,比如对于table类型,我们是没有加法操作的,即两个table是不能够相加的,如果这时候想实现其相加操作怎么办呢,此时就可以为这两个table每一个设置一个元表,在其对应的元表中设置__add()函数, "__add" 是元表的键名(元表的键名也称之为事件(event),一般以 2 个下划线 "_" 开头), "__add" 函数是这个相加操作的元方法(metamethod)。对于值的操作,Lua 都将其关联上一个被称作事件的指定健,当 Lua 需要对一个值发起这些操作中的一个时, 它会去检查值的元表里是否有对应事件。如果有,则键名对应的值(元方法)将控制 Lua 怎样去执行这个操作,有人可能会有疑问,为什么相加操作用__add(),而不是其他,在此的解释是键名是固定写法,在lua中我们可以重定义的元方法有这些:
t1 = {
"a"
,
"b"
,
"c"
,
"d"
}
t2 = {
5
,
6
,
7
,
8
,
9
}
mt = {}
mt.__add =
function
(a, b)
local
tbl = {}
local
ret = {}
for k, v in pairs(a)
do
tbl[v] = true
end
for k, v in pairs(b)
do
tbl[v] = true
end
for k, v in pairs(tbl)
do
table.insert(ret, k)
end
return
ret
end
setmetatable
(t1, mt)
setmetatable
(t2, mt)
t = t1 + t2
for k, v in pairs(t)
do
print
(v)
end
在执行加法操作时(t = t1 + t2),
首先检查该+运算符的两个table操作数的元表中是否有相加操作的元方法,如果没有则会报错,否则执行。
上述是对执行加法操作的元方法的举例,其他的操作类似。除此之外还有几个非常重要的元方法需要注意:__index,__newindex等。
__index用来对表
进行访问:
当你通过键值对表进行访问的时候,如果该键没有值,则会查找对应的元表中是否存在该键对应的值,如果不存在则返回nil,否则返回__index对应的值。
Lua查找一个表元素时的规则,遵循如下3个步骤:
- 1.在表中查找,如果找到,返回该元素,找不到则继续
- 2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
- 3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值。
__newindex用于对表进行更新。
在对一个表中不存在的索引赋值时,不会直接把该值加到该表中,而是会把该值加入到该表的元表中。
mymetatable = {} mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable }) print(mytable.key1) mytable.newkey = "新值2" print(mytable.newkey,mymetatable.newkey) mytable.key1 = "新值1" print(mytable.key1,mymetatable.key1)以上输出结果为
value1 nil 新值2 新值1 nil以上实例中表设置了元方法 __newindex,在对新索引键(newkey)赋值时(mytable.newkey = "新值2"),会调用元方法,而不进行赋值。而如果对已存在的索引键(key1),则会进行赋值,而不调用元方法 __newindex。
以上只是对lua元表元方法的简单总结,如果想详细了解其用法的可以参考以下这两篇文章,个人认为写的比较好,并且本文一部分内容是借鉴以下两位的: