lua元表和元方法

lua中提供的元表(metatable)与元方法(metamethod)是一种非常重要的语法,metatable主要用于做一些类似于C++重载操作符式的功能。元表与元方法会影响table的访问行为。

元表的由来

Lua2.1灵活语义问世,极大的增加了Lua的表达能力,从此,灵活语义就变成了Lua的标志。

灵活语义的一个目标是允许table作为对象和类的基础。为了实现这个目标,需要实现table的继承。另一个目标是将userdata变成应用数据的天然代理,可以作为函数参数而不只是一个句柄。userdata希望能够索引,就好像他们只是一个table,可供调用他们身上的方法。所以fallback机制的实现,让Lua把未定义行为交给程序员处理,而不是直接在语言本身实现这些特性。

Lua2.1提供了fallback机制,支持以下行为:table索引,算术操作符,字符串拼接,顺序比较,函数调用。当这些操作应用到“错误”的类型上,对应的fallback就会被调用到,允许程序员决定Lua如何处理。table索引fallback允许userdata和其它值类型表现的跟表一样。定义当Key不在table时的fallback,从而实现多种形式的继承(通过委托)。为了完善面向对象编程,添加了两个语法糖:function a:foo(…)就好比function a.foo(self,…)一样,以及a:foo(…)作为a.foo(a, …)的语法糖。

Lua2.1里引入的fallback机制,可以很好的支持灵活扩展的语义,但这个机制是全局的:每个事件只有一个钩子。这让共享或重用代码变的很艰难,因为同一事件的fallback在模块里只能定义一次,不能共存。Lua 3.0解决了fallback冲突问题。fallback替换为tag方法:钩子是以(event, tag)的形式挂在字典里的。Tags是在Lua2.1引入的整数标签,可以附在userdata上。最初的动机是希望同类的C对象,在Lua里都有相同的tag(不过,Lua没有强迫要对Tag提供解释)。Lua3.0里对所有值类型提供了tag支持,以支持对应的tag方法。

标记方法机制工作的很好,一直存续到Lua 5.0为止。在Lua 5.0实现了元表和元方法来取代标记和标记方法。元表只是普通的Lua table,所以可以用Lua直接操作,不需要特殊函数。就像标记一样,元表可以用来表示userdatatable的用户定义类型:所有“同类”对象应该共享同一个元表。不像标记,元表和他们的内容会在所有引用消失后自动被回收掉。(相反,标记和标记方法会等到程序结束才会被回收。)元表的引入同时简化了实现:标记方法需要在Lua核心代码里添加特殊的私有表示方法,元表主要就是标准的table机制。
下面的代码展示了Lua 5.0里,继承是如何实现的。index元方法取代了index标记,元表里则是用__index域来表示。代码通过将b的元表里的__index域指向a,实现了b继承a。(一般情况下,index元方法都是函数,但允许它设为table,以直接支持简单的委托继承。)

1
2
3
4
a=Window{x=100, y=200, color="red"}
b=Window{x=300, y=400}
setmetatable(b, {__index = a})
print(b.color) —>red

元方法

Lua中有一个元表,也就是上面说的metatable,我们可以通过元表来修改一个值得行为,使其在面对一个非预定义的操作时执行一个指定的操作。比如,现在有两个table类型的变量ab,我们可以通过metatable定义如何计算表达式a+b
我们是使用getmetatable来获取一个tableuserdata类型变量的元表,当创建新的table变量时,使用getmetatable去获得元表,将返回nil;同理,我们也可以使用setmetatable去设置一个tableuserdata类型变量的元表。
table中,我可以重新定义的元方法有以下几个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__add(a, b) --加法
__sub(a, b) --减法
__mul(a, b) --乘法
__div(a, b) --除法
__mod(a, b) --取模
__pow(a, b) --乘幂
__unm(a) --相反数
__concat(a, b) --连接
__len(a) --长度
__eq(a, b) --相等
__lt(a, b) --小于
__le(a, b) --小于等于
__index(a, b) --索引查询
__newindex(a, b, c) --索引更新(PS:不懂的话,后面会有讲)
__call(a, ...) --执行方法调用
__tostring(a) --字符串输出
__metatable --保护元表

元表的实现

reference

《The evolution of Lua》


Tierra tan rica en belleza, que incontables héroes la ho.