Reputation: 42697
Sample code:
function Account:new (o)
o = o or {} -- create object if user does not provide one
setmetatable(o, self)
self.__index = self
return o
end
taken from "Object-Oriented Programming: Classes" http://www.lua.org/pil/16.1.html
What is the purpose of the self.__index = self
line? And why is it executed every time an object is created?
Upvotes: 9
Views: 7681
Reputation: 625
Note that setmetatable(o, self)
only set Account as metatable of o
(otherwise nil by default). That's the first step of a prototype binding but it's NOT enough make functions of Account searchable from o
!
To make o
search methods on Account, the metatable object (Account) have to include a __index
event with the value pointing to itself, which contains prototype methods.
So it's has to be done in 2 steps:
__index
eventAs said in the original book, this is "a small optimization" -- you will usually need another mold table value created as metatable of o
. But here in this case, the code re-used the Acccount table value, as both metatable and prototype object.
Upvotes: 0
Reputation: 21
Creating objects (which are simply Tables) is quite different with Lua. The basic idea is to create a regular table containing attributes(functions and values) that are common to all instances. This table, I'll call CAT for Common Attributes Table.
If you reference an attribute in a table and Lua can't find this attribute, there is a way to tell Lua where else to look for the attribute. We want Lua to look in the CAT for common attributes. Metatables answer that need. More on how that works later.
We also need methods in the CAT to be able to use instance values. Self answers that need. When you call a table function (method) this way: tableName:methodName()
, Lua automatically places a reference to the table object as the first parameter. The name of this parameter is self. Even though the method is located in the CAT, self will refer to the particular calling object instance table.
Say we have a CAT called Car.
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
For example:
-- instance table is called mustang
-- setmetatable(mustang, metaCar)
Here is a general purpose function that creates new instance objects and sets the metatable for it. If the CAT has a constructor function (init), it gets executed as well.
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
Upvotes: 0
Reputation: 27613
Lua is not an object-oriented language, but it has all of the facilities for writing object-oriented code. However, it is done in a prototyping fashion a la JavaScript. Instead of explicitly creating classes, a prototype object is created and then cloned to create new instances.
The __index
metamethod is invoked to perform key lookups on read accesses to a table when the key is not already present in the table. So, self.__index = self
essentially allows for inheritance of all methods and fields of the Account
"class" by the new "instance" that is created in the o = o or {}
and setmetatable(o, self)
lines.
See also:
Upvotes: 4
Reputation: 79
The Lua documentation is somewhat vague on this detail and many of the answers here either echo the Lua docs or don't thoroughly explain this confusing tidbit.
The line self._index = self
is present purely for the benefit of the newly-created object, o
; it has no meaningful or functional impact on Account
.
The _index
field only has special meaning within the context of metatables; therefore self._index
is just a plain old regular field for Account
. However, when Account
is used as a metatable for o
, the _index
field "becomes" a metamethod for o
. (So what's a field for Account
is a metamethod for o
.)
When you take the two statements in combination ...
(1) setmetatable(o, self) (2) self._index = self
... you're using Account
as the metatable for o
on line (1) and setting the _index
metamethod for o
to Account
on line (2). (On line (2), you're also setting the "plain old field" __index
in Account
to Account
.) So the useful aspect of self._index = self
isn't the setting of the _index
field for Account
, but rather the setting of the _index
metamethod for o
.
The following is functionally equivalent:
setmetatable(o, self) getmetatable(o)._index = self
Upvotes: 7
Reputation: 373
As others have said, self
(the Account
table) is used as a metatable assigned to the objects created using new
. Simplifying slightly (more info available at the links provided) when a field is not found in 'o', it goes to the 'Account' table because o's metatable says to go to Account (this is what __index
does).
It does not, however, need to be executed every time an object is created. You could just as easily stick this somewhere:
Account.__index = Account
and it would work as well.
The somewhat longer story is that if an object o
has a metatable, and that metatable has the __index
field set, then a failed field lookup on o
will use __index
to find the field (__index
can be a table or function). If o
has the field set, you do not go to its metatable's __index
function to get the information. Again, though, I encourage you to read more at the links provided above.
Upvotes: 10
Reputation: 147018
They're used to re-direct table accesses (local y = table[key]) which are also used in method calls. In the above line, object o will have any attempts to access keys re-directed to the current object self, effortlessly inheriting all member functions. And possibly data variables too, depending on what exactly that __index is and how it works.
Upvotes: 0