lua元表
__index元方法:
按照之前的说法,如果A的元表是B,那么如果访问了一个A中不存在的成员,就会访问查找B中有没有这个成员。这个过程大体是这样,但却不完全是这样,实际上,即使将A的元表设置为B,而且B中也确实有这个成员,返回结果仍然会是nil,原因就是B的__index元方法没有赋值。别忘了我们之前说过的:“元表是一个操作指南”,定义了元表,只是有了操作指南,但不应该在操作指南里面去查找元素,而__index方法则是“操作指南”的“索引失败时该怎么办”。这么说有点绕。所以:
举个栗子:
- father = {
- house=1
- }
- son = {
- car=1
- }
- setmetatable(son, father) --把son的metatable设置为father
- print(son.house)
输出的结果是nil,但如果把代码改为
- father = {
- house=1
- }
- father.__index = father -- 把father的__index方法指向自己
- son = {
- car=1
- }
- setmetatable(son, father)
- print(son.house)
输出的结果为1,符合预期
这样一来,结合上例,来解释__index元方法的含义:
在上述例子中,访问son.house时,son中没有house这个成员,但Lua接着发现son有元表father,注意:此时,Lua并不是直接在father中找名为house的成员,而是调用father的__index方法,如果__index方法为nil,则返回nil,如果是一个表(上例中father的__index方法等于自己,就是这种情况),那么就到__index方法所指的这个表中查找名为house的成员,于是,最终找到了house成员。
注:__index方法除了可以是一个表,还可以是一个函数,如果是一个函数,__index方法被调用时将返回该函数的返回值。
到这里,总结一下Lua查找一个表元素时的规则,其实就是如下3个步骤:
1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表(操作指南),如果没有元表,返回nil,有元表则继续
3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值
下面说说与__index有点相似的__newindex元方法。
1.查询与更新
上面我们介绍了__index元方法,总结来说,__index元方法是用于处理调用table中不存在的字段。
注意,【调用】这个词,只是调用,而不是赋值。
如果,我们要对table中某个不存在的字段赋值呢?(小若:就,直接赋值啊!)
没错,我们直接就能赋值了,不会报错的。
问题是,如果我想监控这个操作呢?如果有人想对table不存在的字段进行赋值的时候,我想进行一些额外的处理呢?
这时候就要用到__newindex。
大家要记住这句话:__index用于查询,__newindex用于更新。
等会不要混乱了, 初次接触的话,有可能会混乱。
2.看看普通的赋值情况
我们先来看看正常情况下的赋值,如代码:
name = "none",
money = 9000000,
sayHello = function()
print("大家好,我是聪明的豪。");
end
}
local t1 = {};
local mt = {
__index = smartMan,
}
setmetatable(t1, mt);
t1.sayHello = function()
print("en");
end;
t1.sayHello();
这是上一篇用过的例子,一个模仿继承结构的例子。
来分析一下,mt作为t1的元表,设置__index为smartMan。
于是,当我们调用t1中不存在的字段时,就会自动去smartMan中查找。
比如我们调用了t1.sayHello(),自然能找到对应的函数。
先来看看输出结果:
我们调用t1的sayHello字段,t1并不存在这个字段(虽然可以通过__index的方式来找到smartMan的sayHello字段)。
但这不影响,给这个字段赋值,然后再调用t1.sayHello(),发现是成功的。
这和我们以往的做法一样,对table做正常的赋值操作,不管table本身是否存在这个字段。
3.监控赋值
好了,普通情况我们已经试过了,如果我们想监控table的赋值操作呢?
对于不存在的字段,我们不需要被赋值呢?想要制作一个只读的table呢?
如果你有这些想法,那么欢迎拨打屏幕下方的号码,前10位打进的还赠送价值..(小若:停!)
那么,如果你有这些想法,请看看下面的代码:
name = "none",
money = 9000000,
sayHello = function()
print("大家好,我是聪明的豪。");
end
}
local t1 = {};
local mt = {
__index = smartMan,
__newindex = function(table, key, value)
print(key .. "字段是不存在的,不要试图给它赋值!");
end
}
setmetatable(t1, mt);
t1.sayHello = function()
print("en");
end;
t1.sayHello();
留意mt元表,我们给它加了一个__newindex。
运行代码,输出结果如下:
[LUA-print] 大家好,我是聪明的豪。
很显然,sayHello字段赋值失败,因为给sayHello字段赋值的时候,调用了__newindex元方法,代替了赋值操作。
(小若:为什么?sayHello字段不是存在的么?为什么会说不存在呢?)
这里有一个地方要注意的,t1中确实是不存在sayHello字段的,它只是因为有元表存在,而元表里的__index元方法的值是smartMan这个table。
从而,可以在t1找不到sayHello字段的时候,去smartMan中寻找。
但,实际上,t1确实是不存在sayHello字段的,不知道大家能绕明白不?
因此,当试图给t1的sayHello字段赋值时,Lua判定sayHello字段是不存在的,所以会去调用元表里的__newindex元方法。
__newindex元方法被调用的时候会传入3个参数:table本身、字段名、想要赋予的值。
4.隔山打牛,通过给一个table给另一个table的字段赋值
和__index一样,__newindex元方法也可以赋予一个table值。
这种情况下就有点意思了,先看看代码:
name = "none",
}
local other = {
name = "大家好,我是很无辜的table"
}
local t1 = {};
local mt = {
__index = smartMan,
__newindex = other
}
setmetatable(t1, mt);
print("other的名字,赋值前:" .. other.name);
t1.name = "小偷";
print("other的名字,赋值后:" .. other.name);
print("t1的名字:" .. t1.name);
这次的代码和刚刚差不多,但是我们新加了一个other的table,然后把other作为__newindex的值。
于是,当给t1的name字段赋值时,就会发生一些奇怪的事情…
先来看看输出结果:
[LUA-print] other的名字,赋值后:小偷
[LUA-print] t1的名字:none
当给t1的name字段赋值后,other的name字段反而被赋值了,而t1的name字段仍然没有发生变化。
(实际上t1的name字段还是不存在的,它只是通过__index找到了smartMan的name字段,这个就不唠叨了。)
于是,我们给t1的name赋值的时候,实际上是给other的name赋值了。
好吧,可怜的other。
5.总结规则
这就是__newindex的规则:
a.如果__newindex是一个函数,则在给table不存在的字段赋值时,会调用这个函数。
b.如果__newindex是一个table,则在给table不存在的字段赋值时,会直接给__newindex的table赋值。
6.结束
好了,关于元表和元方法的基础内容基本上告一段落了,接下来还有一篇关于元表和元方法的文章,也是一些比较零散的知识点。
之后,还会提到元表和元方法的,因为它们实在是太重要了。
理解lua 语言中的点、冒号与self
lua编程中,经常遇到函数的定义和调用,有时候用点号调用,有时候用冒号调用,这里简单的说明一下原理。
girl = {money = 200}
function girl.goToMarket(girl ,someMoney)
girl.money = girl.money - someMoney
end
girl.goToMarket(girl ,100)
print(girl.money)
可以看出,这里进行了方法的点号定义和点号调用。
boy = {money = 200}
function boy:goToMarket(someMoney)
self.money = self.money - someMoney
end
boy:goToMarket(100)
print(boy.money)
boy.goToMarket(boy,100)
print(boy.money)
这里进行了冒号定义和冒号调用。
以上的打印结果都是100。
以上的打印结果都是0。
可以看出,冒号定义和冒号调用其实跟上面的效果一样,只是把第一个隐藏参数省略了,而该参数self指向调用者自身
当然了,我们也可以点号定义冒号调用,或者冒号定义点号调用
如:
boy = {money = 200}
function boy.goToMarket(self ,someMoney)
self.money = self.money - someMoney
end
boy:goToMarket(100)
print(boy.money)
lua元表的更多相关文章
- [转]LUA元表
lua元表和元方法 <lua程序设计> 13章 读书笔记 lua中每个值都有一个元表,talble和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表.lua在 ...
- 学习Lua setmetatable Lua 元表
Lua 元表(Metatable) 在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作. 个人理解,这个相当于其他语言的继承,是把这个类的方法 ...
- Step By Step(Lua元表与元方法)
Step By Step(Lua元表与元方法) Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表 ...
- lua元表与元方法
lua中提供的元表(metatable)与元方法(metamethod)是一种非常重要的语法,metatable主要用于做一些类似于C++重载操作符式的功能. lua中提供的元表是用于帮助lua变量完 ...
- lua元表和元方法 《lua程序设计》 13章 读书笔记
lua中每个值都有一个元表,talble和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表.lua在创建table时不会创建元表. t = {} print(getmet ...
- <6>Lua元表和冒号 self
Lua中没有像C.C++.JAVA中的类概念,面向对象等 ,但我们可以模拟出来 1. Lua中有个很重要的概念元表 设置元表setmetatable()函数 获取元表getmetatable()函数 ...
- lua元表学习
a = {, } b= {, } vector2 = {} function vector2.Add(v1, v2) if(v1 == nil or v2 == nil)then return nil ...
- [转]lua元表代码分析
http://lin-style.iteye.com/blog/1012138 版本整理日期:2011/4/21 元表其实就是可以让你HOOK掉一些操作的一张表. 表的定义在ltm.h/c的文件里.对 ...
- lua元表详解
元表的作用 元表是用来定义对table或userdata操作方式的表 举个例子 local t1 = {1} local t2 = {2} local t3 = t1 + t2 我们直接对两个tabl ...
随机推荐
- Inno Setup打包注意事项
Inno Setup是一个开源的,商业的,快捷的脚本打包工具. 具体打包流程根据界面提示就可以搞定,下面讲解几个注意事项 1.在安装包进行安装的过程当中,很多程序都需要修改配置信息,这就要求我们在安装 ...
- #tensorflow入门(1)
tensorflow入门(1) 关于 TensorFlow TensorFlow™ 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库.节点(Nodes)在图中表示数学操 ...
- 安装debian 9.1后,中文环境下将home目录下文件夹改为对应的英文
安装了debian 9.1后,中文环境下home目录下文件夹显示的是中文,相当不方便cd命令,改为对应的英文吧,需要用到的软件xdg-user-dirs-gtk #安装需要的软件 sudo apt i ...
- vue-cli中如何引入jquery
前言 虽然vue不推荐直接操作DOM,而且也提供了操作DOM的方式.但是在某些时候还是要用到jquery(囧),那么如何在使用vue-cli的时候引入jquery呢? 安装 国内镜像 cnpm 安装 ...
- Java正则表达式详解+练习
一.导读 正则表达式,又称规则表达式.(英文名Regular Expression,所以代码中常以regex.regexp.RE表示).正则表达式简单说就是用于操作文本数据的规则表达式,在Java中我 ...
- [译]ASP.NET Core 2.0 本地文件操作
问题 如何在ASP.NET Core 2.0中受限地访问本地目录和文件信息? 答案 新建一个空项目,修改Startup类,添加访问本地文件所需的服务: public void ConfigureSer ...
- 1_ROS学习
1_搭建树莓派环境 树莓派是一个嵌入式计算机,专门用来做嵌入式开发的.我们组上用的树莓派是raspberry pi 3 mode B,5V供电,最大电流不超过2A: 我们需要在树莓派上安装上操作系统. ...
- 读书笔记-你不知道的JS上-声明提升
变量声明提升 Javascript代码一般情况下是由上往下执行的,但是有些情况下不成立. a = 2; //变量声明被提升在当前作用域顶部 var a; console.log(a); console ...
- Python - 单步调试
Python 有一个单步调试器模块,能实现基本的调试效果!详情请看Python标准文档说明:https://docs.python.org/2/library/pdb.html 调试例子: >& ...
- poj 3461 Oulipo(KMP模板题)
Oulipo Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 36903 Accepted: 14898 Descript ...