元表

举个例子,在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。

那如何计算两个table的相加操作a+b?

local t1 = {1,2,3}
local t2 = {4,5,6}

local t3 = t1 + t2   ---->  # 报错,并不会得到:{1,2,3,4,5,6}

这种类似的需求,lua 提供了元表(Metatable)和元方法,允许我们改变table的行为,每个行为关联了对应的元方法。

1)setmetatable(table,metatable): 对指定table设置元表(metatable),

如果元表(metatable)中存在__metatable键值,setmetatable会失败 。

2)getmetatable(table): 返回对象的元表(metatable)。

mytable = {}                          -- 普通表 
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表 

等价于:

mytable = setmetatable({},{})

返回对象元表:

getmetatable(mytable)                 -- 返回mymetatable

元方法的命名都是以 __ 两个下划线开头。

一)__index 元方法

对表读取索引一个元方法

这是 metatable 最常用的键。

当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable) 中的__index元方法。__index是一个表,Lua会在表格中查找相应的键。

t.foo为什么会输出2,就是因为我们重写了__index索引的重载,lua在执行中如果t中没有foo,就会在他的元表中__index中去找,__index等于other,就输出2。

如果我们把t设置一下foo的值为3,在看看结果

如果__index赋值为一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。

__index元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。

分析:

总结:lua对表索引取值的步骤

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

  • 1.在表中查找,如果找到,返回该元素,找不到则继续步骤2

  • 2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续步骤3。

  • 3.判断元表有没有__index元方法,如果__index方法为nil,则返回nil;

    • 如果__index方法是一个表,则重复1、2、3;

    • 如果__index方法是一个函数,则执行该函数,得到返回值。

二)__newindex 元方法

__newindex元方法用来对表更新,__index则用来对表访问 。

当你给表进行索引进行赋值,但此索引不存在;lua会查找是否有元表,有元表就会查找__newindex元方法是否存在:如果存在则调用__newindex的值进行执行操作,但不会对原表进行赋值操作。

以下实例演示了__newindex元方法的应用:

结果:

以上实例中表设置了元方法__newindex

在对新索引键(newkey)赋值时(mytable.newkey = "新值2"),会调用元方法,而对mytable原表不进行赋值。

而对mytable已存在的索引键(key1),则会进行赋值,而不调用元方法__newindex

如果我们要对原来的table进行赋值,那我们就可以用rawset;__newindex函数会传三个参数,

结果:

key2原本是不再mytable表中的,通过元方法__newindex中函数使用了rawset,就可以对原table进行赋值。

三)为表添加操作符“+”

我们这里定义“+”这元方法,把它定义为两个table相连;如

那我们如何写元表? “+”对应的元方法为__add

这样我们就实现了两个table相加

以下是我们的操作符对应关系

四)__call元方法

__call元方法在 Lua 调用一个变量时调用。以下实例演示了计算两个表中所有值相加的和:类似的 t();类似函数调用

五)__tostring 元方法

__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容,把表中所有的元素相加输出:

六)点号与冒号操作符的区别

结果

冒号操作会带入一个 self 参数,用来代表 自己。而点号操作,只是 内容 的展开。

在函数定义时,使用冒号将默认接收一个 self 参数,而使用点号则需要显式传入 self 参数。

等价于

注意:冒号的操作,只有当变量是类对象时才需要。

资料

Lua中元方法__call的使用

lua(6)-元表(metatable)和元方法(meatmethod)

Lua元表——普通表(__index,__newindex,__call,__add,__tostring,.....等等)

Last updated

Was this helpful?