如果 namespace 有前导的名字空间限定符并且有的前导名字空间不存在,则自动建立它们。
namespace eval Counter {
namespace export bump
variable num 0
proc bump {} {
variable num
incr num
}
}
建立包含变量 num 和过程 bump 的一个新的名字空间。在这个名字空间中的命令和变量独立于在同一个程序中的其他命令和变量。例如,如果在全局名字空间中有一个叫 bump
的命令,它不同的于在 Counter 名字空间中的 bump
命令。名字空间变量类似于在 Tcl 中的全局变量。它们存在于名字空间中的过程之外,但象在上面的例子中展示的那样,在同一个名字空间中的过程可以通过 variable 命令访问它。
名字空间是动态的。你可以在任意时候增加及删除命令和变量,所以你可以使用一系列 namespace eval 命令分几次(over time)来建造一个名字空间的内容。例如,下面的一系列命令与上面展示的定义有相同的效果:
namespace eval Counter {
variable num 0
proc bump {} {
variable num
return [incr num]
}
}
namespace eval Counter {
proc test {args} {
return $args
}
}
namespace eval Counter {
rename test ""
}
注意在例子中向 Counter 名字空间增加了 test
过程,后来又用 rename
命令把它删除了。在名字空间内可以又其他的名字空间,它们是有层次的嵌套。一个嵌套的名字空间被封装在它的父名字空间中并且不会被其他名字空间所干扰。
如果你打算从其他的名字空间中访问命令和变量,你必须使用额外的语法。名字必须被包含它们的名字空间所限定。例如我们可以象下面这样访问 Counter 的过程:
Counter::bump 5 Counter::Reset我们可以象下面这样访问当前的 count (变量的值):
puts "count = $Counter::num"当一个名字空间包含另一个的时候,要到达它的元素你可能需要多于一个的限定符。如果我们有一个名字空间 Foo,它包含名字空间 Counter,你可以象下面这样从全局名字空间调用它的 bump 过程:
Foo::Counter::bump 3
你还可以在建立和重命名命令的时候使用限定的名字。例如,你可以象下面这样向 Foo 增加一个过程:
proc Foo::Test {args} {return $args}
你可以象下面这样把同一个过程移动到另一个名字空间中:
rename Foo::Test Bar::Test
我们覆盖(cover)剩下的一些关于限定的名字的要点。除了全局名字空间之外名字空间有非空的名字。除了作为名字空间分隔符,不允许 :: 在简单命令、变量、和名字空间名字中使用。在限定的名字中忽略额外的 : ;就是说,两个或更多的 : 被作为一个名字空间分隔符。在一个限定的变量或命令名字中的尾随的 :: 引用叫做 {} 的变量或命令。但是忽略在一个限定的名字空间名中的尾随的 ::。
在下列例子中,
set traceLevel 0
namespace eval Debug {
printTrace $traceLevel
}
Tcl 在名字空间 Debug
中查找 traceLevel
接着在全局名字空间中查找,它以相同的方式查找命令 printTrace。如果一个变量或命令的名字在这两个上下文中都找不到,则这个名字是未定义的。为了使这一点绝对清楚,考虑下列例子:
set traceLevel 0
namespace eval Foo {
variable traceLevel 3
namespace eval Debug {
printTrace $traceLevel
}
}
这里 Tcl 首先在名字空间 Foo::Debug 中查找 traceLevel。因为在这里未找到,Tcl
接着在全局名字空间中查找。在名字解析过程中完全忽略了变量 Foo::traceLevel。你可以使用 namespace which 命令来清除关于名字解析的任何问题:
namespace eval Foo::Debug {namespace which -variable traceLevel}
返回 ::traceLevel。另一方面,命令,
namespace eval Foo {namespace which -variable traceLevel}
返回 ::Foo::traceLevel。如上面提及的那样,查找名字空间名字与变量和命令的名字不同。总是在当前名字空间中解析名字空间名字。这意味除非新名字空间的名字以一个 :: 开始,否则建立一个新名字空间的 namespace eval 命令总是建立当前名字空间的一个子名字空间。
Tcl 对你可以引用的变量、命令、或名字空间没有访问控制。如果你能提供一个限定的名字来通过名字解析规则解析到一个元素,你就可以访问这个元素。
你可以通过 variable 命令从同一个名字空间中的一个过程中访问一个名字空间变量。非常象 global 命令,它建立到名字空间变量的一个本地连接。如果需要,这个命令还在当前的名字空间中建立这个变量并初始化它。注意:global 命令只建立到在全局名字空间中的变量的连接。如果你总是使用一个适当的限定的名字来引用名字空间变量,则使用 variable 命令不是必须的。
Blt::graph .g -background red Blt::table . .g 0,0如果你频繁使用 graph 和 table 命令,你可能希望访问它们而不用加 Blt:: 前缀。你可以通过把它导入到当前名字空间中的方式来达到此目的。例如:
namespace import Blt::*这个例子把从 Blt 名字空间导出的所有命令增加到当前名字空间上下文中,所以你可以象下面这样写代码:
graph .g -background red table . .g 0,0namespace import 命令从一个名字空间导入的命令只能是那个名字空间中用 namespace export 命令导出的命令。
从一个名字空间导入所有命令一般是一个坏主意,因为你不知道你将会得到些什么。更好的方式是只导入你需要的命令。例如,命令
namespace import Blt::graph Blt::table只把 graph 和 table 命令导入到当前上下文。
如果你要导入一个已经存在的命令,你将得到一个错误。这防止你从两个不同的包导入同一个命令。但是有的时候(可能在调试时),你可能希望超越这个限制。你可能希望重新发起(reissue) namespace import 命令来导入(pick up)一个新命令,而同名的命令在这个名字空间中已经出现过了。在这种情况下,你可以使用 -force 选项,现存命令将悄无声息的被覆写(overwritten):
namespace import -force Blt::graph Blt::table如果出于某种原因,你打算停止使用导入的命令,你可以用 namespace forget 命令删除它们,例如:
namespace forget Blt::*它在当前名子空间中查找从 Blt 导入的所有命令,如果找到则删除它们。否则,它什么都不做。此后,访问 Blt 命令必须使用 Blt:: 前缀。
当你从导出(命令的)名字空间删除一个命令的时候,例如:
rename Blt::graph ""则从所有导入它的名字空间中自动删除这个命令。
namespace eval Counter {
namespace export bump reset
variable Num 0
variable Max 100
proc bump {{by 1}} {
variable Num
incr Num $by
Check
return $Num
}
proc reset {} {
variable Num
set Num 0
}
proc Check {} {
variable Num
variable Max
if {$Num > $Max} {
error "too high!"
}
}
}
过程 bump 和 reset 被导出,所以当你从 Counter
名字空间导入的时候,它们被包括在内。例如:
namespace import Counter::*但是 Check 过程未被导出,所以它被导入操作所忽略。
namespace import ,命令只导入被它们的名字空间导出的命令。namespace export 命令指定什么命令可以被其他名字空间导入。如果一个 namespace import 命令指定了一个未被导出的命令,则不导入这个命令。
Copyright © 1993-1997 Bell Labs Innovations for Lucent Technologies Copyright © 1997 Sun Microsystems, Inc. Copyright © 1995-1997 Roger E. Critchlow Jr.