(一) 元表概念:

  引言: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)的更多相关文章

  1. Lua中的元表(metatable)、元方法(metamethod)详解

    在第一次看见这两样东西的时候,可能会觉得它很深奥,但其实很好理解,虽然实际上它可能真的很深奥.(小若:停!滚粗.) 1.知道为什么1 + 1 = 2吗? 为什么在Lua中,1+1会等于2呢?(小若:难 ...

  2. lua元表Metatable

    Lua 中的每个值都可以用一个 metatable. 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为. 你可以通过在 metatable 中的特定 ...

  3. lua 元表Metatable (六)

    元表理解起来比较抽象,但这是lua设置的一种数据结构而已, 假设有table_A.table_B 这2个table,如果table_A要操作table_B,显然是不可能的 因为者都之间是没有关系的,如 ...

  4. Lua中强大的元方法__index详解

    今天要来介绍比较好玩的内容:__index元方法 我是备胎,记得回头看看 咳咳,相信每一位女生都拥有或者不知不觉中拥有了一些备胎,啊!当然,又或许是成为过别人的备胎. 没有备胎的人,就不是完整的人生. ...

  5. Lua中的元表与元方法

    [前言] 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了“+”符号,就可以进行类的加法运算.在Lua中也有这个道理 ...

  6. Lua中的元表与元方法学习总结

    前言 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了"+"符号,就可以进行类的加法运算.在Lu ...

  7. lua中 table 元表中元方法的重构实现

    转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...

  8. lua编程之元表与元方法

    一. 前言 lua是一种非常轻量的动态类型语言,在1993年由由Roberto Ierusalimschy.Waldemar Celes 和 Luiz Henrique de Figueiredo等人 ...

  9. lua中 table 重构index/pairs元方法优化table内存占用

    转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...

随机推荐

  1. 【转】android中的数据存取-方式一:preference(配置)

    这种方式应该是用起来最简单的Android读写外部数据的方法了.他的用法基本上和J2SE(java.util.prefs.Preferences)中的用法一样,以一种简单. 透明的方式来保存一些用户个 ...

  2. 关于Unity中拖尾渲染器的使用

    拖尾渲染器 是一个组件,能够帮我们绘制出拖尾的效果. 就是跟在运动物体后面的东西,前进的过程中,末尾的不断消失,前面的不断生成,有一个长度.长度是以时间计算的,从末尾到头有多少秒的时间就是拖尾的长度. ...

  3. Eclipse初次java开发问题总结-2

    今天对之前写的servlet程序做了个简单的性能测试发现了一些问题,经过解决这些问题没有再重现,有些问题自己确切知道原因,有的则不太确定. 1.配置文件读取问题 项目中使用.properties作为配 ...

  4. 某软件大赛C#版考题整理——【编程题】

    三.编程题(4小题共40.0分)程序及结果写入对应文框内 1. 孪生素数查找程序. 所谓孪生素数指的是间隔为2 的相邻素数,就像孪生兄弟.最小的孪生素数是(3, 5),在100 以内的孪生素数还有 ( ...

  5. linux下重要的网络配置文件

    linux下重要的网络配置文件:一; /etc/sysconfig/network  文件内容: NETWORKING=yes                                <= ...

  6. C# listview控件右击导出数据到txt文本

    private void 导出成功点击ToolStripMenuItem_Click(object sender, EventArgs e) { ) { MessageBox.Show("列 ...

  7. 前端图片压缩(纯js)

    html代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <t ...

  8. Python爬虫学习——光学字符识别

    使用pytesseract库对图片文件(jpg.png.bmp等)进行识别,把图片转换成字符串输出. import pytesseract from PIL import Image img = Im ...

  9. Backlight当前行背景高亮显示

    下载地址:https://github.com/limejelly/Backlight-for-XCode PS:Xcode 8.0 默认支持了 跟VVDocumenter规范注释生成器的安装方式一样 ...

  10. webdriver 日期控件的处理

    http://www.cnblogs.com/liu-ke/p/4200736.html http://blog.csdn.net/wanglha/article/details/44620627 h ...