前两节的算术类、关系类运算符的元方法都为各种错误情况定义了行为,它们不会改变语言的常规行为。

但是Lua还提供了两种可以改变table行为的方法:

一种是查询table中不存在的字段、一种是修改table中不存在的字段。

__index元方法

当访问一个table中不存在的字段时,得到的结果为nil。这样的访问会促使解释器去查找一个叫__index的元方法。

如果没有这个元方法,那么访问结果就是nil。

一个有关继承的示例:假设要创建一些描述窗口的table,每个table中必须表述一些窗口参数,如:位置、大小、主题....

所有这些参数都有默认值,因此希望在创建窗口对象时可以仅提供那些不同于默认值的参数。

第一种方法是使用一个构造式,在其中填写那些不存在的字段。

第二种方法是让新窗口从一个原型窗口处继承所有不存在的字段。

首先,声明一个原型和一个构造函数,构造函数创建新的窗口,并使它们共享同一个元表:

prototype = {x = , y = ,width =  , height  = } --用默认值来创建一个原型
mt = {} --创建一个元表
function new(o) --声明构造函数
setmetatable(o,mt)
return o
end

现在,定义__index元方法:

mt.__index = function(_,key)
return prototype[key]
end

在这段代码之后,创建一个新窗口,并查询一个它没有的字段:

w = new{x=,y=}
print(w.width) -->100

若Lua检测到w中没有width字段,但在其元素中却有一个__index字段,那么Lua就会以 w 和 “width” 来调用这个__index元方法。

随后元方法用这个key来索引原型table,并返回结果。

在Lua中,将__index元方法用于继承是很普遍的方法,因此Lua还提供了一种更便捷的方式来实现此功能。

__index不一定是一个函数,可以是一个table。

当它是一个table时,Lua就以相同的方式来重新访问这个table。因此,上面的__index的声明可以写为:

mt.__index = prototype

现在,Lua查到元素__index时,发现它是一个table,那么Lua就会在prototype中继续查找。

也就是说,Lua会在这个table中国重复这个访问过程:类似于这样访问

prototype["width"]

这样将一个table作为__index元方法 是快捷的、实现单一继承的方式。虽然用函数更加灵活,但是开销较大。

可以通过函数来实现多重继承、缓存及其他一些功能。

如果不想在访问一个table时涉及到它的__index元方法,可以使用rawget函数。

调用rawget(t , i) 就是对table t 进行了一个原始的访问。

__newindex元方法

它与__index类似,不同之处在于它用于table的更新,而__index用于table的查询。

当对一个table中不存在的索引赋值时,解释器就会查找__newindex元方法。如果有这个元方法,解释器就调用它,而不是执行赋值。

如果这个元方法是一个table,解释器就在此table中执行赋值,而不是对原来的table。

也可以通过rawset( t ,k , v )来绕过元方法,直接设置table  t 中与key k 相关联的value v

组合使用__index和__newindex元方法就可以实现出Lua中的一些强大功能,例如:

只读的table、具有默认值的table、面向对象编程中的继承。

具有默认值的table

常规table中的任何字段默认值都是nil,通过元表就可以很容易地修改这个默认值:

function setDefault(t,d)
local mt = {__index = function() return d end}
setmetatable(t,mt)
end tab = {x=,y=}
print(tab.x,tab.z) --> 10 nil
setDefault(tab, )
print(tab.x,tab.z) -->10 1

在调用setDefault后,任何对tab中不存在的访问都将调用它的__index元方法,而这个元方法会返回d的值。

setDefault函数为所有需要默认值的table创建了一个新的元素。

如果准备创建很多需要默认值的table,这个开销比较大。

由于在元表中默认值d是与元方法关联在一起的,所以setDefault无法为所有table都使用同一个元表。

若要让具有不同默认值的table都使用同一个元表,那么需要将每个元表的默认值都放到table本身中。

可以使用一个额外的字段来保存默认值。避免冲突,可以使用"___"这样的key作为额外的字段:

local mt = {__index = funxtion (t) return t.___ end}
function setDefault(t, d )
t.___ = d
setmetatable(t,mt)
end

如果担心名称冲突,那么要确保这个特殊key的唯一性也很容易,只需创建一个新的额table,并用它作为key即可:

local key = {}  --唯一的key
local mt = {__index = function(t) return t[key] end}
function setDefault(t , d)
t[key] =d
setmetatable(t,mt)
end

