文档章节

lua3 Metatable(元表)

x
 xiao理想
发布于 2014/09/01 20:44
字数 1853
阅读 9
收藏 1


本文来自: http://manual.luaer.cn/ ,2.8章.


Lua 中的每个值都可以用一个 metatable。 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为。 你可以通过在 metatable 中的特定域设一些值来改变拥有这个 metatable 的值 的指定操作之行为。 举例来说,当一个非数字的值作加法操作的时候, Lua 会检查它的 metatable 中 "__add" 域中的是否有一个函数。 如果有这么一个函数的话,Lua 调用这个函数来执行一次加法。

我们叫 metatable 中的键名为 事件 (event) ,把其中的值叫作 元方法 (metamethod)。 在上个例子中,事件是 "add" 而元方法就是那个执行加法操作的函数。

你可以通过 getmetatable 函数来查询到任何一个值的 metatable。

你可以通过 setmetatable 函数来替换掉 table 的 metatable 。 你不能从 Lua 中改变其它任何类型的值的 metatable (使用 debug 库例外); 要这样做的话必须使用 C API 。

每个 table 和 userdata 拥有独立的 metatable (当然多个 table 和 userdata 可以共享一个相同的表作它们的 metatable); 其它所有类型的值,每种类型都分别共享唯一的一个 metatable。 因此,所有的数字一起只有一个 metatable ,所有的字符串也是,等等。

一个 metatable 可以控制一个对象做数学运算操作、比较操作、连接操作、取长度操作、取下标操作时的行为, metatable 中还可以定义一个函数,让 userdata 作垃圾收集时调用它。 对于这些操作,Lua 都将其关联上一个被称作事件的指定健。 当 Lua 需要对一个值发起这些操作中的一个时, 它会去检查值中 metatable 中是否有对应事件。 如果有的话,键名对应的值(元方法)将控制 Lua 怎样做这个操作。

metatable 可以控制的操作已在下面列出来。 每个操作都用相应的名字区分。 每个操作的键名都是用操作名字加上两个下划线 '__' 前缀的字符串; 举例来说,"add" 操作的键名就是字符串 "__add"。 这些操作的语义用一个 Lua 函数来描述解释器如何执行更为恰当。

这里展示的用 Lua 写的代码仅作解说用; 实际的行为已经硬编码在解释器中,其执行效率要远高于这些模拟代码。 这些用于描述的的代码中用到的函数 ( rawget , tonumber ,等等。) 都可以在 §5.1 中找到。 特别注意,我们使用这样一个表达式来从给定对象中提取元方法

     metatable(obj)[event]

这个应该被解读作

     rawget(getmetatable(obj) or {}, event)

