0Pricing
Lua Academy · Lekcja

__index i __newindex

Proszę przechwytywać odczyty i zapisy pól za pomocą metametod __index i __newindex.

__index i __newindex to bezpłatna lekcja Lua Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Lua Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Lua Academy zawiera 4 lekcji w sumie.

Podsumowanie __index

__index jest wywoływane, gdy Lua próbuje odczytać klucz, który nie istnieje w tabeli. Może być tabelą (wyszukiwanie w prototypie) lub funkcją (dynamiczne obliczanie). To najczęściej używana metametoda w Lua — obsługuje dziedziczenie, wartości domyślne i leniwą inicjalizację.

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)

Wywołanie __newindex

__newindex jest wywoływane, gdy Lua próbuje zapisać klucz, który nie istnieje jeszcze w tabeli. Jeśli klucz już istnieje, przypisanie odbywa się bezpośrednio, bez wywoływania __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

Tabele tylko do odczytu

Użyj __newindex, aby utworzyć tabele tylko do odczytu. Przechwytuj wszystkie operacje zapisu i zgłaszaj błąd. Połącz to z __index, aby udostępniać wartości bez przechowywania ich bezpośrednio w tabeli.

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

Śledzenie zmian

Użyj __newindex, aby śledzić wszystkie zmiany w tabeli — jest to przydatne podczas debugowania, rejestrowania zmian lub tworzenia reaktywnych powiązań danych. Przechowuj dane w ukrytej tabeli „backing” i zapisuj zmiany w dzienniku.

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

Obiekty proxy

Proxy opakowuje inny obiekt, aby przechwytywać wszystkie operacje odczytu i zapisu. Jako proxy użyj pustej tabeli, a pola __index i __newindex ustaw na funkcje. Rzeczywiste dane przechowuj w oddzielnej tabeli.

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

Łańcuch __index (dziedziczenie)

Łączenie metatabel __index tworzy dziedziczenie prototypowe. Gdy klucz nie zostanie znaleziony w obiekcie, Lua sprawdza klasę, następnie klasę nadrzędną i tak dalej. Każdy poziom potrzebuje własnej metatabeli z polem __index wskazującym kolejny poziom.

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)

Leniwa inicjalizacja za pomocą __index

Użyj funkcyjnej postaci __index, aby obliczać i buforować kosztowne wartości przy pierwszym dostępie. Przy pierwszym odczycie oblicz wartość i zapisz ją bezpośrednio w tabeli (omijając przyszłe wywołania __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 do walidacji schematu

Użyj __newindex, aby wymusić zgodność ze schematem: zezwalaj tylko na ustawianie wcześniej zdefiniowanych kluczy lub wartości właściwego typu. Zapewnia to tabelom Lua lekkie zabezpieczenie typów.

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

W funkcji __index użyj rawget, aby odczytać dane z tabeli bez ponownego wywoływania __index (co spowodowałoby nieskończoną rekurencję). W metametodach zawsze używaj rawget/rawset do operowania na rzeczywistym przechowywaniu danych tabeli.

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)

Łączenie __index i __newindex

Najpotężniejszy wzorzec polega na użyciu pustej tabeli proxy z polami __index i __newindex, wspieranej przez ukrytą tabelę danych. Zapewnia to pełną kontrolę nad odczytem i zapisem, umożliwiając między innymi wykrywanie zmian, leniwe ładowanie i kontrolę dostępu.

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

Szybkie sprawdzenie

Kiedy __newindex NIE jest wywoływane?

Podsumowanie: __index i __newindex

Najważniejsze informacje:

  • __index: wywoływane przy odczycie brakującego klucza; tabela lub funkcja
  • __newindex: wywoływane tylko przy zapisie nowego klucza
  • Używaj rawget/rawset w metametodach, aby uniknąć rekurencji
  • Wzorce: tabele tylko do odczytu, śledzenie zmian, leniwa inicjalizacja, walidacja schematu
  • Puste proxy + tabela bazowa = pełne przechwytywanie odczytu i zapisu

Często zadawane pytania

Czy lekcja „__index i __newindex” jest bezpłatna?

Tak — pełny tekst „__index i __newindex” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Lua Academy, przejdź na CoddyKit PRO. Kurs Lua Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „__index i __newindex”?

Proszę przechwytywać odczyty i zapisy pól za pomocą metametod __index i __newindex. Ćwiczysz Lua Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Lua Academy?

Nie wymagamy żadnego doświadczenia. Lua Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „__index i __newindex”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Lua Academy?

Tak. Każda lekcja Lua Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Wprowadzenie do metatabel
  2. __index i __newindex
  3. Metamethody arytmetyczne
  4. __tostring i __len
← Powrót do Lua Academy