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