Lua中的元表Metatable
Metatable 元表
元表是table的一种特例 , 其用于定义对table或userdata操作方式 (如两个table的加法运算等).
例如 : 现需要对表tab1
, tab2
执行加法运算 (tab1 + tab2) . Lua将做出如下判断 :
- 查看
tab1
是否有元表 , 若有则查看tab1
的元表是否有__add
元方法 , 若有则调用 . - 查看
tab2
是否有元表 , 若有则查看tab2
的元表是否有__add
元方法 , 若有则调用 . - 若二者都没有对应的
__add
元方法 , 则报错 .
因此 , 通过给table设置元表 , 并为其元定义__add
元方法 , 即可实现该table的加法运算 .
设置元表 / 获取元表
setmetatable(table, metatable) - 设置元表
setmetatable(table, metatable)
设置 table
的元表为 metatable
, 并返回 table
.
- 若元表中存在
__metatable
键值 , 则函数setmetatable失败 .
getmetatable(object) - 获取元表
getmetatable(object)
获取 object
的元表并返回 .
元表的元方法
上例中__add
就是Lua中的一个原方法 , Lua中还有其他的原方法 , 可供实现并使用 .
常用元方法
元方法 | 描述 |
---|---|
__add | 运算符 + |
__sub | 运算符 - |
__mul | 运算符 * |
__div | 运算符 / |
__mod | 运算符 % |
__unm | 运算符 - (取反) |
__concat | 运算符 .. |
__eq | 运算符 == |
__it | 运算符 < |
__le | 运算符 <= |
__call | 当作函数调用 |
__tostring | 转化为字符串 |
__index | 调用一个索引 |
__newindex | 给一个索引赋值 |
__metatable | 用于设置访问元表的权限 |
__add元方法 与 其他运算符元方法
运算符元方法的使用方式都类似 , 以__add为例 :
__add 元方法使用示例 (Code/metatable/add.lua)
1 | local metaTab = {} -- 创建元表 |
输出
__call 元方法的使用
__call
可以让 table 当作一个函数来使用 .
注意 : __call
的第一个参数需要设置为表自身 .
_call示例 (Code/metatable/call.lua)
1 | local meteTab = {} -- 构造元表 |
输出
__tostring 元方法的使用
__tostring
元方法可以修改table , 将其转化为字符串(可以像字符串一样被print()输出) .
注意 : __tostring
的参数(唯一参数)为表自身
__tostring示例 (Code/metatable/tostring.lua)
1 | local metaTab = {} -- 构造元表 |
输出
__index 元方法的使用
当调用table的一个不存在的索引时 , 会使用到 __index
元方法 .
注意 : __index
可以是一个函数 , 也可以是一个table .
- 作为函数 : 将表和这个不存在的索引作为参数传入 , return 一个返回值 .
- 作为table : 调用不存在的索引时 , 将查找这个table , 若有该索引则返回对应的值 , 否则返回nil .
__index示例 (Code/metatable/index.lua)
1 | local metaTab = {} -- 构造元表 |
输出
__newindex 元方法的使用
为table中一个不存在的索引赋值时 , 将调用元表中的__newindex
元方法 .
注意 : __newindex
与 __index
一样 , 可以作为一个function也可以作为一个table .
- 作为function : 将赋值语句中的
表
,索引
,值
当作参数去调用 , 不对表进行改变 . - 作为table : 将该
索引
和值
赋到__newindex
所指向的表中 , 不对原有的表做出改变 .
__newindex示例 (Code/metatable/newindex.lua)
1 | local metaTab = {} -- 构造元表 |
输出
__metatable 的使用
__metatable
作为字符串出现 , 当一个表被设置为其他表的元表时 , 自动获得__metatable=nil
.
一个拥有__metatable
属性的表不可以被设置为其他表的元表 .
- 通过设置元表的
__metatable
为一个字符串 , 可以禁止访问该元表中的成员或修改元表 . - 当出现这些操作时 , 自动返回该字符串 .
__metatable 示例 (Code/metatable/metatable.lua)
1 | local metaTable = {} -- 构造元表 |
输出
rawget(table, index) 与 rawset(table, index, value)
rawget(table, index)
可以直接获取表table
中索引index
的实际值(nil) , 绕过__index
.rawset(table, index, value)
可以直接为表table
中的索引index
赋值(插入新元素) , 绕过__newindex
.
元表的使用场景
- 作为table的元表 : 通过table设置元表 , 可以实现Lua中的面向对象编程
- 作为userdata的元表 : 通过对userdata设置元表 , 可以实现Lua对C结果进行面向对象式的访问 .