Введение в метатаблицы
Устанавливайте и получайте метатаблицы с помощью setmetatable/getmetatable.
«Введение в метатаблицы» — бесплатный урок Lua Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Lua Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Lua Academy содержит 4 уроков всего.
Что такое метатаблица
Метатаблица — это обычная таблица Lua, которая управляет поведением другой таблицы. Когда Lua встречает операцию, которую не может выполнить над таблицей (например, арифметическую операцию или обращение к отсутствующему ключу), он ищет соответствующий метаметод в метатаблице. Это механизм расширения Lua для перегрузки операторов и объектно-ориентированного программирования.
local t = {}
local mt = {__index = function(_, k) return "default_" .. k end}
setmetatable(t, mt)
print(t.foo) -- default_foo
print(t.bar) -- default_bar
print(t[1]) -- default_1setmetatable и getmetatable
setmetatable(t, mt) задаёт для t метатаблицу mt и возвращает t. getmetatable(t) возвращает метатаблицу или nil, если её нет. Метатаблицу можно защитить от изменений, задав mt.__metatable = "protected".
local t = {x = 10}
local mt = {}
setmetatable(t, mt)
print(getmetatable(t) == mt) -- true
-- Protect metatable
mt.__metatable = "locked"
print(getmetatable(t)) -- locked
-- setmetatable(t, {}) -- ERROR: cannot change protected metatable__index как таблица
Если __index является таблицей, Lua ищет отсутствующий ключ в этой таблице. Это основа прототипного наследования в Lua. Если ключ отсутствует и в __index, Lua проверяет её метатаблицу и продолжает поиск вверх по цепочке.
local Animal = {sound = "...", legs = 4}
local Dog = setmetatable({sound = "Woof"}, {__index = Animal})
local Cat = setmetatable({sound = "Meow"}, {__index = Animal})
print(Dog.sound) -- Woof (own field)
print(Dog.legs) -- 4 (from Animal)
print(Cat.sound) -- Meow
print(Cat.legs) -- 4__index как функция
Если __index является функцией, Lua вызывает её, передавая таблицу и отсутствующий ключ. Функция может вычислить или получить значение динамически. Верните nil из __index, чтобы указать, что «ключ не найден».
local dynamic = setmetatable({}, {
__index = function(t, k)
if k:sub(1,4) == "get_" then
local field = k:sub(5)
return function(self) return self[field] end
end
return nil
end
})
dynamic.name = "Lua"
local getter = dynamic.get_name
print(getter(dynamic)) -- Luarawget и rawset
rawget(t, k) читает ключ таблицы, не вызывая __index. rawset(t, k, v) задаёт ключ, не вызывая __newindex. Используйте эти функции, чтобы обходить метаметоды, когда внутри метаметода нужен прямой доступ к таблице и необходимо избежать бесконечной рекурсии.
local logged = setmetatable({}, {
__newindex = function(t, k, v)
print("Setting", k, "=", v)
rawset(t, k, v) -- bypass __newindex
end
})
logged.x = 10 -- Setting x = 10
logged.x = 20 -- no log! key now exists in t
print(logged.x) -- 20Несколько таблиц с общей метатаблицей
Одну метатаблицу можно использовать совместно для множества таблиц. Так работают системы классов Lua: все экземпляры класса используют одну и ту же метатаблицу, в которой хранятся методы. Изменения метатаблицы влияют на все экземпляры.
local Mt = {
__index = {
greet = function(self)
print("Hello from " .. (self.name or "?"))
end
}
}
local a = setmetatable({name="Alice"}, Mt)
local b = setmetatable({name="Bob"}, Mt)
a:greet() -- Hello from Alice
b:greet() -- Hello from BobПоле __metatable
Если присвоить mt.__metatable любое значение, getmetatable будет возвращать это значение вместо настоящей метатаблицы. Это не позволяет пользователям просматривать или заменять метатаблицу — полезно для инкапсуляции и безопасности.
local secret = setmetatable({}, {
__metatable = false, -- hide metatable
__index = {value = 42}
})
print(secret.value) -- 42
print(getmetatable(secret)) -- false (not the real mt)
-- setmetatable(secret, {}) -- ERRORПроверка наличия метатаблицы
Используйте getmetatable(t), чтобы проверить, есть ли у объекта определённая метатаблица (для проверки типа). Поскольку __metatable может скрывать настоящую метатаблицу, для надёжной проверки экземпляра используйте rawequal или поле-маркер.
local MyClass = {}
MyClass.__index = MyClass
MyClass.__metatable = MyClass -- use self as marker
function MyClass.new(x)
return setmetatable({x=x}, MyClass)
end
local obj = MyClass.new(5)
print(getmetatable(obj) == MyClass) -- true
print(obj.x) -- 5Метатаблицы у не таблиц
Строки в Lua уже имеют метатаблицу (модуль string). Поэтому для любой строки можно вызвать s:upper(). В чистом Lua нельзя задавать метатаблицы для строк, чисел или логических значений — только для таблиц и пользовательских данных.
local s = "hello"
local mt = getmetatable(s)
-- String metatable has __index = string library
print(mt.__index == string) -- true
-- So all string functions are methods:
print(s:upper()) -- HELLO
print(s:rep(3, "-")) -- hello-hello-helloОтладка с помощью метатаблиц
Можно добавить метаметод __tostring, чтобы управлять тем, как tostring() и print() отображают таблицу. Это незаменимо при отладке сложных объектов.
local Point = {}
Point.__index = Point
Point.__tostring = function(p)
return string.format("Point(%g, %g)", p.x, p.y)
end
function Point.new(x, y)
return setmetatable({x=x,y=y}, Point)
end
local p = Point.new(3, 4)
print(tostring(p)) -- Point(3, 4)
print(p) -- Point(3, 4) (print calls tostring)Быстрая проверка
Что произойдёт при обращении к ключу, которого нет в таблице, если в её метатаблице __index задан как другая таблица?
Повторение: метатаблицы
Краткое содержание:
setmetatable(t, mt)/getmetatable(t)__index: таблица → поиск в прототипе; функция → динамическая диспетчеризацияrawget/rawsetобходят метаметоды- Общие метатаблицы позволяют создавать поведение, похожее на классовое
__metatableзащищает метатаблицу от просмотра__tostringуправляет выводом через print
Часто задаваемые вопросы
Урок «Введение в метатаблицы» бесплатный?
Да — полный текст урока «Введение в метатаблицы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.
Чему я научусь в уроке «Введение в метатаблицы»?
Устанавливайте и получайте метатаблицы с помощью setmetatable/getmetatable. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Lua Academy?
Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Введение в метатаблицы»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Lua Academy?
Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Введение в метатаблицы
- __index и __newindex
- Арифметические метаметоды
- __tostring и __len