3.1 Lua代码中的元表与元方法

什么是元表?

元表 是一个普通的表,可以附加到另一个表 上,用来定义该表在某些特定操作下的行为。当 Lua 对表进行某些操作(如相加、索引、调用等)时,如果该表有元表,Lua 就会查看元表中是否有对应的元方法来决定如何执行操作。

可以起到一个类似于c++中运算符重载的作用

Lua 复制代码
tt1={1,2,3,4};
tt2={5,6,7,8,9,10};
meT3={};

for index, value in ipairs(tt1) do
    print(index,value);
end

meT3.__add=function (t1,t2)
    local count=0;
    local res={}
    if(#t1<#t2)then
        count=#t2;
    else
        count=#t1;
    end

    for i=1,count do
        if(i<=#t1)and(i<=#t2)then
            res[i]=t1[i]+t2[i];
        elseif i<=#t1 then
            res[i]=t1[i];
        elseif i<=#t2 then
            res[i]=t2[i];
        end
    end
    return res;
end

setmetatable(tt1,meT3);
setmetatable(tt2,meT3);

tt3=tt1+tt2;

print("");

for index, value in ipairs(tt3) do
    print(index,value);
end

运行结果:

Lua 复制代码
1	1
2	2
3	3
4	4

1	6
2	8
3	10
4	12
5	9
6	10

__index的用法

元表中有键值,table中也有键值,会优先调用table中的键值;table中没有键值的话,会调用元表中的键值

Lua 复制代码
t1={id=123,name="Tom"};
met={

    __index={age="999"};
};

print(t1.age);
t1.age="789";
print(t1.age);

setmetatable(t1,met);
print(t1.age);

t1.age=nil;
print(t1.age);

运行结果:

Lua 复制代码
nil
789
789
999

元表中的"__index"是一个函数时

Lua 复制代码
t1={id=123,name="Tom"};
met={

    __index=function (k,v)
        k[v]="aaa";
        return "bbb";
    end
};
print(t1.age);

setmetatable(t1,met);
print(t1.age);
print(t1.age);

t1.age=nil;
print(t1.age);
print(t1.age);

运行结果:

Lua 复制代码
nil
bbb
aaa
bbb
aaa

__newindex的用法

如果是表,则在表里面设一个没有的key值的时候,会写到__newindex 对应的表中,而不会写到本表中;如果本表中有key值,则会更新本表,不会管元表。如果是function ,则直接调用,且本表,key,value都可以作为参数

Lua 复制代码
t1={id=123,name="Tom"};
t2={};
met={
    __index={age="hhh";};
    __newindex=t2;
};

setmetatable(t1,met);
print(t1.age);
print("");
t1.age="567";
print(t1.age);
print(t2.age);

met.__index=t2;
print(t1.age);
print(t2.age);

运行结果:

Lua 复制代码
hhh

hhh
567
567
567

是function的使用方式

Lua 复制代码
t1={id=123,name="Tom"};
t2={};
met={
    __index={age="hhh";};
    __newindex=function (t,k,v)
        rawset(t,k,v);
    end
};

setmetatable(t1,met);
print(t1.age);
print("");
t1.age="567";
print(t1.age);
print(t2.age);

met.__index=t2;
print(t1.age);
print(t2.age);

运行结果:

Lua 复制代码
hhh

567
nil
567
nil

__index与__newindex对比

基本对比示例

Lua 复制代码
local t = {existing = "已有值"}

local mt = {
    -- __index: 当访问不存在的键时调用
    __index = function(table, key)
        print("__index 被调用,键: " .. key)
        return "默认值 for " .. key
    end,
    
    -- __newindex: 当设置不存在的键时调用  
    __newindex = function(table, key, value)
        print("__newindex 被调用,键: " .. key .. ", 值: " .. value)
        -- 注意:这里没有实际设置值!
    end
}

setmetatable(t, mt)

print("1. 访问已存在的键:")
print(t.existing)  -- 输出: 已有值 (不触发元方法)

print("\n2. 访问不存在的键:")
print(t.name)      -- 触发 __index
-- 输出: 
-- __index 被调用,键: name
-- 默认值 for name

print("\n3. 设置不存在的键:")
t.age = 25         -- 触发 __newindex
-- 输出: __newindex 被调用,键: age, 值: 25

print("\n4. 再次访问刚才设置的键:")
print(t.age)       -- 仍然触发 __index! (因为值没有被实际设置)
-- 输出:
-- __index 被调用,键: age  
-- 默认值 for age

__tostring 的用法

__tostring 用函数接管本表的返回值,返回一个string

Lua 复制代码
t1={id=123,name="Tom"};
met={
    __tostring=function (t)
        str="";
        for key, value in pairs(t) do
            str=str..key..":\t"..value.."\n";
        end
        return str;
    end
};
print(t1);
setmetatable(t1,met);
print(t1);

运行结果:

Lua 复制代码
table: 000000000260b5a0
name:	Tom
id:	123

__call 的用法

类似于默认构造

Lua 复制代码
t1={id=123,name="Tom"};
met={
    __call=function (t,...)
        local tt={...}
        for key, value in pairs(t) do
            print(key,value);
        end

        for key, value in pairs(tt) do
            print(key,value);
        end
    end
};
setmetatable(t1,met);
t1(1,2,3,"io","900");

运行结果:

Lua 复制代码
name	Tom
id	123
1	1
2	2
3	3
4	io
5	900

rawget(t,str) 是获取本表 t 中 str 的值,不是获取元表中的值

Lua 复制代码
t1={id=123,name="Tom"};
met={
    __index={age="123"};
};

print(t1.name);
print(t1.age);
setmetatable(t1,met);
print("");
print(t1.name);
print(t1.age);

print("");
print(rawget(t1,"name"));   --获取本表中的值
print(rawget(t1,"age"));

运行结果:

Lua 复制代码
Tom
nil

Tom
123

Tom
nil
相关推荐
逻极8 小时前
Rust流程控制(上):if_else与match模式匹配
开发语言·后端·rust
小雨下雨的雨8 小时前
Rust专项——其他集合类型详解:BTreeMap、VecDeque、BinaryHeap
开发语言·后端·rust
渡我白衣8 小时前
C++世界的混沌边界:undefined_behavior
java·开发语言·c++·人工智能·深度学习·语言模型
剑海风云9 小时前
JDK 26:HTTP/3 支持已可在 HTTP 客户端 API 中使用
java·开发语言·http
下一站丶9 小时前
【JavaScript性能优化实战】
开发语言·javascript·性能优化
GIS好难学9 小时前
Three.js 粒子特效实战③:粒子重组效果
开发语言·前端·javascript
景彡先生9 小时前
Python NumPy广播机制详解:从原理到实战,数组运算的“隐形翅膀”
开发语言·python·numpy
不光头强9 小时前
springDI注入
java·开发语言
zhangfeng113310 小时前
亲测有效的mem 流行病预测,时间序列预测,r语言做移动流行区间法,MEM流行病阈值设置指南
开发语言·r语言·生物信息