Lua之metatable

Lua之metatable

一、元表

Lua中的每个值都有一套预定义的操作集合,也可以通过metatable(元表)来定义一个值的行为,metatable包含了一组meatmethod(元方法)。

Lua中的每个值都有一个metatable,table和userdata可以有各自独立的metatable,而其他类型的值则共享其类型所属的单一metatable。

在Lua代码中,只能设置table的metatable,若要设置其他类型的值的metatable,必须通过C代码来完成。


function


description


getmetatable(t)


获取t的metatable


setmetatable(t, m)


设置t的metatable为m

Lua标准库中只有string库有默认metatable,其它类型在默认情况下都没有metatable。

getmetatable(‘ab‘)         -- table: 0x1b684b0
getmetatable(123)          -- nil
getmetatable({1})          -- nil

下面是一个例子,用table模拟集合操作,并利用metatable实现了交集、并集运算:

Set = {}

local mt = {}       -- metatable

function Set.new(l)
    local set = {}
    setmetatable(set, mt)       -- set metatable for all "set" type
    for _,v in ipairs(l) do set[v] = 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] end
    return res
end

function Set.sub(a, b)
    for k in pairs(a) do
        if not b[k] then return false end
    end
    return true
end

function Set.tostring(set)
    local l = {}
    for e in pairs(set) do
        l[#l + 1] = e
    end
    return "{" .. table.concat(l, ",") .. "}"
end

mt.__add = Set.union
mt.__mul = Set.intersection
mt.__le =  Set.sub
mt.__tostring = Set.tostring

s1 = Set.new{10,20,30,50}
s2 = Set.new{30, 1}
s3 = Set.new{20,50}

print(getmetatable(s1))     -- 0x12e6cb0
print(getmetatable(s2))     -- 0x12e6cb0

print(s1+s2)     -- {1,30,10,50,20}
print(s1*s2)     -- {30}

print(s3<=s1)    -- true

在metatable中,每种算术操作都有对应的字段名,除了上述的__add和__mul外,还有其它的一些字段,如下表:


metamethods


description


__add


+


__sub


-


__mul


*


__div


/


__unm



__pow



__mod



__concat


定义连接行为


__eq


==


__lt


<


__le


<=


__tostring


供print函数调用


__metatable


如果设置了该字段,将禁止查看、修改该元表

metatable中关系运算符只支持 ==、< 、<=,Lua会将 a~=b转化为 not (a==b),将a>b转化为b<a,将a>=b转化为b<=a。

当一个算术表达式中混合了具有不同metatable的值时,例如

s = Set.new{1,2,3}
s = 8 + s

Lua会按照如下步骤查找metatable

1、如果第一个值有metatable,并且有__add字段,那么就使用该字段作为meatmethod;
2、如果第一个值没有metatable,但第二个值有metatable,并且有__add字段,那么就使用该字段作为meatmethod;
3、如果两个值都没有metatable,Lua就引发一个错误。

因此,上例会调用 Set.union,但在Set.union 函数内部会发生错误!

与算术类的meatmethod不同,关系类的meatmethod不能应用于混合的类型。如果试图将一个字符串与一个数字作顺序比较,Lua会引发一个错误。同样,如果试图比较两个具有不同meatmethod的对象,Lua也会引发一个错误。

注意,等于比较永远不会引发错误。如果两个对象具有不同的meatmethod,直接返回false。


二、访问table的元方法

当访问table中一个不存在的字段时,会调用其metatable的__index元方法,如果没有这个元方法,返回nil。否则,就由__index元方法提供查找结果。

Window = {}
Window.prototype = {x=0, y=0, width=100, height=100}
Window.mt = {}

Window.mt.__index = function(table, key)
    return Window.prototype[key]
    end 

function Window.new(o)
    setmetatable(o, Window.mt)
    return o
end

w1 = Window.new({x=10, y=20, width=37})
print(w1.width)     -- 37

w2 = Window.new({x=10, y=20})
print(w2.width)     -- 100

 print(rawget(w1, ‘width‘)) -- 37
 print(rawget(w2, ‘width‘)) -- nil

注意:如果不想使用元表,可以使用rawget方法,它对table进行一次不考虑元表的简单访问。

在Lua中,将__index元方法用于继承是很普遍的方法,__index元方法不必一定是函数,也可以是一个table:
当__index是一个函数时,以table和不存在的key作为参数来调用该函数;
当__index是一个table时,Lua就以相同的方式来重新访问这个table,因此,上面的例子的__index元方法也可以如下:

Window.mt.__index = Window.prototype

当对table中一个不存在的索引赋值时,会查找__newindex元方法,如果有这个元方法,就调用它,而不是执行赋值。

如果__newindex是一个table,就在此table中执行赋值,而不是对原来的table。

类似rawget,调用rawset(t,k,v),可以不涉及任何元方法而直接设置t[k]=v。

组合使用__index和__newindex元方法可以实现一些强大的功能,比如:只读table,具有默认值的table、面向对象编程中的继承等。

例子一、具有默认值的table:

local mt = {__index=function() return t.___ end}

function setDefault(t,d)
    t.___ = d
    setmetatable(t, mt)
end 

tab = {x=10, y=20}
print(tab.x, tab.z) --10, nil

setDefault(tab, 0)
print(tab.x, tab.z) -- 10, 0

注意: __index和__newindex都是在table中没有所需访问的索引时才发挥作用的。

例子二,通过一个空table来代理对原table的访问,从而来跟踪对table的访问过程:

t = {1,3,4}

local _t = t

t = {} -- proxy

local mt = {
    __index = function(t,k) print("access to element " .. tostring(k)) return _t[k] end,
    __newindex = function(t,k,v) print("update of element " .. tostring(k) .. " to " .. tostring(v)) _t[k] = v end,
}       

setmetatable(t, mt)

t[1] = "hello"
print(t[1])

输出为:

update of element 1 to hello
access to element 1
hello

例子三,实现一个只读table:

function readOnly(t)

    local proxy = {}
    local mt = {
        __index = t,
        __newindex = function(t,k,v) error("attempt to update a read-only table", 2) end
    }
    setmetatable(proxy, mt)
    return proxy
end 

days = readOnly{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
print(days[1])
days[2] = "No"    -- error
时间: 2024-08-02 13:30:07

Lua之metatable的相关文章

lua元表(metatable)和元方法(metamethod)

元表概念: 引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等.但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作. 访问机制:一般的元方法都只针对Lua的核心,也就是一个虚拟机.它会检测一个操作中的值是否有元表,这些元表是否定义了关于次操作的元方法.例如两个table相加,先检查两者之一是否有元表,之后检查是否有一个叫“__add”的字段,若找到,则调用对应的值.“__add”等即时字段,其对应的值(往往是一个函数或是table

lua 14 metatable (类似操作符重载)

转自:http://www.runoob.com/lua/lua-metatables.html 感性认识: "Lua中Metatable这个概念, 国内将他翻译为元表. 元表为重定义Lua中任意一个对象(值)的默认行为提供了一种公开入口. 如同许多OO语言的操作符重载或方法重载. Metatable能够为我们带来非常灵活的编程方式. 具体的说, Lua中每种类型的值都有都有他的默认操作方式, 如, 数字可以做加减乘除等操作, 字符串可以做连接操作, 函数可以做调用操作, 表可以做表项的取值赋值

lua元表Metatable

Lua 中的每个值都可以用一个 metatable. 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为. 你可以通过在 metatable 中的特定域设一些值来改变拥有这个 metatable 的值 的指定操作之行为. 举例来说,当一个非数字的值作加法操作的时候, Lua 会检查它的 metatable 中 "__add" 域中的是否有一个函数. 如果有这么一个函数的话,Lua 调用这个函数来执行一次加法. 我们叫 metatable

Lua——3.元表Metatable

lua的metatable也是一个普通的表,lua提供metatable的功能,主要有一下几种作用: 控制对 table 的访问 为 Lua 函数库提供支持 重载算数运算符和关系运算符的行为 1.使用metatable控制对table的访问 当查询table的某个键的时候,如果该table的这个键没有值,那么Lua就会寻找该table的metatable中的__index元方法:如果__index指向一个table,Lua会在此table中查找相应的键, 并且,__index也可以指向一个方法

Cocos2dx+lua合适还是Cocos2dx+js合适?

问题: 开发cocos2dx手游Cocos2dx+lua合适还是Cocos2dx+js合适 百牛信息技术bainiu.ltd整理发布于博客园 回答: 作者:廖宇雷链接:https://www.zhihu.com/question/21130385/answer/18485625来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 2014.02更新:请放心选择 Lua 吧.触控已经收购了 quick-cocos2d-x,2014年肯定会大力强化 cocos2d-x 的

Cocos2d-x下Lua调用自定义C++类和函数的最佳实践

原文地址:http://segmentfault.com/a/1190000000631630 关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正把这事给讲明白了,我自己也是个初学者,摸索了半天,总结如下: cocos2d-x下Lua调用C++这事之所以看起来这么复杂.网上所有的文档都没讲清楚,是因为存在5个层面的知识点: 1.在纯C环境下,把C函数注册进Lua环境,理解Lua和C之间可以互相调用的本质2.在cocos2d-x项目里,把纯C函数注册进Lua环境,理解cocos

【转】Cocos2d-x下Lua调用自定义C++类和函数的最佳实践

转自:http://segmentfault.com/blog/hongliang/1190000000631630 关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正把这事给讲明白了,我自己也是个初学者,摸索了半天,总结如下: cocos2d-x下Lua调用C++这事之所以看起来这么复杂.网上所有的文档都没讲清楚,是因为存在5个层面的知识点: 1.在纯C环境下,把C函数注册进Lua环境,理解Lua和C之间可以互相调用的本质 2.在cocos2d-x项目里,把纯C函数注册进L

Lua之table(表)

Lua table(表) 使用表来统一表示Lua中的一切数据,是Lua区分于其他语言的一个特色.这个特色从最开始的Lua版本保持至今,很大的原因是为了在设计上保持简洁.Lua表分为数组和散列表部分,其中数组部分不像其他语言那样,从0开始作为第一个索引,而是从1开始.散列表部分可以存储任何其他不能存放在数组部分的数据,唯一的要求就是键值不能为nil.尽管内部实现上区分了这两个部分,但是对使用者而言却是透明的.使用Lua表,可以模拟出其他各种数据结构--数组.链表.树等. 虽然设计上简洁,并且对使用

Cocos2d-x下Lua调用自定义C++类和函数的最佳实践 -- 转

原地址: http://blog.segmentfault.com/hongliang/1190000000631630 Cocos2d-x下Lua调用自定义C++类和函数的最佳实践 关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正把这事给讲明白了,我自己也是个初学者,摸索了半天,总结如下: cocos2d-x下Lua调用C++这事之所以看起来这么复杂.网上所有的文档都没讲清楚,是因为存在5个层面的知识点: 1.在纯C环境下,把C函数注册进Lua环境,理解Lua和C之间可以