lua中的元表---metatable

Posted Flytiger1220

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lua中的元表---metatable相关的知识,希望对你有一定的参考价值。

什么是元表

元表像是一个“操作指南”,里面包含了一系列操作的解决方案,例如__index方法就是定义了这个表在索引失败的情况下该怎么办。

Lua 中 metatable 是一个普通的 table,但其主要有以下几个功能:

1.定义算术操作符和关系操作符的行为
2.为 Lua 函数库提供支持
3.控制对 table 的访问

1、Metatables 定义操作符行为

Metatable 能够被用于定义算术操作符和关系操作符的行为。例如:Lua 尝试对两个 table 进行加操作时,它会按顺序检查这两个 table 中是否有一个存在 metatable 并且这个 metatable 是否存在 __add 域,如果 Lua 检查到了这个 __add 域,那么会调用它,这个域被叫做 metamethod。

Lua 中每个 value 都可以有一个 metatable(在 Lua 5.0 只有 table 和 userdata 能够存在 metatable)。每个 table 和 userdata value 都有一个属于自己的 metatable,而其他每种类型的所有 value 共享一个属于本类型的 metatable。在 Lua 代码中,通过调用 setmetatable 来设置且只能设置 table 的 metatable,在 C/C++ 中调用 Lua C API 则可以设置所有 value 的 metatable。默认的情况下,string 类型有自己的 metatable,而其他类型则没有:

代码如下:

print(getmetatable('hi'))&nbsp;--&gt; table: 003C86B8<br>
print(getmetatable(10))&nbsp;&nbsp;--&gt; nil</div>

Metamethod 的参数为操作数(operands),例如:

<div class="codetitle"> 代码如下:</div>
<div class="codebody" id="code69472">
	<br>
	local mt = <br>
	function mt.__add(a, b)<br>
	&nbsp;&nbsp;&nbsp; return 'table + ' .. b<br>
	end<br>
	local t = <br>
	setmetatable(t, mt)<br>
	print(t + 1)</div>

每个算术操作符有对应的 metamethod:

+

__add

*

__mul

-

__sub

/

__div

-

__unm (for negation)

%

__mod

^

__pow

对于连接操作符有对应的 metamethod:__concat

同样,对于关系操作符也都有对应的 metamethod:

==

__eq

<

__lt

<=

__le

其他的关系操作符都是用上面三种表示:
a ~= b 表示为 not (a == b)
a > b 表示为 b < a
a >= b 表示为 b <= a

和算术运算符不同的是,关系运算符用于比较拥有不同的 metamethod(而非 metatable)的两个 value 时会产生错误,例外是比较运算符,拥有不同的 metamethod 的两个 value 比较的结果是 false。

不过要注意的是,在整数类型的比较中 a <= b 可以被转换为 not (b < a),但是如果某类型的所有元素并未适当排序,此条件则不一定成立。例如:浮点数中 NaN(Not a Number)表示一个未定义的值,NaN <= x 总是为 false 并且 x < NaN 也总为 false。

2、为 Lua 函数库提供支持

Lua 库可以定义和使用的 metamethod 来完成一些特定的操作,一个典型的例子是 Lua Base 库中 tostring 函数(print 函数会调用此函数进行输出)会检查并调用 __tostring metamethod:

代码如下:
local mt = 
mt.__tostring = function(t)
return '' .. table.concat(t, ', ') .. ''
end

local t = 1, 2, 3
print(t)
setmetatable(t, mt)
print(t)

另外一个例子是 setmetatable 和 getmetatable 函数,它们定义和使用了 __metatable 域。如果你希望设定的 value 的 metatable 不被修改,那么可以在 value 的 metatable 中设置 __metatable 域,getmetatable 将返回此域,而 setmetatable 则会产生一个错误:

代码如下:
mt.__metatable = "not your business"
local t = 
setmetatable(t, mt)
print(getmetatable(t)) --> not your business
setmetatable(t, )
 stdin:1: cannot change protected metatable

3、控制 table 的访问

__index metamethod

在我们访问 table 的不存在的域时,Lua 会尝试调用 __index metamethod。__index metamethod 接受两个参数 table 和 key:

代码如下:
local mt = 
mt.__index = function(table, key)
 print('table -- ' .. tostring(table)) -->table -- table: 0x149f6f0
 print('key -- ' .. key) -->key -- a
end

local t = 
setmetatable(t, mt)
local v = t.a

__index 域也可以是一个 table,那么 Lua 会尝试在 __index table 中访问对应的域:

代码如下:
local mt = 
mt.__index = 
a = 'Hello World'


local t = 
setmetatable(t, mt)
print(t.a) --> Hello World

__index元方法

很多人对此都有误解,这个误解是:如果A的元表是B,那么如果访问了一个A中不存在的成员,就会访问查找B中有没有这个成员。
而这个理解是完全错误的,实际上,即使将A的元表设置为B,而且B中也确实有这个成员,返回结果仍然会是nil,原因就是B的__index元方法没有赋值。别忘了我们之前说过的:“元表是一个操作指南”,定义了元表,只是有了操作指南,但不应该在操作指南里面去查找元素,而__index方法则是操作指南的索引失败时该怎么办。这么说有点绕。所以:

举个栗子

father = 
	house=1

son = 
	car=1

setmetatable(son, father) --把son的metatable设置为father
print(son.house)

输出的结果是nil,但如果把代码改为

father = 
	house=1

father.__index = father -- 把father的__index方法指向自己
son = 
	car=1

setmetatable(son, father)
print(son.house)

输出的结果为1,符合预期

这样一来,结合上例,来解释__index元方法的含义:

在上述例子中,访问son.house时,son中没有house这个成员,但Lua接着发现son有元表father,注意:此时,Lua并不是直接在father中找名为house的成员,而是调用father的__index方法,如果__index方法为nil,则返回nil,如果是一个表(上例中father的__index方法等于自己,就是这种情况),那么就到__index方法所指的这个表中查找名为house的成员,于是,最终找到了house成员。
注:__index方法除了可以是一个表,还可以是一个函数,如果是一个函数,__index方法被调用时将返回该函数的返回值。

到这里,总结一下Lua查找一个表元素时的规则,其实就是如下3个步骤:

1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表(操作指南),如果没有元表,返回nil,有元表则继续
3.判断元表(操作指南)中有没有关于索引失败的指南(即__index方法),如果没有(即__index方法为nil),则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值

以上是关于lua中的元表---metatable的主要内容,如果未能解决你的问题,请参考以下文章

Lua中的元表(metatable)元方法(metamethod)详解

如何简单易懂的理解lua的元表metatable

Lua 元表(Metatable)

lua的元表与元方法

Lua中的元表与元方法

Lua 的元表