TCL名称空间中的变量

The variable in namespace of TCL

我对TCL命名空间中的变量有疑问。

我有两个.tcl文件a.tcl,b.tcl,我在这两个文件中定义了相同的全局变量,例如:

a.tcl

1
variable same"hello1"

b.tcl

1
2
3
4
variable same"hello2"
proc use {} {
   puts same
}

但是在b.tcl中,我尝试定义一个使用变量" same "的proc,是否有冲突? proc use()中使用的是哪一个?


从您的问题(和对Donal的评论)看来,您认为文件与名称空间有关。这种想法是不正确的。

a.tcl

1
variable same"hello a" ;# global namespace

b.tcl

1
2
3
4
5
6
variable same"hello b" ;# global namespace
proc use {} {
    variable same ;# reads from the global namespace
    puts $same    ;# will puts"hello a" or"hello b" depending on whether
                  ;# a.tcl is sourced after b.tcl or not
}

c.tcl

1
2
3
4
namespace eval ::not_the_global {
    variable same"hello c" ;# a different namespace, and a different variable than
                            ;# the one from the previous two files
}

d.tcl

1
2
3
4
5
6
7
namespace eval ::not_the_global {
    proc use {} {     ;# a different use proc from the one in b.tcl
        variable same ;# the variable defined in this namespace in c.tcl
        puts $same    ;# will output"hello c" no matter is a.tcl or b.tcl
                      ;# were sourced
    }
}

故事的寓意是代码所在的文件与名称空间或其他任何内容都不相关。为了使命令或变量位于单独的命名空间中,必须将其显式放置在该命名空间中。


use过程将与same变量位于同一名称空间中(代码所在的文件与它在其中创建命令和变量的名称空间正交100%)。但是,默认情况下use的主体将无权访问名称空间的变量,因为默认情况下,所有过程声明的变量都是局部变量。这意味着要获取对same的访问,应将其与variable一起使用,可能没有值初始化参数:

1
2
3
4
proc use {} {
    variable same
    puts $same
}

您也可以直接使用变量的全限定名称,但这往往会变慢(尤其是在循环中)。

在您问之前,我希望上面的代码会导致use打印一个" hello1a"?或a?hello2a ?,取决于a.tcl和b.tcl是source d的顺序。必须通过namespace eval ::someNsName { ...script... }明确地进行任何命名间隔。您可能已经在每个脚本文件的其余内容中放了这样的东西。通常,将代码过度依赖于源文件的顺序来编写代码是一种糟糕的形式,主要是因为调试起来往往要困难得多。|


暂时忘掉这两个文件。假设您只有一个文件,内容为:

1
2
3
4
5
variable x hello ;# this is a global variable.

proc use {} {
  puts $x
}

这将导致错误,提示未定义类似$x之类的内容。为什么?因为与C不同,Tcl不会将任何内容导入您不要求输入的函数。让我再说一遍:tcl procs看不到您不告诉它的全局变量或命名空间变量。

因此,导入全局变量的传统方法是使用global命令:

1
2
3
4
5
6
variable x hello

proc use {} {
  global x ;# import $x into this proc
  puts $x
}

这应该有效。

当然,对于命名空间,global一词没有任何意义,因此创建了variable命令以允许在命名空间中定义的proc看到命名空间变量:

1
2
3
4
5
6
7
8
namespace eval foo {
  variable x hello

  proc use {} {
    variable x ;# import $x into this proc
    puts $x
  }
}

还有另一种无需显式使用globalvariable即可导入全局变量和命名空间变量的方法:只需指定完整的命名空间即可。全局名称空间只是::,因此以下内容也适用:

1
2
3
4
5
variable x hello

proc use {} {
  puts $::x
}

,当然:

1
2
3
4
5
6
7
namespace eval foo {
  variable x hello

  proc use {} {
    puts $foo::x
  }
}