lua(6)-元表(metatable)和元方法(meatmethod)
来源:互联网 发布:淘宝分享有礼活动 编辑:程序博客网 时间:2024/05/15 07:29
通常,Lua中的每个值都有一套预定义的操作集合。例如,可以将数字相加,可以连接字符串。但是我们无法将两个table相加,无法对函数作比较,也无法调用一个字符串。因此可以通过元表来修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定的操作。例如,假设a和b都是table,通过元表可以定义如何计算表达式a+b。当Lua试图将两个table相加时,它会先检查两者之一是否有元表,然后检查该元表中是否有一个叫__add的字段。如果Lua找到了该字段,就调用该字段对应的值。这个值也就是“元方法”,它应该是一个函数。
简单地说,元表和元方法就类似于C++、Java等语言中的操作符重载。
默认情况下,除了table和userdata类型,其他的类型默认共享一个元表,因此可以实现诸如以下的这些操作
local a = 1
local b = 2
c = a+b
或
local d ="s1"
local e ="s2"
local f = d..e
在lua代码中声明和定义一个变量的时候,不会为这个变量创建一个新的元表,因此如果想实现两个table相加或相减等操作,得为这两个table设置一个元表,同时赋予相加操作的元方法。例如,重载"+"操作符,使其能允许两个table相加,则需重写元表中的__add方法,重写了__add元方法后,当使用”+”操作符时,如果操作数经过tonumber为nil,如果不为nil,则调用原生的”+”号方法,如果为nil,则检查两个操作数中是否含有__add方法,如果有,则执行__add方法,否则抛出异常。除了算术类元方法__add,lua还提供了其余操作符号的元方法关键字,包括关系类元方法和库定义的元方法。
__sub符号"-"的元方法 (二元操作符,相减)
__mul符号"*"的元方法 (二元操作符,相乘)
__div符号"/"的元方法 (二元操作符,相除)
__mod符号"%"的元方法 (二元操作符,取余)
__pow符号"^"的元方法 (二元操作符,次幂)
__unm符号"-"的元方法(一元操作符,正负取反)
__concat符号".."的元方法 (二元操作符,连接)
__len符号"#"的元方法(一元操作符,取长度)
__eq符号"=="的元方法 (二元操作符,是否相等)
__lt符号"<"或">"的元方法 (二元操作符,小于)
__le符号"<="或">="的元方法 (二元操作符,小于等于)
__indextable元素的下标
__newindextable新分配元素的下标
__call调用一个变量
__tostring格式化为字符串
__metatable元表
__mode引用模式(用于设置一个值是否是弱引用)
(1)__add元方法。
__add元方法对应符号”+”。对于相加的两个操作数op1与op2,op1+op2会进行如下操作:
function add_event (op1, op2) local o1,o2 = tonumber(op1), tonumber(op2) if o1 ando2 then -- (是否op1和op2都可转为数字?) returno1 + o2 -- (这里的"+"代表原始的加法) else -- (如果有一个及以上的操作数不能转化为数字) --(获取操作数的__add元方法) localh = getbinhandler(op1, op2, "__add") ifh then --(调用这个__add元方法,并将两个操作数传递给这个元方法) return(h(op1, op2)) else -- (如果没有元方法,抛出异常) error(···) end endend
以下示例重载__add方法实现两个table相加。
输出
(2)__sub元方法。
__sub元方法对应符号”-”。
以下示例重载__sub方法实现两个table相减。
输出
__mul、__div、__mod等算术类元方法的实现与(1)和(2)相似。
(3)__unm元方法。
__unm元方法是lua中正负数取反的元方法,对应一元操作符”-”,调用__unm元方法的逻辑原型如下:
function unm_event (op) local o =tonumber(op) if othen -- (操作符是否是数字?) return-o -- (取反返回,这里的"-"是原始的取反方法) else -- (如果操作数不是数字) -- (获取__unm元方法) localh = metatable(op).__unm ifh then --(调用__unm元方法) return(h(op)) else -- (抛出异常) error(···) end endend
以下示例重载__unm方法实现对一个table取反。
输出
(4)__eq元方法。
__eq是lua中关系型的元方法,用于判断两个操作数是否相等,即”==”。lua提供的元方法字段中并没有"~="符号的元方法,因为lua代码被编译时将"~="转化成了not(a==b);同样的,a>b会转化为b<a,a>=b转化为b<=a。
关系型的元方法也会对传入元方法的多个元素进行自己的操作。
__eq示例判断两个table是否相等。
输出
(5)__tostring元方法。
以上的元方法都是基于操作符号,lua中还有基于关键字的元方法,这种元方法叫做库定义元方法。比如我们使用print(value)关键字的时候,总能将value的类型格式化成符合print关键字语法的类型,print关键字在使用时会在value的元表中查找__tostring元方法,类似于__tostring就是库定义元方法。
我们来对table进行库定义方法的修改
__tostring示例打印一个table。
输出
(6)__concat元方法。
__concat元方法是lua中字符串连接的方法,对应二元操作符”..”,调用__concat元方法的逻辑原型如下:
function concat_event (op1, op2) if(type(op1) == "string" or type(op1) == "number") and (type(op2)== "string" or type(op2) == "number") then returnop1 .. op2 -- (这里的".."是原始的字符串连接操作) else localh = getbinhandler(op1, op2, "__concat") ifh then return(h(op1, op2)) else error(···) end endend
以下示例重载__concat元方法实现连接两个table的字符串。
输出
(7)__len元方法。
__len元方法是lua中取操作符长度的方法,对应一元操作符”#”,调用__len元方法的逻辑原型如下:
function len_event (op) if type(op) == "string" then return strlen(op) -- (原始的取字符串长度) elseif type(op) == "table" then return #op -- (原始的取table长度) else local h = metatable(op).__len if h then return (h(op)) else error(···) end endend
(8)__lt元方法。
__lt元方法是lua中“小于”的方法,对应一元操作符”<”,调用__lt元方法的逻辑原型如下:
function lt_event (op1, op2) if type(op1) == "number" and type(op2) =="number" then return op1 < op2 -- (对数字进行原始的"<"操作) elseif type(op1) == "string" and type(op2) =="string" then return op1 < op2 -- (对字符串进行原始的"<操作") else local h = getcomphandler(op1, op2, "__lt") if h then return (h(op1, op2)) else error(···) end endend
以下示例重载__lt元方法实现两个数字table的大小比较。
输出
(9)__le元方法。
__le元方法是lua中“小于等于”的方法,对应一元操作符”<=”,其函数原型与重写方法与__lt类似。
(10)__index元方法。
__index元方法用于索引一个table的元素。对于table[key],如果table[key]为nil,则会调用__index元方法,如果__index返回的值为nin,table[key]才会返回nil。
调用__index元方法的逻辑原型如下:
function gettable_event (table, key) local h iftype(table) == "table" then localv = rawget(table, key) -- (用原始的方法获得table[key]值) ifv ~= nil then return v end h =metatable(table).__index --(如果table[key]为空,调用__index) ifh == nil then return nil end else h =metatable(table).__index ifh == nil then error(···) end end if type(h)== "function" then return(h(table, key)) -- (如果__index是function类型,调用它) elsereturn h[key] -- (如果__index是table类型,将索引__index[key]的值) endend
__index元方法可以是函数,也可以是一个table。以下示例一个面向对象的例子,这里__index是一个函数,假设有People这个类,People里面含有name、age这些变量,含有SayHello、New这些成员方法,创建People的两个实例对象XiaoMing和LiHua,然后调用这两个实例对象中不存在的name、age、SayHello、New等字段。
输出
当__index元方法是一个table的时候,table[key]如果为nil,则会去查找__index[key]。以下示例当__index是一个table的时候的用法。
输出
如果在访问一个table时,不想触发它的__index元方法,可以使用函数rawget(t,i)来获取table中元素的值。
输出
(11)__newindex元方法。
__newindex元方法与__index元方法类似,只是__index用于索引一个table的值,而__newindex用于table的赋值,也就是table[key] = value的时候,如果table[key]不存在,则会查找__newindex元方法,如果__newindex不为nil,则调用__newindex元方法。
调用__newindex元方法的逻辑原型如下:
function settable_event (table, key, value) local h iftype(table) == "table" then localv = rawget(table, key) ifv ~= nil then rawset(table, key, value); return end h =metatable(table).__newindex ifh == nil then rawset(table, key, value); return end else h =metatable(table).__newindex ifh == nil then error(···) end end if type(h)== "function" then h(table,key,value) -- (如果__newindex是function,调用它) elseh[key] = value -- (如果__newindex是table,索引__newindex[key]) endend
以下示例当__newindex为函数时的用法。
输出
(12)__call元方法。
__call元方法是“调用”的方法,对应符号”()”,当在一个对象的后面使用调用,lua首先会判断这个对象是否是function类型,如果是则按照执行函数的形式执行这个对象;如果这个对象不是function类型,则调用__call方法。
调用__call元方法的逻辑原型如下:
function function_event (func, ...) iftype(func) == "function" then returnfunc(...) -- (原始的调用) else localh = metatable(func).__call ifh then returnh(func, ...) else error(···) end endend
以下示例__call元方法的用法,实现当调用一个table时,打印它的参数。
输出
(13)__mode元方法。
__mode是lua用于实现table的弱引用的元方法(可以参考lua(5)-table(表)这篇文章),每一次lua的内存回收都会检测该table内是否含有__mode元方法,当一个table的__mode元方法被声明并定义后,内存回收将会清除table被标记为“垃圾”的对象。
以下示例__mode元方法的使用
输出
- lua(6)-元表(metatable)和元方法(meatmethod)
- Lua:元表(metatable)与元方法(meatmethod)
- Lua:元表(metatable)与元方法(meatmethod)
- Lua:元表(metatable)与元方法(meatmethod)
- Lua:元表(metatable)与元方法(meatmethod)
- Lua:元表(metatable)与元方法(meatmethod)
- Lua:元表(metatable)与元方法(meatmethod)
- Lua 元表(metatable)
- Lua-元表(Metatable)
- Lua学习笔记 第十三章 元表(metatable) 与元方法(meatmethod)
- lua学习笔记——元表(Metatable)
- LUA学习之第三篇----元表(metatable)
- Lua -- 元表(Metatable)
- Lua 元表(Metatable)
- Lua 元表(Metatable)
- Lua中的元表(metatable)与元方法(metamethod)
- Lua中的元表(metatable)、元方法(metamethod)详解
- Lua 元表(metatable)与元方法(metamethod)
- 初识jquery(一)
- 3.3V 和 5V电平转换电路分享
- iOS 开发 真机调试报错 An unknown error occurred
- java--类反射(一)
- 获取VolleyError的详细信息,方便和服务器后台定位错误
- lua(6)-元表(metatable)和元方法(meatmethod)
- Centos 更新python
- JVM的内存区域划
- VMware虚拟机克隆Linux系统引起的网卡问题
- 初识ajax
- 测试面试题整理
- HTML快速学习2
- Spring事务隔离级别和事务的传播属性
- 枚举