什么是Metatable
metatable是Lua中的重要概念,每一个table都可以加上metatable,以改变相应的table的行为。
Metatables举例
lo_table =lo_meta_table =(lo_table)
上边的代码也可以写成一行,如下所示
-- setmetatable函数的返回值,就是该函数的第一个参数lo_table = setmetatable({}, {})
创建复杂的元表变量
metatable可以包括任何东西,metatable特有的键一般以__
开头,例如__index
和__newindex
,它们的值一般是函数或其他table。
lo_table = setmetatable({}, {
__index = function(lo_table, key) if key == "foo" then return 0 else return table[key] end end})
__index
这是metatable最常用的键了。
当你通过键来访问table的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index
键。如果__index
包含一个表格,Lua会在表格中查找相应的键。
-- 创建元表变量lo_meta_table = { name = "蓝鸥" }-- 设置该元表变量作为关系变量的lo_table = setmetatable({}, { __index = lo_meta_table })-- 打印lo_table变量的姓名 蓝鸥print(lo_table.name)-- 打印lo_table变量年龄 nilprint(lo_table.age)
如果__index
包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
-- 创建元表变量lo_meta_table = {
name = "蓝鸥" ,
action = function ( param ) -- body if(param == "学生") then print("让教育回归本质") else print("让蓝鸥维护教育") end end}-- 设置该元表变量作为关系变量的lo_table = setmetatable({}, { __index = lo_meta_table })-- 打印lo_table变量的动作 让教育回归本质print(lo_table.action("学生"))-- 打印lo_table变量的动作 让蓝鸥维护教育print(lo_table.action("肖浩"))-- 打印lo_table变量年龄 nilprint(lo_table.age)
__newindex
类似__index
,__newindex
的值为函数或table,用于按键赋值的情况。
-- 创建元表变量lo_meta_table = {}-- 设置该元表变量作为关系变量的lo_table = setmetatable({}, { __newindex = lo_meta_table })-- 设置lo_table变量的name关键字的值lo_table.name = "蓝鸥"-- 打印lo_meta_table元表变量name关键字的值值print(lo_meta_table.name)-- 打印lo_table变量name关键字的值print(lo_table.name)
-- 创建元表变量lo_meta_table = {}-- 设置该元表变量作为关系变量的lo_table = setmetatable({}, { __newindex = function(t, key, value) if type(value) == "number" then rawset(t, key, value * value) else rawset(t, key, value) end end})-- 设置lo_table变量的name关键字的值lo_table.name = "蓝鸥"-- 设置lo_table变量的age关键字的值lo_table.age = 3-- 打印lo_meta_table元表变量name关键字的值值print(lo_meta_table.name)-- 打印lo_table变量name关键字的值print(lo_table.name)-- 打印lo_meta_table元表变量age关键字的值值print(lo_meta_table.age)-- 打印lo_table变量age关键字的值print(lo_table.age)
上面的代码中使用了rawget
和rawset
以避免死循环。使用这两个函数,可以避免Lua使用__index
和__newindex
。
运算符
利用metatable可以定义运算符,例如+
:
-- 创建重载+号行为的表变量lo_table = setmetatable({ 1, 2, 3 }, {
__add = function(lo_table, other)
new = {} -- 遍历元素加other for _, v in ipairs(lo_table)
do table.insert(new, v + other)
end return new end})-- 进行计算+lo_table = lo_table + 2-- 打印得到的结果print(lo_table[1])print(lo_table[2])print(lo_table[3])
和__index
、__newindex
不同,__mul
的值只能是函数。与__mul
类似的键有:
__add
(+)__sub
(-)__div
(/)__mod
(%)__unm
取负__concat
(..)__eq
(==)__lt
(<
)__le
(<=
)
__call
__call使得你可以像调用函数一样调用table
t = = (a + b + c) *, , , )
__tostring
最后讲下__tostring,它可以定义如何将一个table转换成字符串,经常和 print 配合使用,因为默认情况下,你打印table的时候会显示 table: 0x7f86f3d04d80 这样的代码
lo_table = setmetatable({ 1, 2, 3 }, {
__tostring = function(lo_table)
sum = 0 for _, v in pairs(lo_table)
do
sum = sum + v
end
return "计算的结果是: " .. sum end})-- prints out "计算的结果是: 6" print(lo_table)
创建一个简单的向量Vector类
Vector = {}
Vector.__index = Vectorfunction Vector.new(x, y) return setmetatable({ x = x or 0, y = y or 0 }, Vector)end-- __call关键字setmetatable(Vector, { __call = function(_, ...) return Vector.new(...) end })-- + 运算符function Vector:__add(other) -- ... local result = Vector(self.x + other.x,self.y + other.y) return resultend-- __tostring关键字function Vector:__tostring() -- body return "x: " .. self.x .. " y: " .. self.yenda = Vector.new(12, 10)
b = Vector(20, 11)
c = a + bprint(a)print(c)