Lua元表不一致

我不理解为什么这两个示例中 __index 元方法的行为会有所不同:

A = { __index = A }
function A:speak()
    print("I'm an A")
end
An_A = setmetatable({},A)
An_A:speak()

会引发以下错误:lua: l.lua:8: attempt to call method 'speak' (a nil value)

B = { __index = function(t,key)  return B[key] end }
function B:speak()
    print("I'm an B")
end
An_B = setmetatable({},B)
An_B:speak()

则按预期执行,输出 I'm an B


为了理解为什么是这样的,我阅读了 PiL 的 这一节。其中提到:

因为使用 __index 元方法实现继承如此普遍,Lua 提供了一种快捷方式。尽管它的名字是 __index 元方法,但它不需要是一个函数:它可以是一个表。当它是一个函数时,Lua 以它的表和缺失的键作为参数调用它。当它是一个表时,Lua 重新在该表中进行访问。

我理解这意味着在涉及 'A' 的代码片段中,__index = A 使访问在表 A 中进行(如上面引用段落的粗体所示)。如果是这样,我不理解为什么与键 "speak" 关联的函数没有被找到。为了试图修复这个问题,我决定在 B 片段中尝试实现函数方法,返回与 B 中的键 key 关联的值,并且这个方法起作用了。肯定的是 __index = A 和 (从 B 中衍生的)__index = function(t,key) return A[key] end 有相同的效果。

任何澄清都将不胜感激。

点赞
用户234175
用户234175

在你的第一个例子中正在发生的是 A.__index == nil。当你在第一行创建 'A'时:

A = { __index = A }

赋值的右侧 'A' 评估为 nil,因为此时它还不存在。因此,稍后当你在这里设置元表时:

An_A = setmetatable({},A)

它实际上变成了这样:

An_A = setmetatable({}, {__index = nil} )

为了使它按照你想要的方式工作,你必须确保 __index 不是 nil。例如,在表构造之后指定它:

A = {}
A.__index = A

function A:speak()
  print("I'm an A")
end
An_A = setmetatable({},A)
An_A:speak()              --> 输出 I'm an A
2013-05-11 02:49:15