__index y __newindex
Intercepte la lectura y escritura de campos con los metamétodos __index y __newindex.
__index y __newindex es una lección gratuita de Lua Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Lua Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Lua Academy incluye 4 lecciones en total.
Repaso de __index
__index se activa cuando Lua intenta leer una clave que no existe en la tabla. Puede ser una tabla (búsqueda en el prototipo) o una función (cálculo dinámico). Este es el metamétodo más utilizado en Lua: permite implementar herencia, valores predeterminados e inicialización diferida.
local defaults = {timeout=30, retries=3}
local config = setmetatable({timeout=60}, {__index=defaults})
print(config.timeout) -- 60 (own value)
print(config.retries) -- 3 (from defaults)
print(config.missing) -- nil (not in either)Activación de __newindex
__newindex se activa cuando Lua intenta escribir una clave que todavía no existe en la tabla. Si la clave ya existe, la asignación se realiza directamente sin activar __newindex.
local proxy = setmetatable({}, {
__newindex = function(t, k, v)
print("New key:", k, "=", v)
rawset(t, k, v) -- actually store it
end
})
proxy.x = 10 -- New key: x = 10
proxy.x = 20 -- no trigger! x already exists
print(proxy.x) -- 20Tablas de solo lectura
Use __newindex para crear tablas de solo lectura. Intercepte todas las escrituras y genere un error. Combínelo con __index para proporcionar valores sin almacenarlos en la propia tabla.
local function readOnly(t)
return setmetatable({}, {
__index = t,
__newindex = function(_, k, _)
error("attempt to write to read-only table key: " .. tostring(k), 2)
end
})
end
local cfg = readOnly({host="localhost", port=8080})
print(cfg.host) -- localhost
-- cfg.host = "x" -- ERROR: attempt to write to read-only tableSeguimiento de cambios
Use __newindex para realizar un seguimiento de todos los cambios en una tabla, lo que resulta útil para la depuración, el registro de cambios o los enlaces de datos reactivos. Almacene los datos en una tabla de respaldo oculta y registre los cambios en un registro.
local log = {}
local data = {}
local tracked = setmetatable({}, {
__index = data,
__newindex = function(_, k, v)
log[#log+1] = {key=k, old=data[k], new=v}
data[k] = v
end
})
tracked.name = "Alice"
tracked.age = 30
tracked.name = "Bob"
for _, entry in ipairs(log) do
print(entry.key, entry.old, "->", entry.new)
endObjetos proxy
Un proxy envuelve otro objeto para interceptar todas las lecturas y escrituras. Use una tabla vacía como proxy, con __index y __newindex apuntando a funciones. Los datos reales se almacenan en una tabla independiente.
local function makeProxy(target)
return setmetatable({}, {
__index = function(_, k) return target[k] end,
__newindex = function(_, k, v)
print("Setting", k, "on target")
target[k] = v
end,
})
end
local real = {x=10}
local p = makeProxy(real)
print(p.x) -- 10 (reads from real)
p.y = 20 -- Setting y on target
print(real.y) -- 20Cadena de __index (herencia)
Encadenar metatablas mediante __index crea una herencia basada en prototipos. Cuando no se encuentra una clave en el objeto, Lua comprueba la clase, después la clase principal y así sucesivamente. Cada nivel necesita su propia metatabla, con __index apuntando al nivel siguiente.
local Base = {type = "base", version = 1}
local Child = setmetatable({type = "child"}, {__index = Base})
local Obj = setmetatable({}, {__index = Child})
print(Obj.type) -- base? No: child (Child has it)
print(Obj.version) -- 1 (from Base)
print(Obj.type) -- child (Child overrides Base.type)Inicialización diferida con __index
Use un __index con forma de función para calcular y almacenar en caché valores costosos la primera vez que se accede a ellos. En la primera lectura, calcule el valor y guárdelo directamente en la tabla (evitando futuras invocaciones de __index).
local lazy = setmetatable({}, {
__index = function(t, k)
if k == "expensiveData" then
print("Computing...")
local result = {1,2,3,4,5} -- simulate work
rawset(t, k, result)
return result
end
end
})
print(lazy.expensiveData) -- Computing... then table
print(lazy.expensiveData) -- (no "Computing" this time)__newindex para validar esquemas
Use __newindex para aplicar un esquema: permita establecer únicamente claves predefinidas o valores del tipo correcto. Esto añade una seguridad de tipos ligera a las tablas de Lua.
local schema = {name="string", age="number", active="boolean"}
local function schemaTable()
local store = {}
return setmetatable({}, {
__index = store,
__newindex = function(_, k, v)
local expected = schema[k]
if not expected then error("unknown key: "..k,2) end
if type(v) ~= expected then
error(k.." must be "..expected..", got "..type(v),2)
end
store[k] = v
end
})
end
local user = schemaTable()
user.name = "Alice"
user.age = 30
-- user.age = "thirty" -- ERROR__index frente a rawget
Dentro de una función __index, use rawget para leer de la tabla sin volver a activar __index (lo que provocaría una recursión infinita). Use siempre rawget/rawset dentro de los metamétodos para manipular el almacenamiento real de la tabla.
local counter = setmetatable({count=0}, {
__index = function(t, k)
-- Use rawget to avoid recursion
local c = rawget(t, "count")
rawset(t, "count", c + 1)
return rawget(t, k)
end
})
print(counter.count) -- 0 (direct, no __index)
print(counter.missing) -- nil
print(counter.count) -- 1 (incremented)Combinar __index y __newindex
El patrón más potente consiste en usar una tabla proxy vacía con __index y __newindex, respaldada por una tabla de datos oculta. Esto proporciona un control total sobre las lecturas y escrituras, y permite funciones como la detección de cambios, la carga diferida y el control de acceso.
local function observable(init)
local data = init or {}
local listeners = {}
local obj = setmetatable({}, {
__index = data,
__newindex = function(_, k, v)
local old = data[k]
data[k] = v
for _, cb in ipairs(listeners) do cb(k,old,v) end
end
})
obj._onchange = function(_, cb) listeners[#listeners+1]=cb end
return obj
end
local obs = observable({score=0})
obs:_onchange(function(k,o,n) print(k,o,"->",n) end)
obs.score = 100 -- score 0 -> 100Comprobación rápida
¿Cuándo NO se activa __newindex?
Repaso: __index y __newindex
Puntos clave:
__index: se activa al leer una clave inexistente; puede ser una tabla o una función__newindex: se activa únicamente al escribir una clave nueva- Use
rawget/rawsetdentro de los metamétodos para evitar la recursión - Patrones: tablas de solo lectura, seguimiento de cambios, inicialización diferida y validación de esquemas
- Proxy vacío + tabla de respaldo = interceptación completa de lecturas y escrituras
Preguntas frecuentes
¿La lección «__index y __newindex» es gratis?
Sí — el texto completo de «__index y __newindex» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Lua Academy, actualiza a CoddyKit PRO. El curso de Lua Academy incluye 4 lecciones en total.
¿Qué aprenderé en «__index y __newindex»?
Intercepte la lectura y escritura de campos con los metamétodos __index y __newindex. Practicas Lua Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Lua Academy?
No se requiere experiencia previa. Lua Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «__index y __newindex»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Lua Academy?
Sí. Cada lección de Lua Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Introducción a las metatablas
- __index y __newindex
- Metamétodos aritméticos
- __tostring y __len