【问题标题】:Why does setting the value of nested table set the metatable's table?为什么设置嵌套表的值会设置元表的表?
【发布时间】:2022-02-05 10:17:53
【问题描述】:

我正在学习 lua,刚刚发现了元表的一个怪癖:

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 (?)

注意带有(?) 的两个cmets。

如果我正确理解这一点,那么发生的事情是因为t1 没有nested 的密钥,它返回mtnested 表。因此,t1.nested.value=20 有效地设置了 mt 中的值,因此所有将其用作元表的表。

我有两个问题:

  1. 我的理解正确吗?如果没有,有人可以解释这里发生了什么吗?
  2. 不管发生了什么,如何只设置特定表的nested 表而不影响元表?

【问题讨论】:

    标签: lua metatable


    【解决方案1】:
    1. 是正确的。
      因此,您在元表中设置了现在持有新值的值。

    2. 在设置nested.value之前先设置nested
      就像你对 value 所做的那样。
      并销毁它以取回元表nested 表,包括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,你绝对可以做你想做的事。

    【讨论】:

      【解决方案2】:

      是的,1) 是正确的。

      对于 2),最简单的方法是在 t1 中创建一个新表,该表又具有一个带有 __index 字段的元表。

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-12
        • 1970-01-01
        • 2014-11-07
        • 2020-01-25
        • 1970-01-01
        • 2017-09-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多