元表与元方法 #
一、元表概述 #
1.1 什么是元表 #
元表是一个普通的表,用于定义其他表的行为。通过元表,可以:
- 重定义运算符行为
- 自定义表的访问方式
- 实现面向对象编程
lua
-- 每个表都可以有一个元表
local t = {}
local mt = {}
setmetatable(t, mt)
-- 或简写
local t = setmetatable({}, {})
1.2 获取和设置元表 #
lua
local t = {}
local mt = {}
-- 设置元表
setmetatable(t, mt)
-- 获取元表
print(getmetatable(t) == mt) -- true
-- 创建时设置
local t2 = setmetatable({}, {
__index = {x = 1, y = 2}
})
print(t2.x) -- 1
二、元方法 #
2.1 算术运算元方法 #
lua
local Vector = {}
Vector.__index = Vector
function Vector:new(x, y)
return setmetatable({x = x, y = y}, self)
end
-- 加法
function Vector:__add(other)
return Vector:new(self.x + other.x, self.y + other.y)
end
-- 减法
function Vector:__sub(other)
return Vector:new(self.x - other.x, self.y - other.y)
end
-- 乘法(与标量)
function Vector:__mul(scalar)
return Vector:new(self.x * scalar, self.y * scalar)
end
-- 除法
function Vector:__div(scalar)
return Vector:new(self.x / scalar, self.y / scalar)
end
-- 取负
function Vector:__unm()
return Vector:new(-self.x, -self.y)
end
-- 字符串表示
function Vector:__tostring()
return string.format("(%g, %g)", self.x, self.y)
end
-- 使用
local v1 = Vector:new(1, 2)
local v2 = Vector:new(3, 4)
print(v1 + v2) -- (4, 6)
print(v1 - v2) -- (-2, -2)
print(v1 * 2) -- (2, 4)
print(-v1) -- (-1, -2)
2.2 比较运算元方法 #
lua
local Vector = {}
Vector.__index = Vector
function Vector:new(x, y)
return setmetatable({x = x, y = y}, self)
end
-- 相等
function Vector:__eq(other)
return self.x == other.x and self.y == other.y
end
-- 小于
function Vector:__lt(other)
return self.x < other.x or (self.x == other.x and self.y < other.y)
end
-- 小于等于
function Vector:__le(other)
return self < other or self == other
end
-- 使用
local v1 = Vector:new(1, 2)
local v2 = Vector:new(1, 3)
print(v1 == v2) -- false
print(v1 < v2) -- true
print(v1 <= v2) -- true
2.3 __index 元方法 #
lua
-- __index 可以是表或函数
-- 作为表
local defaults = {x = 0, y = 0, z = 0}
local point = setmetatable({}, {__index = defaults})
print(point.x) -- 0(从 defaults 获取)
point.x = 10
print(point.x) -- 10(自己的值)
-- 作为函数
local t = setmetatable({}, {
__index = function(tbl, key)
print("访问不存在的键:" .. key)
return nil
end
})
print(t.name) -- 访问不存在的键:name
-- 实现继承
local Animal = {name = "Animal"}
function Animal:speak()
print(self.name .. " makes a sound")
end
local Dog = setmetatable({}, {__index = Animal})
Dog.name = "Dog"
function Dog:speak()
print(self.name .. " says: Woof!")
end
Dog:speak() -- Dog says: Woof!
2.4 __newindex 元方法 #
lua
-- __newindex 在赋值时触发
-- 监控赋值
local t = setmetatable({}, {
__newindex = function(tbl, key, value)
print(string.format("设置 %s = %s", key, value))
rawset(tbl, key, value)
end
})
t.name = "Lua" -- 设置 name = Lua
-- 只读表
local function readonly(t)
local proxy = {}
setmetatable(proxy, {
__index = t,
__newindex = function()
error("表是只读的", 2)
end
})
return proxy
end
local original = {x = 1, y = 2}
local ro = readonly(original)
print(ro.x) -- 1
-- ro.x = 10 -- 错误:表是只读的
2.5 __call 元方法 #
lua
-- __call 使表可以像函数一样调用
local Counter = {}
Counter.__index = Counter
function Counter:new()
return setmetatable({count = 0}, self)
end
function Counter:__call(n)
self.count = self.count + (n or 1)
return self.count
end
local counter = Counter:new()
print(counter()) -- 1
print(counter(5)) -- 6
print(counter.count) -- 6
2.6 __len 元方法 #
lua
-- __len 定义 # 运算符的行为
local Set = {}
Set.__index = Set
function Set:new(items)
local set = {}
for _, v in ipairs(items or {}) do
set[v] = true
end
return setmetatable(set, self)
end
function Set:__len()
local count = 0
for _ in pairs(self) do
count = count + 1
end
return count
end
local s = Set:new({1, 2, 3, 4, 5})
print(#s) -- 5
2.7 __tostring 元方法 #
lua
-- __tostring 定义 tostring() 的行为
local Person = {}
Person.__index = Person
function Person:new(name, age)
return setmetatable({name = name, age = age}, self)
end
function Person:__tostring()
return string.format("Person{name=%s, age=%d}", self.name, self.age)
end
local p = Person:new("Alice", 30)
print(p) -- Person{name=Alice, age=30}
print(tostring(p)) -- Person{name=Alice, age=30}
2.8 __pairs 和 __ipairs(Lua 5.2+) #
lua
-- 自定义遍历行为
local Array = {}
Array.__index = Array
function Array:new(...)
return setmetatable({...}, self)
end
function Array:__pairs()
local i = 0
local n = #self
return function()
i = i + 1
if i <= n then
return i, self[i]
end
end
end
local arr = Array:new(1, 2, 3, 4, 5)
for i, v in pairs(arr) do
print(i, v)
end
三、运算符重载表 #
| 元方法 | 运算符 | 描述 |
|---|---|---|
| __add | + | 加法 |
| __sub | - | 减法 |
| __mul | * | 乘法 |
| __div | / | 除法 |
| __mod | % | 取模 |
| __pow | ^ | 幂运算 |
| __unm | - | 取负 |
| __idiv | // | 整除 |
| __band | & | 按位与 |
| __bor | | | 按位或 |
| __bxor | ~ | 按位异或 |
| __bnot | ~ | 按位非 |
| __shl | << | 左移 |
| __shr | >> | 右移 |
| __concat | … | 连接 |
| __len | # | 长度 |
| __eq | == | 相等 |
| __lt | < | 小于 |
| __le | <= | 小于等于 |
四、实用示例 #
4.1 实现类 #
lua
local Class = {}
function Class:new(parent)
local class = {}
class.__index = class
if parent then
setmetatable(class, {__index = parent})
end
function class:new(...)
local instance = setmetatable({}, self)
if instance.init then
instance:init(...)
end
return instance
end
return class
end
-- 使用
local Animal = Class:new()
function Animal:init(name)
self.name = name
end
function Animal:speak()
print(self.name .. " makes a sound")
end
local Dog = Class:new(Animal)
function Dog:init(name, breed)
Animal.init(self, name)
self.breed = breed
end
function Dog:speak()
print(self.name .. " says: Woof!")
end
local dog = Dog:new("Buddy", "Golden Retriever")
dog:speak() -- Buddy says: Woof!
4.2 实现代理表 #
lua
local function create_proxy(t)
local proxy = {}
local mt = {
__index = function(_, key)
print("获取:" .. key)
return t[key]
end,
__newindex = function(_, key, value)
print("设置:" .. key .. " = " .. tostring(value))
t[key] = value
end,
__pairs = function()
return pairs(t)
end
}
setmetatable(proxy, mt)
return proxy
end
local data = {x = 1, y = 2}
local proxy = create_proxy(data)
print(proxy.x) -- 获取:x 1
proxy.z = 3 -- 设置:z = 3
4.3 实现默认值 #
lua
local function table_with_default(default)
return setmetatable({}, {
__index = function()
return default
end
})
end
local t = table_with_default(0)
t.x = 10
print(t.x) -- 10
print(t.y) -- 0(默认值)
4.4 实现弱引用表 #
lua
-- 弱键表
local weak_keys = setmetatable({}, {__mode = "k"})
-- 弱值表
local weak_values = setmetatable({}, {__mode = "v"})
-- 弱键弱值
local weak_both = setmetatable({}, {__mode = "kv"})
-- 使用示例:缓存
local cache = setmetatable({}, {__mode = "v"})
local function get_cached(key, factory)
if cache[key] then
return cache[key]
end
local value = factory()
cache[key] = value
return value
end
五、总结 #
本章介绍了 Lua 的元表和元方法:
- 元表概念:定义表的行为
- 算术元方法:重定义运算符
- 比较元方法:自定义比较行为
- __index:访问不存在的键
- __newindex:赋值时触发
- __call:使表可调用
- 其他元方法:__len、__tostring、__pairs
下一章,我们将学习面向对象编程。
最后更新:2026-03-27