詳解Lua中的元表和元方法
一、元表
元表可以修改一個(gè)值在面對(duì)一個(gè)未知操作時(shí)的行為,Lua 中使用 table 作為元表的承載。
元表只能給出預(yù)先定義的操作集合的行為,比類會(huì)更加受限制,不支持繼承。
Lua 每一個(gè)值都可以有元表 :
- 表和用戶數(shù)據(jù)類型都具有各自獨(dú)立的元表;
- 其他類型的值則共享對(duì)應(yīng)類型所屬的同一個(gè)元表;
二、元表的設(shè)置
1、類型的原始元表
Lua 中,元表的設(shè)置只能針對(duì) table ,其他類型都不能設(shè)置。
table 的初始化元表為 nil ,即沒有設(shè)置元表,只能通過 setmetatable
進(jìn)行設(shè)置,多個(gè) table 可以共享一個(gè) table 作為元表(當(dāng)然也可以使用他自己作為自己的元表,因?yàn)樗陨硪彩且粋€(gè) table)
Lua 只有 string 初始化了元表,而且是針對(duì)了所有的字符串,即 string 都是同一個(gè)元表。其他的類型為 nil 。
print("表的初始值", getmetatable({})) --> 表的初始值 nil print("整型的初始值", getmetatable(10)) --> 整型的初始值 nil print("浮點(diǎn)型的初始值", getmetatable(10.0)) --> 浮點(diǎn)型的初始值 nil --- 通過打印可以看到兩個(gè)字符串的元表是同一個(gè) print("字符串的初始值", getmetatable("江澎涌")) --> 字符串的初始值 table: 0x600000b14640 print("字符串的初始值", getmetatable("jiangpengyong")) --> 字符串的初始值 table: 0x600000b14640 print("布爾型的初始值", getmetatable(true)) --> 布爾型的初始值 nil print("nil的初始值", getmetatable(nil)) --> nil的初始值 nil function sayHello() end print("函數(shù)的初始值", getmetatable(sayHello)) --> 函數(shù)的初始值 nil
2、設(shè)置元表和獲取元表
2-1、setmetatable(table, metatable)
給表 table 設(shè)置元表 metatable
參數(shù)
- table:要被設(shè)置元表的表
- metabale:元表,如果值為 nil ,則表明要?jiǎng)h除 table 的原有元表。如果原來的元表有
__metatable
字段,則不能再設(shè)置元表,否則會(huì)拋出異常cannot change a protected metatable
__metatable
會(huì)在下面的 “表相關(guān)方法” 小節(jié)分享
返回值:
返回被設(shè)置元表的表,就是參數(shù) table
2-2、getmetatable(object)
如果 object
沒有元表,則返回 nil
如果對(duì)象 Object 有元表,且該元表有一個(gè) "__metatable"
字段,則返回關(guān)聯(lián)的值。否則,返回給定對(duì)象 Object 的元表
2-3、舉個(gè)例子
local oriTable = {} local metaTable = {} print(setmetatable(oriTable, metaTable), oriTable, metaTable) --> table: 0x600001ac0840 table: 0x600001ac0840 print(getmetatable(oriTable), metaTable) --> table: 0x600001ac0900 table: 0x600001ac0900
給元表帶有 __metatable
字段的表,設(shè)置新的元表,則會(huì)拋出異常(見下圖)
t2 = { c = 1 } t2.__metatable = { a = 1 } s1 = {} setmetatable(s1, t2) print('getmetatable(s1)["a"]', getmetatable(s1)["a"]) --> getmetatable(s1)["a"] 1 -- 此處會(huì)拋出異常:cannot change a protected metatable print(setmetatable(s1, {}))
三、元表方法
1、具有的元方法
元表方法方法很像 kotlin 中的操作符方法
1-1、算術(shù)運(yùn)算符
元表方法 | 含義 |
---|---|
__add | 加法 |
__mul | 乘法 |
__sub | 減法 |
__div | 除法 |
__floor | floor除法 |
__unm | 負(fù)數(shù) |
__mod | 取模 |
__pow | 冪運(yùn)算 |
__band | 按位與 |
__bor | 按位或 |
__bxor | 按位異或 |
__bnot | 按位取反 |
__shl | 向左移 |
__shr | 向右移 |
__concat | 定義連接運(yùn)算符 |
1-2、關(guān)系運(yùn)算符
元表方法 | 含義 |
---|---|
__eq | 等于 |
__lt | 小于 |
__le | 小于等于 |
值得注意: ~=
、a > b
、a >= b
沒有對(duì)應(yīng)的元方法,會(huì)被轉(zhuǎn)為如下
a ~= b
會(huì)被轉(zhuǎn)為not( a == b)
a > b
會(huì)被轉(zhuǎn)為b < a
a >= b
會(huì)被轉(zhuǎn)為b <= a
關(guān)系運(yùn)算符遇到兩個(gè)不同類型的對(duì)象,則會(huì)直接返回 false ,不會(huì)進(jìn)行搜尋任何的元方法
1-3、庫(kù)相關(guān)方法
元表方法 | 含義 |
---|---|
__tostring | 當(dāng)調(diào)用 tostring 時(shí),會(huì)先檢查值是否有一個(gè)元方法 __tostring ,有則會(huì)先使用。 |
__metatable | 使用該元方法可以保護(hù)元表,用戶無法獲取也無法修改該元表。當(dāng)元表設(shè)置了 __metatable 的字段,則 getmetatable 會(huì)返回這個(gè)字段的值,而 setmetatable 則會(huì)引發(fā)錯(cuò)誤 |
__pairs | 從 Lua 5.2 開始,當(dāng)元表?yè)碛幸粋€(gè) __pairs 的元方法時(shí),pairs 會(huì)調(diào)用這個(gè)元方法來完成遍歷 |
1-4、表相關(guān)方法
元表方法 | 含義 |
---|---|
__index | 一旦訪問 table 中不存在的字段,正常情況下會(huì)返回 nil 。如果設(shè)置了這個(gè)元表方法,則會(huì)調(diào)用自身元表對(duì)應(yīng)的該 __index 元方法,并以被調(diào)用的表(即此處的 table ,不是元表)和鍵作為參,進(jìn)行調(diào)用。也可以給 __index 設(shè)置一個(gè) table,這樣就會(huì)直接在這table 中查詢,速度比方法稍快。可以通過 rawget 獲取原始數(shù)據(jù),不考慮元表。 |
__newindex | 當(dāng)調(diào)用 table 進(jìn)行索引賦值時(shí),如果設(shè)置了該元表方法,則會(huì)使用被調(diào)用的表(即此處的 table ,不是元表),鍵和值作為參,調(diào)用該方法。同樣也可以給 __newindex 設(shè)置一個(gè)表,則會(huì)將值直接存至該表??梢酝ㄟ^ rawset(talbe, key, value) 相當(dāng)于 table[key] = value 進(jìn)行直接對(duì) table 的賦值,不考慮元表。 |
__len | 獲取 table 的長(zhǎng)度時(shí),會(huì)調(diào)用該方法,獲取長(zhǎng)度 |
__index
和 __newindex
的異同
- 相同點(diǎn):兩個(gè)函數(shù)都是發(fā)生在 table 中沒有對(duì)應(yīng)的 key 時(shí)觸發(fā)
- 不同點(diǎn):當(dāng)前需要的索引沒有對(duì)應(yīng)的值時(shí)則調(diào)用
__index
,如果 table 設(shè)置值時(shí),則調(diào)用__newindex
2、元方法的搜索
如果兩個(gè)變量相加,搜索規(guī)則如下:
- 先查看第一個(gè)值是否有元表,并且是否存在所需元方法,如果存在則使用該元方法,此時(shí)第二個(gè)值的元方法被忽略。否則進(jìn)入下一條
- 查看第二個(gè)值是否有元表,并且是否存在所需的元方法,如果有則進(jìn)行使用。否則進(jìn)入第三條
- 拋出異常
3、舉個(gè)例子
這里元方法比較多,就不一一粘貼代碼了,不然會(huì)讓文章非常冗長(zhǎng)。建議各位童鞋們移步 github clone 下代碼自行運(yùn)行一下會(huì)更加深刻理解。
“算術(shù)運(yùn)算符” 、 “關(guān)系運(yùn)算符”、“庫(kù)相關(guān)方法” 相關(guān)的代碼 可以查看以下的代碼:
集合.lua:github.com/zincPower/l…
集合調(diào)用.lua:github.com/zincPower/l…
“表相關(guān)方法” 相關(guān)的代碼
表相關(guān)元方法.lua:github.com/zincPower/l…
四、寫在最后
以上就是詳解Lua中的元表和元方法的詳細(xì)內(nèi)容,更多關(guān)于Lua元表和元方法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Lua獲取文件長(zhǎng)度和判斷文件是否存在函數(shù)分享
這篇文章主要介紹了Lua獲取文件長(zhǎng)度和判斷文件是否存在函數(shù)分享,需要的朋友可以參考下2015-04-04解析Lua中的全局環(huán)境、包、模塊組織結(jié)構(gòu)
Lua中也擁有和Python世界相似的代碼的作用范圍和組織方式,下面我們就來簡(jiǎn)單解析Lua中的全局環(huán)境、包、模塊組織結(jié)構(gòu),需要的朋友可以參考下2016-06-06Lua中的協(xié)同程序之resume-yield間的數(shù)據(jù)返回研究
這篇文章主要介紹了Lua中的協(xié)同程序之resume-yield間的數(shù)據(jù)返回研究本文講解了resume的參數(shù)、resume函數(shù)的第二個(gè)返回值、yield的返回值等內(nèi)容,需要的朋友可以參考下2014-09-09Lua腳本實(shí)現(xiàn)遞歸刪除一個(gè)文件夾
這篇文章主要介紹了Lua腳本實(shí)現(xiàn)遞歸刪除一個(gè)文件夾,本文給出了C++和Lua兩個(gè)版本的實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-05-05