Lua中的面向对象实现探讨

元表概念 Lua中,面向对向是用元表这种机制来实现的。元表是个很“道家”的机制,很深遂,很强大,里面有一些基本概念比较难理解透彻。不过,只有完全理解了元表,才能对Lua的面向对象使用自如,才能在写Lua代码的高级语法时游刃有余。 首先,一般来说,一个表和它的元表是不同的个体(不属于同一个表),在创建新的table时,不会自动创建元表。 但是,任何表都可以有元表(这种能力是存在的)。 e.g. t = {} print(getmetatable(t))   --> nil t1 = {} setmetatable(t,t1) assert(getmetatable(t) == t1) setmetatable( 表1, 表2) 将表2挂接为表1的元表,并且返回经过挂接后的表1。 元表中的__metatable字段,用于隐藏和保护元表。当一个表与一个赋值了__metatable的元表进行挂接时,用getmetatable操作这个表,就会返回__metatable这个字段的值,而不是元表!用setmetatable操作这个表(即给这个表赋予新的元表),那么就会引发一个错误。 table: 0x9197200 Not your business lua: metatest.lua:12: cannot change a protected metatable stack traceback:     [C]: in function 'setmetatable'     metatest.lua:12: in main chunk     [C]: ? __index方法 元表中的__index元方法,是一个非常强力的元方法,它为回溯查询(读取)提供了支持。而面向对象的实现基于回溯查找。 当访问一个table中不存在的字段时,得到的结果为nil。这是对的,但并非完全正确。实际上,如果这个表有元表的话,这种访问会促使Lua去查找元表中的__index元方法。如果没有这个元方法,那么访问结果就为nil。否则,就由这个元方法来提供最终的结果。 __index可以被赋值为一个函数,也可以是一个表。是函数的时候,就调用这个函数,传入参数(参数是什么后面再说),并返回若干值。是表的时候,就以相同的方式来重新访问这个表。(是表的时候,__index就相当于元字段了,概念上还是分清楚比较好,虽然在Lua里面一切都是值) 注意,这个时候,出现了三个表的个体了。这块很容易犯晕,我们来理一下。 我们直接操作的表,称为表A,表A的元表,称为表B,表B的__index字段被赋予的表,称为表C。整个过程是这样的,查找A中的一个字段,如果找不到的话,会去查看A有没有元表B,如果有的话,就查找B中的__index字段是否有赋值,这个赋值是不是表C,如果是的话,就再去C中查找有没有想访问的那个字段,如果找到了,就返回那个字段值,如果没找到,就返回nil。 对于没有元表的表,访问一个不存在的字段,就直接返回一个nil了。 __newindex是对应__index的方法,它的功能是“更新(写)”,两者是互补的。这里不细讲__newindex,但是过程很相似,灵活使用两个元方法会产生很多强大的效果。 从继承特性角度来讲,初步的效果使用__index就可以实现了。 面向对象的实现 Lua应该说,是一种原型语言。原型是一种常规的对象,当其他对象(类的实例)遇到一个未知的操作时,原型会去查找这个原型。在这种语言中要表示一个类,只需创建一个专用作其他对象的原型。实际上,类和原型都是一种组织对象间共享行为的方式。 Lua中实现原型很简单,在上面分析的的那个三个表中,C就是A的原型。 原理讲通后,来一点小技巧。其实,上面说的三个表嘛,不一定就是完全不同的。A和C可以是同一个。看下面的例子。 A = {} setmetatable( A,{ __index = A } ) 这时,相当于A是A自身的原型了,自己是自己的原型,是个很有趣的字眼。就是说在查找的时候,在自己身上找不到就不会去其他地方找了。不过,自身是自身的原型本身并没有多大用的。如果A能做为一个类,然后生成的新对象以A做为原型,这才有用,后面谈。 再看,自身也可以是自身的元表的。即A可以是A的元表。 A = {} setmetatable( A,A ) 这时就可以这样写了, A.__index = 表或函数 自己是自己的元表有用处的,如果A.__index是赋予的一个表,至少能在内存中少产生一个表;而如果A.__index是一个函数,那么就会产生很简洁强大的效果。(__index为其本身的一个字段了,不是很简洁吗) 然后,元表B与原型表C也可以是同一个。 A = {} B = {} B.__index = B setmetatable( A,B ) 这时,一个表的元表,就是这个表的原型,在面向对象的概念里,就是这个表的类。 我们甚至可以,这样来写: A = {} setmetatable( A,A ) A.__index = A 从语法原理上,是行得通的。但Lua解释器为了避免出现不必要的麻烦(循环定义),把这种情况给Kick掉了,如果这样写,会报错,并提示 loop in gettable 说真的,这样定义也确实没什么用处。 下面开始正式进入面向对象的实现。 先引用一下Sputnik中的实现片断, local Sputnik = {} local Sputnik_mt = {__metatable = {},__index = Sputnik} function new(config,logger)    -- 这里生成obj对象之后,obj的原型就是Sputnik了,而后面会有很多的Sputnik的方法定义    local obj = setmetatable({},Sputnik_mt)    -- 这里的方法就是“继承”的Sputnik的方法    obj:init(config)    返回这个对象的引用    return obj end 由上面可见,两个表定义加上一个方法,实现了类,及由类产生对象的方案。因为这是在模块中,故new前面没有表名称。这种方式实现有个好处,就是在外界调用此模块的时候,使用 sputnik = require "sputnik" 然后,调用 s = sputnik.new() 就可以生成一个sputnik对象s了,这个对象会继承原型Sputnik(就是上面定义的那个表)的所有方法和属性。 但是,这种方法定义的,也有点问题,就是,类的继承实现上不方便。它只是在类的定义上,和生成对象的方式上比较方便,但是在类之间的继承上不方便。 下面,用另一种方式实现。 A = {     x = 10,    y = 20 } function A:new( t )     local t = t or {}     self.__index = self     setmetatable( t,self )     return t end 从A中产生一个对象AA AA = A:new() 此时,AA就是一个新表了,它是一个对象,但也是一个类。它还可以继续如下操作: s = AA:new() AA中本来是没有new这个方法的,但它被赋予了一个元表(同时也是原型),这个时候是A,A中有new方法和x,y两个字段。 AA通过__index回溯到A找到了new方法,并且执行new的代码,同时还会传入self参数。这就是奇妙所在,此时候传入的self参数引用的是AA这个表,而不再是第一次调用时A这个表了。因此 AA:new() 执行后,同样,是生成了一个新的对象s,同时这个对象以AA为原型,并且继承AA的所有内容。至此,我们不是已经实现了类的继承了吗?AA现在是A的子类,s是AA的一个对象实例。后面还可以以此类推,建立长长的继承链。 由上也可见,类与原型概念上还是有区别的,Lua是一种原型语言,这点体现的得很明显,类在这种语言中,就是原型,而原型仅仅是一个常规对象。 下面,如果在A中定义了函数: function A:acc( v )     self.x = self.x + v end function A:dec( v )     if v > self.x then error "not more than zero" end     self.x = self.x - v end 然后,现在调用 s:acc(5) 那么,是这样调用的,先是查找s中有无acc这个方法,没有找到,然后去找AA中有无acc这个方法,还是没找到,就去A中找有无此方法,找到了。找到后,将指向s的self参数和5这个参数传进acc函数中,并执行acc的代码,执行里面代码的时候,这一句: self.x = self.x + v 在表达式右端,self.x是一个空值,因为self现在指向的是s,因此,根据__index往回回溯,一直找到A中有一个x,然后引用这个x值,10,因此,上面表达式就变成 self.x = 10 + 5 右边计算得15,赋值给左边,但这时self.x没有定义,但是s(及s的元表)中也没有定义__newindex元方法,于是,就在self(此时为s)所指向的表里面新建一个x字段,然后将15赋值给这个字段。 经过这个操作之后,实例s中,就有一个字段(成员变量)x了,它的值为15。 下次,如果再调用 s:dec(10) 的话,就会做类似的回溯操作,不过这次只做方法的回溯,而不做成员变量x的回溯,因为此时s中已经有x这个成员变量了,执行了这个函数后,s.x会等于5。 综上,这就是整个类继承,及对象实例方法引用的过程了。不过,话还没说完。 AA作为A的子类,本身是可以有一些作为的,因为AA之下的类及对象在查找时,都会先通过它这一关,才会到它的父亲A那里去,因此,它这里可以重载A的方法,比如,它可以定义如下函数: function AA:acc(v)     ... end function AA:dec(v)     ... end 函数里面可以写入一些新的不一样的内容,以应对现实世界中复杂的差异性。这个特性用面向对象的话来说,就是子类可以覆盖父类的方法及成员变量(字段),也就是重载。这个特性是必须的。 AA中还可以定义一些A中没有的方法和字段,操作是一样的,这里提一下。 Lua中的对象还有一个很灵活强大的特性,就是无须为指定一种新行为而创建一个新类。如果只有一个对象需要某种特殊的行为,那么可以直接在该对象中实现这个行为。也就是说,在对象被创建后,对象的方法和字段还可以被增加,重载,以应对实际多变的情况。而毋须去劳驾类定义的修改。这也是类是普通对象的好处。更加灵活。 可以看出,A:new()这个函数是一个很关键的函数,在类的继承中起了关键性因素。不过为了适应在模块中使用的情况(很多),在function A:new(t)之外还定义一个 function new(t)     A:new(t) end 将生成函数封装起来,然后,只需使用 模块名.new() 就可以在模块外面生成一个A的实例对象了。 差不多了吧,可以看到,这种类实现的机制是多么自洽,简洁,灵活,强大!不过要折磨下你的大脑了。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


