Lua Metatable
In Lua table we can access the corresponding key to get the value, but we cannot operate on the two tables.
Therefore, Lua provides a metatable (Metatable), which allows us to change the behavior of the table. Each behavior is associated with the corresponding metamethod.
For example, using metatables we can define how Lua calculates the addition operation a+b of two tables.
When Lua tries to add two tables, it first checks whether one of the two tables has a metatable, and then checks whether there is a field called "__add". If found, the corresponding value is called. For real-time fields such as "__add", their corresponding value (often a function or table) is a "meta-method".
There are two very important functions to handle metatables:
##setmetatable(table, metatable): Set metatable for the specified table ( metatable), if the __metatable key value exists in the metatable (metatable), setmetatable will fail.
getmetatable(table): Returns the metatable of the object.
mytable = {} -- 普通表 mymetatable = {} -- 元表 setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表The above code can also be written directly in one line:
mytable = setmetatable({},{})The following is returned Object metatable:
getmetatable(mytable) -- 这回返回mymetatable
__index metamethodThis is the most commonly used key of metatable.
When you access a table by key, if the key has no value, Lua will look for the __index key in the table's metatable (assuming there is a metatable). If __index contains a table, Lua looks for the corresponding key in the table.
$ lua Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio > other = { foo = 3 } > t = setmetatable({}, { __index = other }) > t.foo 3 > t.bar nilIf __index contains a function, Lua will call that function, and the table and key will be as Parameters are passed to the function. __index meta method checks whether the element exists in the table. If it does not exist, the return result is nil; if it exists, the result is returned by __index.
mytable = setmetatable({key1 = "value1"}, { __index = function(mytable, key) if key == "key2" then return "metatablevalue" else return nil end end }) print(mytable.key1,mytable.key2)The output result of the example is:
value1 metatablevalueExample analysis:
- ##mytable table assignment is
- {key1 = "value1"}
.
mytable sets the metatable, and the metamethod is __index. - Look for key1 in the mytable table. If found, return the element. If not found, continue.
- Look for key2 in the mytable table. If found, return metatablevalue. If not found, continue.
- Determine whether the metatable has an __index method. If the __index method is a function, call the function.
- In the meta method, check whether the "key2" key parameter is passed in (mytable.key2 has been set). If the "key2" parameter is passed in, "metatablevalue" is returned, otherwise the corresponding value of mytable is returned. key value.
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } }) print(mytable.key1,mytable.key2)
Summary
When Lua searches for a table element The rules are actually the following 3 steps:
- 1. Search in the table, if found, return the element, if not found, continue
2. Determine whether the table has a metatable. If there is no metatable, return nil. If there is a metatable, continue.
3. Determine whether the metatable has an __index method. If the __index method is nil, return nil; if the __index method is a table, repeat 1, 2, and 3; If the __index method is a function, the return value of that function is returned.
__newindex meta-method
__newindex The meta-method is used to update the table, and __index is used to access the table.
When you assign a value to a missing index of the table, the interpreter looks for the __newindex metamethod: if it exists, it calls this function without performing the assignment.
The following examples demonstrate the application of the __newindex meta-method:
mymetatable = {} mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable }) print(mytable.key1) mytable.newkey = "新值2" print(mytable.newkey,mymetatable.newkey) mytable.key1 = "新值1" print(mytable.key1,mymetatable.key1)
The execution output of the above examples is:
value1 nil 新值2 新值1 nil
In the above examples, the table sets the meta-method __newindex. When assigning a value to a new index key (newkey) (mytable.newkey = "new value 2"), the metamethod will be called without assigning a value. If the existing index key (key1) is assigned, the value will be assigned without calling the meta-method __newindex.
The following example uses the rawset function to update the table:
mytable = setmetatable({key1 = "value1"}, { __newindex = function(mytable, key, value) rawset(mytable, key, "\""..value.."\"") end }) mytable.key1 = "new value" mytable.key2 = 4 print(mytable.key1,mytable.key2)
The execution output of the above example is:
new value "4"
Add operators to the table
The following example demonstrates the addition operation of two tables:
-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用 -- 自定义计算表中最大值函数 table_maxn function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end -- 两表相加操作 mytable = setmetatable({ 1, 2, 3 }, { __add = function(mytable, newtable) for i = 1, table_maxn(newtable) do table.insert(mytable, table_maxn(mytable)+1,newtable[i]) end return mytable end }) secondtable = {4,5,6} mytable = mytable + secondtable for k,v in ipairs(mytable) do print(k,v) end
The output result of the execution of the above example is:
1 1 2 2 3 3 4 4 5 5 6 6
__add The key is included in the metatable and the addition operation is performed. The corresponding operation list in the table is as follows:
Mode | Description |
---|---|
__add | The corresponding operator '+'. |
__sub | The corresponding operator '-'. |
__mul | Corresponding operator '*'. |
__div | Corresponding operator '/'. |
__mod | corresponds to the operator '%'. |
__unm | and corresponds to the operator '-'. |
__concat | The corresponding operator '..'. |
__eq | The corresponding operator '=='. |
__lt | The corresponding operator '<'. |
__le | The corresponding operator '<='. |
##__call metamethod__call The metamethod is called when Lua calls a value. The following example demonstrates the calculation of the sum of elements in the table:
-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用 -- 自定义计算表中最大值函数 table_maxn function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end -- 定义元方法__call mytable = setmetatable({10}, { __call = function(mytable, newtable) sum = 0 for i = 1, table_maxn(mytable) do sum = sum + mytable[i] end for i = 1, table_maxn(newtable) do sum = sum + newtable[i] end return sum end }) newtable = {10,20,30} print(mytable(newtable))The execution output of the above example is:
70
__tostring metamethod__tostring metamethod is used to modify The output behavior of the table. In the following example, we have customized the output content of the table:
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)The output result of the execution of the above example is:
表所有元素的和为 60From this article, we can know that the metatable can greatly simplify our code functions. Therefore, understanding Lua's metatable allows us to write simpler and better Lua code.