lua元表Metatable

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

原文链接: 云风lua5.1参考手册 @2.8

时间: 2024-10-05 05:07:50

lua元表Metatable的相关文章

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

元表概念: 引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等.但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作. 访问机制:一般的元方法都只针对Lua的核心,也就是一个虚拟机.它会检测一个操作中的值是否有元表,这些元表是否定义了关于次操作的元方法.例如两个table相加,先检查两者之一是否有元表,之后检查是否有一个叫“__add”的字段,若找到,则调用对应的值.“__add”等即时字段,其对应的值(往往是一个函数或是table

lua面向对象封装及元表(metatable)性能测试

Lua本身是没有面向对象支持的,但面向对象编程在逻辑复杂的大型工程却很有用.于是很多人用Lua本身的数据结构table来模拟面向对象.最简单的一种方法是把对象的方法.成员都放到table中.如: -- file:test.lua local test = {} function test:get_x() return self.x or 0 end function test:set_x( _x ) self.x = _x end local test_module = {} function

Lua&#30340;&#20803;&#34920;(metatable)

metatable允许我们改变table的行为 Lua中的每一个表都可以有metatable(后面我们将看到userdata也有Metatable) Lua默认创建一个不带metatable的新表 setmetatable(只能用于table)和 getmetatable(用于任何对象) 可以使用setmetatable函数设置或者改变一个表的metatable 任何一个表都可以是其他一个表的metatable 一组相关的表可以共享一个metatable(描述他们共同的行为) 一个表也可以是自身

Lua——3.元表Metatable

lua的metatable也是一个普通的表,lua提供metatable的功能,主要有一下几种作用: 控制对 table 的访问 为 Lua 函数库提供支持 重载算数运算符和关系运算符的行为 1.使用metatable控制对table的访问 当查询table的某个键的时候,如果该table的这个键没有值,那么Lua就会寻找该table的metatable中的__index元方法:如果__index指向一个table,Lua会在此table中查找相应的键, 并且,__index也可以指向一个方法

lua——元表、元方法、继承

[元表] 元表中的键为事件(event),称值为元方法(metamethod). 通过函数getmetatable查询任何值的元表,通过函数setmetatable替换表的元表. setmetatable(只能用于table)和getmetatable(用于任何对象) 语法:setmetatable (table, metatable),对指定table设置metatable      [如果元表(metatable)中存在__metatable键值,setmetatable会失败] 语法:tm

lua元表与元方法

lua中每个值都有一套预定义的操作集合,比如数字是可以相加的,字符串是可以连接的,但是对于两个table类型,则不能直接进行"+"操作.这需要我们进行一些操作.在lua中有一个元表(metatable),我们可以通过元表来改变一个值的行为,使其在面对一个非预定义的操作时执行一个指定的操作.比如,现在有两个table类型的变量a和b,我们可以通过metatable定义如何计算表达式a+b,具体的在Lua中是按照以下步骤进行的: 1.先判断a和b两者之一是否有元表 2.检查该元表中是否有一

Lua之metatable

Lua之metatable 一.元表 Lua中的每个值都有一套预定义的操作集合,也可以通过metatable(元表)来定义一个值的行为,metatable包含了一组meatmethod(元方法). Lua中的每个值都有一个metatable,table和userdata可以有各自独立的metatable,而其他类型的值则共享其类型所属的单一metatable. 在Lua代码中,只能设置table的metatable,若要设置其他类型的值的metatable,必须通过C代码来完成. functio

lua 14 metatable (类似操作符重载)

转自:http://www.runoob.com/lua/lua-metatables.html 感性认识: "Lua中Metatable这个概念, 国内将他翻译为元表. 元表为重定义Lua中任意一个对象(值)的默认行为提供了一种公开入口. 如同许多OO语言的操作符重载或方法重载. Metatable能够为我们带来非常灵活的编程方式. 具体的说, Lua中每种类型的值都有都有他的默认操作方式, 如, 数字可以做加减乘除等操作, 字符串可以做连接操作, 函数可以做调用操作, 表可以做表项的取值赋值

Lua 学习之基础篇八&lt;Lua 元表(Metatabble)&amp;&amp;继承&gt;

讲到元表,先看一段table的合并动作. t1 = {1,2} t2 = {3,4} t3 = t1 + t2 attempt to perform arithmetic on a table value (global 't1') 程序会报错,因为不知道如何对两个table执行+运算,这个时候就需要通过元表来定义,有点类似c中的运算符加载.我们看一下如何通过元表实现合并操作. local mt = {} --定义mt.__add元方法(其实就是元表中一个特殊的索引值)为将两个表的元素合并后返回