Buscar..
Sintaxis
- [[local] mt =] getmetatable ( t ) -> recupera metatable asociado para ' t '
- [[local] t =] setmetatable ( t , mt ) -> establece la metatable para ' t ' en ' mt ' y devuelve ' t '
Parámetros
Parámetro | Detalles |
---|---|
t | Variable referente a una tabla lua; También puede ser una tabla literal. |
monte | Tabla para usar como metatable; puede tener cero o más campos metamétodo establecidos. |
Observaciones
Hay algunos metamétodos no mencionados aquí. Para ver la lista completa y su uso, consulte la entrada correspondiente en el manual de lua .
Creación y uso de metatables.
Un metatable define un conjunto de operaciones que alteran el comportamiento de un objeto lua. Un metatable es solo una tabla ordinaria, que se usa de una manera especial.
local meta = { } -- create a table for use as metatable
-- a metatable can change the behaviour of many things
-- here we modify the 'tostring' operation:
-- this fields should be a function with one argument.
-- it gets called with the respective object and should return a string
meta.__tostring = function (object)
return string.format("{ %d, %d }", object.x, object.y)
end
-- create an object
local point = { x = 13, y = -2 }
-- set the metatable
setmetatable(point, meta)
-- since 'print' calls 'tostring', we can use it directly:
print(point) -- prints '{ 13, -2 }'
Usando tablas como metamétodos
Algunos metamétodos no tienen que ser funciones. El ejemplo más importante para esto es el __index
metamethod. También puede ser una tabla, que luego se utiliza como búsqueda. Esto es bastante usado en la creación de clases en lua. Aquí, una tabla (a menudo, la propia metatable) se usa para mantener todas las operaciones (métodos) de la clase:
local meta = {}
-- set the __index method to the metatable.
-- Note that this can't be done in the constructor!
meta.__index = meta
function create_new(name)
local self = { name = name }
setmetatable(self, meta)
return self
end
-- define a print function, which is stored in the metatable
function meta.print(self)
print(self.name)
end
local obj = create_new("Hello from object")
obj:print()
Recolector de basura - el metamétodo __gc
Los objetos en lua son recolectados basura. A veces, necesita liberar algún recurso, desea imprimir un mensaje o hacer otra cosa cuando un objeto es destruido (recolectado). Para esto, puedes usar el __gc
__gc, que se llama con el objeto como argumento cuando el objeto es destruido. Podrías ver este método como una especie de destructor.
Este ejemplo muestra el __gc
__gc en acción. Cuando la tabla interna asignada a t
obtiene la basura recolectada, imprime un mensaje antes de ser recolectada. Del mismo modo para la tabla exterior al llegar al final del script:
local meta =
{
__gc = function(self)
print("destroying self: " .. self.name)
end
}
local t = setmetatable({ name = "outer" }, meta)
do
local t = { name = "inner" }
setmetatable(t, meta)
end
Más metamétodos
Hay muchos más metamétodos, algunos de ellos son aritméticos (por ejemplo, suma, resta, multiplicación), hay operaciones a nivel de bits (y, o, xor, cambio), comparación (<,>) y también operaciones de tipo básico como == y # (Igualdad y longitud). Permite construir una clase que admita muchas de estas operaciones: una llamada a la aritmética racional. Si bien esto es muy básico, muestra la idea.
local meta = {
-- string representation
__tostring = function(self)
return string.format("%s/%s", self.num, self.den)
end,
-- addition of two rationals
__add = function(self, rhs)
local num = self.num * rhs.den + rhs.num * self.den
local den = self.den * rhs.den
return new_rational(num, den)
end,
-- equality
__eq = function(self, rhs)
return self.num == rhs.num and self.den == rhs.den
end
}
-- a function for the creation of new rationals
function new_rational(num, den)
local self = { num = num, den = den }
setmetatable(self, meta)
return self
end
local r1 = new_rational(1, 2)
print(r1) -- 1/2
local r2 = new_rational(1, 3)
print(r1 + r2) -- 5/6
local r3 = new_rational(1, 2)
print(r1 == r3) -- true
-- this would be the behaviour if we hadn't implemented the __eq metamethod.
-- this compares the actual tables, which are different
print(rawequal(r1, r3)) -- false
Hacer mesas invocables.
Hay un método llamado __call
, que define el comportamiento del objeto cuando se usa como una función, por ejemplo, object()
. Esto se puede utilizar para crear objetos de función:
-- create the metatable with a __call metamethod
local meta = {
__call = function(self)
self.i = self.i + 1
end,
-- to view the results
__tostring = function(self)
return tostring(self.i)
end
}
function new_counter(start)
local self = { i = start }
setmetatable(self, meta)
return self
end
-- create a counter
local c = new_counter(1)
print(c) --> 1
-- call -> count up
c()
print(c) --> 2
El metamétodo se llama con el objeto correspondiente, todos los argumentos restantes se pasan a la función después de eso:
local meta = {
__call = function(self, ...)
print(self.prepend, ...)
end
}
local self = { prepend = "printer:" }
setmetatable(self, meta)
self("foo", "bar", "baz")
Indexación de tablas
Quizás el uso más importante de los metatables es la posibilidad de cambiar la indexación de tablas. Para esto, hay dos acciones a considerar: leer el contenido y escribir el contenido de la tabla. Tenga en cuenta que ambas acciones solo se activan si la clave correspondiente no está presente en la tabla.
Leyendo
local meta = {}
-- to change the reading action, we need to set the '__index' method
-- it gets called with the corresponding table and the used key
-- this means that table[key] translates into meta.__index(table, key)
meta.__index = function(object, index)
-- print a warning and return a dummy object
print(string.format("the key '%s' is not present in object '%s'", index, object))
return -1
end
-- create a testobject
local t = {}
-- set the metatable
setmetatable(t, meta)
print(t["foo"]) -- read a non-existent key, prints the message and returns -1
Esto podría usarse para generar un error al leer una clave que no existe:
-- raise an error upon reading a non-existent key
meta.__index = function(object, index)
error(string.format("the key '%s' is not present in object '%s'", index, object))
end
Escritura
local meta = {}
-- to change the writing action, we need to set the '__newindex' method
-- it gets called with the corresponding table, the used key and the value
-- this means that table[key] = value translates into meta.__newindex(table, key, value)
meta.__newindex = function(object, index, value)
print(string.format("writing the value '%s' to the object '%s' at the key '%s'",
value, object, index))
--object[index] = value -- we can't do this, see below
end
-- create a testobject
local t = { }
-- set the metatable
setmetatable(t, meta)
-- write a key (this triggers the method)
t.foo = 42
Ahora puede preguntarse cómo se escribe el valor real en la tabla. En este caso, no lo es. El problema aquí es que los metamétodos pueden desencadenar metamétodos, lo que daría lugar a un bucle infinitivo, o más precisamente, a un desbordamiento de pila. Entonces, ¿cómo podemos resolver esto? La solución para esto se llama acceso de tabla sin formato .
Acceso a la tabla sin procesar
A veces, no desea activar metamétodos, sino escribir o leer exactamente la clave dada, sin algunas funciones inteligentes envueltas alrededor del acceso. Para esto, lua le proporciona métodos de acceso a la tabla en bruto:
-- first, set up a metatable that allows no read/write access
local meta = {
__index = function(object, index)
-- raise an error
error(string.format("the key '%s' is not present in object '%s'", index, object))
end,
__newindex = function(object, index, value)
-- raise an error, this prevents any write access to the table
error(string.format("you are not allowed to write the object '%s'", object))
end
}
local t = { foo = "bar" }
setmetatable(t, meta)
-- both lines raise an error:
--print(t[1])
--t[1] = 42
-- we can now circumvent this problem by using raw access:
print(rawget(t, 1)) -- prints nil
rawset(t, 1, 42) -- ok
-- since the key 1 is now valid, we can use it in a normal manner:
print(t[1])
Con esto, ahora podemos reescribir el antiguo método __newindex
para escribir el valor en la tabla:
meta.__newindex = function(object, index, value)
print(string.format("writing the value '%s' to the object '%s' at the key '%s'",
value, object, index))
rawset(object, index, value)
end
Simulando OOP
local Class = {} -- objects and classes will be tables
local __meta = {__index = Class}
-- ^ if an instance doesn't have a field, try indexing the class
function Class.new()
-- return setmetatable({}, __meta) -- this is shorter and equivalent to:
local new_instance = {}
setmetatable(new_instance, __meta)
return new_instance
end
function Class.print()
print "I am an instance of 'class'"
end
local object = Class.new()
object.print() --> will print "I am an instance of 'class'"
Los métodos de instancia pueden escribirse pasando el objeto como primer argumento.
-- append to the above example
function Class.sayhello(self)
print("hello, I am ", self)
end
object.sayhello(object) --> will print "hello, I am <table ID>"
object.sayhello() --> will print "hello, I am nil"
Hay algo de azúcar sintáctica para esto.
function Class:saybye(phrase)
print("I am " .. self .. "\n" .. phrase)
end
object:saybye("c ya") --> will print "I am <table ID>
--> c ya"
También podemos agregar campos predeterminados a una clase.
local Class = {health = 100}
local __meta = {__index = Class}
function Class.new() return setmetatable({}, __meta) end
local object = Class.new()
print(object.health) --> prints 100
Class.health = 50; print(object.health) --> prints 50
-- this should not be done, but it illustrates lua indexes "Class"
-- when "object" doesn't have a certain field
object.health = 200 -- This does NOT index Class
print(object.health) --> prints 200