为什么设置 nested table 的值会设置 metatable 的 table?
Why does setting the value of nested table set the metatable's table?
我正在学习 lua 并且刚刚发现 metatables 的一个怪癖:
local mt = {
value = 0,
nested = { value = 0 },
}
mt.__index = mt
local t1 = {}
setmetatable(t1, mt)
local t2 = {}
setmetatable(t2, mt)
print(t1.value) -- 0
print(t1.nested.value) -- 0
print(t2.value) -- 0
print(t2.nested.value) -- 0
print(mt.value) -- 0
print(mt.nested.value) -- 0
t1.value = 10
t1.nested.value = 20;
print(t1.value) -- 10
print(t1.nested.value) -- 20
print(t2.value) -- 0
print(t2.nested.value) -- 20 (?)
print(mt.value) -- 0
print(mt.nested.value) -- 20 (?)
注意带(?)
的两条评论。
如果我理解正确的话,这是因为 t1
没有 nested
的密钥 returns mt
的 nested
table。因此,t1.nested.value=20
有效地设置了 mt
中的值,因此所有 table 将其用作 metatable.
我有两个问题:
- 我的理解对吗?如果没有,有人可以解释这里发生了什么吗?
- 不管发生什么,我如何只设置特定的 table 的
nested
table 而不影响 metatable?
是的,1) 是正确的。
对于 2) 最简单的方法是在 t1
中创建一个新的 table,它也有一个带有 __index
字段的元 table。
t1.nested = setmetatable({ }, {__index = t1.nested})
print(t1.nested.value) -- 20
t1.nested.value = 30
print(t1.nested.value) -- 30
print(mt.nested.value) -- 20
正确。
因此,您在 metatable 中设置的值现在持有一个新值。
先设置nested
再设置nested.value
.
就像你对 value
.
所做的一样
并销毁它以取回 metatable nested
table 包括 value
.
我建议在交互式 Lua 解释器中做更多事情,看看发生了什么...
$ lua
Lua 5.4.3 Copyright (C) 1994-2021 Lua.org, PUC-Rio
> code=[[do
>> local mt = {
>> value = 0,
>> nested = { value = 0 },
>> }
>> mt.__index = mt
>>
>> local t1 = {}
>> setmetatable(t1, mt)
>>
>> local t2 = {}
>> setmetatable(t2, mt)
>> dump(t1); dump(t2);
>> print(t1.value) -- 0
>> print(t1.nested.value) -- 0
>> print(t2.value) -- 0
>> print(t2.nested.value) -- 0
>> print(mt.value) -- 0
>> print(mt.nested.value) -- 0
>>
>> t1.value = 10
>> t1.nested.value = 20;
>> dump(t1); dump(t2);
>> print(t1.value) -- 10
>> print(t1.nested.value) -- 20
>> print(t2.value) -- 0
>> print(t2.nested.value) -- 20 (?)
>> print(mt.value) -- 0
>> print(mt.nested.value) -- 20 (?)
>> end]]
> dump=require('dump') load(code)()
0
0
0
0
0
0
1 Index key: value (string) equals to 10 (number) integer number
10
20
0
20
0
20
好的,你需要我的 dump.lua...
return function(...)
local args={...}
local counter=0
-- Next patches function to work also as/for __call metamethod
-- (First argument to __call always is self, so an argument to __call is always second)
if #args == 2 then args[0]=args[1] args[1]=args[2] table.remove(args) end
-- Loop
for k,v in pairs(args[1]) do
counter=counter+1 -- For counting table keys
-- Put a funny colored out what is in the table
io.stdout:write(tostring(counter)..' Index key: \x1b[1;'..tostring(math.random(31,34))..'m',
tostring(k),
'\x1b[0m (',
type(k),
')',
' equals to \x1b[1;'..tostring(math.random(31,34))..'m',
tostring(v),
'\x1b[0m (',
type(v),
')'):flush()
-- Add some info depending on datatype (except userdata)
if type(v)=='string' then io.stdout:write(' '..tostring(#v)..' char(s)'):flush() end
if type(v)=='number' then io.stdout:write(' '..tostring(math.type(v))..' number'):flush() end
if type(v)=='table' then io.stdout:write(' '..tostring(#v)..' numbered keys in sequence'):flush() end
if type(v)=='function' then io.stdout:write(' '..tostring(debug.getinfo(v).source)..' source'):flush() end
io.stdout:write('\n'):flush() -- And add a final newline
end
-- return 'Not used'
end
PS:我喜欢将 __index 用于不可见但使用且可访问的东西
通常它应该包含数据类型 string
显示的函数。
看...
> dump(getmetatable(_VERSION).__index)
1 Index key: unpack (string) equals to function: 0x565a3530 (function) =[C] source
2 Index key: pack (string) equals to function: 0x565a3950 (function) =[C] source
3 Index key: find (string) equals to function: 0x565a4db0 (function) =[C] source
4 Index key: gsub (string) equals to function: 0x565a4dc0 (function) =[C] source
5 Index key: byte (string) equals to function: 0x565a3f20 (function) =[C] source
6 Index key: len (string) equals to function: 0x565a1750 (function) =[C] source
7 Index key: dump (string) equals to function: 0x565a2d00 (function) =[C] source
8 Index key: sub (string) equals to function: 0x565a4210 (function) =[C] source
9 Index key: char (string) equals to function: 0x565a2060 (function) =[C] source
10 Index key: rep (string) equals to function: 0x565a1be0 (function) =[C] source
11 Index key: gmatch (string) equals to function: 0x565a40d0 (function) =[C] source
12 Index key: upper (string) equals to function: 0x565a1ac0 (function) =[C] source
13 Index key: reverse (string) equals to function: 0x565a1b50 (function) =[C] source
14 Index key: packsize (string) equals to function: 0x565a3420 (function) =[C] source
15 Index key: match (string) equals to function: 0x565a4da0 (function) =[C] source
16 Index key: lower (string) equals to function: 0x565a1d90 (function) =[C] source
17 Index key: format (string) equals to function: 0x565a22d0 (function) =[C] source
...这是 string
...
的方法
print(_VERSION:rep(100,', '):upper():reverse())
( 也许 number
在 Lua 6.0 中成为 math
方法 ;-) )
但是有了table
,你绝对可以做你想做的事。
我正在学习 lua 并且刚刚发现 metatables 的一个怪癖:
local mt = {
value = 0,
nested = { value = 0 },
}
mt.__index = mt
local t1 = {}
setmetatable(t1, mt)
local t2 = {}
setmetatable(t2, mt)
print(t1.value) -- 0
print(t1.nested.value) -- 0
print(t2.value) -- 0
print(t2.nested.value) -- 0
print(mt.value) -- 0
print(mt.nested.value) -- 0
t1.value = 10
t1.nested.value = 20;
print(t1.value) -- 10
print(t1.nested.value) -- 20
print(t2.value) -- 0
print(t2.nested.value) -- 20 (?)
print(mt.value) -- 0
print(mt.nested.value) -- 20 (?)
注意带(?)
的两条评论。
如果我理解正确的话,这是因为 t1
没有 nested
的密钥 returns mt
的 nested
table。因此,t1.nested.value=20
有效地设置了 mt
中的值,因此所有 table 将其用作 metatable.
我有两个问题:
- 我的理解对吗?如果没有,有人可以解释这里发生了什么吗?
- 不管发生什么,我如何只设置特定的 table 的
nested
table 而不影响 metatable?
是的,1) 是正确的。
对于 2) 最简单的方法是在 t1
中创建一个新的 table,它也有一个带有 __index
字段的元 table。
t1.nested = setmetatable({ }, {__index = t1.nested})
print(t1.nested.value) -- 20
t1.nested.value = 30
print(t1.nested.value) -- 30
print(mt.nested.value) -- 20
正确。
因此,您在 metatable 中设置的值现在持有一个新值。先设置
nested
再设置nested.value
.
就像你对value
.
所做的一样 并销毁它以取回 metatablenested
table 包括value
.
我建议在交互式 Lua 解释器中做更多事情,看看发生了什么...
$ lua
Lua 5.4.3 Copyright (C) 1994-2021 Lua.org, PUC-Rio
> code=[[do
>> local mt = {
>> value = 0,
>> nested = { value = 0 },
>> }
>> mt.__index = mt
>>
>> local t1 = {}
>> setmetatable(t1, mt)
>>
>> local t2 = {}
>> setmetatable(t2, mt)
>> dump(t1); dump(t2);
>> print(t1.value) -- 0
>> print(t1.nested.value) -- 0
>> print(t2.value) -- 0
>> print(t2.nested.value) -- 0
>> print(mt.value) -- 0
>> print(mt.nested.value) -- 0
>>
>> t1.value = 10
>> t1.nested.value = 20;
>> dump(t1); dump(t2);
>> print(t1.value) -- 10
>> print(t1.nested.value) -- 20
>> print(t2.value) -- 0
>> print(t2.nested.value) -- 20 (?)
>> print(mt.value) -- 0
>> print(mt.nested.value) -- 20 (?)
>> end]]
> dump=require('dump') load(code)()
0
0
0
0
0
0
1 Index key: value (string) equals to 10 (number) integer number
10
20
0
20
0
20
好的,你需要我的 dump.lua...
return function(...)
local args={...}
local counter=0
-- Next patches function to work also as/for __call metamethod
-- (First argument to __call always is self, so an argument to __call is always second)
if #args == 2 then args[0]=args[1] args[1]=args[2] table.remove(args) end
-- Loop
for k,v in pairs(args[1]) do
counter=counter+1 -- For counting table keys
-- Put a funny colored out what is in the table
io.stdout:write(tostring(counter)..' Index key: \x1b[1;'..tostring(math.random(31,34))..'m',
tostring(k),
'\x1b[0m (',
type(k),
')',
' equals to \x1b[1;'..tostring(math.random(31,34))..'m',
tostring(v),
'\x1b[0m (',
type(v),
')'):flush()
-- Add some info depending on datatype (except userdata)
if type(v)=='string' then io.stdout:write(' '..tostring(#v)..' char(s)'):flush() end
if type(v)=='number' then io.stdout:write(' '..tostring(math.type(v))..' number'):flush() end
if type(v)=='table' then io.stdout:write(' '..tostring(#v)..' numbered keys in sequence'):flush() end
if type(v)=='function' then io.stdout:write(' '..tostring(debug.getinfo(v).source)..' source'):flush() end
io.stdout:write('\n'):flush() -- And add a final newline
end
-- return 'Not used'
end
PS:我喜欢将 __index 用于不可见但使用且可访问的东西
通常它应该包含数据类型 string
显示的函数。
看...
> dump(getmetatable(_VERSION).__index)
1 Index key: unpack (string) equals to function: 0x565a3530 (function) =[C] source
2 Index key: pack (string) equals to function: 0x565a3950 (function) =[C] source
3 Index key: find (string) equals to function: 0x565a4db0 (function) =[C] source
4 Index key: gsub (string) equals to function: 0x565a4dc0 (function) =[C] source
5 Index key: byte (string) equals to function: 0x565a3f20 (function) =[C] source
6 Index key: len (string) equals to function: 0x565a1750 (function) =[C] source
7 Index key: dump (string) equals to function: 0x565a2d00 (function) =[C] source
8 Index key: sub (string) equals to function: 0x565a4210 (function) =[C] source
9 Index key: char (string) equals to function: 0x565a2060 (function) =[C] source
10 Index key: rep (string) equals to function: 0x565a1be0 (function) =[C] source
11 Index key: gmatch (string) equals to function: 0x565a40d0 (function) =[C] source
12 Index key: upper (string) equals to function: 0x565a1ac0 (function) =[C] source
13 Index key: reverse (string) equals to function: 0x565a1b50 (function) =[C] source
14 Index key: packsize (string) equals to function: 0x565a3420 (function) =[C] source
15 Index key: match (string) equals to function: 0x565a4da0 (function) =[C] source
16 Index key: lower (string) equals to function: 0x565a1d90 (function) =[C] source
17 Index key: format (string) equals to function: 0x565a22d0 (function) =[C] source
...这是 string
...
print(_VERSION:rep(100,', '):upper():reverse())
( 也许 number
在 Lua 6.0 中成为 math
方法 ;-) )
但是有了table
,你绝对可以做你想做的事。