Skip to content

Instantly share code, notes, and snippets.

@tdp100
Created September 21, 2015 09:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tdp100/a8494a2f922f7b6bbeaa to your computer and use it in GitHub Desktop.
Save tdp100/a8494a2f922f7b6bbeaa to your computer and use it in GitHub Desktop.
Lua 的metatable以及支持的metamethod

Lua 的metatable以及支持的metamethod


metatable和metamethod能够用来定义Lua的默认操作, 比如默认场景下Lua是不支持两个table的+,-,*,/操作的,但如果给table定义了metatable,并且在metatable中定义了相关操作的method,那么table就可以支持这些操作了. Lua默认只有string 库中给string设置了metatable, 其它类型都没有metatable.

print(getmetatable("aasfas"))  --Table
print(getmetatable({})) --nil

metatable中支持的metamethod

注意: 随着Lua版本的不断演进, 其支持的metamethod在各个版本中也不一样, 请参考其官方文档

1. 算术metamethod

  • __add: +
  • __sub: -
  • __mul: *
  • __div: /
  • __unm: negation
  • __pow: exponentiation
  • __concat: the .. (concatenation) operation

不同metatable类型对象的操作时选择metamethod的方式如下:

if the first value has a metatable with an __add field, Lua uses this field as the metamethod, in- dependently of the second value; otherwise, if the second value has a metatable with an __add field, Lua uses this field as the metamethod; otherwise, Lua raises an error.

2. 逻辑metamethod

  • __eq (equal to),
  • __lt (less than),
  • __le (less than or equalto)

3. lib中定义的metamethod

  • __tostring
  • __metatable

If you set a __metatable field in the metatable, getmetatable will return the value of this field, whereas setmetatable will raise an error

4. 访问table的metamethod

  • __index

当access table时, 会触发Lua解释器调用table 的metamethod: __index, __index 可以是一个function, 也可以赋值为一个table.

当赋值为一个function时, 第一个参数是table 本身, 第二个参数是要访问的key

mt = {}
mt.__index = function(_, key) {
    ...
}
function new (o)
    setmetatable(o, mt)
    return o
end

当赋值为一个prototype table时, Lua解释器将重复访问prototype table中的key, 这种方式相当于实现对象的继存, 即如下:

prototype = {x=5, y=10, width=15, height=20}
mt.__index = prototype

local rect = new{x=10, y=20}
--rect.width  equivalent of prototype["width"]
--rect.x  解释器先访问rect中的x, 如果没有,再访问prototype table中的x

如果只想取table中的real value, 而不是调用__index, 可以调用:rawget

  • __newindex

当update table时, 会触发Lua解释器调用table的metamethod: __newindex. 可以赋一个function

mt.__newindex = function(t, k, v)
    ...
end

采用__index__newindex可以实现只读表的功能.

如果只想给table的real index赋值,而不调用__newindex, 可以调用:rawset

如果实现一个metatable被多个table共用?

local index = {}  -- create private index
local mt = {    -- create metatable
    __index = function (t, k)
        print("*access to element " .. tostring(k))
        return t[index][k]    -- access the original table
    end,
    __newindex = function (t, k, v)
        print("*update of element " .. tostring(k) .." to " .. tostring(v))
        t[index][k] = v   -- update original table
    end,
    __pairs = function (t)
        return function (t, k)
            return next(t[index], k)
        end, t
    end
}
function track (t)
    local proxy = {}
    proxy[index] = t
    setmetatable(proxy, mt)
    return proxy
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment