1. 程式人生 > >csdn學習筆記三:meta元表、元方法 __index, __newindex、rawset、rawget

csdn學習筆記三:meta元表、元方法 __index, __newindex、rawset、rawget

重要:在表和元表的__index 和 __newindex 都沒有需要操作的key時,賦值table操作會呼叫__newindex, 取值操作會呼叫__index 

元表設定setmetatable

t1 = {};
t2 = {};
print("t1=",t1);
print("t2=",t2);
print(setmetatable(t1,t2));  ----t2是元表  t1是普通表  列印的是t1的表
print(getmetatable(t1));   --getmetatable(t1),得到的是t2的表的地址
-----------------------------------------------------------------------
-----------------------------------------------------------------------
output:
t1=     table: 00719A08
t2=     table: 00719A30
table: 00719A08
table: 00719A30

元方法:過載加號"+"

t1 = {1,2,3};
t2 = {4,5,6};

mt = {}
mt.__add = function(a,b)
    local n = #a;
    local res = {};
    for i=1,n do
        res[i] = a[i] + b[i];
    end
    return res;
end
setmetatable(t1,mt);
setmetatable(t2,mt);

t3 = t1 + t2;  ----__add過載了+號,所有t1可以加t2
print(t3[1]);
---------------------------------------------------------------
output:
5

過載關係比較運算子"=="

a = {x=1,y=2,z=3};
b = {x=1,y=1,z=1};


mt = {}
mt.__eq = function(a,b)
    if a[1] == b[1] and a[2] == b[2] and a[3] == b[3] then
        return true;
    else
        return false;
    end
end
setmetatable(a,mt);
setmetatable(b,mt);


if a == b then
    print("a == b");   --設定了元方法eq就相等
else
    print("a ~= b");   --如果不過載元方法eq就會不相等
end
----------------------------------------------------------------------
output:
a == b

__index 元方法

這是metatable最常用的鍵,當你通過key來訪問table的時候,如果沒有這個鍵值,那麼lua就會尋找該table的metatable(假定有metatable)中的__index鍵,如果__index包含一個表,lua會在表格中查詢相應的鍵

t = {};
mt = {};
setmetatable(t,mt);  ----設定mt為t的元表
print(t.x);          ----t.x沒有這個鍵,然後就會去查詢mt.__index 鍵,mt也沒有這個鍵所以會返回nil
-----------------------------------
output:
nil

 元表有__index鍵

t = {};
mt = {
    __index = {          
        x = "123"
    }
};
setmetatable(t,mt);  ----設定mt為t的元表
print(t.x);          ----t.x沒有這個鍵,然後就會去查詢mt.__index 鍵,x = "123"
---------------------------------------------------
output:
123

元表沒有__index鍵,元表有x鍵

t = {};
mt = {
    x = "123"
};
setmetatable(t,mt);  ----設定mt為t的元表
print(t.x);          ----t.x沒有這個鍵,取值搜尋只會取元表的__index鍵,所有mt有x,t.x也是輸出nil
-----------------------------------
output:
nil

__index為函式

如果__index包含一個函式的話,且沒有取值的鍵,lua就會呼叫哪個函式,table和鍵會作為引數傳遞給函式

t = {};
mt = {
    __index = function ()
        print("__index function")
    end
};

setmetatable(t,mt);
print(t.x);     
-------------------------------------------
output:
__index function
nil

如果有函式,但是也有jian,就不執行函式

t = {};
mt = {
    __index = {function ()
            print("__index function")
        end,
        x = "100"
    }
};

setmetatable(t,mt);
print(t.x);     
-------------------------------------------------------
output:
100

實際當__index只有函式時,會把table和取值key傳入到__index的函式中

t = {};
print("t:",t);
mt = {
    __index = function (t,k)      ---
        print(t,k)
        if k == "x" then
            t[k] = 777;
            return 888;
        else
            return 999;
        end
    end
};

setmetatable(t,mt);
print(t.x);     ----會把table和key傳入到__index的function中去    ,output :  888,這裡呼叫t.x沒有值,然後會給t.x賦值777
print(t.x);   ----output:777  , 這裡t.x = 777

--------------------------------------------------------
output:
t:      table: 00E19D00    -----這裡是t的table
table: 00E19D00 x          -----這裡列印的是t的table和 t.x傳遞的x鍵名稱
888
777

__newindex元方法

__newindex元方法用來對錶進行更新(賦值),__index則用來對錶進行訪問(取值)

當你給表一個缺少的索引賦值,直譯器就會查詢__newindex元方法:如果存在則呼叫這個函式而不進行賦值操作

__newindex元方法如果是一張表,lua對這張表索引賦值操作,新的k v會出現在表中

t = { };
mt = { };

print("table t:",t);   ---output:  table t:        table: 008E9C10

setmetatable(t,mt);

mt.__newindex = function (t,k,v)    --- t.y會把 table:t,key: y,value:100 ,傳遞進去 
    print(t,k,v);
end

mt.__index = function (t,k)         ---
            if k == "x" then
                t[k] = 777;    ---table: 008E9C10 x  777 這裡是賦值還會呼叫一次__newindex
                return 888;    
            else
                return 999;
            end
end


t.x = 100;      ------output: table: 008E9C10 x       100 ,t沒有x鍵,執行了__newindex的函式但是不進行賦值
print(t.x);     ------output: 888  t.x是取值不進行賦值操作,

--------------------------------------
output:
table t:        table: 008E9C10
table: 008E9C10 x       100
table: 008E9C10 x       777
888

表象函式一樣呼叫(原理還是過載了方法__call)

pow = {};
setmetatable(pow,{__call = function(t,x)
        return x * x ;
end})

print(pow(10));
---------------------------------
output:
100

過載__tostring方法

pow = setmetatable({10,20,30},{__tostring = function(t)
        sum = 0;
        for k,v in pairs(t) do 
            sum = sum + v;
        end
        return "sum = " .. sum;
end})

print(pow);
---------------------------------------------------
output:
sum = 60

rawset 和 rawget是直接操作表,而不去訪問元表

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

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

Window.mt.__index = function (t ,key)  
    return 1000  
end 

Window.mt.__newindex = function (table ,key ,value)  
    if key == "wangbin" then  
        rawset(table ,"wangbin" ,"yes,i am")  --(1)
        --table.wangbin = "yes,i am"   --(2)反例
    end  
end 

w = Window.new({x = 10 ,y = 20}  )
print(rawget(w ,w.wangbin)) ------取的是w表裡面的wangbin鍵,為nil
print(w.wangbin)            ------沒有使用raw取的就是元表裡面的__index所以為1000

w.wangbin = "nVal"          ------w沒有wangbin鍵,就執行元表裡面的__newindex的方法
print(w.wangbin)            ------輸出yes,i am


rawset(w,"wangbin","nVal")  ----使用rawset,就設定w表裡面的wangbin鍵位nVal
print(w.wangbin)            ----輸出nVal


----------------------------------------------------
output:
nil
1000
yes,i am
nVal