Lua元表

@date: 2018-3-18


在Lua5.1语言中,元表 (metatable) 的表现行为类似于 C++ 语言中的操作符重载,类似PHP的魔术方法。Python里也有元类(metaclass)一说。

通过元表,Lua有了更多的扩展特性。Lua的面向对象特性就是基于元表实现的。

Lua 提供了两个十分重要的用来处理元表的方法,如下:

  • setmetatable(table, metatable):此方法用于为一个表设置元表。
  • getmetatable(table):此方法用于获取表的元表对象。

设置元表的方法很简单,如下:

local mytable = {}
local mymetatable = {}
setmetatable(mytable, mymetatable)

上面的代码可以简写成如下的一行代码:

local mytable = setmetatable({}, {})

例如我们可以重载 __add 元方法 (metamethod),实现重载+操作符,来计算两个 Lua 数组的并集:

-- 计算集合的并集实例
set1 = {10,40}
set2 = {10,20,30}

setmetatable(set1, {
    __add = function(self, another)
        local res = {}
        local set = {}
        
        for k,v in pairs(self) do set[v] = true end -- 防止集合元素重复
        for k,v in pairs(another) do set[v] = true end -- 防止集合元素重复
        
        for k,v in pairs(set) do table.insert(res, k) end
        
        return res
    end
})

local set3 = set1 + set2
for k,v in pairs(set3) do print(v) end 

输出:

30
20
10
40

类似的元方法还有:

  • __add +操作

  • __sub -操作 其行为类似于 add 操作

  • __mul *操作 其行为类似于 add 操作

  • __div /操作 其行为类似于 add 操作

  • __mod %操作 其行为类似于 add 操作

  • __pow ^(幂)操作 其行为类似于 add 操作

  • __unm 一元 - 操作

  • __concat ..(字符串连接) 操作

  • __len #操作

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

  • __lt <操作

  • __le <=操作

  • __index 取下标操作用于访问 table[key]

  • __newindex 赋值给指定下标 table[key] = value

  • __tostring 转换成字符串

  • __call 当 Lua 调用一个值时调用

  • __mode 用于弱表(week table)

  • __metatable 用于保护metatable不被访问

__index 元方法

该方法实现了在表中查找键不存在时转而在元表中查找该键的功能。有两种写法:

第一种是给__index 元方法一个函数:

local mytable = setmetatable({}, {
    __index = function(self, key)
        return "__index"
    end
})
print(mytable.key1) -- __index 

另一种方法是__index 元方法一个表:

local _M = {
    add = function(x,y) return x+y end,
    mul = function(x,y) return x*y end,
    ver = "1.0",
}
local mytable = setmetatable({}, {
    __index = _M
})
print(mytable.ver) -- 1.0 
print(mytable.add(1,3)) -- 4 

Lua查找一个表元素时的规则,其实就是如下3个步骤:

  1. 在表中查找,如果找到,返回该元素,找不到则继续
  2. 判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
  3. 判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值。

通过__index这个方法,我们可以实现继承的特性。下节再详细讲述。

__newindex 元方法

如果说__index具有PHP里__get的一些特性,那么__newindex则类似__set

以下实例使用了 rawset 函数来更新表:

mytable = setmetatable({key1 = "value1"}, {
  __newindex = function(self, key, value)
        rawset(self, key, "\""..value.."\"")

  end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)

以上实例执行输出结果为:

new value    "4"

__tostring 元方法

如果设置了__tostring 元方法,当直接输出表时会自动调用该方法。示例:

local mytable = setmetatable({ 10, 20, 30 }, {
  __tostring = function(mytable)
    sum = 0
    for k, v in pairs(mytable) do
        sum = sum + v
    end
    return sum
  end
})
print(mytable) -- 60

__call 元方法

__call 元方法的功能类似于 C++ 中的仿函数,使得普通的表也可以被调用。

local mytable = setmetatable({}, {
  __call = function(self, arg)
    local sum = 0
    for _,v in pairs(arg) do
        sum = sum + v
    end
    print(sum)
  end
})
mytable({10,20,30}) -- 60

示例里我们调用自定义的表,并给该表传了参数,最终算出了参数的和。

__metatable 元方法

如果给表设置了 __metatable 元方法的值,getmetatable 将返回这个域的值,而调用 setmetatable将会被禁止,会直接报错。

local mytable = setmetatable({}, {
  __metatable = "no access"
})
print(getmetatable(mytable)) -- no access
setmetatable(mytable, {}) -- 引发编译器报错

更多系列文章查看:
http://me.52fhy.com/lua-book/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,727评论 0 38
  • table 作为 Lua 中唯一的数据结构,我们可以利用 table 实现面向对象编程中的类、继承、多重继承等等。...
    eddy_wiki阅读 4,135评论 0 7
  • 前言 元表对应的英文是metatable,元方法是metamethod。我们都知道,在C++中,两个类是无法直接相...
    BobWong阅读 1,034评论 0 9
  • 在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。 因...
    谁说我是小小云阅读 759评论 0 0
  • 01 在上小学的时候,曾有一位特别好的朋友,可以说是形影不离、无话不谈,后来某一天不知因为什么事产生了隔阂,已经好...
    毓灵雨阅读 414评论 2 2