Насколько особенной является глобальная переменная G?

фрагмент в Lua 5.3 руководство:

_G

глобальная переменная (не функция), которая содержит глобальную среду (см. §2.2). Сама Lua не использует эту переменную; изменение ее значения не влияет ни на какую среду, и наоборот.

соответствующая часть из §2.2

[...] каждый фрагмент компилируется в области внешней локальной переменной с именем _ENV, так что это ни одного свободного имени в куске.

[...]

любая таблица, используемая в качестве значения _ENV называется средой.

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

когда Lua загружает кусок, значение по умолчанию для его _ENV upvalue является глобальная окружающая среда. Поэтому по умолчанию свободные имена в коде Lua относятся к записям в глобальной среде

я понимаю, что для каждого загруженного куска, так как _ENV будет первым upvalue, это указал на глобальная таблица окружающей среды, указанная _G by load.

> =_G, _ENV
table: 006d1bd8 table: 006d1bd8

подтверждает, что оба указывают на одну и ту же таблицу. В руководстве несколько раз утверждается, что _ENV и _G просто обычные имена без скрытого значения и что сама Lua не использует его внутренне. Я попробовал этот кусок ниже:

local a = { }
local b = a      -- since tables are objects, both refer to the same table object
print(a, b)      -- same address printed twice
a = { }          -- point one of them to a newly constructed table
print(a, b)      -- new, old table addresses printed

Теперь делаем то же самое с _G и _ENV:

local g = _G          -- make an additional reference
print(g, _G, _ENV)    -- prints same address thrice
local p = print       -- backup print for later use
_ENV = { }            -- point _ENV to a new table/environment
p(g, _G, _ENV)        -- old, nil, new

table: 00ce1be0    table: 00ce1be0    table: 00ce1be0
table: 00ce1be0    nil                table: 00ce96e0

если _G является обычным глобальным, почему он становится nil здесь? Если подсчет ссылок выполнен,_G, все еще держал ссылку в то время _ENV отпустил его. Как b выше, он тоже должен держаться за старый стол, нет?

однако, для ниже чанк, _G не изменяется / сохраняется!

_ENV = { _G = _G }
_G.print(_G, _ENV, _ENV._G)   -- old, new, old

но вот это убило:

_ENV = { g = _G }
_ENV.g.print(_ENV, _ENV.g, _G)    -- new, old, nil

другой случай, когда он сохраняется:

print(_G, _ENV)                       -- print same address twice
local newgt = {}                      -- create new environment
setmetatable(newgt, {__index = _G})   -- set metatable with _G as __index metamethod
_ENV = newgt                          -- point _ENV to newgt
print(_G, newgt, _ENV)                -- old, new, new

С таким количеством вариаций в поведении _G, оригинал заверения руководства кажется шаткой. Я что-то упускаю?

1 ответов


насколько особенной является глобальная переменная _G?

он особенный в трех отношениях:

  1. он использует имя зарезервировано для внутреннего использования Lua.
  2. он создается одним из стандартных модулей Lua (в частности "базу" модуль). Если вы создадите свежий lua_State без открыв модуль "base", у вас не будет _G переменной. Этот автономный интерпретатор уже имеет все стандартные библиотеки нагруженный, хотя.
  3. некоторые сторонние модули Lua используют глобальную переменную _G, и изменение / удаление может сломать эти модули.

в чем смысл _G?

глобальные переменные в Lua реализованы с использованием обычной таблицы. Любой доступ к переменной, которая не является local переменная или upvalue будет попадете в эту таблицу. Локальные переменные всегда имеют приоритет, поэтому если у вас есть глобальная переменная и локальная переменная с тем же имя, вы всегда получите местный. И вот!--0--> вступает в игру: если вы хотите глобальную переменную, вы можете сказать _G.name вместо name. Принимая имя _G не является локальной переменной (она зарезервирована для Lua, помнишь?!), это всегда будет Вам значение глобальной переменной используя синтаксис индексирования таблицы и, таким образом, устраняя двусмысленность с помощью имена локальных переменных. В более новых версиях Lua (5.2+) вы также можете использовать _ENV.name в качестве альтернативы, но _G до тех версии и сохранено для совместимости.

