如何在 Lua 中创建一个简单的可导入 class?

How to create a simple importable class in Lua?

我想在单独的文件 myclass.lua 中的 Lua 中创建 MyClass class,我可以将其导入并在以后使用。 它应该按以下方式工作:

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

但是,按照 Lua 文档中的代码,我无法使其正常工作并且出现错误 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 中编写 classes,但我不认为我理解其中的区别,因此在实现细节中迷失了方向。是否有或多或少的常规方法来做到这一点?

没有 __init 元方法可用于 table。当您执行以下操作时:

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

在 Lua 中,您通常不能像调用函数那样调用 table。例如,此代码将产生 "attempt to call local 't' (a table value)".

的错误
local t = {}
t()

不过,有一种方法可以通过使用元tables 来完成这项工作。

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

当您尝试像调用函数一样调用 table 时,Lua 首先检查 table 是否有元 table。如果是,则它会尝试调用该元 table 的 __call 属性 中的函数。 __call 函数的第一个参数是 table 本身,后续参数是 table 作为函数调用时传递的参数。如果 table 没有 metatable,或者 metatable 没有 __call 函数,则会引发 "attempt to call local 't'" 错误。

您的示例代码存在三个问题:

  1. 您正在尝试使用 __init 而不是 __call。 Lua 没有 __init 元方法。
  2. __call 采用与您使用的参数不同的参数。 __call 函数的第一个参数是 table 本身。您可以使用 function MyClass.__call(self, tab),或使用冒号语法 function MyClass:__call(tab),它会隐式地为您添加 self 参数。这两种语法在功能上是相同的。
  3. 您还没有为 MyClass table 设置元table。当您为 MyClass 的对象设置 metatable 时,这并不意味着会自动为 MyClass 本身设置 metatable。

要解决此问题,您可以执行以下操作:

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 设置为将自身用作元table,这是完全有效的Lua。

metatables 的系统非常灵活,允许您拥有几乎任何您想要的 class/object 方案。例如,如果您愿意,您可以内联完成所有操作。

local MyClass = {}

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

return MyClass

除了简洁之外,这还有一个好处,即如果人们可以访问 class table,则他们无法更改 class 的元方法。