zoukankan      html  css  js  c++  java
  • lua元表

    __index元方法:
    按照之前的说法,如果A的元表是B,那么如果访问了一个A中不存在的成员,就会访问查找B中有没有这个成员。这个过程大体是这样,但却不完全是这样,实际上,即使将A的元表设置为B,而且B中也确实有这个成员,返回结果仍然会是nil,原因就是B的__index元方法没有赋值。别忘了我们之前说过的:“元表是一个操作指南”,定义了元表,只是有了操作指南,但不应该在操作指南里面去查找元素,而__index方法则是“操作指南”的“索引失败时该怎么办”。这么说有点绕。所以:


    举个栗子:

     
    1. father = {  
    2.     house=1  
    3. }  
    4. son = {  
    5.     car=1  
    6. }  
    7. setmetatable(son, father) --把son的metatable设置为father  
    8. print(son.house)  

    输出的结果是nil,但如果把代码改为

     
    1. father = {  
    2.     house=1  
    3. }  
    4. father.__index = father -- 把father的__index方法指向自己  
    5. son = {  
    6.     car=1  
    7. }  
    8. setmetatable(son, father)  
    9. 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.看看普通的赋值情况

    我们先来看看正常情况下的赋值,如代码:

        local smartMan = {
            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();
     smartMan.sayHello();

    这是上一篇用过的例子,一个模仿继承结构的例子。
    来分析一下,mt作为t1的元表,设置__index为smartMan。
    于是,当我们调用t1中不存在的字段时,就会自动去smartMan中查找。
    比如我们调用了t1.sayHello(),自然能找到对应的函数。
     
    先来看看输出结果:

    [LUA-print] en
    [LUA-print] 大家好,我是聪明的豪。


    我们调用t1的sayHello字段,t1并不存在这个字段(虽然可以通过__index的方式来找到smartMan的sayHello字段)。

    但这不影响,给这个字段赋值,然后再调用t1.sayHello(),发现是成功的。

    这和我们以往的做法一样,对table做正常的赋值操作,不管table本身是否存在这个字段。

    3.监控赋值

    好了,普通情况我们已经试过了,如果我们想监控table的赋值操作呢?
    对于不存在的字段,我们不需要被赋值呢?想要制作一个只读的table呢?
     
    如果你有这些想法,那么欢迎拨打屏幕下方的号码,前10位打进的还赠送价值..(小若:停!)
    那么,如果你有这些想法,请看看下面的代码:


        local smartMan = {
            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字段是不存在的,不要试图给它赋值!
    [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值。
    这种情况下就有点意思了,先看看代码:


        local smartMan = {
            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的名字,赋值前:大家好,我是很无辜的table
    [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)
  • 相关阅读:
    4G(LTE)背后的技术和利益纠结——VoIP,VoLTE,SIP,IMS的前世今生
    Windows抓取本地回环数据包
    SIP中的早期媒体与回铃音的产生
    SpringMVC整合
    浮点数转换为十进制
    将Sublime Text 2搭建成一个好用的IDE
    python3 'gbk' codec can't decode byte 0x80 in position 读取文件编码集错误的一个bug
    Matplotlib入门教程
    turtle教程-Python绘图
    python画图模块之一:turtle(1) 画五角星、正方形等
  • 原文地址:https://www.cnblogs.com/quansir/p/5521538.html
Copyright © 2011-2022 走看看