这就是说,访问一个元方法不再会触发任何的元方法, 而且访问一个没有 metatable 的对象也不会失败(而只是简单返回 nil)。

  • "add": + 操作。

    下面这个 getbinhandler 函数定义了 Lua 怎样选择一个处理器来作二元操作。 首先,Lua 尝试第一个操作数。 如果这个东西的类型没有定义这个操作的处理器,然后 Lua 会尝试第二个操作数。

         function getbinhandler (op1, op2, event)
           return metatable(op1)[event] or metatable(op2)[event]
         end

    通过这个函数, op1 + op2 的行为就是

         function add_event (op1, op2)
           local o1, o2 = tonumber(op1), tonumber(op2)
           if o1 and o2 then  -- 两个操作数都是数字?
             return o1 + o2   -- 这里的 '+' 是原生的 'add'
           else  -- 至少一个操作数不是数字时
             local h = getbinhandler(op1, op2, "__add")
             if h then
               -- 以两个操作数来调用处理器
               return h(op1, op2)
             else  -- 没有处理器:缺省行为
               error(···)
             end
           end
         end
  • "sub": - 操作。 其行为类似于 "add" 操作。

  • "mul": * 操作。 其行为类似于 "add" 操作。

  • "div": / 操作。 其行为类似于 "add" 操作。

  • "mod": % 操作。 其行为类似于 "add" 操作, 它的原生操作是这样的 o1 - floor(o1/o2)*o2

  • "pow": ^ (幂)操作。 其行为类似于 "add" 操作, 它的原生操作是调用 pow 函数(通过 C math 库)。

  • "unm": 一元 - 操作。

         function unm_event (op)
           local o = tonumber(op)
           if o then  -- 操作数是数字?
             return -o  -- 这里的 '-' 是一个原生的 'unm'
           else  -- 操作数不是数字。
             -- 尝试从操作数中得到处理器
             local h = metatable(op).__unm
             if h then
               -- 以操作数为参数调用处理器
               return h(op)
             else  -- 没有处理器:缺省行为
               error(···)
             end
           end
         end
  • "concat": .. (连接)操作,

         function concat_event (op1, op2)
           if (type(op1) == "string" or type(op1) == "number") and
              (type(op2) == "string" or type(op2) == "number") then
             return op1 .. op2  -- 原生字符串连接
           else
             local h = getbinhandler(op1, op2, "__concat")
             if h then
               return h(op1, op2)
             else
               error(···)
             end
           end
         end
  • "len": # 操作。

         function len_event (op)
           if type(op) == "string" then
             return strlen(op)         -- 原生的取字符串长度
           elseif type(op) == "table" then
             return #op                -- 原生的取 table 长度
           else
             local h = metatable(op).__len
             if h then
               -- 调用操作数的处理器
               return h(op)
             else  -- 没有处理器:缺省行为
               error(···)
             end
           end
         end

    关于 table 的长度参见 §2.5.5 。

  • "eq": == 操作。 函数 getcomphandler 定义了 Lua 怎样选择一个处理器来作比较操作。 元方法仅仅在参于比较的两个对象类型相同且有对应操作相同的元方法时才起效。

         function getcomphandler (op1, op2, event)
           if type(op1) ~= type(op2) then return nil end
           local mm1 = metatable(op1)[event]
           local mm2 = metatable(op2)[event]
           if mm1 == mm2 then return mm1 else return nil end
         end

    "eq" 事件按如下方式定义:

         function eq_event (op1, op2)
           if type(op1) ~= type(op2) then  -- 不同的类型?
             return false   -- 不同的对象
           end
           if op1 == op2 then   -- 原生的相等比较结果?
             return true   -- 对象相等
           end
           -- 尝试使用元方法
           local h = getcomphandler(op1, op2, "__eq")
           if h then
             return h(op1, op2)
           else
             return false
           end
         end

    a ~= b 等价于 not (a == b) 。

  • "lt": < 操作。

         function lt_event (op1, op2)
           if type(op1) == "number" and type(op2) == "number" then
             return op1 < op2   -- 数字比较
           elseif type(op1) == "string" and type(op2) == "string" then
             return op1 < op2   -- 字符串按逐字符比较
           else
             local h = getcomphandler(op1, op2, "__lt")
             if h then
               return h(op1, op2)
             else
               error(···);
             end
           end
         end

    a > b 等价于 b < a.

  • "le": <= 操作。

         function le_event (op1, op2)
           if type(op1) == "number" and type(op2) == "number" then
             return op1 <= op2   -- 数字比较
           elseif type(op1) == "string" and type(op2) == "string" then
             return op1 <= op2   -- 字符串按逐字符比较
           else
             local h = getcomphandler(op1, op2, "__le")
             if h then
               return h(op1, op2)
             else
               h = getcomphandler(op1, op2, "__lt")
               if h then
                 return not h(op2, op1)
               else
                 error(···);
               end
             end
           end
         end

    a >= b 等价于 b <= a 。 注意,如果元方法 "le" 没有提供,Lua 就尝试 "lt" , 它假定 a <= b 等价于 not (b < a) 。

  • "index": 取下标操作用于访问 table[key] 。

         function gettable_event (table, key)
           local h
           if type(table) == "table" then
             local v = rawget(table, key)
             if v ~= nil then return v end
             h = metatable(table).__index
             if h == nil then return nil end
           else
             h = metatable(table).__index
             if h == nil then
               error(···);
             end
           end
           if type(h) == "function" then
             return h(table, key)      -- 调用处理器
           else return h[key]          -- 或是重复上述操作
           end
         end
  • "newindex": 赋值给指定下标 table[key] = value 。

         function settable_event (table, key, value)
           local h
           if type(table) == "table" then
             local v = rawget(table, key)
             if v ~= nil then rawset(table, key, value); return end
             h = metatable(table).__newindex
             if h == nil then rawset(table, key, value); return end
           else
             h = metatable(table).__newindex
             if h == nil then
               error(···);
             end
           end
           if type(h) == "function" then
             return h(table, key,value)    -- 调用处理器
           else h[key] = value             -- 或是重复上述操作
           end
         end
  • "call": 当 Lua 调用一个值时调用。

         function function_event (func, ...)
           if type(func) == "function" then
             return func(...)   -- 原生的调用
           else
             local h = metatable(func).__call
             if h then
               return h(func, ...)
             else
               error(···)
             end
           end
         end



