Как создать простой импортируемый класс в 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'".
В вашем примере кода есть три проблемы:
- Вы пытаетесь использовать
__init
вместо__call
, Луа не имеет__init
Метаметод. __call
принимает другие параметры, чем те, которые вы используете. Первый параметр для__call
Функция - это сама таблица. Вы можете использоватьfunction MyClass.__call(self, tab)
или используйте синтаксис двоеточия,function MyClass:__call(tab)
, который неявно добавляетself
Параметр для вас. Эти два синтаксиса функционально идентичны.- Вы не установили метатаблицу для
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