全局变量_G有多特殊?
How special is the global variable _G?
_G
A global variable (not a function) that holds the global environment (see §2.2). Lua itself does not use this variable; changing its value does not affect any environment, nor vice versa.
§2.2 中的相关部分
[…] every chunk is compiled in the scope of an external local variable named _ENV
, so _ENV
itself is never a free name in a chunk.
[…]
Any table used as the value of _ENV
is called an environment.
Lua keeps a distinguished environment called the global environment. This value is kept at a special index in the C registry. In Lua, the global variable _G
is initialized with this same value. (_G
is never used internally.)
When Lua loads a chunk, the default value for its _ENV
upvalue is the global environment. Therefore, by default, free names in Lua code refer to entries in the global environment
我知道对于每个加载的块,由于 _ENV
将是第一个上值,它 指向 全局环境 table,指向_G
来自 load
.
> =_G, _ENV
table: 006d1bd8 table: 006d1bd8
确认两者指向同一个 table。手册声明,而不是多次保证,_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
一样,它也应该坚持旧的 table,不是吗?
但是,对于下面的块,_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
的行为变化如此之多,手册最初给出的保证似乎站不住脚。我在这里错过了什么?
全局变量_G
有多特别?
它在三个方面很特别:
- 它使用了一个名字reserved for internal use by Lua。
- 它是由 Lua 的标准模块之一创建的(特别是
"base" 模块)。如果你创建一个新的
lua_State
没有
打开 "base" 模块,您将没有 _G
变量。这
独立解释器已经加载了所有标准库,
不过
- 一些第三方Lua模块使用了全局变量
_G
,并且
changing/removing 它可以破坏那些模块。
_G
有什么意义?
Lua 中的全局变量是使用普通 table 实现的。任何
访问不是 local
变量或上值的变量将
被重定向到此 table。局部变量总是优先的,所以
如果你有一个全局变量和一个同名的局部变量,
你总是会得到当地的。这里 _G
发挥作用:如果
你想要全局变量,你可以说 _G.name
而不是 name
。
假设名称 _G
不是局部变量(它保留给 Lua,
记得吗?!),这将始终为您提供全局变量的值
通过使用 table 索引语法,从而消除歧义
局部变量名。在较新的 Lua 版本 (5.2+) 中,您还可以使用
_ENV.name
作为替代方案,但 _G
早于这些版本并且是
保留兼容性。
在其他情况下,您想要掌握全局变量
table,例如用于设置元table。 Lua 允许您自定义
tables(和其他值)的行为通过使用
setmetatable
函数,但你必须将 table 作为
参数不知何故。 _G
帮助你做到这一点。
如果您在全局 table 中添加了元 table,在某些情况下
你可能想绕过元方法(__index
and/or
__newindex
) 您刚刚安装。您可以使用 rawget
和
rawset
,但你需要将全局变量 table 作为参数传递
还有。
请注意,上面列出的所有用例仅适用于 Lua 代码 不适用于
C代码。在 C 代码中你没有命名的局部变量,只有堆栈
指数。所以没有歧义。如果你想要参考
globals table 传递给一些函数,你可以使用
lua_pushglobaltable
(which uses the registry 而不是 _G
)。
因此,用 C 实现的模块不 use/need _G
全局变量。这适用于 Lua 的标准库(即
也在 C) 中实现。其实参考手册
guarantees,即_G
(变量,不是table)
通过 Lua 或其标准库。
_G
与 _ENV
有什么关系?
自版本 5.0 Lua 允许您更改 table 用于查找
基于每个(Lua-)函数的全局变量。在 Lua 5.0 和 5.1 中
你为此使用了 setfenv
函数(全局变量 table 是
也称为 "function environment",因此得名 setfenv
)。 Lua 5.2
引入了一种使用另一个特殊变量名 _ENV
的新方法。
_ENV
是 而不是 全局变量,Lua 确保每个
块以 _ENV
上值开头。新方法的工作原理是让
Lua 转换对非本地(和非上值)变量的任何访问
名称 a
到 _ENV.a
。无论 _ENV
在代码中的那个点是什么
用于解析全局变量。这种方式更安全,因为
您无法更改不是您自己编写的代码的环境
(不使用调试库),也更灵活,因为你
可以通过创建来改变单个代码块的环境
local
_ENV
个范围有限的变量。
然而,在任何情况下你都需要一个default环境
在程序员有机会设置一个自定义的之前(或者如果程序员
不想改变它)。在启动时,Lua 创建此默认值
您和商店的环境(也称为 "global environment")
它在 registry。此默认环境用作
_ENV
所有区块的升值,除非您将自定义环境传递给
load
or loadfile
。 lua_pushglobaltable
还有
直接从注册表中检索这个全局环境,所以
所有 C 模块自动使用它来访问全局变量。
并且如果标准"base"C模块已经加载,这
默认 "global environment" 有一个名为 _G
的 table 字段是指
回到全球环境。
总结一下:
- 全局变量
_G
其实是_ENV._G
.
_ENV
不是全局变量,而是上值变量或局部变量。
- 默认"global environment"的
_G
字段指向回
全球环境。
_G
和 _ENV
默认引用相同的 table(表示全局
环境)。
- C 代码也不使用,而是注册表中的一个字段(
再次指向全局环境根据定义)。
- 可以替换
_G
(在全局环境下)不破坏
C 模块或 Lua 本身(但你可能会破坏第三方 Lua
模块,如果不小心)。
- 你可以随时替换
_ENV
,因为它只影响
你自己的代码(目前最多chunk/file)。
- 如果替换
_ENV
,可以自己决定是否_G
(_ENV._G
) 将在受影响的代码中可用,以及它是什么
指向.
_G
A global variable (not a function) that holds the global environment (see §2.2). Lua itself does not use this variable; changing its value does not affect any environment, nor vice versa.
§2.2 中的相关部分
[…] every chunk is compiled in the scope of an external local variable named
_ENV
, so_ENV
itself is never a free name in a chunk.[…]
Any table used as the value of
_ENV
is called an environment.Lua keeps a distinguished environment called the global environment. This value is kept at a special index in the C registry. In Lua, the global variable
_G
is initialized with this same value. (_G
is never used internally.)When Lua loads a chunk, the default value for its
_ENV
upvalue is the global environment. Therefore, by default, free names in Lua code refer to entries in the global environment
我知道对于每个加载的块,由于 _ENV
将是第一个上值,它 指向 全局环境 table,指向_G
来自 load
.
> =_G, _ENV
table: 006d1bd8 table: 006d1bd8
确认两者指向同一个 table。手册声明,而不是多次保证,_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
一样,它也应该坚持旧的 table,不是吗?
但是,对于下面的块,_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
的行为变化如此之多,手册最初给出的保证似乎站不住脚。我在这里错过了什么?
全局变量_G
有多特别?
它在三个方面很特别:
- 它使用了一个名字reserved for internal use by Lua。
- 它是由 Lua 的标准模块之一创建的(特别是
"base" 模块)。如果你创建一个新的
lua_State
没有 打开 "base" 模块,您将没有_G
变量。这 独立解释器已经加载了所有标准库, 不过 - 一些第三方Lua模块使用了全局变量
_G
,并且 changing/removing 它可以破坏那些模块。
_G
有什么意义?
Lua 中的全局变量是使用普通 table 实现的。任何
访问不是 local
变量或上值的变量将
被重定向到此 table。局部变量总是优先的,所以
如果你有一个全局变量和一个同名的局部变量,
你总是会得到当地的。这里 _G
发挥作用:如果
你想要全局变量,你可以说 _G.name
而不是 name
。
假设名称 _G
不是局部变量(它保留给 Lua,
记得吗?!),这将始终为您提供全局变量的值
通过使用 table 索引语法,从而消除歧义
局部变量名。在较新的 Lua 版本 (5.2+) 中,您还可以使用
_ENV.name
作为替代方案,但 _G
早于这些版本并且是
保留兼容性。
在其他情况下,您想要掌握全局变量
table,例如用于设置元table。 Lua 允许您自定义
tables(和其他值)的行为通过使用
setmetatable
函数,但你必须将 table 作为
参数不知何故。 _G
帮助你做到这一点。
如果您在全局 table 中添加了元 table,在某些情况下
你可能想绕过元方法(__index
and/or
__newindex
) 您刚刚安装。您可以使用 rawget
和
rawset
,但你需要将全局变量 table 作为参数传递
还有。
请注意,上面列出的所有用例仅适用于 Lua 代码 不适用于
C代码。在 C 代码中你没有命名的局部变量,只有堆栈
指数。所以没有歧义。如果你想要参考
globals table 传递给一些函数,你可以使用
lua_pushglobaltable
(which uses the registry 而不是 _G
)。
因此,用 C 实现的模块不 use/need _G
全局变量。这适用于 Lua 的标准库(即
也在 C) 中实现。其实参考手册
guarantees,即_G
(变量,不是table)
通过 Lua 或其标准库。
_G
与 _ENV
有什么关系?
自版本 5.0 Lua 允许您更改 table 用于查找
基于每个(Lua-)函数的全局变量。在 Lua 5.0 和 5.1 中
你为此使用了 setfenv
函数(全局变量 table 是
也称为 "function environment",因此得名 setfenv
)。 Lua 5.2
引入了一种使用另一个特殊变量名 _ENV
的新方法。
_ENV
是 而不是 全局变量,Lua 确保每个
块以 _ENV
上值开头。新方法的工作原理是让
Lua 转换对非本地(和非上值)变量的任何访问
名称 a
到 _ENV.a
。无论 _ENV
在代码中的那个点是什么
用于解析全局变量。这种方式更安全,因为
您无法更改不是您自己编写的代码的环境
(不使用调试库),也更灵活,因为你
可以通过创建来改变单个代码块的环境
local
_ENV
个范围有限的变量。
然而,在任何情况下你都需要一个default环境
在程序员有机会设置一个自定义的之前(或者如果程序员
不想改变它)。在启动时,Lua 创建此默认值
您和商店的环境(也称为 "global environment")
它在 registry。此默认环境用作
_ENV
所有区块的升值,除非您将自定义环境传递给
load
or loadfile
。 lua_pushglobaltable
还有
直接从注册表中检索这个全局环境,所以
所有 C 模块自动使用它来访问全局变量。
并且如果标准"base"C模块已经加载,这
默认 "global environment" 有一个名为 _G
的 table 字段是指
回到全球环境。
总结一下:
- 全局变量
_G
其实是_ENV._G
. _ENV
不是全局变量,而是上值变量或局部变量。- 默认"global environment"的
_G
字段指向回 全球环境。 _G
和_ENV
默认引用相同的 table(表示全局 环境)。- C 代码也不使用,而是注册表中的一个字段( 再次指向全局环境根据定义)。
- 可以替换
_G
(在全局环境下)不破坏 C 模块或 Lua 本身(但你可能会破坏第三方 Lua 模块,如果不小心)。 - 你可以随时替换
_ENV
,因为它只影响 你自己的代码(目前最多chunk/file)。 - 如果替换
_ENV
,可以自己决定是否_G
(_ENV._G
) 将在受影响的代码中可用,以及它是什么 指向.