Lua中的metatable

Lua中的metatable

Lua 中每个 值value(或者对象)都可以有一个 metatable(在 Lua 5.0 只有tableuserdata能够存在 metatable)。每个 tableuserdata 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
    获取一个对象的metatable

  • setmetatable
    设置一个对象的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)