English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Lua Metamethods (Metatable)

In Lua table, we can access the corresponding key to get the value, but we cannot perform operations on two tables.

Therefore, Lua provides metatables that allow us to change the behavior of tables, each behavior associated with the corresponding meta method.

For example, using metatables, we can define how Lua calculates the addition operation of two tables a+b.

When Lua tries to add two tables, it first checks if one of them has a metatable, then checks if there is a field called "__add", if found, it calls the corresponding value. "__add" and other immediate fields, whose corresponding values (often a function or a table) are called "meta methods".

There are two very important functions to handle metatables:

  • setmetatable(table, metatable): If the metatable (metatable) of the specified table contains a __metatable key value, setmetatable will fail.

  • getmetatable(table): Return the metatable of the object.

The following example demonstrates how to set a metatable for a specified table:

mytable = {}                          -- Normal 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 is the returned object metatable:

getmetatable(mytable)                 -- This will return mymetatable

__index meta method

This is the most commonly used key in metatable.

When accessing 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 will look for the corresponding key in the table.

We can use the lua command to enter interactive mode and view:

$ 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
nil

If __index contains a function, Lua will call that function, passing the table and key as arguments to the function.

__index meta-method checks if the element exists in the table, if not, 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 example output result is:

value1    metatablevalue

Example parsing:

  • The value assigned to the mytable table is {key1 = "value1"}.

  • mytable has set a metatable, the meta-method is __index.

  • Search for key in mytable1If found, return the element, if not found, then continue.

  • Search for key in mytable2If found, return metatablevalue, if not found, then continue.

  • If the __index method is a function, call the function.

  • Check in the meta-method if the "key2" key parameter (mytable.key2If the "key" parameter (mytable.key is set), if passed "key2" parameter returns "metatablevalue", otherwise returns the corresponding key value of mytable.

We can simply write the above code as:

mytable = setmetatable({key1 = "value1"}2 = "metatablevalue"}
print(mytable.key1,mytable.key2)

Summary

The rules that Lua uses to find a table element are actually as follows 3 steps:

  • 1. Search in the table, if found, return the element, if not found, then continue

  • 2. Check if the table has a metatable, if not, return nil, if it has a metatable, then continue.

  • 3. Check if the metatable has a __index method, if __index method is nil, then return nil; if __index method is a table, then repeat 1,2,3; if the __index method is a function, then return the return value of the function.

This part of the content is from the author Huanzi: https://blog.csdn.net/xocoder/article/details/9028347

__newindex meta-method

The __newindex meta-method is used to update tables, while __index is used for table access.

When you assign a value to a missing index of a table, the interpreter looks for the __newindex meta-method: if it exists, it calls this function without performing the assignment operation.

The following examples demonstrate the application of the __newindex meta-method:

mymetatable = {}
mytable = setmetatable({key1 = "value1"}
print(mytable.key1)
mytable.newkey = "新值"2"
print(mytable.newkey, mymetatable.newkey)
mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1)

The output result of the above example is:

value1
nil        new value2
new value1    nil

In the above example, the table has set the meta method __newindex, and when assigning a new index key (mytable.newkey = "new value2If a meta method is called on an existing index key (key1) will perform assignment 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 output result of the above example is:

new value        "4"

Add operator to table

The following example demonstrates the two-table addition operation:

-- Calculate the maximum value in the table, table.maxn in Lua5.2This version can no longer be used
-- Custom calculation function table_maxn, which calculates 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
-- Two-table addition operation
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 above example is:

1    1
2    2
3    3
4    4
5    5
6    6

__add key is included in the meta table and performs addition operations. The corresponding operation list in the table is as follows: (Note:__is two underscores)

PatternDescription
__addcorresponds to the operator '+'.
__subcorresponds to the operator '-'.
__mulcorresponds to the operator '*'.
__divcorresponds to the operator '/'.
__modcorresponds to the operator '%'.
__unmcorresponds to the operator '-'.
__concatcorresponds to the operator '..'.
__eqcorresponds to the operator '=='.
__ltcorresponds to the operator '<'.
__lecorresponds to the operator '<='.

__call meta method

__call meta method is called when a value is invoked in Lua. The following example demonstrates the calculation of the sum of elements in a table:

-- Calculate the maximum value in the table, table.maxn in Lua5.2This version can no longer be used
-- Custom calculation function table_maxn, which calculates 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 result of the above example is:

70

__tostring Metamethod

The __tostring metamethod is used to modify the output behavior of the table. In the following example, we customize 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 "The sum of all elements in the table is " .. sum
  end
)
print(mytable)

The output result of the above example is:

The sum of all elements in the table is 60

From this article, we can know that metamethods can greatly simplify our code functionality. Therefore, understanding Lua's metamethods can help us write simpler and excellent Lua code.