Поиск…


Синтаксис

  • [[local] mt =] getmetatable ( t ) -> получить связанный метатебель для ' t '
  • [[local] t =] setmetatable ( t , mt ) -> установить метатебель для ' t ' в ' mt ' и возвращает ' t '

параметры

параметр подробности
T Переменная, относящаяся к таблице lua; также может быть литералом таблицы.
Монтана Таблица для использования в качестве метатетируемого; может иметь ноль или более полей метаметода.

замечания

Есть некоторые метаметоды, не упомянутые здесь. Полный список и их использование см. В соответствующей записи в руководстве lua .

Создание и использование метаданных

Metatable определяет набор операций, которые изменяют поведение объекта lua. Метатизируемый - это просто обычная таблица, которая используется особым образом.

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

Использование таблиц в качестве метаметодов

Некоторые метаметоды не должны быть функциями. Наиболее важным примером для этого является __index . Это также может быть таблица, которая затем используется в качестве поиска. Это довольно часто используется при создании классов в lua. Здесь таблица (часто метатетированная) используется для хранения всех операций (методов) класса:

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

Сборщик мусора - метатет __gc

5,2

Объекты в lua собирают мусор. Иногда вам нужно освободить некоторый ресурс, хотите распечатать сообщение или сделать что-то еще, когда объект будет уничтожен (собран). Для этого вы можете использовать __gc , который __gc с объектом в качестве аргумента при уничтожении объекта. Вы могли видеть этот метаметод как своего рода деструктор.

Этот пример показывает __gc в действии. Когда внутренняя таблица, присвоенная t получает сбор мусора, она печатает сообщение до сбора. Аналогично для внешней таблицы при достижении конца скрипта:

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

Больше метаметодов

Есть еще много метаметодов, некоторые из них являются арифметическими (например, сложение, вычитание, умножение), есть побитовые операции (и, или, xor, shift), сравнение (<,>), а также операции базового типа, такие как == и # (равенство и длина). Давайте построим класс, который поддерживает многие из этих операций: призыв к рациональной арифметике. Хотя это очень просто, это показывает идею.

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

Сделать таблицы доступными

Существует __call , называемый __call , который определяет bevahiour объекта при использовании в качестве функции, например object() . Это можно использовать для создания объектов функции:

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

Метаметод вызывается с соответствующим объектом, все остальные аргументы передаются функции после этого:

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

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

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

Индексирование таблиц

Возможно, наиболее важным использованием метатежей является возможность изменения индексации таблиц. Для этого необходимо рассмотреть два действия: чтение содержимого и запись содержимого таблицы. Обратите внимание, что оба действия запускаются только в том случае, если соответствующий ключ отсутствует в таблице.

чтение

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

Это можно использовать для повышения ошибки при чтении несуществующего ключа:

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

Пишу

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

Теперь вы можете спросить себя, как фактическое значение написано в таблице. В этом случае это не так. Проблема здесь в том, что метаметоды могут вызывать метаметоды, что приведет к инфинитивному циклу или, точнее, переполнению стека. Итак, как мы можем это решить? Решение для этого называется доступным доступом к таблице .

Доступ к исходной таблице

Иногда вы не хотите запускать метаметоды, но на самом деле пишете или читаете именно данный ключ, без каких-либо умных функций, обернутых вокруг доступа. Для этого lua предоставляет вам методы доступа к исходным таблицам:

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

С этим мы теперь можем переписать __newindex метод __newindex чтобы фактически записать значение в таблицу:

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

Моделирование ООП

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

Методы экземпляра могут быть записаны путем передачи объекта в качестве первого аргумента.

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

Для этого есть синтаксический сахар.

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

Мы также можем добавлять поля по умолчанию в класс.

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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow