Lua元表(Metatable)
元表的基本概念
元表(Metatable)是Lua中一种特殊的表,用于定义或扩展其他表的行为。通过元表,可以自定义表的操作方式,例如加法、减法、索引、调用等。每个表可以关联一个元表,元表本身也是一个普通的表,但包含特定的元方法(Metamethods)来定义行为。
设置和获取元表
使用setmetatable(table, metatable)
为表设置元表,getmetatable(table)
获取表的元表。元表通常包含一系列以双下划线(__
)开头的键,称为元方法。
local t = {}local mt = {}setmetatable(t, mt) -- 设置mt为t的元表print(getmetatable(t) == mt) -- 输出true
常用元方法
算术运算元方法
__add
: 定义加法操作(+
)__sub
: 定义减法操作(-
)__mul
: 定义乘法操作(*
)__div
: 定义除法操作(/
)__mod
: 定义取模操作(%
)__pow
: 定义幂运算操作(^
)__unm
: 定义负号操作(-
)
示例:
local vec1 = {x = 1, y = 2}local vec2 = {x = 3, y = 4}local mt = { __add = function(a, b) return {x = a.x + b.x, y = a.y + b.y} end}setmetatable(vec1, mt)setmetatable(vec2, mt)local vec3 = vec1 + vec2print(vec3.x, vec3.y) -- 输出4, 6
关系运算元方法
__eq
: 定义相等操作(==
)__lt
: 定义小于操作(<
)__le
: 定义小于等于操作(<=
)
示例:
local mt = { __eq = function(a, b) return a.value == b.value end}local a = {value = 10}local b = {value = 10}setmetatable(a, mt)setmetatable(b, mt)print(a == b) -- 输出true
其他元方法
__index
: 定义当访问表中不存在的键时的行为。__newindex
: 定义当给表中不存在的键赋值时的行为。__call
: 定义当表被调用时的行为。__tostring
: 定义当表被转换为字符串时的行为。__len
: 定义当使用#
操作符获取表长度时的行为。
__index
元方法
__index
可以是一个函数或表。如果是表,当键不存在时会从该表中查找;如果是函数,则会调用函数并返回结果。
示例(函数形式):
local mt = { __index = function(table, key) return \"Key \" .. key .. \" not found\" end}local t = {}setmetatable(t, mt)print(t.name) -- 输出\"Key name not found\"
示例(表形式):
local defaults = {name = \"Unknown\", age = 0}local mt = {__index = defaults}local t = {}setmetatable(t, mt)print(t.name) -- 输出\"Unknown\"
__newindex
元方法
__newindex
可以是一个函数或表。如果是表,赋值操作会修改该表;如果是函数,则会调用函数处理赋值。
示例(函数形式):
local mt = { __newindex = function(table, key, value) rawset(table, key, value .. \" (modified)\") end}local t = {}setmetatable(t, mt)t.name = \"Alice\"print(t.name) -- 输出\"Alice (modified)\"
__call
元方法
允许表像函数一样被调用。
示例:
local mt = { __call = function(table, arg) print(\"Called with\", arg) end}local t = {}setmetatable(t, mt)t(42) -- 输出\"Called with 42\"
__tostring
元方法
自定义表的字符串表示形式。
示例:
local mt = { __tostring = function(table) return \"Table: \" .. table.name end}local t = {name = \"Test\"}setmetatable(t, mt)print(t) -- 输出\"Table: Test\"
使用rawget
和rawset
rawget(table, key)
和rawset(table, key, value)
可以绕过元方法直接操作表,避免递归调用。
示例:
local t = {}local mt = { __newindex = function(table, key, value) rawset(table, key, value * 2) -- 直接赋值,避免递归 end}setmetatable(t, mt)t.x = 10print(t.x) -- 输出20
元表的继承
通过__index
可以实现类似继承的效果。
示例:
local parent = {name = \"Parent\"}local child = {}setmetatable(child, {__index = parent})print(child.name) -- 输出\"Parent\"
Warring:
- 元表的功能强大,但滥用可能导致代码难以维护。
- 避免在元方法中执行耗时操作,可能影响性能。
- 元表是Lua实现面向对象编程的基础机制之一。