Как создать простой импортируемый класс в Lua?

Я хотел бы создать MyClass класс в Lua в отдельном файле myclass.lua, который я могу импортировать и использовать позже. Это должно работать следующим образом:

local MyClass = require 'myclass'
tab = {1,2,3}
m = MyClass(tab)

Однако, следуя коду в Lua docs, я не могу заставить его работать и получаю ошибки attempt to call global 'MyClass' (a table value),

Код, который я написал для myclass.lua:

local MyClass = {}
MyClass.__index = MyClass

function MyClass.__init(tab)
    self.tab = tab or {}
    setmetatable({},MyClass)
    return self
end
return MyClass

Существует множество примеров того, как писать классы на Lua, но я не думаю, что понимаю разницу и в результате теряюсь в деталях реализации. Есть ли более или менее традиционный способ сделать это?

2 ответа

Решение

В Lua вы не можете обычно вызывать таблицу, как если бы вы вызывали функцию. Например, этот код выдаст ошибку "попытка вызвать локальное" t "(табличное значение)".

local t = {}
t()

Однако есть способ заставить эту работу использовать метатаблицы.

local hello = {}
local mt = {} -- The metatable
mt.__call = function ()
  print("Hello!")
end
setmetatable(hello, mt)
hello() -- prints "Hello!"

Когда вы пытаетесь вызвать таблицу как функцию, Lua сначала проверяет, есть ли у таблицы метатабельность. Если это так, то он пытается вызвать функцию в __call свойство этого метатабельного. Первый аргумент __call Функция - это сама таблица, а последующие аргументы - это аргументы, которые были переданы, когда таблица была вызвана как функция. Если таблица не имеет метатаблицы или метатаблицы не имеют __call функции, то возникает ошибка "попытка вызвать локальную 't'".

В вашем примере кода есть три проблемы:

  1. Вы пытаетесь использовать __init вместо __call, Луа не имеет __init Метаметод.
  2. __call принимает другие параметры, чем те, которые вы используете. Первый параметр для __call Функция - это сама таблица. Вы можете использовать function MyClass.__call(self, tab)или используйте синтаксис двоеточия, function MyClass:__call(tab), который неявно добавляет self Параметр для вас. Эти два синтаксиса функционально идентичны.
  3. Вы не установили метатаблицу для MyClass Таблица. Хотя вы устанавливаете метатабель для объектов MyClass, это не означает, что метатабель автоматически устанавливается для самого MyClass.

Чтобы это исправить, вы можете сделать что-то вроде следующего:

local MyClass = {}
setmetatable(MyClass, MyClass)
MyClass.__index = MyClass

function MyClass:__call(tab)
    local obj = {}
    obj.tab = tab or {}
    setmetatable(obj, MyClass)
    return obj
end

return MyClass

Это устанавливает MyClass для использования себя в качестве метатаблицы, что является совершенно допустимым Lua.

Система метатаблиц очень гибкая и позволяет вам иметь практически любую схему классов / объектов, которую вы хотите. Например, если вы хотите, вы можете делать все встроенные.

local MyClass = {}

setmetatable(MyClass, {
    __call = function (class, tab)
        local obj = {}
        obj.tab = tab or {}
        setmetatable(obj, {
            __index = MyClass
        })
        return obj
    end
})

return MyClass

Помимо краткости, это также имеет то преимущество, что люди не могут изменять метаметоды класса, если у них есть доступ к таблице класса.

Здесь нет __init метаметод доступен для таблицы. Когда вы делаете следующее:

m = MyClass(tab)

это выглядит для MyClass.__call определение метода. Просто обновите свой myclass.lua как:

local MyClass = {}
MyClass.__index = MyClass

function MyClass:__call(tab)
    self.tab = tab or {}
    setmetatable({},MyClass)
    return self
end

return MyClass
Другие вопросы по тегам