1.github代码实践源代码是lua脚本语言,下载th之后运行thmain.lua-netTypevgg-savevgg_cifar10/-S0.0001,报错: 试看看安装lua:报错了,参考这篇文章:ubuntu18.04安装lua的步骤以及出现的问题_weixin_41355132的博客-CSDN博客问题解决,安装成功:情况并没有好转,出现相
此文为搬运帖,原帖地址https://www.cnblogs.com/zwywilliam/p/5999924.html前言在看了uwa之前发布的《Unity项目常见Lua解决方案性能比较》,决定动手写一篇关于lua+unity方案的性能优化文。整合lua是目前最强大的unity热更新方案,毕竟这是唯一可以支持ios热更新的办法。然而作
Rime输入法通过定义lua文件,可以实现获取当前时间日期的功能。1.TIMERime是一款可以高度自定义的输入法,相关教程可以查看往期文章,关于时间获取是指输入一个指定关键字,输出当前时间,效果如下(我定义了time关键字):实现如下:①在用户文件夹中新建一个rime.lua文件加入如下代码 ti
localfunctiongenerate_action(params)localscale_action=cc.ScaleTo:create(params.time,params.scale_x,params.scale_y)localfade_action=cc.FadeIn:create(params.time)returncc.Spawn:create(scale_action,fade_action)end
2022年1月11日13:57:45 官方:https://opm.openresty.org/官方文档:https://opm.openresty.org/docs#table-of-contents为什么建议使用opm不建议使用luarocks?http://openresty.org/cn/using-luarocks.html官方解释:请注意!LuaRocks并不是OpenResty官方推荐的装包方式。LuaRoc
在Lua中的table(表),就像c#中的HashMap(哈希表),key和value一一对应。元表:table的一个操作的拓展,里面包含关联了对应的方法,元方法就是其中一个。元方法:当你通过键来访问table的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index键。如果__inde
表排序:table.sort(list[,comp])参数list:指定表,可选参数comp:排序函数,无参数时通常按升序排序。排序函数针对表中连续的序列,其间不可以存在空洞或nil,排序函数需要两个形参(对应表中每次参加比较的两个数据),需要一个比较两个形参表达式的返回值,不能含有等于关系,例如>=,<=,==。do
一、安装lua环境1.1安装依赖包[root@centos7~]#yuminstallgccreadline-devel1.2下线lua源码包并解压[root@centos7~]#wgethttp://www.lua.org/ftp/lua-5.3.5.tar.gz[root@centos7~]#tarxvflua-5.3.5.tar.gz-C/usr/local/src1.3进行编译[root@centos7~]
官网OpenResty® 是一个基于 Nginx 与Lua的高性能Web平台,其内部集成了大量精良的Lua库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态Web应用、Web服务和动态网关。OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由
表参考《lua程序设计》可以认为,表是一种动态分配的对象,程序只能操作指向表的引用(或指针)。除此以外,Lua语言不会进行隐藏的拷贝(hiddencopies)或创建新的表--创建表a={}--创建空表k="x"a[k]=10--键“x”值10a[20]="great"--键20值“great”print(a["x"])-->10
https://github.com/galenho/crossover.git一个跨平台的lua游戏服务器开发框架,该框架采用多线程并发来处理消息,开发者只需要调用相应的接口函数并绑定相应的回调函数即可,在逻辑层表现为单线程的开发模式,使开发者易用,易调试,易维护,易扩展,同时拥有快速的响应能力。   框架使用面
参考链接:https://www.runoob.com/lua/lua-metatables.htmlhttps://www.jianshu.com/p/cb945e7073a3 元表是一个table,可以让我们改变table的行为,每个行为有对应的元方法例如,对table进行设置键值,查找键值,运算等,就会触发对应的元方法1--__index:table被访问时,如果找不到这
https://github.com/yuin/gopher-luahttps://github.com/yuin/gopher-lua Lua5.1ReferenceManual-contentshttp://www.lua.org/manual/5.1/ go中使用luapackagemainimport( lua"github.com/yuin/gopher-lua")funcmain(){ l:=lua.NewState() d
编译问题不要留到运行时才跑出来啊。早上9:00-中午3:00,6个小时,服了自己了。 写了一个测试,springboot+redis+lua执行到redisTemplate.execute(redisScript,idList)的时候一直报错,integer无法转换为string。我一直以为是lua脚本写错了,翻文档翻过来又翻过去,写法变了又变,还是解
        。。是字符串连接符,字典用=号连接,  注意fordoend都是连一起,  注意ifthen,  如果local在函数里,是可以访问,非local出了函数一样能用,  doend代码块也是一样,    注意点号表示,只能key是字符串,  注意括号不是必须
