Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Lua meta-table (Metatable)


May 12, 2021 Lua


Table of contents


Lua meta-table (Metatable)

In Lua table we can access the corresponding key to get the value of the value, but we can't operate on both table.

So Lua provides a metatable that allows us to change the behavior of the table, each of which corred with the corresponding meta-method.

For example, using meta-tables, we can define how Lua calculates the addition of two tables.

When Lua tries to add two tables, first check if one of them has a meta-table, then check if there is a field called __add, and if so, call the corresponding value. An __add field, such as "Property", whose corresponding value (often a function or table) is the "meta-method".

There are two important functions to handle metasheets:

  • setmetable (table, metatable): Set the metatable for the specified table, and if there is a key value __metatable the metametable, the setmetable will fail.
  • getmetable (table): The meta-table (metatable) that returns the object.

The following example shows how to set up a meta-table on 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 the return object meta-table:

getmetatable(mytable)                 -- 这回返回mymetatable

The following are commonly used fields in meta-tables:

  • Arithmetic meta-methods: fields: __add (-), __mul(*), s sub(-), __div(/), __unm, __mod(%), __pow, (__concat)
  • Relationship class meta-methods: fields: __eq, __lt (-lt;), __le (-lt;), other Lua auto-converts a-b--b--not (a- b) a --gt; b-lt; a-gt;b-- b--gt;
  • Meta-methods accessed by table: field: __index, __newindex
  • __index: Query: Access fields that do not exist in the table
    rawget(t, i)
  • __newindex: Update: The index does not exist in the table

    rawset(t, k, v)


__index meta-method

This is the most common key used by metatable.

When you access table through a key, if the key has no value, Lua looks for the key in the table's metalable (assuming there is a __index) key. If __index contains a table, Lua looks for the appropriate key in the table.

We can view in interactive mode using the lua command:

$ 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 calls that function, and table and keys are passed to the function as arguments.

__index meta-method to see if an element in the table exists, returns a result of nil if it does not exist, or __index returns a result.

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 of the instance is:

value1	metatablevalue

Instance resolution:

  • The mytable table is assigned a value of "value1".

  • mytable sets up a meta-table, which is __index.

  • Look for key1 in the mytable table, and if you find it, return the element, and continue if you can't find it.

  • Look for key2 in the mytable table and, if found, return the element, and continue if it is not found.

  • Determines whether a metasheet has __index method, __index if the method is a function, the function is called.

  • The meta-method sees whether the parameters of the "key2" key are passed in (mytable.key2 has been set), and if the incoming "key2" parameter returns "metatablevalue", otherwise the key value corresponding to mytable is returned.

We can simply write the above code as:

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

__newindex meta-method

__newindex method is used to update the table, __index is used to access the table.

When you assign a missing index to a table, the interpreter looks for the __newindex method: if present, the function is called without the assignment.

The following example demonstrates the __newindex the binary 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.newkey1)

The output of the above examples is:

value1
nil    新值2
新值1    nil

In the example above, the table sets the meta-method __newindex, which is called without assigning a meta-method when assigning a new index key (mytable.newkey s ."new value 2"). If you assign an index key (key1) that already exists, you are assigned a value 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 of the above examples is:

new value  "4"

Add an operator to the table

The following example demonstrates a two-table add-up operation:

-- 计算表中最大值,table.maxnLua5.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 of the above examples is:

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

__add keys are included in the meta-table and are added together. The list of actions in the table is as follows:

Mode Describe
__add The corresponding operator, 'plus'.
__sub The corresponding operator '-'.
__mul The corresponding operator' .
__div Corresponding operator '/'.
__mod The corresponding operator '%'.
__unm The corresponding operator '-'.
__concat Corresponding operator '..'.
__eq The corresponding operator' .
__lt The corresponding operator, 'lt;'.
__le The corresponding operator, 'lt;'.

__call meta-method

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

-- 计算表中最大值,table.maxnLua5.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 output of the above examples is:

70

__tostring meta-method

__tostring method is used to modify the output behavior of the table. Here are some examples where we customized the output of a 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 of the above examples is:

表所有元素的和为 60

From this article we can see that meta-tables can simplify our code functionality, so understanding Lua's meta-tables allows us to write simpler and better Lua code.