Lua: 类方法跳过第一个参数

我正在尝试在Lua中实现一个可以实例化的类。然而,我得到了一个错误,即函数BatchLoader.init的输入参数被移位了。我是否缺失了像python中的self关键字作为任何成员函数的参数输入的实现,或者类定义中还有其他错误?

整个类代码看起来像这样(我剥离了不重要的内容)。:

function BatchLoader:new()
    setmetatable({}, BatchLoader)
    self.epoch_done = false
    return self
end

function BatchLoader:init(X, y, sids, batch_size, argshuffle)
    print("In batchloader")
    print(X:size())    -- Prints size(62000, 1) [which is 'y']
    print(y:size())    -- Prints size(62000, 1) [which is 'sids']
    print(sids)        -- Prints '10'           [which is 'batch_size']
end

但是当我创建这个类,然后调用函数BatchLoader.init,似乎y被解释为X,y被解释为sids,以此类推。因此,将sids作为batch_size的输入解释为实际参数'偏离'了。

在进入这个BatchLoader.init之前,下面的代码是我所调用的。这里的所有内容都按预期输出。

local BatchLoader = require "../datahandler/BatchLoader.lua"
local trainLoader = BatchLoader:new()
print(X_data[{{1, 62000}, {}, {}}]:size()) -- Prints size(62000, 8, 16)
print(y_data[{{1, 62000}, {}}]:size())     -- Prints size(62000, 1)
print(sid_data[{{1, 62000}, {}}]:size())   -- Prints size(62000, 1)

local X_train, y_train, sid_train = trainLoader.init(
    X_data[{{1, a}, {}, {}}],
    y_data[{{1, a}, {}}],
    sid_data[{{1, a}, {}}],
    10, true)

我的问题是:这里的类声明是否存在问题?这是我在Lua中的第一个OOP代码,所以非常感谢任何想法或者帮助! :)

点赞
用户204011
用户204011

在 Lua 中,下面的语法:

o:f(x, y)

是这个的简写:

o.f(self, x, y)

这个对于定义和调用都是这样的。在这里,你使用冒号语法定义 init 并使用点语法调用它,所以它不能工作,因为第一个参数将变成 self,而其他参数将向后错位一位。解决方案是像这样调用 init

local X_train, y_train, sid_train = trainLoader:init(
    X_data[{{1, a}, {}, {}}],
    y_data[{{1, a}, {}}],
    sid_data[{{1, a}, {}}],
    10, true)

这解决了这个问题,但请注意,你的例子中 构造函数也是完全错误的。因为你使用冒号语法定义它,它将始终返回 BatchLoader 表本身,而不是一个新实例。既然它是一个类方法,你应该使用点语法来编写和调用它:

function BatchLoader.new()
    local self = setmetatable({}, BatchLoader)
    self.epoch_done = false
    return self
end

或者简单地:

function BatchLoader.new()
    return setmetatable({epoch_done = false}, BatchLoader)
end

然后你也使用点语法调用它:

local trainLoader = BatchLoader.new()

如果你还没有了解这些内容,我强烈建议你购买最新版的《Lua 程序设计》并阅读面向对象编程部分以了解这些内容。

2017-07-23 14:15:56
用户805875
用户805875

方法语法(使用foo:bar而不是foo.bar)会自动引入self参数。

在函数定义中,function Foo: bar(...)等效于function Foo.bar(self,...)。在函数调用上,foo:bar(...)大致相当于foo.bar(foo,...)(后者评估foo两次)。

那么我们来解析一下您的代码:

function BatchLoader:new()
    setmetatable({}, BatchLoader) -- 创建一个表并将其丢弃
    self.epoch_done = false       -- self是隐藏的第一个参数
    return self
end

local trainLoader = BatchLoader:new()  --即BatchLoader.new(BatchLoader)

这意味着您实际上正在运行:

function BatchLoader:new()
    setmetatable({}, BatchLoader)
    BatchLoader.epoch_done = false
    return BatchLoader
end

这确实不是您想要的。实现您想要的几种方法是

--固定的创建方案
function BatchLoader.new()
    local self = {epoch_done = false}
    return setmetatable(self,BatchLoader)
end

--可接受的用法:
local trainLoader = BatchLoader.new()
local trainLoader = BatchLoader:new() - 忽略传递的自身

--允许通过基本对象传递
function BatchLoader:new()--等效于:BatchLoader.new(self)
   self = self or {} - 使用传递的表或创建新表
   self.epoch_done = false
   return setmetatableselfBatchLoaderend

--可接受的用法:
local trainLoader = BatchLoader.new()--从头开始创建
local trainLoader = BatchLoader.new {n = 23} - 重新使用此表
--错误用法:
local trainLoader = BatchLoadernew()--重用BatchLoader类作为对象

或其他许多选项。

您同样混淆了其他调用的方法符号:

function BatchLoader:init(X, y, sids, batch_size, argshuffle)

良好且等效于

function BatchLoader.init(self, X, y, sids, batch_size, argshuffle)

然后您将其错误地调用为

local X_train,y_train,sid_train =
  trainLoader.init(X,y,...)

(看到为什么一切都被移动了吗?)

使用trainLoader:init(X,y,...)trainLoader作为self传递。

2017-07-23 14:22:23