Насколько особенной является глобальная переменная 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
?
он особенный в трех отношениях:
- он использует имя зарезервировано для внутреннего использования Lua.
- он создается одним из стандартных модулей Lua (в частности
"базу" модуль). Если вы создадите свежий
lua_State
без открыв модуль "base", у вас не будет_G
переменной. Этот автономный интерпретатор уже имеет все стандартные библиотеки нагруженный, хотя. - некоторые сторонние модули 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
) будет доступен в затронутом коде, и что это точки к.