Lua中的metatable
Lua 中每个 值value(或者对象)都可以有一个 metatable(在 Lua 5.0 只有table和 userdata能够存在 metatable)。每个 table和userdata value 都有一个属于自己的 metatable,而其他每种类型的所有 value 共享一个属于本类型的metatable。
先用一个示例检查一下对象的metatable:
t = {}
print(getmetatable(t)) --> nil
print(getmetatable('a')) --> table: 0x7fce64c05000
print(getmetatable('b')) --> table: 0x7fce64c05000
mt = {}
setmetatable(t, mt)
print(getmetatable(t) == mt) --> true
上面的字符类型拥有相同的metatable。
做一下整体的说明:
Lua 中的每个值或对象都可以拥有一个 metatable。这个 metatable 就是一个原始的 Lua table ,它用来定义对象在特定操作下的行为。
一个metatable 可以控制一个对象做数学运算操作、比较操作、连接操作、取长度操作、取下标操作时的行为,
而且metatable 中还可以扩展,比如定义一个函数,让 userdata 作垃圾收集时调用它。
对于这些操作,Lua 都将其关联上一个被称作事件的指定键。
当 Lua 需要对一个值发起这些操作中的一个时,它会去检查值中 metatable 中是否有对应事件。
如果有的话,键名对应的值(称为metamethod元方法)将控制 Lua 怎样做这个操作。
metatable通过其包含的函数来给所挂接的table定义一些特殊的操作,包括下面的meta-method:
数学运算方法
- __add: 定义所挂接table的加法操作
- __mul: 定义乘法操作
- __div: 定义除法操作
- __sub: 定义减法操作
- __mod(a, b) –取模
- __pow(a, b) –乘幂
- __unm: 定义负操作, 即: -table的含义
- __concat: 定义连接操作(“..”运算符)
关系运算方法
- __eq(a, b) –相等
- __lt(a, b) –小于
- __le(a, b) –小于等于
工具方法
- __len(a) –长度
- __tostring: 定义当table作为tostring()函式之参数被呼叫时的行为(例如: print(table)时将呼叫tostring(table)作为输出结果)
- __call(a, …) –执行方法调用
类似setter/getter方法
- __index: 定义当table中不存在的key值被试图获取时的行为
- __newindex: 定义在table中产生新key值时的行为
metatable操作函数
getmetatable
获取一个对象的metatablesetmetatable
设置一个对象的metatable
示例
这是来自网友翻译的lua示例
Set = {} -- 存储对于集合的所有操作函数。
Set.mt = {} -- "metatable"。
-- 创建一个新的集合。
function Set.new (t)
local set = {} -- 新的集合。
setmetatable(set, Set.mt) -- 所有的集合共享同一个"metatable"。
-- "key-value",集合中的元素值-"true"。
for _, l in ipairs(t) do set[l] = true end
return set
end
-- 计算两个集合的并集。
function Set.union (a,b)
local res = Set.new{}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end
-- 计算两个集合的交集。
function Set.intersection (a,b)
local res = Set.new{}
for k in pairs(a) do
res[k] = b[k] -- 很聪明的方法,如果集合"b"中没有元素"k"的话就会返回"nil"。
end
return res
end
-- 我们规定"s1 - s2"相当于求"(s1 * s2)"在"s1"中的补集。
function Set.sub(a, b)
local res = Set.new{}
for k in pairs(a) do
if not b[k] then
res[k] = true
end
end
return res
end
-- 我们规定"s1 / s2",仅是打印一句话,以证明调用了正确的"metamethod"。
function Set.div(a, b)
local res = Set.new{"__div"}
return res
end
-- 我们规定"-s"仅是打印一句话,以证明调用了正确的"metamethod"。
function Set.unm(a)
local res = Set.new{"__unm"}
return res
end
-- 我们规定"s1 ^ s2"仅是打印一句话,以证明调用了正确的"metamethod"。
function Set.pow(a, b)
local res = Set.new{"__pow"}
return res
end
-- 将集合转换为字符串形式。
function Set.tostring (set)
local s = "{"
local sep = ""
for e in pairs(set) do
s = s .. sep .. e
sep = ", "
end
return s .. "}"
end
-- 打印集合。
function Set.print (s)
print(Set.tostring(s))
end
-- 定义meta-method
Set.mt.__add = Set.union -- 定义两个"table"相加的逻辑,并集。
Set.mt.__mul = Set.intersection -- 定义两个"table"相乘的逻辑,交集。
Set.mt.__sub = Set.sub -- 定义两个"table"相减的逻辑。
Set.mt.__div = Set.div -- 定义两个"table"相除的逻辑。
Set.mt.__unm = Set.unm -- 定义一个"table"取反的逻辑。
Set.mt.__pow = Set.pow -- 定义两个"table"相幂的逻辑。
s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
-- 两个集合共享同一个"metatable"。
print(getmetatable(s1) == getmetatable(s2)) --> true
Set.print(s1 + s2) --> {1, 10, 20, 30, 50}
Set.print(s1 * s2) --> {30}
Set.print(s1 - s2) --> {20, 50, 10}
Set.print(s1 / s2) --> {__div}
Set.print(-s2) --> {__unm}
Set.print(s1 ^ s2) --> {__pow}
__index metamethod控制 table 的访问
由于metamethod __index用于访问对象的属性,那么我们可以修改来添加访问控制。
在我们访问 table 的不存在的域时,Lua 会尝试调用 __index metamethod。__index metamethod 接受两个参数 table 和 key:
local mt = {}
function mt.__add(a, b)
return 'table + ' .. b
end
print ('--1--')
print("mt", mt)
local t = {}
print("meta-table origin: ", getmetatable(t)) -- nil
print ('--2--')
setmetatable(t, mt)
print("meta-table", getmetatable(t)) -- table
print(t + 1)
----
----
local mt = {}
mt.__index = function(table, key)
print('table -- ' .. tostring(table))
print('key -- ' .. key)
return mt.key
end
mt.__newindex = function(table, key, value)
print('table -- ' .. tostring(table))
print('key -- ' .. key)
print('value -- '..value)
mt.key = value;
end
local tt = {}
setmetatable(tt, mt)
print ('--3--')
local v = tt.a
print("v=", v) -- nil
print ('--4--')
tt.a = "a"
local va = mt.a;
print("v-a=",va)
print ('--5--')
local vb= tt.b;
print("v-b=",vb)
print ('--6--')
tt.b = "b"
local vbb = tt.b
print("v-b=",vb, vbb)
输出:
--1--
mt table: 0x7ff195c04490
meta-table origin: nil
--2--
meta-table table: 0x7ff195c04490
table + 1
--3--
table -- table: 0x7ff195c08550
key -- a
v= nil
--4--
table -- table: 0x7ff195c08550
key -- a
value -- a
v-a= nil
--5--
table -- table: 0x7ff195c08550
key -- b
v-b= a
--6--
table -- table: 0x7ff195c08550
key -- b
value -- b
table -- table: 0x7ff195c08550
key -- b
v-b= a b
本文深入讲解Lua中的metatable机制,包括metatable的基本概念、metatable的操作函数、metatable的使用示例及如何利用metatable实现对象属性的访问控制。

被折叠的 条评论
为什么被折叠?



