_G
保存全局环境的全局变量(不是函数)(参见§2.2)。 Lua本身不使用这个变量;改变它的价值不会影响任何环境,反之亦然。
§2.2的相关部分
[...]每个块都在名为
_ENV
的外部局部变量的范围内编译,因此_ENV
本身从不是块中的自由名称。[...]
任何用作
_ENV
值的表都称为环境。Lua拥有一个称为全球环境的杰出环境。此值保存在C注册表中的特殊索引处。在Lua中,全局变量
_G
使用相同的值进行初始化。 (_G
从不在内部使用。)当Lua加载一个块时,其
_ENV
upvalue的默认值是全局环境。因此,默认情况下,Lua代码中的自由名称是指全局环境中的条目
我理解,对于每个加载的块,由于_ENV
将是第一个upvalue,因此指向全局环境表,由_G
指向{{1 }}
load
确认两者都指向同一张表。手册陈述,而不是多次保证,> =_G, _ENV
table: 006d1bd8 table: 006d1bd8
和_ENV
只是常规名称,没有隐藏的意义,Lua本身不在内部使用它。我在下面尝试了这个块:
_G
现在对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
的行为有很多变化,手册给出的原始保证似乎不稳定。我在这里缺少什么?
答案 0 :(得分:8)
_G
有多特别?它有三种特殊之处:
lua_State
打开"基地"模块,你没有_G
变量。该
独立解释器已经加载了所有标准库,
虽然。_G
,和
更改/删除它可能会破坏这些模块。_G
Lua中的全局变量使用普通表实现。任何
访问不是local
变量或upvalue的变量
被重定向到这个表。局部变量总是优先考虑的
如果你有一个全局变量和一个同名的局部变量,
你将永远得到当地的。在这里_G
发挥作用:如果
你想要全局变量,你可以说_G.name
而不是name
。
假设名称_G
不是局部变量(它是为Lua保留的,
还记得吗?!),这总能得到全局变量的值
通过使用表索引语法,从而消除歧义
局部变量名。在较新的Lua版本(5.2+)中,您也可以使用
_ENV.name
作为替代方案,但_G
早于这些版本
保持兼容性。
在其他情况下,您希望获得全局变量
表格,例如用于设置元表。 Lua允许您自定义
通过使用设置元表来表(和其他值)的行为
setmetatable
函数,但你必须将表作为一个
参数不知何故。 _G
可以帮助您实现这一目标。
如果您在globals表中添加了metatable,在某些情况下
你可能想绕开元方法(__index
和/或
__newindex
)您刚安装完毕。您可以使用rawget
和
rawset
,但您需要将globals表作为参数传递
同样。
请注意,上面列出的所有用例仅适用于Lua代码而不是
C代码。在C代码中,您没有命名局部变量,只有堆栈
指数。所以没有歧义。如果你想要参考
globals表传递给某些函数,可以使用
lua_pushglobaltable
(uses the registry代替_G
)。
因此,用C实现的模块不需要_G
全局变量。这适用于Lua的标准库(即
也在C)中实施。实际上,参考手册
guarantees,未使用_G
(变量,而不是表)
Lua或其标准库。
_G
与_ENV
的关系如何?从5.0版开始,Lua允许您更改用于查找的表
基于per(Lua)函数的全局变量。在Lua 5.0和5.1中
你使用了setfenv
函数(全局表是
也称为"功能环境",因此名称为setfenv
)。 Lua 5.2
使用另一个特殊变量名_ENV
引入了一种新方法。
_ENV
不是一个全局变量,Lua确保每一个
chunk以_ENV
upvalue开头。新方法通过让出来
Lua转换对非本地(和非upvalue)变量的任何访问
将a
命名为_ENV.a
。无论代码中的_ENV
是什么
习惯于解决全局变量。这种方式更安全,因为
你无法改变自己没有写过的代码环境
(不使用调试库),也因为你更灵活
可以通过创建来更改单个代码块的环境
范围有限的local
_ENV
个变量。
但是,无论如何,您需要使用默认环境
在程序员有机会设置自定义程序之前(或者程序员
不想改变它)。在启动时,Lua创建此默认值
适用于您和商店的环境(也称为"global environment")
它在registry。此默认环境用作
除非您将自定义环境传递给所有块,否则_ENV
值为upvalue
load
或loadfile
。 lua_pushglobaltable
也是
直接从注册表中检索此全局环境
所有C模块都自动使用它来访问全局变量。
如果标准" base"这个C模块已经加载了
默认"全球环境"有一个名为_G
的表字段引用
回到全球环境。
_G
实际上是_ENV._G
。_ENV
不是全局变量,而是upvalue变量或局部变量。_G
字段"回到
全球环境。_G
和_ENV
引用同一个表格(表示全局
环境)。_G
(在全局环境中)
C模块或Lua本身(但你可能会打破第三方Lua
模块,如果不小心)。_ENV
,因为它只会影响
你自己的代码(最多当前的块/文件)。_ENV
,您可以自行决定是否_G
(_ENV._G
)将在受影响的代码中提供,以及它是什么
指向。答案 1 :(得分:-1)
通过阅读参考资料和用户注释以及答案,我想知道为什么默认情况下无法对如此重要的表进行元映射。就像Lua5.3中的字符串一样。他们已经使用包含几乎所有字符串函数的表对
__index
进行了元表化。因此,我决定检查发生了什么,以及元可配置的_G
(或_ENV
)有多有用。我能说/写什么。我发现这对我非常有用,而且副作用是,所有已定义的__index函数/变量/对象都是全局的。一个简单的示例是定义一个printf()
函数,因此可以在lua -i
中自己尝试一下...
# lua -i
Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
> setmetatable(_G,{__index={}})
table: 0x566816d0
> -- Using getmetatable(_G).__index.new=whatever now
> -- for setting/changing after setmetatable()
> getmetatable(_G).__index.printf=function(s,...) io.write(string.format(s,...)) end
> printf
function: 0x5669bee0
> printf("%s\n",_VERSION)
Lua 5.3
> printf("%s\n",_VERSION:rep(10,' is cool '))
Lua 5.3 is cool Lua 5.3 is cool Lua 5.3 is cool Lua 5.3 is cool Lua 5.3 is cool Lua 5.3 is cool Lua 5.3 is cool Lua 5.3 is cool Lua 5.3 is cool Lua 5.3
> printf("%s\n",_VERSION.dump(printf))
uaS�
�
xV(w@=stdiF@�@@��@�-�d@&�printstringforma_ENV
所以-在工作之后,我将所有表函数放在
__index
中,并将dump()
函数显示表内容。help()
函数还有一个位置,用于显示例如元表的内容。
_G:help()
__index=table: 0x5669bbe0
printf=function: 0x5669eee0
sort=function: 0x56665b60
help=function: 0x566a0de0
concat=function: 0x566655b0
remove=function: 0x56665300
pack=function: 0x56664dc0
dump=function: 0x5669bc40
move=function: 0x56665060
unpack=function: 0x56664c80
insert=function: 0x56665450
> -- Now _Global __index.help call on string _VERSION
help(_VERSION)
__index=table: 0x566846b0
find=function: 0x56664550
gmatch=function: 0x56661e70
rep=function: 0x566619f0
char=function: 0x56661f10
byte=function: 0x566614b0
dump=function: 0x56662c80
match=function: 0x56664540
upper=function: 0x566617a0
unpack=function: 0x56663250
format=function: 0x56662030
len=function: 0x566612c0
gsub=function: 0x56664560
packsize=function: 0x56663110
pack=function: 0x566635f0
sub=function: 0x566618c0
lower=function: 0x56661ba0
reverse=function: 0x56661830
第一次调用
:
会将_G放入help()(help(_G)
)-第二个调用显示已定义字符串的元表。