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.

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

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 metamethod

This 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.


We can use the lua command to enter interactive mode to 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, 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	metatablevalue

Example 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.

  • We can simply write the above code as:
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:

ModeDescription
__add The corresponding operator '+'.
__subThe corresponding operator '-'.
__mulCorresponding operator '*'.
__divCorresponding operator '/'.
__mod corresponds to the operator '%'.
__unm and corresponds to the operator '-'.
__concatThe corresponding operator '..'.
__eqThe corresponding operator '=='.
__ltThe corresponding operator '<'.
__leThe 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:

表所有元素的和为 60

From 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.