今天花了一些时间学习了Lua元表与元表方法,这里做个笔记,方便下次理解查看
引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等。但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作。
表和完整的用户数据具有独立的元表(尽管多个表和用户数据可共享元表);每种其他类型的所有值共享一个元表。所以,所有数字共享一个元表,字符串也是,等等。
元表的作用:通常可以使用元表来修改一个值的行为, 使其在面对一个非预定的操作时执行一个指定的操作。
元表可以控制对象的数学运算、顺序比较、连接、取长、和索引操作的行为。元表也能定义用户数据被垃圾收集时调用的函数。Lua给这些操作的每一个都关联了称为事件的特定键。当Lua对某值执行其中一个操作时,检查该值是否含有元表以及相应的事件。如果有,与该键关联的值(元方法)控制Lua如何完成操作。
获取元表和设置元表的方法
可通过函数getmetatable查询任何值的元表。
可通过函数setmetatable替换表的元表。不能从Lua中改变其他类型的元表(除了使用调试库);必须使用C API才能做到。
lua查找一个元素时的规则
1 在表中查找,如果找到,返回该元素,结束查找, 反之继续
2 判断该表是否有元表, 如果没有元表则直接返回nil, 反之继续
3 判断元表有没有__index有没有元表方法,如果__index方法为空,则返回nil, 如果__index为一个函数,则返回该函数的返回值, 如果__index是一个表,则重复1、2、3步骤
例子:
local A = {
a = 100
}
local B = {
b = 50
}
setmetatable(B, A)
print(B.a)
运行结果: [LUA-print]: nil
分析: 此时查找时步骤1未找到。执行步骤二,存在元表, 继续执行步骤三, 此时__index方法为空, 所有直接返回为nil
修改:
local A = {
a = 100
}
local B = {
b = 50
}
A.__index = A;
setmetatable(B, A)
print(B.a)
运行结果:[LUA-print]: 100
元方法:
我们称元表中的键为事件(event), 称值为元方法(metamethod). 上面的例子中的index就是一个元方法
每个操作的键是由其名字前缀两个下划线“__”的字符串;例如,操作“加(add)”的键是字符串"__add"。这些操作的语义通过一个Lua函数描述解释器如何执行操作作了更好的说明。
Lua中支持的元方法
metatable通过其包含的函数来给所挂接的table定义一些特殊的操作,包括:
算数类元方法:
__add: 定义所挂接table的加法操作
__mul: 定义乘法操作
__div: 定义除法操作
__sub: 定义减法操作
__mod: 定义取模操作
__pow:定义乘幂操作
__concat: 定义连接操作(".."运算符)
关系类元方法:
__eq: 定义等于操作
__lt: 定义小于操作
__le: 定义小于等于操作
__len: 定义取长度操作 即# 操作
__unm: 定义一元-操作, 即: -table的含义
__tostring: 定义当table作为tostring()函式之参数被呼叫时的行为(例如: print(table)时将呼叫tostring(table)作为输出结果)
__index: 定义当table中不存在的key值被试图获取时的行为
__newindex: 定义在table中产生新key值时的行为
创建只读的table
function readOnly(t)
local proxy = {};
local mt = {
__index = t,
__newindex = function(t, k, v)
error("attempt to update a read-only table", 2);
end
}
setmetatable(proxy, mt);
return proxy;
end
local weekdays = readOnly{"Mon", "Tue", "Wed", "Thu", "Fri", "sat", "sun"};
print(weekdays[3]);
weekdays[1] = "Monday"
运行结果:
[LUA-print]: Wed
test.lua:(19): attempt to update a read-only table
Stack Traceback:
[C]: in function 'error'