0Pricing
Lua Academy · Урок

Введение в метатаблицы

Устанавливайте и получайте метатаблицы с помощью 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_1

setmetatable и 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))   -- Lua

rawget и 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 — локальная установка не требуется.

Все уроки этого курса

  1. Введение в метатаблицы
  2. __index и __newindex
  3. Арифметические метаметоды
  4. __tostring и __len
← Назад к Lua Academy