C语言与Lua之间的相互调用详解写一个C调用Lua的Demo编译运行C语言调用Lua编译问题总结正确的编译命令问题1:缺少-lm参数问题2:缺少-ldl参数​1、为什么会出现undefinedreferenceto‘xxxxx’错误?​2、-l参数和-L参数写一个C调用Lua的Demo编译运行add.c内容//你需要
1、动态输出打开E:\study\openresty\openresty-1.19.9.1-win64目录下的confginx.conf文件在server中增加一下代码 location/hello{ default_typetext/html; content_by_lua'ngx.say("<p>hello,world</p>")'; }运行后,效果如下图localhost
参见:lipp/lua-websockets:WebsocketsforLua.(github.com)github网址可能需手动转换lipp.github.com/lua-websockets/>github.com/lipp/lua-websocketswebsockets为底层的类似于TCP、UDP的socket(实现上基于更底层的socket),不同于上层的webserver服务端(Service)需并行地支持多
lua发送消息到rabbitmq,我们选择类库lua-resty-rabbitmqstomp 来完成这个任务。类库安装:进入nginx.conf中 lua_package_path 中对应的目录下的resty目录(没有则创建),执行:wget-chttps:/aw.githubusercontent.com/wingify/lua-resty-rabbitmqstomp/master/libes
1Lua介绍Lua是一门以其性能著称的脚本语言,被广泛应用在很多方面。Lua一般用于嵌入式应用,现在越来越多应用于游戏当中,魔兽世界,愤怒的小鸟都有用到。优势Lua极易嵌入到其他程序,可当做一种配置语言。提升应用性能,比如:游戏脚本,nginx,wireshark的脚本兼容性强,可以直接使用C