本文转载自:http://manual.luaer.cn/

x
粉丝 1
博文 22
码字总数 6276
作品 0
广州
私信 提问
Lua 学习笔记(6)元表(metatable)与元方法(metamethod)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/l773575310/article/details/82959466 Lua 学习笔记(6)元表(metatable)与元方法(metamethod) 《Lua程序...

ChiLi_Lin
2018/10/07
0
0
Lua 的表、元表及实现的面向对象模型 (施工中 )

1、表 表(table)是Lua语言中非常独特的地方; Lua中的表即支持数组风格的实现(顺序表),也支持key-value风格的实现(哈希表),并且这两种方式还能混用(即使不推荐); 表中可以存放任何...

shangluyi
2017/03/05
0
0
【Lua高级教程】什么是Metatable举例说明

什么是Metatable metatable是Lua中的重要概念,每一个table都可以加上metatable,以改变相应的table的行为。 Metatables举例 lotable =lometatable =(lotable) 上边的代码也可以写成一行,如...

浩浩老师
2015/09/08
115
0
[Lua]Lua高级教程Metatables

什么是Metatable metatable是Lua中的重要概念,每一个table都可以加上metatable,以改变相应的table的行为。 Metatables举例 -- 声明一个正常的关系变量 lotable = {} -- 声明空元表变量 lo...

浩浩老师
2015/10/20
38
0
Lua程序转载 - 面向对象的实现探讨

转载出处:http://blog.csdn.net/xenyinzen/article/details/3536708 元表概念 Lua中,面向对向是用元表这种机制来实现的。首先,一般来说,一个表和它的元表是不同的个体(不属于同一个表)...

80后小子
2014/07/19
147
0

没有更多内容

加载失败,请刷新页面

加载更多

为什么要在网站中应用CDN加速?

1. 网页加载速度更快 在网站中使用CDN技术最直接的一个好处就是它可以加快网页的加载速度。首先,CDN加速的内容分发是基于服务器缓存的,由于CDN中缓存了不少数据,它能够给用户提供更快的页...

云漫网络Ruan
26分钟前
4
0
亚玛芬体育(Amer Sports)和信必优正式启动合作开发Movesense创新

亚玛芬体育和信必优正式启动合作开发Movesense创新,作为亚玛芬体育的完美技术搭档,信必优利用Movesense传感器技术为第三方开发移动应用和服务。 Movesense基于传感器技术和开放的API,测量...

symbiochina88
37分钟前
2
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
38分钟前
3
0
好程序员Java学习路线分享MyBatis之线程优化

  好程序员Java学习路线分享MyBatis之线程优化,我们的项目存在大量用户同时访问的情况,那么就会出现大量线程并发访问数据库,这样会带来线程同步问题,本章我们将讨论MyBatis的线程同步问...

好程序员官方
44分钟前
6
0
IDEA 自定义方法注解模板

IDEA 自定义方法注解模板 1、使用效果 /*** 计算交易费用* @Author wangjiafang* @Date 2019/9/11* @param feeComputeVo* @return*/@PostMapping("/v1/fee_compute")public ApiResp......

小白的成长
44分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部