lua元表和元方法 《lua程序设计》 13章 读书笔记

lua中每个值都有一个元表,talble和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表。lua在创建table时不会创建元表。

t = {}
print(getmetatable(t)) --显示过元表 此时是nil --可以用setmetatable来设置或修改任何table的元表
t1 = {}
setmetatable(t,t1)
assert(getmetatable(t) == t1)

任何table可以作为任何值的元表,而一组相关的table可以共享一个通用的元表,此元表描述了一个共同的行为。一个tabel甚至可以作为它自己的元表,用于描述其特有行为。

在lua中,只能设置table的元表。要设置其它类型的元表,必须通过C代码来完成

print(getmetatable("hi"))  --005DECD8 说明字符串有元表
print(getmetatable(10)) --number没有元表

13.1  算术类的元方法

Set = {}  --集合

local mt = {}  --集合元表

--根据参数列表中的值创建一个新的集合
function Set.new(l)
local set = {}
setmetatable(set,mt) --指定 table set的元表为mt
for k,v in ipairs(l) do
set[v] = true --注意,是拿索来当数据用的
end
return set
end
function Set.union(a,b)
local res = Set.new{}
for k,v in pairs(a) do res[k] = true end
for k,v in pairs(b) do res[k] = true end
return res
end function Set.intersection(a,b)
local res = Set.new{}
for k,v in pairs(a) do
if b[k] then
res[k] = true
end
end
return res
end function Set.tostring(set)
local l = {}
for k,v in pairs(set) do
l[#l + 1] = k
end
return "{" .. table.concat(l,", ") .. "}"
end function Set.print(s)
print(Set.tostring(s))
end --将元方法加入元表
mt.__add = Set.union --指定加号为求并集的方法
mt.__mul = Set.intersection --指定乘号为交集的方法 s1 = Set.new{11,22,31,44,56}
s2 = Set.new{66,33,22,31}
s3 = s1 + s2 --求并集
Set.print(s3) --输出 {11, 31, 66, 22, 33, 56, 44}
s4 = s1 * s2 --求交集
Set.print(s4) --输出 {31, 22}

13.2 关系类元方法

关系是指 __eq(等于)、__lt(小于)等

mt.__le = function(a,b)
for k in pairs(a) do
if not b[k] then return false end
end
return true
end mt.__lt = function(a,b)
return a<=b and not (b<=a)
end mt.__eq = function(a,b)
return a<=b and b<=a
end ss1 = Set.new{2,4}
ss2 = Set.new{4,10,2}
print(ss1<=ss2) --true
print(ss1<ss2) --true
print(ss1>=ss1) --true
print(ss1>ss1) --false
print(ss1 == ss2*ss1) --true

13.3 库定义的元方法

tostring是一个典型的实例。它能将各种类型的值表示为简单的文本格式

print({}) ----table: 003ECEF0

函数总是调用tostring来格式化输出。当格式化任意值时,tostring会检测该值是否有一个 __tostring元方法。如果有,他就调用这个方法用来作为tostring的返回值

在集合实例中,我们定议了将任命表示为字符串的方法,我们可以设置元表的__tostring字段

mt.__tostring = Set.tostring
sstext = Set.new{33,55,6666}
print(sstext) --{55, 33, 6666}

假设想要保护集合的元表,使用户即不能看也不能修改集合的元表。那么就需要用到__metatable。当设置了该字段时,getmetatable就会返回这个字段的值,而setmetatable会引发一个错误

mt.__metatable = "not your business"
sstext1 = Set.new{}
print(getmetatable(sstext1)) --not your business
setmetatable(s1,{})

13.4 table 访问的元方法

13.4.1 __index元方法

当访问一个table中不存在的字段中时,如果这个字段不存在得到nil,但是如果这个table有一个元方法__index那么如果没有这个字段,就由这个元方法来提供结果

Window = {}

Window.prototype = {x=0,y=0,width = 100,height = 100}
Window.mt = {} function Window.new(o)
setmetatable(o,Window.mt)
return o
end --现在定义一个元方法
Window.mt.__index = function(table,key)
return Window.prototype[key]
end w = Window.new{x=10,y=20}
print(w.width) -- 100 window实际上没有width这个字段

__index元方法还可以是一个table

13.4.2  __newindex元方法

与__index不同的是__index是在查询的时候用的而_newindes是在更新的时候用的

13.4.3具有默认值的table

以下代码为table设置默认值

function setDefault(t,d)
local mt = {__index = function() return d end}
setmetatable(t,mt)
end

13.4.4 跟踪table的访问

__index和__newindex都是在table中没有所需的index才发挥作用。因为只有table保持空才能捕捉到所有对他的访问,为了监视一个table的所有访问就得为真正的 table 创建一个代理

t_src = {}  --要跟踪的表
local _t = t_src t = {} --创建代理 --创建元表
local mt = {
__index = function(t,k)
print("*access to element " .. tostring(k))
return _t[k]
end,
__newindex = function(t,k,v)
print("*update of element " .. tostring(k) .. " to " .. tostring(v))
_t[k] = v
end
}
setmetatable(t,mt) t[2] = "hello" -- *update of element 2 to hello
print(t[2]) --*access to element 2

13.4.5 只读的table

只读table与上一节跟踪table类似,是通过__newindex来限制修改table内存

rawget是为了绕过__index而出现的,直接点,就是让__index方法的重写无效。(我这里用到"重写"二字,可能不太对,希望能得到纠正)
    Window = {}  

    Window.prototype = {x = 0 ,y = 0 ,width = 100 ,height = 100,}
Window.mt = {}
function Window.new(o)
setmetatable(o ,Window.mt)
return o
end
Window.mt.__index = function (t ,key)
return 1000
end
Window.mt.__newindex = function (table ,key ,value)
if key == "wangbin" then
rawset(table ,"wangbin" ,"yes,i am")
end
end
w = Window.new{x = 10 ,y = 20}
print(rawget(w ,w.wangbin))

打印结果是:nil。这里的元表中__index函数就不再起作用了。

但是rawset呢,起什么作用呢?我们再来运行一段代码。

    Window = {}
Window.prototype = {x = 0 ,y = 0 ,width = 100 ,height = 100,}
Window.mt = {}
function Window.new(o)
setmetatable(o ,Window.mt)
return o
end
Window.mt.__index = function (t ,key)
return 1000
end
Window.mt.__newindex = function (table ,key ,value)
table.key = "yes,i am"
end
w = Window.new{x = 10 ,y = 20}
w.wangbin = "55"

然后我们的程序就stack overflow了。可见,程序陷入了死循环。因为w.wangbin这个元素本来就不存在表中,然后这里不断执行进入__newindex,陷入了死循环。

 

This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The third edition targets Lua 5.2 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.

Programming in Lua
  Part II. Tables and Objects

Chapter 13. Metatables and Metamethods

 

13.1 – Arithmetic Metamethods

In this section, we will introduce a simple example
to explain how to use metatables.
Suppose we are using tables to represent sets,
with functions to compute the union of two sets,
intersection, and the like.
As we did with lists,
we store these functions inside a table
and we define a constructor to create new sets:

    Set = {}

    function Set.new (t)
local set = {}
for _, l in ipairs(t) do set[l] = true end
return set
end function Set.union (a,b)
local res = Set.new{}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end function Set.intersection (a,b)
local res = Set.new{}
for k in pairs(a) do
res[k] = b[k]
end
return res
end

To help checking our examples, we also define a function to print sets:

    function Set.tostring (set)
local s = "{"
local sep = ""
for e in pairs(set) do
s = s .. sep .. e
sep = ", "
end
return s .. "}"
end function Set.print (s)
print(Set.tostring(s))
end

Now, we want to make the addition operator (`+´) compute the union of two sets. For that, we will arrange that all tables representing sets share a metatable and this metatable will define how they react to the addition operator. Our first step is to create a regular table that we will use as the metatable for sets. To avoid polluting our namespace, we will store it in the Set table:

    Set.mt = {}    -- metatable for sets

The next step is to modify the Set.new function, which creates sets. The new version has only one extra line, which sets mt as the metatable for the tables that it creates:

    function Set.new (t)   -- 2nd version
local set = {}
setmetatable(set, Set.mt)
for _, l in ipairs(t) do set[l] = true end
return set
end

After that, every set we create with Set.new will have that same table as its metatable:

    s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
print(getmetatable(s1)) --> table: 00672B60
print(getmetatable(s2)) --> table: 00672B60

Finally, we add to the metatable the so-called metamethod, a field __add that describes how to perform the union:

    Set.mt.__add = Set.union

Whenever Lua tries to add two sets, it will call this function, with the two operands as arguments.

With the metamethod in place, we can use the addition operator to do set unions:

    s3 = s1 + s2
Set.print(s3) --> {1, 10, 20, 30, 50}

Similarly, we may use the multiplication operator to perform set intersection:

    Set.mt.__mul = Set.intersection

    Set.print((s1 + s2)*s1)     --> {10, 20, 30, 50}

For each arithmetic operator there is a corresponding field name in a metatable. Besides __add and __mul, there are __sub (for subtraction), __div (for division), __unm (for negation), and __pow (for exponentiation). We may define also the field __concat, to define a behavior for the concatenation operator.

When we add two sets, there is no question about what metatable to use. However, we may write an expression that mixes two values with different metatables, for instance like this:

    s = Set.new{1,2,3}
s = s + 8

To choose a metamethod, Lua does the following: (1) If the first value has a metatable with an __add field, Lua uses this value as the metamethod, independently of the second value; (2) otherwise, if the second value has a metatable with an __add field, Lua uses this value as the metamethod; (3) otherwise, Lua raises an error. Therefore, the last example will call Set.union, as will the expressions 10 + s and "hy" + s.

Lua does not care about those mixed types, but our implementation does. If we run the s = s + 8 example, the error we get will be inside Set.union:

    bad argument #1 to `pairs' (table expected, got number)

If we want more lucid error messages, we must check the type of the operands explicitly before attempting to perform the operation:

    function Set.union (a,b)
if getmetatable(a) ~= Set.mt or
getmetatable(b) ~= Set.mt then
error("attempt to `add' a set with a non-set value", 2)
end
... -- same as before

Metatable Events

wiki
 

A listing of all the 'special' keys in a metatable, and the metamethods which they perform.

  • __index - Control 'prototype' inheritance. When
    accessing "myTable[key]" and the key does not appear in the table, but
    the metatable has an __index property:

    • if the value is a function, the function is called, passing in the
      table and the key; the return value of that function is returned as the
      result.
    • if the value is another table, the value of the key in that table is asked for and returned
      • (and if it doesn't exist in that table, but that table's metatable has an __index property, then it continues on up)
    • Use "rawget(myTable,key)" to skip this metamethod.
  • __newindex - Control property assignment. When
    calling "myTable[key] = value", if the metatable has a __newindex key
    pointing to a function, call that function, passing it the table, key,
    and value.

    • Use "rawset(myTable,key,value)" to skip this metamethod.
    • (If the __newindex function does not set the key on the table (using rawset) then the key/value pair is not added to myTable.)
  • __mode - Control weak references. A string value with one or both of the characters 'k' and 'v' which specifies that the the keys and/or values in the table are weak references.
  • __call - Treat a table like a function. When a
    table is followed by parenthesis such as "myTable( 'foo' )" and the
    metatable has a __call key pointing to a function, that function is
    invoked (passing the table as the first argument, followed by any
    specified arguments) and the return value is returned.
  • __metatable - Hide the metatable. When
    "getmetatable( myTable )" is called, if the metatable for myTable has a
    __metatable key, the value of that key is returned instead of the actual
    metatable.
  • __tostring - Control string representation. When
    the builtin "tostring( myTable )" function is called, if the metatable
    for myTable has a __tostring property set to a function, that function
    is invoked (passing myTable to it) and the return value is used as the
    string representation.
  • __len - Control table length. When the table
    length is requested using the length operator ( '#' ), if the metatable
    for myTable has a __len key pointing to a function, that function is
    invoked (passing myTable to it) and the return value used as the value
    of "#myTable".
  • __gc - Userdata finalizer code. When userdata is
    set to be garbage collected, if the metatable has a __gc field pointing
    to a function, that function is first invoked, passing the userdata to
    it. The __gc metamethod is not called for tables. (See http://lua-users.org/lists/lua-l/2006-11/msg00508.html)

Mathematic Operators

  • __unm - Unary minus. When writing "-myTable", if
    the metatable has a __unm key pointing to a function, that function is
    invoked (passing the table), and the return value used as the value of
    "-myTable".
  • __add - Addition. When writing "myTable +
    object" or "object + myTable", if myTable's metatable has an __add key
    pointing to a function, that function is invoked (passing the left and
    right operators in order) and the return value used.

    • ''If both operands are tables, the left table is checked before the right table for the presence of an __add metaevent.
  • __sub - Subtraction. Similar to addition, using the '-' operator.
  • __mul - Multiplication. Similar to addition, using the '*' operator.
  • __div - Division. Similar to addition, using the '/' operator.
  • __mod - Modulo. Similar to addition, using the '%' operator.
  • __pow - Involution. Similar to addition, using the '^' operator.
  • __concat - Concatenation. Similar to addition, using the '..' operator.

Equivalence Comparison Operators

  • __eq - Check for equality. This method is invoked
    when "myTable1 == myTable2" is evaluated, but only if both tables have
    the exact same metamethod for __eq.

    • For example, see the following code:
    t1a = {}
    t1b = {}
    t2 = {}
    mt1 = { __eq = function( o1, o2 ) return 'whee' end }
    mt2 = { __eq = function( o1, o2 ) return 'whee' end } setmetatable( t1a, mt1 )
    setmetatable( t1b, mt1 )
    setmetatable( t2, mt2 ) print( t1a == t1b ) --> true
    print( t1a == t2 ) --> false
    • If the function returns nil or false, the result of the comparison is false; otherwise, the result is true.
      • If t1 and t2 are referencing the same table, the __eq method is not invoked for t1 == t2 :
function foo (o1, o2)
print( '__eq call' )
return false
end t1 = {}
setmetatable( t1, {__eq = foo} ) t2 = t1
print( t1 == t2 ) --> true
-- string '__eq call' not printed (and comparison result is true, not like the return value of foo(...)), so no foo(...) call here t3 = {}
setmetatable( t3, {__eq = foo} )
if t1 == t3 then end --> __eq call
-- foo(...) was called
  • __lt - Check for less-than. Similar to equality, using the '<' operator.

    • Greater-than is evaluated by reversing the order of the operands passed to the __lt function.
    a > b == b < a
    
  • __le - Check for less-than-or-equal. Similar to equality, using the '<=' operator.

    • Greater-than-or-equal is evaluated by reversing the order of the operands passed to the __le function.
    a >= b == b <= a
    

RecentChanges · preferences
edit · history
Last edited December 19, 2014 5:15 am GMT (diff)

[转]LUA元表的更多相关文章

  1. 学习Lua setmetatable Lua 元表

    Lua 元表(Metatable) 在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作. 个人理解,这个相当于其他语言的继承,是把这个类的方法 ...

  2. Step By Step(Lua元表与元方法)

    Step By Step(Lua元表与元方法) Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表 ...

  3. lua元表与元方法

    lua中提供的元表(metatable)与元方法(metamethod)是一种非常重要的语法,metatable主要用于做一些类似于C++重载操作符式的功能. lua中提供的元表是用于帮助lua变量完 ...

  4. lua元表和元方法 《lua程序设计》 13章 读书笔记

    lua中每个值都有一个元表,talble和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表.lua在创建table时不会创建元表. t = {} print(getmet ...

  5. lua元表

    __index元方法:按照之前的说法,如果A的元表是B,那么如果访问了一个A中不存在的成员,就会访问查找B中有没有这个成员.这个过程大体是这样,但却不完全是这样,实际上,即使将A的元表设置为B,而且B ...

  6. <6>Lua元表和冒号 self

    Lua中没有像C.C++.JAVA中的类概念,面向对象等 ,但我们可以模拟出来 1. Lua中有个很重要的概念元表 设置元表setmetatable()函数  获取元表getmetatable()函数 ...

  7. lua元表学习

    a = {, } b= {, } vector2 = {} function vector2.Add(v1, v2) if(v1 == nil or v2 == nil)then return nil ...

  8. [转]lua元表代码分析

    http://lin-style.iteye.com/blog/1012138 版本整理日期:2011/4/21 元表其实就是可以让你HOOK掉一些操作的一张表. 表的定义在ltm.h/c的文件里.对 ...

  9. lua元表详解

    元表的作用 元表是用来定义对table或userdata操作方式的表 举个例子 local t1 = {1} local t2 = {2} local t3 = t1 + t2 我们直接对两个tabl ...

随机推荐

  1. 第三章 对象(JavaScript:语言精粹)

      对象是属性的容器,其中每个属性都有名字和值.   3.0. 概览:对象字面量 | 检索 | 更新 | 引用 | 原型 | 反射 | 枚举 | 删除 | 减少全局变量污染   3.1. 对象字面量 ...

  2. 皇后(queen)

    皇后(queen)[题目描述] 众所不知,rly现在不会玩国际象棋.但是,作为一个OIer,rly当然做过八皇后问题.这里再啰嗦几句,皇后可以攻击到同行同列同对角线,在n*n的方格中摆n个皇后使其互不 ...

  3. didFinishLaunchingWithOptions

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launc ...

  4. 关于 Oracle 的数据导入导出及 Sql Loader (sqlldr) 的用法

    在 Oracle 数据库中,我们通常在不同数据库的表间记录进行复制或迁移时会用以下几种方法: 1. A 表的记录导出为一条条分号隔开的 insert 语句,然后执行插入到 B 表中2. 建立数据库间的 ...

  5. Django migrations 重命名

    1:如果migrations文件中想要重命名文件,重命名后有一次修改models文件,再次执行python makegirations ******,再次执行migrate的时候发现报错了,在我往后的 ...

  6. 解决打开CHM文件后,右侧空白

    在网上下了一个chm的文件,打开后只有目录,右侧不显示内容. 不知道是文件有问题,还是系统有问题. <ignore_js_op> 右键点击文件–属性 看到 最下面有一个提示 说是这个文件是 ...

  7. Thread-0" kafka.common.FailedToSendMessageException: Failed to send messages after 3 tries.

    http://blog.csdn.net/jingshuigg/article/details/25001979 zookeeper.connect=localhost:2181改成zookeeper ...

  8. linux笔记:文件系统管理-fdisk分区

    fdisk命令分区过程: 1.添加新硬盘 2.查看新硬盘: fdisk -l 3.使用fdisk命令分区: fdisk 硬盘设备文件名(如:fdisk /dev/sdb) fdisk交互指令说明: 4 ...

  9. python—基础类的那点儿所以然

    老师说:‘要知其然,更要知其所以然’~~~那么今天就来说点儿所以然,对python中的int,str,lst,dict和tuple等基础类中的方法做一些解析 那么类是什么呢? 官方的解释是这样的:对象 ...

  10. display:inline 和display:inline-block和display:block的区别

    之前讲过块级元素使用display:block 行内元素使用display:inline 那么今天我们就来区分一下display:inline,display:inline-block和display ...