首页 > 解决方案 > 在原型“构造函数”内部或外部定义 __index

问题描述

我正在阅读https://www.lua.org/pil/16.html上“Lua 编程”中的“面向对象编程”一章。

在那个例子中,他们创建了这个“构造函数”:

Account = {balance = 0}
function Account:new (o)
  o = o or {}   -- create object if user does not provide one
  setmetatable(o, self)
  self.__index = self
  return o
end

我在 Linux 上做了一些“sintetic benchmarks”,在一个简单time的脚本中有 1000 万个类似这样的操作。Account.__index = Account在表初始化之外定义要快 200 毫秒。

我的问题是,self.__index如果我们可以在外部定义并执行一次,为什么还要在这个函数内部设置每次调用这个函数时都会执行?也许是遗产?

编辑:

谢谢luther的回答,我这里就给大家举个例子给大家有这个疑问:

local a = {}
a.__index = a
function a:foo()
    return 'foo'
end

function a:new(o)
    print(self)
    o = o or {}
    setmetatable(o, self)
    -- self.__index = self
    return o
end

local b = a:new()
-- b.__index = b
function b:bar()
    return 'bar'
end

local z = a:new()
print(z:foo()) -- this will work

local y = b:new()
print(y:foo()) -- attempt to call method 'foo' (a nil value)
print(y:bar()) -- attempt to call method 'bar' (a nil value)

当然y会有一个与b那个表一样的元表,但b没有__index条目,这个条目只是在b的元表中。如果您仍想避免__index在“构造函数”中声明,则需要在每个派生原型或“子类”中指定它。

标签: lua

解决方案


PiL 的作者似乎试图通过让该new方法像处理所有子对象一样处理根对象来简化事情。这可能会让初学者感到困惑,因为通常不是很清楚这self.__index = self通常是多余的。

此外,这样做实际上比__index向每个对象添加一个更快。请记住,在原型系统中,每个对象都可能是其他对象的原型。在我的机器上,经过 1e8 次试验,PiL 方式需要 14 秒,而添加__index到所有对象需要 23 秒。新键意味着表必须增长,因此它比分配给已经存在的键慢。

令人困惑的是,这个 PiL 部分的标题是“类”,但在第一段中,他说他正在模拟基于原型的语言,其中“对象没有类”。这进一步加深了读者的期望。本节实现了一个自我复制的对象,而不是一个类。

这是我的不太令人困惑但较慢的实现:

Account = {balance = 0}
Account.__index = Account

function Account:new (o)
  o = o or {}   -- create object if user does not provide one
  setmetatable(o, self)
  o.__index = o
  return o
end

推荐阅读