Sök…


Syntax

  • [[local] mt =] getmetatable ( t ) -> hämta tillhörande metatable för ' t '
  • [[local] t =] setmetatable ( t , mt ) -> ställ in metatable för ' t ' till ' mt ' och returnerar ' t '

parametrar

Parameter detaljer
t Variabel med hänvisning till ett lua-bord; kan också vara en bokstavlig bokstav.
mt Tabell att använda som en metatabel; kan ha noll eller fler metametodfält inställda.

Anmärkningar

Det finns några metametoder som inte nämns här. För den fullständiga listan och deras användning, se motsvarande post i lua manualen .

Skapande och användning av metatabler

En metatabel definierar en uppsättning operationer som förändrar beteendet hos ett lua-objekt. En metatabel är bara ett vanligt bord som används på ett speciellt sätt.

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 }'

Använda tabeller som metametoder

Vissa metametoder behöver inte vara funktioner. Det viktigaste exemplet för detta är __index metametoden. Det kan också vara en tabell som sedan används som uppslagning. Detta används ganska vanligt vid skapandet av klasser i lua. Här används en tabell (ofta själva metatabeln) för att hålla alla operationer (metoder) i klassen:

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

Garbage Collector - __gc metametoden

5,2

Föremål i lua är skräp som samlas in. Ibland måste du frigöra en resurs, vill skriva ut ett meddelande eller göra något annat när ett objekt förstörs (samlas in). För detta kan du använda __gc metametoden, som kallas med objektet som argument när objektet förstörs. Du kunde se denna metod som en slags förstörare.

Detta exempel visar __gc metametoden i aktion. När den inre tabellen som tilldelas t samlas in sopor, skriver det ut ett meddelande innan det samlas in. Likaså för det yttre bordet när man når slutet på skriptet:

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

Fler metametoder

Det finns många fler metametoder, några av dem är aritmetiska (t.ex. tillägg, subtraktion, multiplikation), det finns bitvisa operationer (och, eller, xor, skift), jämförelse (<,>) och även grundläggande typoperationer som == och # (jämlikhet och längd). Låter bygga en klass som stöder många av dessa operationer: en uppmaning till rationell aritmetik. Även om detta är mycket grundläggande, visar det idén.

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

Gör tabeller som kan kallas in

Det finns en metametod som heter __call , som definierar objektets bevahiour när den används som en funktion, t.ex. object() . Detta kan användas för att skapa funktionsobjekt:

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

Metametoden kallas med motsvarande objekt, alla återstående argument skickas till funktionen efter det:

local meta = {
    __call = function(self, ...)
        print(self.prepend, ...)
    end
}

local self = { prepend = "printer:" }
setmetatable(self, meta)

self("foo", "bar", "baz")

Indexering av tabeller

Kanske är den viktigaste användningen av metatabler möjligheten att ändra indexering av tabeller. För detta finns det två åtgärder att tänka på: läsa innehållet och skriva innehållet i tabellen. Observera att båda åtgärderna endast utlöses om motsvarande nyckel inte finns i tabellen.

Läsning

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

Detta kan användas för att höja ett fel när du läser en icke-existerande nyckel:

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

Skrivning

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

Du kan nu fråga dig själv hur det verkliga värdet skrivs i tabellen. I det här fallet är det inte det. Problemet här är att metametoder kan utlösa metametoder, vilket skulle resultera i en infinitiv slinga, eller mer exakt, ett stacköverskridande. Så hur kan vi lösa detta? Lösningen för detta kallas rå tabellåtkomst .

Råbordstillgång

Ibland vill du inte utlösa metametoder, men verkligen skriva eller läsa exakt den givna nyckeln utan några smarta funktioner som är lindade runt åtkomst. För detta ger lua dig metoder för rå tabellåtkomst:

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

Med detta kan vi nu skriva om vår tidigare __newindex metod för att faktiskt skriva värdet till tabellen:

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

Simulera 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'"

Instansmetoder kan skrivas genom att lämna objektet som det första argumentet.

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

Det finns lite syntaktiskt socker för detta.

function Class:saybye(phrase)
    print("I am " .. self .. "\n" .. phrase)
end
object:saybye("c ya") --> will print "I am <table ID>
                      -->             c ya"

Vi kan också lägga till standardfält i en klass.

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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow