美文网首页
Lua元表使用

Lua元表使用

作者: 我和我的火柴 | 来源:发表于2021-03-28 11:38 被阅读0次

    lua中每个值都可以拥有一个元表,元表是一个普通的lua table,定义了原始值在特定操作下的行为。

    • setmetatable、getmetatable就可以设置元表和获取元表:
    local t = {}
    local mt = {}
    setmetatable(t, mt)
    getmetatable(t)
    
    
    • 改变元表中特定的key,来改变原始值的对应行为,这个key对应的value(table或function)叫”元方法“。

    通过例子可以更清楚的理解元表\元方法的含义:

    1. __call 元方法

    -- 尝试调用一个非函数类型值
    
    local A = {}
    local metaA = {
        __call = function(...)
            print("call A with args : ", ...)
        end
    }
    
    setmetatable(A, metaA)
    
    A("string", 1)  -- output: call A with args: table:0x2814cd640 string 1
    
    

    当调用A(A是一个table)时,会找A的元表中的__call元方法,A作为第一个参数,调用A的参数列表随后。
    也可以用”:“定义__call元方法,传入的第一个参数A 则就是self :

    -- 尝试调用一个非函数类型值
    
    local A = {}
    local metaA = {}
    
    function metaA:__call(...)
        print("call A with self : ", self)
        print("with args : ", ...)
    end
    
    setmetatable(A, metaA)
    
    A("string", 1)  
    -- output: call A with self: table:0x2814cd640
    --         with args: string 1
    
    

    tips:
    (1) __call元方法可以用来实现类似构造方法的调用形式。
    (2) 在调用某个值a前不确定它是否是function时,除了判断 type(a) == "function" 外,还有可能a不是function 但a的元表__call是function,也是可以成功调用的。


    2. __index 元方法

    -- 尝试访问一个不存在的key
    local A = {}
    print( A.a )    -- output: nil
    
    -- 1.__index是一个table
    local metaA = {
        __index = {
        a = "123"
        }
    }
    setmetatable(A, metaA)
    print( A.a )    
    -- output: 123
    
    -- 2.__index是一个function
    local metaA2 = {
        __index = function(t, k)
            print("try to get key : "..k.." from : ",t)
        end
    }
    setmetatable(A, metaA2)
    print( "A.a is : ", A.a )   
    -- output: try to get key : a from table: 0x281495640
    --         A.a is : nil  (这里由于__index并没有返回任何值,所以打印 nil )
    
    

    lua中查找表元素的过程:
    1.在表中查找,如果找到,返回该元素,找不到则继续
    2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
    3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值(传入查找的表和key)。


    3. __newindex 元方法
    __newindex 元方法用来对表更新,当你给表的一个不存在的key赋值,解释器就会查找__newindex 元方法:

    -- 尝试给一个不存在的 key 赋值
    local A = {}
    A.a = "123"
    print("A.a is ", A.a)
    -- output: A.a is 123
    
    local metaA = {
        __newindex = function(t, k, v)
            print("try to set value:", v, " for key:", k, " for ", t)
        end
    }
    setmetatable(A, metaA)
    A.a = "456"
    print("A.a is ", A.a)
    -- output: A.a is "456"
    -- 此时 A 已经包含 key "a", 所以直接改变 A.a 而没有走元表的__newindex
    
    A.a = nil
    A.a = "789"
    print("A.a is ", A.a)
    -- output: try to set value: 789 for key: a for table: 0x2814aec00
    --         A.a is nil
    -- 这里由于已经将 A.a 置空,再次赋值时没有这个 key 所以走了元表的__newindex,元方法只进行了打印并没有任何操作,所以打印 A.a 仍是nil
    
    

    给table的某个key赋值时,只有当表中不存在这个key时才会找元表的__newindex方法,传入表t、要赋值的key和value,当表中已经存在这个key时则会直接赋值。

    利用__index和__newindex可以实现一些有意思的功能,比如:

    • 实现类似switch/case中的default语句:
    local Score = setmetatable({
        ["A"] = "91~100",
        ["B"] = "81~90",
        ["C"] = "61~80",
        ["D"] = "0~60"
    }, {
        __index = function()
            return "error";
        end
    })
    print(Score.A)      -- "91~100"
    print(Score.E)      -- "error"  
    
    
    • 将一个表作为另一个表的元表的__index,从而实现表继承关系:Lua实现继承
    • 将__index 和 __newindex作为get、set方法:Lua实现KVO

    4. 运算符方法
    运算符方法用来定义table的运算操作,类似C++中的运算符重载

    local A = {
        a = "a"
    }
    local B = {
        a = "b"
    }
    local m = {
        __add = function(t1, t2)
            return {
                a = t1.a .. t2.a
            }
        end
    }
    setmetatable(A, m)
    setmetatable(B, m)
    print((A + B).a)    -- output: ab
    print((B + A + B).a)    -- output: bab
    
    

    Lua中所有的运算符元方法:https://www.lua.org/manual/5.3/manual.html#2.4

    5. 垃圾回收方法

    local A = {}
    local metaA = {
        __gc = function(...)
            print("receive gc with args : ", ...)
        end
    }
    setmetatable(A, metaA)
    collectgarbage()
    
    

    ps. lua5.2以上版本


    6. 弱引用表
    弱引用表允许它的key和value被gc回收,通过设置元表的__mode实现,__mode是一个字符串,如果包含"k"则key是弱引用,如果包含"v"则value是弱引用。

    local k = {}
    local v = {}
    a = {}
    a[k] = v
    k = nil
    v = nil
    collectgarbage()
    for i, v in pairs(a) do
        print(i, v)
    end
    -- output: table: 0x282314a40  table: 0x282314840
    
    setmetatable(a, {__mode = "k"})
    collectgarbage()
    print("weak table:")
    for i, v in pairs(a) do
        print(i, v)
    end
    -- output: weak table:
    

    转载作者:正谦

    相关文章

      网友评论

          本文标题:Lua元表使用

          本文链接:https://www.haomeiwen.com/subject/fqnyhltx.html