Есть и другие случаи, когда вы хотите получить доступ к глобалам таблица, например для установки metatable. Lua позволяет настроить поведение таблиц (и других значений) путем установки metatable используя setmetatable функция, но вы должны передать таблицу как параметр как-то. _G поможет вам сделать это.

если вы добавили метатаблиц в глобалы стол, в некоторых случаях возможно, вы захотите обход метаметодов (__index и/или __newindex) вы только что установили. Вы можете использовать rawget и rawset, но вам нужно передать таблицу globals в качестве параметра также.

обратите внимание, что все варианты использования, перечисленные выше, применяются только к коду Lua не C код. В коде C у вас нет именованных локальных переменных, только стек индексы. Поэтому нет никакой двусмысленности. И если вы хотите ссылку на таблица globals для передачи некоторой функции, вы можете использовать lua_pushglobaltable (где использует реестр вместо _G). Как следствие, модули, реализованные на C, не используют/не нуждаются в _G глобальная переменная. Это относится к стандартной библиотеке Lua (которая также реализовано в C). Фактически, справочное руководство гарантии, что _G (переменная, а не таблица) не используется Lua или его стандартная библиотека.

как _G относятся к _ENV?

начиная с версии 5.0 Lua позволяет изменить таблицу, используемую для поиска глобальные переменные на основе функции per-(Lua -). В Lua 5.0 и 5.1 вы использовали setfenv функция для этого (таблица globals также называется "функциональная среда", отсюда и название setfenv). В Lua 5.2 введен новый подход с использованием другой специальной переменной имени _ENV. _ENV is не глобальная переменная, хотя Lua гарантирует, что каждый кусок начинается с _ENV upvalue. Новый подход работает, позволяя Lua перевести любой доступ к нелокальной (и не upvalue) переменной имя a to _ENV.a. Все _ENV в этом месте в коде используется для разрешения глобальных переменных. Этот путь намного безопаснее, потому что вы не можете изменить среду кода, который не написали сами. (без использования библиотеки отладки), а также более гибким, потому что вы можно изменить среду для отдельных блоков кода, создав local _ENV переменные с ограниченными областями.

однако, в любом случае вам нужно по умолчанию среда, которая используется прежде чем программист имеет возможность установить один (или если программист не хочет его менять). При запуске Lua создает это значение по умолчанию окружающая среда (также называемая "глобальная среда") для вас и магазинах его в реестр. Эта среда по умолчанию используется как _ENV upvalue для всех блоков, Если вы не пройти таможню сред load или loadfile. lua_pushglobaltable также извлекает эту глобальную среду непосредственно из реестра, так все модули C автоматически используют его для доступа к глобальным переменным.

и если стандартный модуль "base" C был загружен, это по умолчанию "глобальная среда" имеет поле таблицы с именем _G, Что относится вернемся к глобальной окружающей среде.

в итоге:

  • в глобальная переменная _G на самом деле _ENV._G.
  • _ENV не является глобальной, а upvalue или локальной переменной.
  • на _G поле по умолчанию "глобальная среда" указывает на глобальная окружающая среда.
  • _G и _ENV по умолчанию ссылаются на ту же таблицу (сказал глобальный окружающая среда.)
  • код C также не использует, но поле в реестре (которое снова указывает на глобальную среду by определение).
  • вы можете заменить _G (в окружающей среде), не нарушая Модули C или сам Lua (но вы можете сломать сторонний Lua модули если не осторожны).
  • вы можете заменить _ENV всякий раз, когда вы хотите, потому что это влияет только на ваш собственный код (не более текущего фрагмента/файла).
  • если заменить _ENV, вы можете решить для себя, Является ли _G (_ENV._G) будет доступен в затронутом коде, и что это точки к.