Lua元表理解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaixh_89/article/details/84301882

之前接触lua时看到了元表的使用,但是一直一知半解,借此机会对自己的理解做一下总结,方便日后回顾

元表本质上来说是一种用来存放元方法的table。我们可以通过对应的key来得到value值,作用就是修改一个值的行为(更确切的说,这是元方法的能力),需要注意的是,这种修改会覆盖掉原本该值可能存在的相应的预定义行为。

1. lua中的每个值都可以有一个元表,只是table和userdata可以有各自独立的元表,而其他类型的值则共享其类型所属的单一元表。
   lua代码中只能设置table的元表,至于其他类型值的元表只能通过C代码设置。
   多个table可以共享一个通用的元表,但是每个table只能拥有一个元表。 
我们称元表中的键为事件(event),称值为元方法(metamethod)。前述例子中的事件是"add",元方法是执行加法的函数。
可通过函数getmetatable查询任何值的元表。
可通过函数setmetatable替换表的元表

lua查找表中的元素时规则如下:

 1.在表中查找,如果找到,返回该元素,找不到则继续
  2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续
  3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值

例如下面代码

father = {
	house=1
}
son = {
	car=1
}
setmetatable(son, father) --把son的metatable设置为father
print(son.house)

返回为nil,如果改一下

father = {
	house=1
}
son = {
	car=1
}
setmetatable(son, father) --把son的metatable设置为father
father.__index = father
print(son.house)

返回为1,这是因为在son表中没有house字段,虽然father是son的元表,但是father没有元方法,即元方法没有指向father自己,因此找不到house字段,这也就解释通了为什么会经常在lua中这么写了

到这里,我们对元表有了最基本的认识,它更像是一个备用的查找表,如果a的元表是b,在a中查找不到的东西会尝试从b中继续找,当然,前提是b设置了元方法

__index元方法:

上面我们说道在b中查找a中不存在的值时,需要给b的元方法赋值,不然仍然会像上面的例子返回为nil,因此,元方法在这里像是一个a打开b表查找入口的钥匙,我们顺着上面的逻辑再理一下整个查找思路:

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

下面引用一个菜鸟教程的例子,很能说明问题

对指定的表设置元表:

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

以上代码也可以直接写成一行:

mytable = setmetatable({},{})

以下为返回对象元表:

getmetatable(mytable)                 -- 这回返回mymetatable

如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。

mytable = setmetatable({key1 = "value1"}, {
  __index = function(mytable, key)
    if key == "key2" then
      return "metatablevalue"
    else
      return nil
    end
  end
})

print(mytable.key1,mytable.key2)

实例输出结果为: value1    metatablevalue

实例解析:

  • mytable 表赋值为 {key1 = "value1"}

  • mytable 设置了元表,元方法为 __index。

  • 在mytable表中查找 key1,如果找到,返回该元素,找不到则继续。

  • 在mytable表中查找 key2,如果找到,返回 metatablevalue,找不到则继续。

  • 判断元表有没有__index方法,如果__index方法是一个函数,则调用该函数。

  • 元方法中查看是否传入 "key2" 键的参数(mytable.key2已设置),如果传入 "key2" 参数返回 "metatablevalue",否则返回 mytable 对应的键值。

我们可以将以上代码简单写成:

mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)

猜你喜欢

转载自blog.csdn.net/zhaixh_89/article/details/84301882
今日推荐