Lua 的元表
Posted 河乐不为
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lua 的元表相关的知识,希望对你有一定的参考价值。
简述
元表指的是 Lua 中的 MetaTable
,它提供了一种重定义任意一个 Lua 中对象或值默认行为的公开入口,让 Lua 也能像许多面向对象语言一样实现操作符或方法的重载。
原理解析
在 Lua 中,每种类型的数据都有其默认的操作,如:number 有加减操作,string 的拼接操作,function 的调用操作,这些都是该类型的默认操作,而我们可以通过修改其原来来实现对这些默认操作进行修改。
元表(MetaTable)和元方法
用来定义对 table 或 userdata 操作方式的表,例如通过元表来定义 table 相加 "+"
操作(类似与 C 语言中的运算符重载)
local mt =
-- 合并两个表
mt.__add = function(t1, t2)
local temp =
for _,v in pairs(t1) do
table.insert(temp,v)
end
for _,v in pairs(t2) do
table.insert(temp,v)
end
return temp
end
local tb1 = 1, 2
local tb2 = 3
-- 为 tb1 设置元表为 mt
setmetatable(tb1, mt)
local tb3 = tb1 + tb2
如上,当 tb1 + tb2 时,会调用 tb1 的元表中的 __add 元方法来计算结果。具体步骤如下:
-
检查 tb1 是否有元表,若有,则查看其元表中是否有 __add 元方法,若有则调用;
-
检查 tb2 是否有元表,若有,则查看其元表中是否有 __add 元方法,若有则调用;
-
若都没有,则会报错。
因此只需要 tb1 或 tb2 其中一个有元表且具有 __add 元方法即可。
元方法: 上述 __add
就叫做 元方法
,常见的元方法还有:
函数 | 描述 |
---|---|
__add | 运算符 + |
__sub | 运算符 - |
__mul | 运算符 * |
__ div | 运算符 / |
__mod | 运算符 % |
__unm | 运算符 -(取反) |
__concat | 运算符 .. |
__eq | 运算符 == |
__lt | 运算符 < |
__le | 运算符 <= |
__call | 当函数调用 |
__tostring | 转化为字符串 |
__index | 调用一个索引 |
__newindex | 给一个索引赋值 |
1. __index
元方法
-
作为函数时:将表与索引传入 __index 元方法,并 return 一个返回值
local mt = --第一个参数是表自己,第二个参数是调用的索引 mt.__index = function(t,key) return "it is "..key end t = 1,2,3 --输出未定义的key索引,输出为nil print(t.key) setmetatable(t,mt) --设置元表后输出未定义的key索引,调用元表的__index函数,返回"it is key"输出 print(t.key)
结果:
nil it is key
-
作为 table 时:查找 __index 元方法表,若有该索引,则返回该索引对应的值,否则返回 nil
local mt = mt.__index = key = "it is key" t = 1,2,3 --输出未定义的key索引,输出为nil print(t.key) setmetatable(t,mt) --输出表中未定义,但元表的__index中定义的key索引时,输出__index中的key索引值"it is key" print(t.key) --输出表中未定义,但元表的__index中也未定义的值时,输出为nil print(t.key2)
结果:
nil it is key nil
2. __newindex 元方法
当为 table 中一个不存在的索引赋值时,会调用元表中的 _newindex
元方法
-
作为函数时:会将赋值语句中的表、索引、赋的值当做参数去掉用(不对表进行改变)
local mt = --第一个参数时表自己,第二个参数是索引,第三个参数是赋的值 mt.__newindex = function(t,index,value) print("index is "..index) print("value is "..value) end t = key = "it is key" setmetatable(t,mt) --输出表中已有索引key的值 print(t.key) --为表中不存在的newKey索引赋值,调用了元表的__newIndex元方法,输出了参数信息 t.newKey = 10 --表中的newKey索引值还是空,上面看着是一个赋值操作,其实只是调用了__newIndex元方法,并没有对t中的元素进行改动 print(t.newKey)
结果:
it is key index is newKey value is 10 nil
-
作为 table 时:为原 table 中不存在的索引赋值会将该索引和值赋到 __newindex 所指向的表中,不对原 table 进行改变
local mt = --将__newindex元方法设置为一个空表newTable local newTable = mt.__newindex = newTable t = setmetatable(t,mt) print(t.newKey,newTable.newKey) --对t中不存在的索引进行负值时,由于t的元表中的__newindex元方法指向了一个表,所以并没有对t中的索引进行赋值操作将,而是将__newindex所指向的newTable的newKey索引赋值为了"it is newKey" t.newKey = "it is newKey" print(t.newKey,newTable.newKey)
结果:
nil nil nil it is newKey
3. rawget 和 rawset
-
rawget 可以直接获取表中索引的真实值,而不通过 __index 元方法
local mt = mt.__index = key = "it is key" t = setmetatable(t,mt) print(t.key) --通过rawget直接获取t中的key索引 print(rawget(t,"key"))
结果:
it is key nil
-
rawset 可以直接为表中索引赋值,而不通过 __newindex 元方法
local mt = local newTable = mt.__newindex = newTable t = setmetatable(t,mt) print(t.newKey,newTable.newKey) --通过rawset直接向t的newKey索引赋值 rawset(t,"newKey","it is newKey") print(t.newKey,newTable.newKey)
结果:
nil nil it is newKey nil
运用场景
-
通过为 table 设置元表,可以在 lua 中实现面向对象编程
-
通过对 userdata 和元表,可以实现 lua 中对 c 中的结构进行面向对象式的访问
参考:
以上是关于Lua 的元表的主要内容,如果未能解决你的问题,请参考以下文章