还有一种方法可以将table与其默认值关联起来:使用一个独立的table,它的key为各种table,value就是各种table的默认值。

不过,为了正确地实现这种做法,需要一种特殊性质的table,就是“弱引用table”。

还有一种备忘录元表的方法,它能使具有相同默认值的table复用同一个元表,也需要用到弱引用table。以后会讲到.....

以上内容来自:《Lua程序设计第二版》和《Programming in Lua  third edition 》

chapter 13_3 table访问的元方法的更多相关文章

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

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

  2. chapter 13_1 算术类的元方法

    假设用table来表示集合,用function去计算集合的交集.并集. 为了保持名称空间的整齐,将这些函数存入一个名为Set的table中. 现在,假设用“+”来计算两个集合的并集,那么就要让所有用于 ...

  3. Lua 学习笔记(十一)元表与元方法

    在Lua中的每个值都有一套预定义的操作集合.例如可以将数字相加,可以连接字符串,还可以在table中插入一对key-value等.但是我们无法将两个table相加,无法对函数作比较,也无法调用一个字符 ...

  4. lua元表与元方法

    lua中提供的元表(metatable)与元方法(metamethod)是一种非常重要的语法,metatable主要用于做一些类似于C++重载操作符式的功能. lua中提供的元表是用于帮助lua变量完 ...

  5. lua元表和元方法 《lua程序设计》 13章 读书笔记

    lua中每个值都有一个元表,talble和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表.lua在创建table时不会创建元表. t = {} print(getmet ...

  6. lua metatable和metamethod元表和元方法

    Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表达式a+b.当Lua试图将两个table相加时, ...

  7. lua元表(metatable)和元方法(metamethod)

    (一) 元表概念: 引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等.但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作. 访问机制 ...

  8. Step By Step(Lua元表与元方法)

    Step By Step(Lua元表与元方法) Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表 ...

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

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

随机推荐

  1. Scala 中Null, None, Nothing, Nil

    转自:http://blog.csdn.net/bluejoe2000/article/details/30465175 在scala中这四个类型名称很类似,作用确实完全不同的. None是一个obj ...

  2. get_class_vars 返回由类的默认属性组成的数组

    get_class_vars (PHP 4, PHP 5) get_class_vars - 返回由类的默认属性组成的数组 说明 array get_class_vars ( string $clas ...

  3. CSU 1812 三角形和矩形

    湖南省第十二届大学生计算机程序设计竞赛$J$题 计算几何. #pragma comment(linker, "/STACK:1024000000,1024000000") #inc ...

  4. JavaScript忍者秘籍——运行时代码求值

    1. 代码求值机制 JavaScript中,有很多不同的代码求值机制. ● eval()函数 ● 函数构造器 ● 定时器 ● <script>元素 - 用eval()方法进行求值 作为定义 ...

  5. C#异常性能影响

    何谓异常 很多人在讨论异常的时候很模糊,仿佛所谓异常就是try{}catch{},异常就是Exception,非常的片面,所以导致异常影响性能,XXXX……等很多奇怪的言论,所以在此我意在对异常正名. ...

  6. cocos2d安装配置及打包成Android

    vs+python+cocos2d python下载:点这里 这里需要下载Python 2.X版本.曾经以为要下载3.x版本 后来装上发现cocos2d-x提供的python运行报错,所以卸载以后重新 ...

  7. Python基础知识学习_Day2

    一.for循环 1.1功能及语法 for循环是迭代循环机制(while是条件循环),语法如下: for i in a b c: print(i) 1.2典型例子: 1.2.1猜年龄循环 realy_a ...

  8. 更新BLUZ

    bluez的编译安装依赖好些软件,下面记录下,可能比较简陋. configure: error: GLib >= 2.28 is required解决方法:一般glib会被安装,主要是一些开发文 ...

  9. 去掉word冗余格式 java正则表达式

    word转换html时,会留下很多格式,有些格式并不是我们所需要的,然而这些格式比真正的文章内容还要多,严重影响页面的加载速度,因此就需要找个一个好的解决方案把这些多余的格式个去掉.网上有很多去除wo ...

  10. 1.部分(苹果)移动端的cookie不支持中文字符,2.从json字符串变为json对象时,只支持对象数组

    1.移动端的cookie不支持中文字符.可以用编码,解码的方式解决. 2.json字符串变成相应 的,json对象数组字符串.就这样 3.不同客户端(移动端.电脑)的请求,在C#服务端的取时间的格式竟 ...