教程 > Lua 教程 > Lua 基础 阅读:35

Lua 元表(Metatables)

元表是一个表,它有助于在键集和相关元方法的帮助下修改它所附加的表的行为。 这些元方法是强大的 Lua 功能,可以实现以下功能

  • 在表上向运算符更改/添加功能。
  • 当键在表中不可用时使用元表中的 __index 查找元表。

有两种重要的方法用于处理元表,包括

  • setmetatable(table,metatable) - 此方法用于设置表的元表。
  • getmetatable(table) - 此方法用于获取表的元表。

让我们首先看看如何将一个表设置为另一个表的元表。 如下所示。

mytable = {}
mymetatable = {}
setmetatable(mytable,mymetatable)

上面的代码可以用一行来表示,如下所示。

mytable = setmetatable({},{})

__index

当元表在表中不可用时,用于查找元表的简单示例如下所示。

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

print(mytable.key1,mytable.key2)

运行示例

当我们运行上述程序时,我们将得到以下输出。

value1 metatablevalue

让我们逐步解释上述示例中发生的情况。

  • 这里的表 mytable 是 {key1 = "value1"}
  • 为 mytable 设置了 Metatable,其中包含 __index 的函数,我们称之为元方法。
  • 元方法执行查找索引“key2”的简单工作,如果找到,则返回“metatablevalue”,否则返回对应索引的 mytable 值。

我们可以拥有上述程序的简化版本,如下所示。

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

__newindex

当我们将 __newindex 添加到 metatable 时,如果表中没有可用的键,则新键的行为将由元方法定义。 下面给出了一个简单的例子,当索引在主表中不可用时设置元表的索引。

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print(mytable.key1)

mytable.newkey = "new value 2"
print(mytable.newkey,mymetatable.newkey)

mytable.key1 = "new  value 1"
print(mytable.key1,mymetatable.newkey1)

运行示例

上述代码运行结果如下

value1
nil    new value 2
new  value 1    nil

我们可以在上面的程序中看到,如果主表中存在一个键,它只是更新它。 当主表中的键不可用时,它会将该键添加到元表中。

另一个使用 rawset 函数更新同一个表的例子如下所示。

mytable = setmetatable({key1 = "value1"}, {

   __newindex = function(mytable, key, value)
      rawset(mytable, key, "\""..value.."\"")
   end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)

运行示例

上述代码运行结果如下

new value    "4"

rawset 设置值而不使用元表的 __newindex。 类似地,有一个 rawget 可以在不使用 __index 的情况下获得值。


将运算符行为添加到表

使用 + 运算符组合两个表的简单示例如下所示

-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大键值函数 table_maxn,即计算表的元素个数
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)
    for i = 1, table_maxn(newtable) do
      table.insert(mytable, table_maxn(mytable)+1,newtable[i])
    end
    return mytable
  end
})

secondtable = {4,5,6}

mytable = mytable + secondtable
        for k,v in ipairs(mytable) do
print(k,v)
end

运行示例

上述代码结果如下所示

1      1
2      2
3      3
4      4
5      5
6      6

__add 键包含在元表中以添加运算符 + 的行为。 键和相应的运算符表如下所示。

序号 模式 描述
1 __add 对应的运算符 '+'.
2 __sub 对应的运算符 '-'.
3 __mul 对应的运算符 '*'.
4 __div 对应的运算符 '/'.
5 __mod 对应的运算符 '%'.
6 __unm 对应的运算符 '-'.
7 __concat 对应的运算符 '..'.
8 __eq 对应的运算符 '=='.
9 __lt 对应的运算符 '<'.
10 __le 对应的运算符 '<='.

__call 元方法

__call 元方法在 Lua 调用一个值时调用。以下实例演示了计算表中元素的和:

-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大键值函数 table_maxn,即计算表的元素个数
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- 定义元方法__call
mytable = setmetatable({10}, {
  __call = function(mytable, newtable)
        sum = 0
        for i = 1, table_maxn(mytable) do
                sum = sum + mytable[i]
        end
    for i = 1, table_maxn(newtable) do
                sum = sum + newtable[i]
        end
        return sum
  end
})
newtable = {10,20,30}
print(mytable(newtable))

运行示例

以上实例执行输出结果为:

70

__tostring 元方法

__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容:

mytable = setmetatable({ 10, 20, 30 }, {
  __tostring = function(mytable)
    sum = 0
    for k, v in pairs(mytable) do
                sum = sum + v
        end
    return "表所有元素的和为 " .. sum
  end
})
print(mytable)

运行示例

以上实例执行输出结果为:

表所有元素的和为 60

从本文中我们可以知道元表可以很好的简化我们的代码功能,所以了解 Lua 的元表,可以让我们写出更加简单优秀的 Lua 代码。

查看笔记

扫码一下
查看教程更方便