当前位置: 首页 > news >正文

【Lua 入门基础篇(八)】元表

在这里插入图片描述
在这里插入图片描述

文章目录

  • 一、元表(Metatable)
    • 1. setmetatable
    • 2. __tostring 元方法
    • 3. __call 元方法
    • 4. __index 元方法
    • 5. __newindex 元方法
    • 6. 运算符重载

一、元表(Metatable)

Lua的表本质其实是个类似HashMap的东西,其元素是很多的Key-Value对,如果尝试访问了一个表中并不存在的元素时,就会触发Lua的一套查找机制,也是凭借这个机制来模拟了类似“继承”的行为

在 Lua table 中我们可以访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作(比如相加)。

因此 Lua 提供了元表(Metatable),允许我们改变 table 的行为,每个行为关联了对应的元方法。

  • 例如,使用元表我们可以定义 Lua 如何计算两个 table 的相加操作 a+b。
  • 当 Lua 试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫 __add 的字段,若找到,则调用对应的值。 __add 等即时字段,其对应的值(往往是一个函数或是 table)就是"元方法"。

有两个很重要的函数来处理元表:

  • setmetatable(table,metatable): 对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。

  • getmetatable(table): 返回对象的元表(metatable)。

mytable = {}                      -- 普通表
mymetatable = {}                  -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表

以下为返回对象元表:

getmetatable(mytable) -- 返回 mymetatable

1. setmetatable

元表概念:

  • 任何表变量都可以作为另一个表变量的元表
  • 任何表变量都可以有自己的元表(父表)。
  • 当子表中进行一些特定的操作,会执行元表中的内容。

设置 table 的元表为 meta。

  • 第一个参数:子表
  • 第二个参数:元表
meta = {}
table = {}
setmetatable(table, meta)

2. __tostring 元方法

当子表要被当作字符串使用,会默认调用元表中的__tostring方法。

fa = {
    __tostring = function()
        return "fa"
    end
}
son = {}
setmetatable(son, fa)
print(son) -- 执行fa表中的 __tostring
-- 输出:fa
  • 传入参数,默认将关联的表传入
fa = {
    __tostring = function(a)
        return a.name
    end
}
son = { name = 'lua' }
setmetatable(son, fa)
print(son)  -- lua

3. __call 元方法

子表被当作一个函数来使用时,会默认调用元表中的 __call 中的内容。

参数表中的第一个参数默认为调用的子表自己。

table = { name = 'lua' }
meta = {
	__tostring = function(a)
		return a.name
	end, -- 注意!!! 这里,隔开
	__call = function(a, b)
		print(a, b)
	end
}
setmetatable(table, meta)
table(1) -- lua 	1

只有在元表中实现了 __call 才能将表当作函数调用


4. __index 元方法

通过如下代码段,解释__index方法的含义。

访问table.age时,table中没有age这个成员,但Lua接着发现table有元表meta。此时,Lua并不是直接在table中找名为age的成员,而是调用meta的__index方法。如果__index方法为nil,则返回nil;如果是一个表,那么就到__index方法所指定的这个表中查找age成员。

  • 注:__index方法除了可以是一个表,还可以是一个函数,如果是一个函数,__index方法被调用时将返回该函数的返回值。
meta = {
    age = 1,
    __index = { age = 2 } -- 指定表 { age = 2 }
    -- __index = meta 不可这样写,会返回nil,可以写在外面
}
meta.__index = meta -- 指定自己

table = {}
setmetatable(table, meta)
print(table.age) -- 输出:2

__index指定函数示例:

table = {}
meta = {
    __index = function(a, b)
        if b == "name" then
            return "lua"
        else
            return nil
        end
    end
}
setmetatable(table, meta)
print(table.name)
  • __index 可以嵌套。
metafather = {
        age = 3,
        __index = { age = 2 }
    }
meta = { __index = metafather } 
-- 去到指定表找,而不是继续找metafather的__index对应的表
table = {}
setmetatable(table, meta)
setmetatable(meta, metafather)
print(table.age) --输出:3
-- 如果注释调第二行age=3,则输出nil

5. __newindex 元方法

先看一个例子:
给不存在的索引赋值,table.age=1,那么可以直接修改table表中,增加了成员age=1。

table = {}
table.age = 1
print(table.age) -- 1

当一个表有元表时,并且存在元方法__newindex。那么对子表赋值一个不存在的索引,会将这个值赋值到__newindex所指定的表中,并不会修改自己。

table = {}
meta = {}
meta.__newindex = {}
setmetatable(table, meta)

table.name = "lua"
print(table.name) -- nil
print(meta.__newindex.name) -- lua
  • 如果只想知道自己表内有没有这个变量,忽略 __index,那么可以使用 rawget
