Lua Metatable
In a Lua table, we can access the corresponding key to get the value, but we cannot perform operations on two tables (such as addition).
Therefore Lua provides metatables, allowing us to change the behavior of tables. Each behavior is associated with a 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 either of them has a metatable, then checks whether there is a field called__addIf found, the corresponding value is called.__addFields such as these; their corresponding values (often a function or a table) are the "metamethods".
There are two very important functions for handling metatables:
- setmetatable(table,metatable):Sets the metatable for the specified table. If the metatable contains a __metatable key, setmetatable will fail.
- getmetatable(table):Returns the metatable of the object.
The following example demonstrates how to set a metatable for a specified table:
mymetatable = {} -- Metatable
setmetatable(mytable,mymetatable) -- Set mymetatable as the metatable of mytable
The above code can also be written in one line:
mytable = setmetatable({},{})
The following returns the metatable of an object:
getmetatable(mytable) -- 这会返回 mymetatable
__index metamethod
This is the most commonly used key in a 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 up the corresponding key in that table.
We can use the lua command to enter interactive mode and check:
Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
> other = { foo = 3 }
> t = setmetatable({}, { __index = other })
> t.foo
3
> t.bar
nil
If __index contains a function, Lua will call that function, passing the table and the key as arguments to the function.
The __index metamethod checks whether an element exists in the table. If it does not exist, the result returned is nil; if it exists, the result is returned by __index.
Example
__index = function(mytable, key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
})
print(mytable.key1,mytable.key2)
The output of the example is:
value1 metatablevalue
Example analysis:
The mytable table is assigned as{key1 = "value1"}。
mytable has a metatable set, 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 that function.
In the metamethod, check whether the "key2" key parameter is passed (mytable.key2 is set). If the "key2" parameter is passed, return "metatablevalue"; otherwise, return the corresponding value of mytable for the key.
We can rewrite the above code simply as:
print(mytable.key1,mytable.key2)
Summary
The rules when Lua looks up a table element are actually the following 3 steps:
- 1. Look in the table. If found, return the element; if not found, continue.
- 2. Determine whether the table has a metatable. If it has no metatable, return nil; if it has 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 steps 1, 2, and 3; if the __index method is a function, return the return value of that function.
This part is from author Huanzi: https://blog.csdn.net/xocoder/article/details/9028347
__newindex metamethod
The __newindex metamethod is used to update tables, while __index is used to access tables.
When you assign a value to a missing index of a table, the interpreter looks for the __newindex metamethod: if it exists, it calls this function without performing the assignment.
The following example demonstrates the application of the __newindex metamethod:
Example
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print(mytable.key1)
mytable.newkey = "new value 2"
print(mytable.newkey,mymetatable.newkey)
mytable.key1 = "new value 1"
print(mytable.key1,mymetatable.key1)
The output after executing the above example is:
value1 nil 新值2 新值1 nil
In the above example, the table has the metamethod __newindex set. When assigning to a new index key (newkey) (mytable.newkey = "new value 2"), the metamethod is called and the assignment is not performed. When assigning to an existing index key (key1), the assignment is performed and the __newindex metamethod is not called.
The following example uses the rawset function to update a table:
Example
__newindex = function(mytable, key, value)
rawset(mytable, key, "\""..value.."\"")
end
})
mytable.key1 = "new value"
mytable.key2 = 4
print(mytable.key1,mytable.key2)
The output after executing the above example is:
new value "4"
Adding operators to tables
The following example demonstrates the addition of two tables:
Example
function table_maxn(t)
local mn = 0
for k, _ in pairs(t) do
if type(k) == "number" and k > mn then
mn = k
end
end
return mn
end
-- Addition operation of two tables
mytable = setmetatable({ 1, 2, 3 }, {
__add = function(mytable, newtable)
local max_key_mytable = table_maxn(mytable)
for i = 1, table_maxn(newtable) do
table.insert(mytable, max_key_mytable + i, 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 after executing the above example is:
1 1 2 2 3 3 4 4 5 5 6 6
The __add key is included in the metatable and performs the addition operation. The list of corresponding operations in the table is as follows: (Note:__are two underscores)
| Pattern | Description |
|---|---|
| __add | Corresponding operator '+'. |
| __sub | Corresponding operator '-'. |
| __mul | Corresponding operator '*'. |
| __div | Corresponding operator '/'. |
| __mod | Corresponding operator '%'. |
| __unm | Corresponding operator '-'. |
| __concat | Corresponding operator '..'. |
| __eq | Corresponding operator '=='. |
| __lt | Corresponding operator '<'. |
| __le | Corresponding operator '<='. |
__call metamethod
The __call metamethod is called when Lua calls a value. The following example demonstrates calculating the sum of elements in a table:
Example
-- Custom function table_maxn to calculate the maximum key value in a table, i.e., calculate the number of elements in the table
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
-- Define the metamethod __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 output after executing the above example is:
70
__tostring metamethod
The __tostring metamethod is used to modify the output behavior of a table. In the following example, we customize the output content of a table:
Example
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "The sum of all elements in the table is " .. sum
end
})
print(mytable)
The output after executing the above example is:
表所有元素的和为 60
From this article, we can learn that metatables can effectively simplify our code functionality, so understanding Lua's metatables can help us write simpler and better Lua code.
Other extensions