Lua中的metatable
Lua中的metatable
Lua 中每個 值value(或者對象)都可以有一個 metatable(在 Lua 5.0 只有table和 userdata能夠存在 metatable)。每個 table和userdata value 都有一個屬于自己的 metatable,而其他每種類型的所有 value 共享一個屬于本類型的metatable。
先用一個示例檢查一下對象的metatable:
t = {} print(getmetatable(t)) --> nil print(getmetatable('a')) --> table: 0x7fce64c05000 print(getmetatable('b')) --> table: 0x7fce64c05000mt = {} setmetatable(t, mt) print(getmetatable(t) == mt) --> true上面的字符類型擁有相同的metatable。
做一下整體的說明:
Lua 中的每個值或對象都可以擁有一個 metatable。這個 metatable 就是一個原始的 Lua table ,它用來定義對象在特定操作下的行為。
一個metatable 可以控制一個對象做數學運算操作、比較操作、連接操作、取長度操作、取下標操作時的行為,
而且metatable 中還可以擴展,比如定義一個函數,讓 userdata 作垃圾收集時調用它。
對于這些操作,Lua 都將其關聯上一個被稱作事件的指定鍵。
當 Lua 需要對一個值發起這些操作中的一個時,它會去檢查值中 metatable 中是否有對應事件。
如果有的話,鍵名對應的值(稱為metamethod元方法)將控制 Lua 怎樣做這個操作。
metatable通過其包含的函數來給所掛接的table定義一些特殊的操作,包括下面的meta-method:
數學運算方法
- __add: 定義所掛接table的加法操作
- __mul: 定義乘法操作
- __div: 定義除法操作
- __sub: 定義減法操作
- __mod(a, b) –取模
- __pow(a, b) –乘冪
- __unm: 定義負操作, 即: -table的含義
- __concat: 定義連接操作(“..”運算符)
關系運算方法
- __eq(a, b) –相等
- __lt(a, b) –小于
- __le(a, b) –小于等于
工具方法
- __len(a) –長度
- __tostring: 定義當table作為tostring()函式之參數被呼叫時的行為(例如: print(table)時將呼叫tostring(table)作為輸出結果)
- __call(a, …) –執行方法調用
類似setter/getter方法
- __index: 定義當table中不存在的key值被試圖獲取時的行為
- __newindex: 定義在table中產生新key值時的行為
metatable操作函數
getmetatable
獲取一個對象的metatablesetmetatable
設置一個對象的metatable
示例
這是來自網友翻譯的lua示例
Set = {} -- 存儲對于集合的所有操作函數。 Set.mt = {} -- "metatable"。-- 創建一個新的集合。 function Set.new (t)local set = {} -- 新的集合。setmetatable(set, Set.mt) -- 所有的集合共享同一個"metatable"。-- "key-value",集合中的元素值-"true"。for _, l in ipairs(t) do set[l] = true endreturn set end-- 計算兩個集合的并集。 function Set.union (a,b)local res = Set.new{}for k in pairs(a) do res[k] = true endfor k in pairs(b) do res[k] = true endreturn res end-- 計算兩個集合的交集。 function Set.intersection (a,b)local res = Set.new{}for k in pairs(a) dores[k] = b[k] -- 很聰明的方法,如果集合"b"中沒有元素"k"的話就會返回"nil"。endreturn res end-- 我們規定"s1 - s2"相當于求"(s1 * s2)"在"s1"中的補集。 function Set.sub(a, b)local res = Set.new{}for k in pairs(a) doif not b[k] thenres[k] = trueendendreturn res end-- 我們規定"s1 / s2",僅是打印一句話,以證明調用了正確的"metamethod"。 function Set.div(a, b)local res = Set.new{"__div"}return res end-- 我們規定"-s"僅是打印一句話,以證明調用了正確的"metamethod"。 function Set.unm(a)local res = Set.new{"__unm"}return res end-- 我們規定"s1 ^ s2"僅是打印一句話,以證明調用了正確的"metamethod"。 function Set.pow(a, b)local res = Set.new{"__pow"}return res end-- 將集合轉換為字符串形式。 function Set.tostring (set)local s = "{"local sep = ""for e in pairs(set) dos = s .. sep .. esep = ", "endreturn s .. "}" end-- 打印集合。 function Set.print (s)print(Set.tostring(s)) end-- 定義meta-method Set.mt.__add = Set.union -- 定義兩個"table"相加的邏輯,并集。 Set.mt.__mul = Set.intersection -- 定義兩個"table"相乘的邏輯,交集。 Set.mt.__sub = Set.sub -- 定義兩個"table"相減的邏輯。 Set.mt.__div = Set.div -- 定義兩個"table"相除的邏輯。 Set.mt.__unm = Set.unm -- 定義一個"table"取反的邏輯。 Set.mt.__pow = Set.pow -- 定義兩個"table"相冪的邏輯。s1 = Set.new{10, 20, 30, 50} s2 = Set.new{30, 1} -- 兩個集合共享同一個"metatable"。 print(getmetatable(s1) == getmetatable(s2)) --> trueSet.print(s1 + s2) --> {1, 10, 20, 30, 50} Set.print(s1 * s2) --> {30} Set.print(s1 - s2) --> {20, 50, 10} Set.print(s1 / s2) --> {__div} Set.print(-s2) --> {__unm} Set.print(s1 ^ s2) --> {__pow}__index metamethod控制 table 的訪問
由于metamethod __index用于訪問對象的屬性,那么我們可以修改來添加訪問控制。
在我們訪問 table 的不存在的域時,Lua 會嘗試調用 __index metamethod。__index metamethod 接受兩個參數 table 和 key:
local mt = {} function mt.__add(a, b)return 'table + ' .. b endprint ('--1--') print("mt", mt)local t = {} print("meta-table origin: ", getmetatable(t)) -- nilprint ('--2--') setmetatable(t, mt) print("meta-table", getmetatable(t)) -- tableprint(t + 1)---- ---- local mt = {} mt.__index = function(table, key) print('table -- ' .. tostring(table)) print('key -- ' .. key) return mt.key end mt.__newindex = function(table, key, value) print('table -- ' .. tostring(table)) print('key -- ' .. key) print('value -- '..value)mt.key = value; end local tt = {} setmetatable(tt, mt)print ('--3--') local v = tt.aprint("v=", v) -- nil print ('--4--') tt.a = "a" local va = mt.a; print("v-a=",va) print ('--5--') local vb= tt.b; print("v-b=",vb) print ('--6--') tt.b = "b" local vbb = tt.b print("v-b=",vb, vbb)輸出:
--1-- mt table: 0x7ff195c04490 meta-table origin: nil --2-- meta-table table: 0x7ff195c04490 table + 1 --3-- table -- table: 0x7ff195c08550 key -- a v= nil --4-- table -- table: 0x7ff195c08550 key -- a value -- a v-a= nil --5-- table -- table: 0x7ff195c08550 key -- b v-b= a --6-- table -- table: 0x7ff195c08550 key -- b value -- b table -- table: 0x7ff195c08550 key -- b v-b= a b總結
以上是生活随笔為你收集整理的Lua中的metatable的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中的函数修饰器
- 下一篇: ChatGPT 所有者 OpenAI 推