This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.
Programming in Lua | ||
Part II. Tables and Objects Chapter 13. Metatables and Metamethods |
__index
MetamethodI said earlier that,
when we access an absent field in a table,
the result is nil.
This is true, but it is not the whole truth.
Actually, such access triggers the interpreter to look
for an __index
metamethod:
If there is no such method, as usually happens,
then the access results in nil;
otherwise, the metamethod will provide the result.
The archetypal example here is inheritance. Suppose we want to create several tables describing windows. Each table must describe several window parameters, such as position, size, color scheme, and the like. All these parameters have default values and so we want to build window objects giving only the non-default parameters. A first alternative is to provide a constructor that fills in the absent fields. A second alternative is to arrange for the new windows to inherit any absent field from a prototype window. First, we declare the prototype and a constructor function, which creates new windows sharing a metatable:
-- create a namespace Window = {} -- create the prototype with default values Window.prototype = {x=0, y=0, width=100, height=100, } -- create a metatable Window.mt = {} -- declare the constructor function function Window.new (o) setmetatable(o, Window.mt) return o endNow, we define the
__index
metamethod:
Window.mt.__index = function (table, key) return Window.prototype[key] endAfter that code, we create a new window and query it for an absent field:
w = Window.new{x=10, y=20} print(w.width) --> 100When Lua detects that
w
does not have the requested field,
but has a metatable with an __index
field,
Lua calls this __index
metamethod,
with arguments w
(the table) and "width"
(the absent key).
The metamethod then indexes the prototype with the given key
and returns the result.
The use of the __index
metamethod for inheritance is so common
that Lua provides a shortcut.
Despite the name,
the __index
metamethod does not need to be a function:
It can be a table, instead.
When it is a function,
Lua calls it with the table and the absent key as its arguments.
When it is a table, Lua redoes the access in that table.
Therefore,
in our previous example,
we could declare __index
simply as
Window.mt.__index = Window.prototypeNow, when Lua looks for the metatable's
__index
field,
it finds the value of Window.prototype
, which is a table.
Consequently, Lua repeats the access in this table, that is,
it executes the equivalent of
Window.prototype["width"]which gives the desired result.
The use of a table as an __index
metamethod provides
a cheap and simple way of implementing single inheritance.
A function, although more expensive, provides more flexibility:
We can implement multiple inheritance,
caching, and several other variations.
We will discuss those forms of inheritance in Chapter 16.
When we want to access a table without
invoking its __index
metamethod,
we use the rawget
function.
The call rawget(t,i)
does a raw access to table t
.
Doing a raw access will not speed up your code
(the overhead of a function call kills any gain you could have),
but sometimes you need it, as we will see later.
Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. |