Lua Academy · Lezione

__index e __newindex

Intercetti la lettura e la scrittura dei campi con i metametodi __index e __newindex.

Lezione 2 di 412 passaggi

__index e __newindex è una lezione Lua Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Lua Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Lua Academy include 4 lezioni in totale.

Riepilogo di __index

__index viene attivato quando Lua prova a leggere una chiave che non esiste nella tabella. Può essere una tabella (ricerca nel prototipo) oppure una funzione (calcolo dinamico). È il metametodo più usato in Lua: abilita l'ereditarietà, i valori predefiniti e l'inizializzazione differita.

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)

Attivazione di __newindex

__newindex viene attivato quando Lua prova a scrivere una chiave che non esiste ancora nella tabella. Se la chiave esiste già, l'assegnazione avviene direttamente senza attivare __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)  -- 20

Tabelle di sola lettura

Usi __newindex per creare tabelle di sola lettura. Intercetti tutte le scritture e generi un errore. Combini questo metametodo con __index per fornire valori senza memorizzarli direttamente nella tabella.

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 table

Monitoraggio delle modifiche

Usi __newindex per monitorare tutte le modifiche a una tabella, una soluzione utile per il debug, la registrazione delle modifiche o i binding reattivi dei dati. Memorizzi i dati in una tabella di supporto nascosta e registri le modifiche in un log.

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)
end

Oggetti proxy

Un proxy avvolge un altro oggetto per intercettare tutte le letture e le scritture. Usi una tabella vuota come proxy, con __index e __newindex che puntano entrambi a funzioni. I dati reali risiedono in una tabella separata.

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) -- 20

Catena di __index (ereditarietà)

Collegare metatabelle tramite __index crea un'ereditarietà basata sui prototipi. Quando una chiave non viene trovata nell'oggetto, Lua controlla la classe, poi la classe padre e così via. Ogni livello richiede una propria metatabella con __index che punta al livello successivo.

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)

Inizializzazione differita con __index

Usi un __index in forma di funzione per calcolare e memorizzare nella cache i valori costosi al primo accesso. Alla prima lettura, calcoli il valore e lo memorizzi direttamente nella tabella, evitando le successive invocazioni di __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 per la validazione dello schema

Usi __newindex per applicare uno schema: consenti solo l'impostazione di chiavi predefinite o di valori del tipo corretto. In questo modo aggiungi alle tabelle Lua una sicurezza sui tipi di base.

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 e rawget a confronto

All'interno di una funzione __index, usi rawget per leggere dalla tabella senza attivare nuovamente __index, cosa che causerebbe una ricorsione infinita. All'interno dei metametodi, usi sempre rawget/rawset per manipolare l'archiviazione effettiva della tabella.

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)

Combinare __index e __newindex

Il pattern più potente consiste nell'usare una tabella proxy vuota con __index e __newindex, supportata da una tabella di dati nascosta. In questo modo si ottiene il pieno controllo sulle letture e sulle scritture, abilitando funzionalità come il rilevamento delle modifiche, il caricamento differito e il controllo degli accessi.

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 -> 100

Verifica rapida

Quando NON viene attivato __newindex?

Riepilogo: __index e __newindex

Punti chiave:

  • __index: si attiva quando si legge una chiave mancante; può essere una tabella o una funzione
  • __newindex: si attiva solo quando si scrive una nuova chiave
  • Usi rawget/rawset all'interno dei metametodi per evitare la ricorsione
  • Pattern: tabelle di sola lettura, monitoraggio delle modifiche, inizializzazione differita, validazione dello schema
  • Proxy vuoto + tabella di supporto = intercettazione completa delle letture e delle scritture
Gratis per iniziare

Impara Lua con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
40
Lezioni
159

Domande Frequenti

La lezione «__index e __newindex» è gratuita?

Sì — il testo completo di «__index e __newindex» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Lua Academy, passa a CoddyKit PRO. Il corso Lua Academy include 4 lezioni in totale.

Cosa imparerò in «__index e __newindex»?

Intercetti la lettura e la scrittura dei campi con i metametodi __index e __newindex. Eserciti Lua Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Lua Academy?

Non è richiesta alcuna esperienza precedente. Lua Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «__index e __newindex»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Lua Academy?

Sì. Ogni lezione Lua Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Introduzione alle metatabelle
  2. __index e __newindex
  3. Metametodi aritmetici
  4. __tostring e __len
← Torna a Lua Academy