print(rawget(table, 'name')) -- nil
print(rawget(meta, 'name')) -- nil
print(rawget(meta.__newindex, 'name')) -- lua
  • 如果只想对自己的表进行修改,忽略 __newindex设置 ,可以对应使用 rawset
rawset(table, 'height', '180')
print(table.height) -- 180

总结:

  • __newindex 元方法用来对表更新,__index则用来对表访问 。
  • 如果 __newindex 是一个函数,则给table中不存在的字段赋值时,会调用这个函数,并且赋值不成功。
  • 如果 __newindex 是一个table,则给table中不存在的字段赋值时,会直接给 __newindex 的table赋值。

6. 运算符重载

可以重载如下这些运算符:

-- 运算符 -: __add
-- 运算符 -: __sub
-- 运算符 *: __mul
-- 运算符 /: __div
-- 运算符 %: __mod
-- 运算符 ^: __pow
-- 运算符 ..: __concat
-- 运算符 ==: __eq
-- 运算符 <: __lt
-- 运算符 <=: __le

-- __eq, __lt, __le
-- (其它运算关系自动转换为这三个的基本运算)

-- 注意:条件运算符没有 ~=, >, >=, 只能通过 not 取反, 但实际上不需要。
-- 因为 t1 > t2 相当于 t2 < t1,也是重载 <
--(这也是两个对象的元表一定要一致才能正确使用条件运算符的原因)
meta = {
    __add = function(a, b)
        return a.age + b.age
    end,
    __eq = function(a, b)
        return true
    end,
    __concat = function(a, b)
        return a.name .. ' + ' .. b.name
    end
}

a = { age = 20, name = 'A' }
b = { age = 18, name = 'B' }
setmetatable(a, meta)
print(a + b)
print(a .. b)
print(a == b)
setmetatable(b, meta)
print(a == b)
38
A + B
false
true

如果要用条件运算符来比较两个对象,这两个对象的元表一定要一致,才能准确调用方法。(b没设置元表是false,设置元表后是true)


相关文章:

  • 创新案例分享 | 建设医院绩效管理系统,促进医院健康良性发展
  • 检查或复位状态[ feof()函数、ferror()函数和clearerr()函数 ]
  • GOLANG SLICE 切片扩容
  • 并发编程Bug起源:可见性、有序性和原子性问题
  • LastPass 开发者系统被黑已窃取源代码
  • 设计模式摘要
  • 2.Hive表结构变更时,滥用MSCK REPAIR TABLE语句,导致变更语句执行时间过长
  • [I2C]I2C通信协议详解(一) --- 什么是I2C
  • 寄——在外拼搏的你一路平安,早日团圆
  • C++11之右值引用:移动语义和完美转发(带你了解移动构造函数、纯右值、将亡值、右值引用、std::move、forward等新概念)
  • 【手把手带你学JavaSE】第八篇:抽象类和接口
  • 18年程序员生涯,读了200多本编程书,挑出一些精华分享给大家
  • 广播解决方案:Livemind Recorder:录音机
  • 罗克韦尔AB PLC(RSLogix 5000)在线修改程序的具体方法示例
  • 2020 关于Map Map,String> Map,Object>的简单使用
  • 分享的文章《人生如棋》
  • 【技术性】Search知识
  • Android 控件背景颜色处理
  • canvas 绘制双线技巧
  • Java反射-动态类加载和重新加载
  • Twitter赢在开放,三年创造奇迹
  • Vue 2.3、2.4 知识点小结
  • 构造函数(constructor)与原型链(prototype)关系
  • 警报:线上事故之CountDownLatch的威力
  • 说说动画卡顿的解决方案
  • 微服务框架lagom
  • 系统认识JavaScript正则表达式
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • (1)(1.13) SiK无线电高级配置(五)
  • (14)Hive调优——合并小文件
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (附源码)计算机毕业设计SSM在线影视购票系统
  • (九)One-Wire总线-DS18B20
  • (三)elasticsearch 源码之启动流程分析
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十一)手动添加用户和文件的特殊权限
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (转)shell调试方法
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • **PHP二维数组遍历时同时赋值
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET/C# 的字符串暂存池
  • .NET程序员迈向卓越的必由之路
  • .net专家(高海东的专栏)
  • @RequestBody与@ModelAttribute
  • [ vulhub漏洞复现篇 ] ThinkPHP 5.0.23-Rce
  • [2010-8-30]
  • [20170705]lsnrctl status LISTENER_SCAN1
  • [acwing周赛复盘] 第 94 场周赛20230311
  • [AI]文心一言爆火的同时,ChatGPT带来了这么多的开源项目你了解吗
  • [AutoSar]BSW_Com07 CAN报文接收流程的函数调用
  • [boost]使用boost::function和boost::bind产生的down机一例