在Lua编程中,对象是什么?

9

示例代码:

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

来源:

http://www.lua.org/pil/16.1.html

以下是需要翻译的内容:

什么是:

self.__index = self

line是什么?它为什么会在每次创建对象时执行?

6个回答

9
正如其他人所说,self (即Account表)被用作元表,分配给使用new创建的对象。略微简化一下(更多信息可在提供的链接中找到),当在'o'中未找到字段时,它会去 'Account'表中查找,因为o的元表指示要去 Account表中查找(这就是__index的作用)。
然而,并不需要每次创建对象时都执行这个过程。你可以把它放在任何地方:
Account.__index = Account

同时也能正常工作。

稍微详细一些的解释是,如果一个对象 o 有元表,并且该元表设置了 __index 字段,则在 o 上执行的 失败的 字段查找将使用 __index 来查找该字段(__index 可以是表或函数)。如果 o 已经设置了该字段,则不会去其元表的 __index 函数中获取信息。但我仍然鼓励您阅读上面提供的链接以获取更多信息。


7
Lua文档在这个细节上有些模糊,这里的许多答案都是回应Lua文档或没有充分解释这个令人困惑的细节。
代码行`self._index = self`只为新创建的对象`o`的好处而存在;它对`Account`没有实际意义或功能影响。
`_index`字段只有在元表的上下文中才有特殊含义;因此,`self._index`只是`Account`的普通字段。但是,当`Account`用作`o`的元表时,`_index`字段“变成”了`o`的元方法。(因此,对于`Account`的字段是`o`的元方法。)
当你将两个语句结合起来使用时...
``` (1) setmetatable(o, self) (2) self._index = self ```
...你正在使用`Account`作为行(1)中`o`的元表,并将行(2)中`o`的`_index`元方法设置为`Account`。(在行(2)中,您还将`Account`中的“普通字段”`__index`设置为`Account`。)因此,`self._index = self`的有用方面不是为`Account`设置`_index`字段,而是为`o`设置`_index`元方法。
以下是功能上等效的:
``` setmetatable(o, self) getmetatable(o)._index = self ```

请注意,虽然__index是一个元方法的名称,但Lua提供了一个方便的快捷方式,可以将__index分配给一个表而不是一个函数。在这些情况下,当调用__index时会执行查找操作。因此,当你说__index = self时,实际上是在说,如果在Account实例中找不到给定的字段,请查看(也就是“索引”)Account类/原型本身。 - Aaron
快速问题:我经常在new函数内看到self.__index = self语句,这让我认为它会在每个类的新实例中被执行。如果它只在基本表(在此情况下为Account)上设置一次,它不会产生完全相同的效果吗? - albizgil

4
Lua不是面向对象的语言,但它具备编写面向对象代码所需的所有功能。然而,它是以类似JavaScript的原型方式实现的。与其显式地创建类,而是创建一个原型对象,然后克隆它来创建新实例。
当在表格上进行读取访问时,如果键不存在于表格中,则会调用__index元方法执行键查找。因此,self.__index = self实际上允许通过在o = o or {}setmetatable(o, self)行中创建的新"实例"继承Account "类"的所有方法和字段。
另请参见:

1
我发现这个关于Lua 继承的“教程”结合这个答案特别有启发性。 - Indolering
@Indolering 谢谢。我已将你的链接放入回答中。 - Judge Maygarden

0
创建对象(即简单的表格)在Lua中与其他语言有很大不同。基本思想是创建一个常规表格,其中包含所有实例共有的属性(函数和值)。我将这个表格称为CAT(Common Attributes Table)。
如果您在表格中引用一个属性,而Lua找不到该属性,则有一种方法可以告诉Lua在哪里查找该属性。我们希望Lua在CAT中查找常见属性。元表可以满足这个需求。稍后会详细介绍它的工作原理。
我们还需要在CAT中添加方法以便使用实例值。Self可以满足这个需求。当您以这种方式调用表格函数(方法):tableName:methodName()时,Lua会自动将对表格对象的引用作为第一个参数放置。此参数的名称为self。即使该方法位于CAT中,self也将指向特定的调用对象实例表格。
假设我们有一个名为Car的CAT。
metaCar = { __index = Car }  
-- this table will be used as the metatable for all instances of Car  
-- Lua will look in Car for attributes it can't find in the instance

例如:
-- instance table is called mustang    
-- setmetatable(mustang, metaCar)

这是一个通用的函数,它创建新的实例对象并为其设置元表。如果CAT有构造函数(init),它也会被执行。
function newObj(metatable)  
..obj = {}      -- create new empty instance object  
..setmetatable(obj, metatable) –- connect the metatable to it  
..if obj.init then  -- if the CAT has an init method, execute it  
....obj:init()  
..end  
..return obj  
end

0

它们用于重定向表访问(本地y = table [key]),这些访问也用于方法调用。在上面的行中,对象o将重定向任何尝试访问键到当前对象self,轻松继承所有成员函数。还可能包括数据变量,具体取决于__index是什么以及它如何工作。


0
请注意,setmetatable(o, self) 仅将 Account 设置为 o 的元表(否则默认为 nil)。这是原型绑定的第一步,但这不足以使 Account 的函数可从 o 中搜索到!
要使 o 搜索 Account 上的方法,元表对象(Account)必须包含一个 __index 事件,其值指向自身,其中包含原型方法。
因此,需要分两步完成:
  1. 创建一个带有 __index 事件的元表值
  2. 将其设置为目标表的元表。
正如原书中所说,这是“一个小优化”--通常您需要创建另一个模板表值作为 o 的元表。但在这种情况下,代码重用了 Acccount 表值,作为元表和原型对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接