元表是用来定义对table或userdata操做方式的表编程
local t1 = {1} local t2 = {2} local t3 = t1 + t2
咱们直接对两个table执行+运算,会报错函数
lua: /usercode/file.lua:3: attempt to perform arithmetic on local 't1' (a table value)
由于程序不知道如何对两个表执行+运行,这时候就须要经过元表来定义如何执行t1的+运算,有点相似于c语言中的运算符重载。lua
local mt = {} --定义mt.__add元方法(其实就是元表中一个特殊的索引值)为将两个表的元素合并后返回一个新表 mt.__add = function(t1,t2) local temp = {} for _,v in pairs(t1) do table.insert(temp,v) end for _,v in pairs(t2) do table.insert(temp,v) end return temp end local t1 = {1,2,3} local t2 = {2} --设置t1的元表为mt setmetatable(t1,mt) local t3 = t1 + t2 --输出t3 local st = "{" for _,v in pairs(t3) do st = st..v..", " end st = st.."}" print(st)
结果为:code
{1, 2, 3, 2, }
由于程序在执行t1+t2的时候,会去调用t1的元表mt的__add元方法进行计算。
具体的过程是:
1.查看t1是否有元表,如有,则查看t1的元表是否有__add元方法,如有则调用。
2.查看t2是否有元表,如有,则查看t2的元表是否有__add元方法,如有则调用。
3.若都没有则会报错。
因此说,咱们经过定义了t1元表的__add元方法,达到了让两个表经过+号来相加的效果orm
函数 | 描述 |
---|---|
__add | 运算符 + |
__sub | 运算符 - |
__mul | 运算符 * |
__ div | 运算符 / |
__mod | 运算符 % |
__unm | 运算符 -(取反) |
__concat | 运算符 .. |
__eq | 运算符 == |
__lt | 运算符 < |
__le | 运算符 <= |
__call | 当函数调用 |
__tostring | 转化为字符串 |
__index | 调用一个索引 |
__newindex | 给一个索引赋值 |
因为那几个运算符使用相似,因此就不单独说明了,接下来讲 __call, __tostring, __index, __newindex四个元方法。对象
__call可让table当作一个函数来使用。索引
local mt = {} --__call的第一参数是表本身 mt.__call = function(mytable,...) --输出全部参数 for _,v in ipairs{...} do print(v) end end t = {} setmetatable(t,mt) --将t看成一个函数调用 t(1,2,3)
结果:ip
1 2 3
__tostring能够修改table转化为字符串的行为字符串
local mt = {} --参数是表本身 mt.__tostring = function(t) local s = "{" for i,v in ipairs(t) do if i > 1 then s = s..", " end s = s..v end s = s .."}" return s end t = {1,2,3} --直接输出t print(t) --将t的元表设为mt setmetatable(t,mt) --输出t print(t)
结果:get
table: 0x14e2050 {1, 2, 3}
调用table的一个不存在的索引时,会使用到元表的__index元方法,和前几个元方法不一样,__index能够是一个函数也但是一个table。
做为函数:
将表和索引做为参数传入__index元方法,return一个返回值
local mt = {} --第一个参数是表本身,第二个参数是调用的索引 mt.__index = function(t,key) return "it is "..key end t = {1,2,3} --输出未定义的key索引,输出为nil print(t.key) setmetatable(t,mt) --设置元表后输出未定义的key索引,调用元表的__index函数,返回"it is key"输出 print(t.key)
结果:
nil it is key
做为table:
查找__index元方法表,如有该索引,则返回该索引对应的值,不然返回nil
local mt = {} mt.__index = {key = "it is key"} t = {1,2,3} --输出未定义的key索引,输出为nil print(t.key) setmetatable(t,mt) --输出表中未定义,但元表的__index中定义的key索引时,输出__index中的key索引值"it is key" print(t.key) --输出表中未定义,但元表的__index中也未定义的值时,输出为nil print(t.key2)
结果:
nil it is key nil
当为table中一个不存在的索引赋值时,会去调用元表中的__newindex元方法
做为函数
__newindex是一个函数时会将赋值语句中的表、索引、赋的值看成参数去调用。不对表进行改变
local mt = {} --第一个参数时表本身,第二个参数是索引,第三个参数是赋的值 mt.__newindex = function(t,index,value) print("index is "..index) print("value is "..value) end t = {key = "it is key"} setmetatable(t,mt) --输出表中已有索引key的值 print(t.key) --为表中不存在的newKey索引赋值,调用了元表的__newIndex元方法,输出了参数信息 t.newKey = 10 --表中的newKey索引值仍是空,上面看着是一个赋值操做,其实只是调用了__newIndex元方法,并无对t中的元素进行改动 print(t.newKey)
结果:
it is key index is newKey value is 10 nil
做为table
__newindex是一个table时,为t中不存在的索引赋值会将该索引和值赋到__newindex所指向的表中,不对原来的表进行改变。
local mt = {} --将__newindex元方法设置为一个空表newTable local newTable = {} mt.__newindex = newTable t = {} setmetatable(t,mt) print(t.newKey,newTable.newKey) --对t中不存在的索引进行负值时,因为t的元表中的__newindex元方法指向了一个表,因此并无对t中的索引进行赋值操做将,而是将__newindex所指向的newTable的newKey索引赋值为了"it is newKey" t.newKey = "it is newKey" print(t.newKey,newTable.newKey)
结果:
nil nil nil it is newKey
有时候咱们但愿直接改动或获取表中的值时,就须要rawget和rawset方法了。
rawget可让你直接获取到表中索引的实际值,而不经过元表的__index元方法。
local mt = {} mt.__index = {key = "it is key"} t = {} setmetatable(t,mt) print(t.key) --经过rawget直接获取t中的key索引 print(rawget(t,"key"))
结果:
it is key nil
rawset可让你直接为表中索引的赋值,而不经过元表的__newindex元方法。
local mt = {} local newTable = {} mt.__newindex = newTable t = {} setmetatable(t,mt) print(t.newKey,newTable.newKey) --经过rawset直接向t的newKey索引赋值 rawset(t,"newKey","it is newKey") print(t.newKey,newTable.newKey)
结果:
nil nil it is newKey nil
经过为table设置元表能够在lua中实现面向对象编程。
经过对userdata和元表能够实如今lua中对c中的结构进行面向对象式的访问。