lua元表(metatable)和元方法(metamethod)
(一) 元表概念:
引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等。但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作。
访问机制:一般的元方法都只针对Lua的核心,也就是一个虚拟机。它会检测一个操作中的值是否有元表,这些元表是否定义了关于这次操作的元方法。例如两个table相加,先检查两者之一是否有元表,之后检查是否有一个叫“__add”的字段,若找到,则调用对应的值。“__add”等即是字段,其对应的值(往往是一个函数或是table)就是“元方法”。
1, 元表实例
setmetatable(只能用于table)和getmetatable(用于任何对象)
语法:setmetatable (table, metatable),对指定table设置metatable 【如果元表(metatable)中存在__metatable键值,setmetatable会失败】
语法:tmeta = getmetatable (tab),返回对象的元表(metatable) 【如果元表(metatable)中存在__metatable键值,当返回__metatable的值】
1. 系统使用字段:
算术类元方法: 字段:__add(+), __mul(*), __ sub(-), __div(/), __unm, __mod(%), __pow, (__concat)
关系类元方法: 字段:__eq, __lt(<), __le(<=),其他Lua自动转换 a~=b --> not(a == b) a > b --> b < a a >= b --> b <= a 【注意NaN的情况】
table访问的元方法: 字段: __index, __newindex
__index:
查询:访问表中不存的字段
rawget(t, i)
__newindex:
更新:向表中不存在索引赋值
rawset(t, k, v)
2. 自定义字段:
上面字段是供系统使用的字段,比如当我们给元表的__add字段赋值的时候,那么当执行"t1 + t2"时,就会调用到__add操作;我们也可以定义我们自己需要的字段,比如使用lua构造的类最常定义的__new操作,可以通过定义此字段来完成类对象的创建, 等等.
例子:
运行结果:
"sub": - 操作。 行为类似于“add”操作。
"mul": * 操作。 行为类似于“add”操作。
"div": / 操作。 行为类似于“add”操作。
"mod": % 操作。 行为类似于“add”操作。以o1 - floor(o1/o2)*o2为操作原语。
"pow": ^ (取幂)操作。 行为类似于“add”操作,以函数pow(来自C数学库)为操作原语。
"unm": 一元-操作。
(二) 下面介绍rawget 和rawset
有时需要get 和set表的索引,不想使用metatable.你可能回猜想, rawget 允许你得到索引无需__index,rawset允许你设置索引的值无需__newindex (相对传统元表的方式,这些不会提高速度)。为了避免陷在无限循环里,你才需要使用它们。 在上面的例子里, t[key] = value * value将再次调用__newindex函数,这让你的代码陷入死循环。使用rawset(t, key, value * value) 可以避免。你可能看到,使用这些函数, 我们必须传递参数目标table, key, 当你使用rawset时还有value。
1, lua中使用元表和元方法进行类多继承的实现
local function search(k, plist)
for i=, #plist do
-- 尝试第i个父类
local v = plist[i][k];
if v then return v end
end
end function createClass( ... )
-- 新类
local c = {}
local parents = { ... }
-- 在父类列表中搜索
setmetatable(c, { __index = function(t, k)
return search(k, parents)
end}) -- c表的__index指向其自身
c.__index = c -- 注释1 -- 新类的构造函数
----[[ 注释2
function c:new(o)
local o = o or {}
-- 将c表作为其实例o的元表
setmetatable(o, c)
return o
end
--]] -- 注释3
-- function c:getName()
-- return "die"
-- end return c
end Named = {}
-- 使用lua语法糖 ":", 隐式传递self
function Named:getName()
return self.name
end
function Named:setName(n)
self.name = n
end Account = {balance = }
----[[ 注释4
function Account:new(o)
local o = o or {}
setmetatable(o, self)
return o
end
--]] -- 调用
NamedAccount = createClass(Named, Account)
account = NamedAccount:new({name = "Paul", })
print(account:getName())
(注:开 --> 打开注释,代码失效;关 --> 关闭注释,代码生效。)
代码的工作流程:account:getname()语句, 首先,Lua在表account(为{ name = "Paul"}, 由createClass函数中调用new返回的o,也即此处的account表 = { name = "Paul"})中无法找到字段"getname"。因此,就查找account表的元表(根据语句"setmetatable(o, c)", 可知为account表的元表为NamedAccount表(c表),也即createClass函数最后返回的 c 表)的__index字段, 发现c表的__index字段指向c表自身,故而在c表中查找,这时同样没找到"getname"字段, 继续查找,此时开始进入c表的元表(account表的元表为 c 表(此例子中为NamedAccount表),c 表的元表为{__index = function(t, k) return search(k, parent) end})查找, 如下:
setmetatable(c, {__index = function(t, k)
return search(k, parents)
end})
由于这个字段是一个函数,Lua就调用了它。该函数先在c表的父表Account中查找"getname"。未找到后,继而查找Named父表。最终在Named中找到了一个非nil值,即为搜索的最终结果。
lua表的查找过程:
1. 先在当前表 curr_tbl 中查找,这里curr_t = {name = "Paul",}; 如果curr_t找到所需字段getName,返回结果,查找成功,停止向上查找;否则,进入步骤2;
2. 如果找不到所需字段, 如果当前表 curr_tbl 没有元表, 则找不到所需字段,查找失败,停止查找;否则,进入步骤3;
3. 如果 curr_tbl 有元表,则进入其元表(此处为c表)并找到__index字段,如果没有__index字段,则同样查找失败(所以,如果把上面注释1处的语句"c.__index = c"删除的话,则查找失败,且不会再进一步向上查找);否则, 进入步骤4;
4. 如果有__index字段,则从__index字段所指定的表 1_tbl 重新开始执行查找(上面例子中, c表的__index字段指定的表是其自身, 所以是在c表中查找), 此时如果c表有所需字段,如注释3所示,那么便在c表中查找到了所需字段,查找成功,同时停止向上查找; 否则, 进入步骤5;
5. 在c表并没有找到所需字段, 如果c表没有指定元表,则查找失败,停止向上查找; 否则, 进入步骤6;
6. c表指定了元表 2_tbl, 进入 2_tbl并找到__index字段, 如果在 2_tbl中找不到__index字段, 插找失败,返回并停止查找; 否则, 进入步骤7;
7. 进入__index字段指定的表中查找, 和步骤4一样; 但此时 2_tbl 的__index字段指定的是一个搜索函数, 那么就进入此搜索函数进行搜索;
8. 在search函数中, 当查找到了"NamedAccount = createClass(Named, Account)"中的父类Named的时候,找到了所需字段getName, 查找成功,返回结果并停止查找.
注: 去掉“2”号注释处的代码,程序会调用到“4”号注释处的代码;
lua元表(metatable)和元方法(metamethod)的更多相关文章
- Lua中的元表(metatable)、元方法(metamethod)详解
在第一次看见这两样东西的时候,可能会觉得它很深奥,但其实很好理解,虽然实际上它可能真的很深奥.(小若:停!滚粗.) 1.知道为什么1 + 1 = 2吗? 为什么在Lua中,1+1会等于2呢?(小若:难 ...
- lua元表Metatable
Lua 中的每个值都可以用一个 metatable. 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为. 你可以通过在 metatable 中的特定 ...
- lua 元表Metatable (六)
元表理解起来比较抽象,但这是lua设置的一种数据结构而已, 假设有table_A.table_B 这2个table,如果table_A要操作table_B,显然是不可能的 因为者都之间是没有关系的,如 ...
- Lua中强大的元方法__index详解
今天要来介绍比较好玩的内容:__index元方法 我是备胎,记得回头看看 咳咳,相信每一位女生都拥有或者不知不觉中拥有了一些备胎,啊!当然,又或许是成为过别人的备胎. 没有备胎的人,就不是完整的人生. ...
- Lua中的元表与元方法
[前言] 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了“+”符号,就可以进行类的加法运算.在Lua中也有这个道理 ...
- Lua中的元表与元方法学习总结
前言 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了"+"符号,就可以进行类的加法运算.在Lu ...
- lua中 table 元表中元方法的重构实现
转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...
- lua编程之元表与元方法
一. 前言 lua是一种非常轻量的动态类型语言,在1993年由由Roberto Ierusalimschy.Waldemar Celes 和 Luiz Henrique de Figueiredo等人 ...
- lua中 table 重构index/pairs元方法优化table内存占用
转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...
随机推荐
- 怎么用一个ppt介绍一个项目
- SAP 金税接口代码 供参考
程序可以通过抓取 客户 开票信息等 下载文本 导出 需要事先创建好几个structure zc0000sdt0016, zc0000sdt0017 REPORT zc0000sdr0016 NO ST ...
- 【Vue】VS Code+Vue入门 Helloworld
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- JDBC创建表实例
在本教程将演示如何在JDBC应用程序中创建一个数据库表. 在执行以下示例之前,请确保您已经准备好以下操作: 具有数据库管理员权限,以在给定模式中创建数据库表. 要执行以下示例,需要用实际用户名和密码替 ...
- Cg入门10:Vertex Shader - 几何变换 —MVP矩阵变换
Unity内建矩阵类型: M:世界矩阵 V:摄像机矩阵 P:投影矩阵 T :矩阵的转置 IT : 转置的的逆 _Object2World: 模型到世界矩阵 _World2Object:世界到模型矩阵 ...
- C# 判断网站是不是discuz论坛
if (this.txturl.Text == "") { this.lblmess.Text = "请输入网址"; } else { GetHttp getH ...
- UGUI 加载图片
图片是动态加载的,然后转换为sprite赋值到ugui的按钮上 代码如下 using UnityEngine; using System.Collections; using System.IO; u ...
- mysql使用sql语句查询数据库所有表注释已经表字段注释
场景: 1. 要查询数据库 "mammothcode" 下所有表名以及表注释 /* 查询数据库 ‘mammothcode’ 所有表注释 */ SELECT TABLE_NAME,T ...
- ASP.NET MVC下Ajax.BeginForm方式无刷新提交表单
有时候,不得不考虑到以下场景问题: 数据库表字段会频繁更改扩展,而流行的重业务的js框架过于依赖json数据接口,导致的问题是,数据库表更改 -> 数据接口更改 -> 前段框架逻辑更改.. ...
- RMAN:简单的duplicate创建新数据库 for 12c+
构建参数文件 *.db_name='test2' ##### 需要注意的地方,和rman的duplicate目标库一致 *.compatible='18.0.0' ##### 关键的地方,每个版本的模 ...