TCL脚本语言-10-名字空间
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
作者:雷雨后 Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
set m_count 0 } ; }{ #! ! !这里应该有分号,否则会出错! ! !
116
#这里定义过程 Increase,增加计数器,默认参数为 1 proc Increase { {m 1} } { variable m_count incr m_count $m } } #名字空间已经 Counter 存在,继续在 Counter 中执行如下代码 namespace eval Counter { #定义过程 GetCounter,返回得到计数器的实际值 proc GetCounter {} { variable m_count return $m_count } #定义子名字空间 Operator,里面定义了一个函数 Print namespace eval Operator { proc Print {} { variable ::Counter::m_count puts "The counter is $m_count" ;#打印计数器当前值 } } } #下面是测试代码,增加计数器技术,然后打印计数器的值 Counter::Reset for {set i 0} {$i<10} {incr i} { Counter::Increase } puts [Counter::GetCounter] ::Counter::Operator::Print ;#直接打印计数器的值 ;#调用内部定义过程来打印
创建名字空间
TCL 中没有 namespace create xxx 这样的命令来创建名字空间, 使用的是另外一个命令: namespce eval,其语法如下: namespace eval namespace arg ?arg? 其中第二个参数 namespace 是名字空间的名字;后面的参数是需要在这个名字空间中执 行的代码,如果 arg 参数有多个,那么命令会先把它们用空格连接起来组装成一个字符串。 该命令在名字空间 namespace 中执行后面的代码,如果参数 namespace 指定的名字空间还不 存在,那么就会先创建这个名字空间。 下面是一个简单的名字空间,实现一个计数器以及相关的操作: #―――――――――――――――――――――――――――― #创建名字空间 Counter namespace eval Counter { namespace export * variable m_count 0 ;#定义名字空间内部变量 #定义过程 Reset,将计数器复位 proc Reset {} { variable m_count
;#直接引用该全局变量
名字和名字解析
前面我们看到名字空间是可以嵌套的,就好比 Unix 操作系统中的文件系统中的目录结 构一样。Unix 中表示目录的方式是用“/”作为分隔,TCL 中表示嵌套名字空间的方式比较 类似,只不过分隔符号换成了“::” ,两个连续的冒号。 每一个名字空间都必须有一个名字来标识,除了全局名字空间(global namespace) ,它 只需要用两个冒号(::)就行了,就好比文件系统根目录用“/”表示。例如,set 命令是标 准的 TCL 命令,存在于全局名字空间中,我们可以这样使用:
作者:雷雨后 Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
118
种用法! 读者可能会问,如果我使用的变量是数组,应该如何给其赋初始值?可不可以象下面这 样写? variable m_days {1 Mon 2 Tru 3 Wed} 回答是可以这样下,但是结果不是我们要的,m_days 是一个列表!正确的方法是:使 用 variable 来声明变量, 省略掉后面的 value 参数, 然后调用 arrar set 等方法来为数组变量赋 值?例如: variable m_days array set m_days {1 Mon 2 Tru 3 Wed } 还有读者会问:过程中调用 variable 关联变量的时候,这个变量必须存在吗?必须是用 variable 声明并且创建的吗?两个问题的答案都是否定的。例如下面的代码: proc t {} { variable m 100 ;#定义全局变量,初值为 100 puts "In procedure t : $m" } t ;#调用该过程 puts "Global variable m = $m" 上面代码执行如下: In procedure t : 100 Global variable m = 100 variable 命令使用很灵活,常见用法,总结一下: 1. 在名字空间中创建全局变量,并且为其赋值! 2. 在名字空间中的过程中调用 variable,可以象局部变量一样引用该变量! 总之,variable 使用非常灵活,我们要求把握的是该命令的本质!
作者:雷雨后 Email: leiyuhou010@gmail.com
TCL、Python 和软件测来自百度文库自动化
::set myage 23
119
不过这样总有画蛇添足的嫌疑。 例如前面代码中,为了在全局名字空间中调用 Print 过程,我们写出如下的代码: ::Counter::Operator::Print 如果我们直接写 Print 来调用,TCL 解释器是无法找到这个过程的。同样,如果需要在 全局名字空间中引用 Counter 中的变量 m_counter,也需要这样写: puts $::Counter::m_counter 名字空间中变量名和过程名的解析,有一定的规则。如果它们有名字空间来修饰,那么 就按照指定的名字空间来定位寻找,否则,先在当前名字空间中寻找,如果没有找到,就到 全局名字空间中寻找,如果还没有找到,那么就报告错误。下面是一个嗲行的例子: set m “Global ::m” ;#全局的变量 m
TCL、Python 和软件测试自动化
115
名字空间
TCL 为什么要引入名字空间的概念呢?这是因为随着程序规模的增加, 参与代码编写的 人员数量的增加,就有可能在全局变量名字,过程名字的命名上产生重复和冲突。对于 C 这样的语言还好,编译器会报告重复定义的编译错误或者链接错误;对于 TCL 而言,命令 是可以重复定义的,这样一旦程序运行产生错误,查找起来将会非常的困难。 可能有人会说了,脚本语言开发程序,基本上都是我一个人搞定,怎么可能会有很多人 参与呢?不要忘了,TCL 是与网络紧密结合的、开放源代码的语言,在网络上存在无数的人 来为 TCL 开发各种各样的程序库和扩展包,他们互相之间既有可能根本不认识,所以命令 同名是在太有可能发生了,比如象 Create、Initial 和 Close 等等这些都要被用滥了的名字。 为了解决这些问题,名字空间应运而生。名字空间 namespace,顾名思义,可以理解成 有名字的空间,它包含了那些属于该空间的变量名和过程名。不同名字空间里面,即使包含 同名的命令,TCL 解释器也能够区分它们,看待成不同的命令。 TCL 中所有和名字空间相关的命令只有一个,就是 namespace,命令的第一个参数决定 了完成什么功能,下面我们逐一介绍。
;#这是过程 a::print
proc print {args} { puts "I am a::b::print . Value is : $args" } variable m “m in ::a::b” ;#定义变量::a::b::m print $m ;#再次打印变量 m } } 上面的代码执行结果如下:
第一个 namespace eval 命令带有三个参数:名字为 Counter,剩下的两个参数是两块代 码, 用来在 Counter 之中执行, 分别定义了两个过程 Reset 和 Increase。 因为该 namesapce eval
作者:雷雨后 Email: leiyuhou010@gmail.com
proc print {args} { puts "I am ::print . Value is : $args" } namespace eval a { set m “ m in ::a”
;#全局的过程 print
;创建::a::m 变量,也是全局,但是属于 a
proc print {args} { puts "I am a::print .Value is : $args" } namespace eval b { ;#定义名字空间 b print $m ;#直接打印变量 m
TCL、Python 和软件测试自动化
117
执行的时候 Counter 还不存在, 所以就创建了该名字空间。 紧接着后面的一个 namespace eval 命令,用来在名字空间 Counter 中创建过程 GetCounter。再往下就是测试代码,用来检查一 下这个计数器名字空间是否正常工作。最后的打印结果是“10” 细致的读者可能会发现,过程 Reset 定义结束后,后面跟了一个分号,有什么用处?分 号的用处在于,如果后面有多个语句块,那么在将它们连接成一个字符串之后,不会因为没 有命令分隔而出现错误。一般情况下我们不赞成象上面的代码那样后面跟多个 arg 参数,而 是把所有的代码全部放到一对花括号之中。 名字空间是可以嵌套的,一个名字空间可以包含多个孩子名字空间,例如上面的代码就 定义了嵌套的名字空间 Operator,并且在其中定义了过程 Print。 上面的代码中还出现了一个我们没有见过的命令 variable,下面我们详细讨论:
variable 命令
variable 一般用在 namespace eval 的语句块中,定义该名字空间内部的全局变量。这里 要区分全局和局部的概念:局部(local)表示在过程内部生成的变量,过程外部的变量都是 全局(global)的变量。但是在名字空间出现后,也存在一个全局的概念,这就是全局名字 空间,比如 TCL 内建的命令 set 等命令都属于全局名字空间。这里不要搞混淆了。 每一个名字空间,都可以有自己的全局变量,variable 就可以用来定义以及引用这些全 局变量。该命令的语法如下: variable ?name value…? name ?value? 命令 variable 执行的时候,如果 name 指定的变量还不存在,那么就创建这个变量。在 这种情况下,如果指定了 value,那么就给变量赋值为 value,否则变量处于未定义状态;如 果变量已经存在,那么当 value 参数有的时候,就直接给该变量赋值,否则变量值保持不变。 如果 variable 是在某一个 TCL 过程中被调用, 那么就会创建一个同名的局部变量, 和对 应的全局变量关联起来。在这种情况下,variable 命令和 global 非常的类似,它们的差别在 于:global 关联的是全局名字空间(::)中的全局变量,而 varialbe 关联的是命令所在名字空 间的全局变量。 例如上面的 Counter 中,就有如下两种语句: 1. variable m_counter 0 2. variable m_counter 第一个我们是在 Counter 中任何过程外面调用的,并且给出了初始值,那么该语句就在 Counter 中创建了一个全局变量 m_counter 并且初始值未 0;第二个则是在 Counter 内部定义 的过程中调用的,没有给出 value 参数,那么它的功能就是在过程内部创建一个同名的局部 变量并且和全局变量关联起来,然后我们就可以象使用局部变量一样使用全局变量。 variable 命令的 name 参数可以带有名字空间来修饰,例如上面代码中的 Print 过程,这 个过程是在::Counter::Operator 中定义的, 其内部就使用了 variable ::Counter::m_counter 语句, 用来声明一个局部变量 m_counter,和其他名字空间中的变量关联起来。我们一般不推荐这
TCL、Python 和软件测试自动化
set m_count 0 } ; }{ #! ! !这里应该有分号,否则会出错! ! !
116
#这里定义过程 Increase,增加计数器,默认参数为 1 proc Increase { {m 1} } { variable m_count incr m_count $m } } #名字空间已经 Counter 存在,继续在 Counter 中执行如下代码 namespace eval Counter { #定义过程 GetCounter,返回得到计数器的实际值 proc GetCounter {} { variable m_count return $m_count } #定义子名字空间 Operator,里面定义了一个函数 Print namespace eval Operator { proc Print {} { variable ::Counter::m_count puts "The counter is $m_count" ;#打印计数器当前值 } } } #下面是测试代码,增加计数器技术,然后打印计数器的值 Counter::Reset for {set i 0} {$i<10} {incr i} { Counter::Increase } puts [Counter::GetCounter] ::Counter::Operator::Print ;#直接打印计数器的值 ;#调用内部定义过程来打印
创建名字空间
TCL 中没有 namespace create xxx 这样的命令来创建名字空间, 使用的是另外一个命令: namespce eval,其语法如下: namespace eval namespace arg ?arg? 其中第二个参数 namespace 是名字空间的名字;后面的参数是需要在这个名字空间中执 行的代码,如果 arg 参数有多个,那么命令会先把它们用空格连接起来组装成一个字符串。 该命令在名字空间 namespace 中执行后面的代码,如果参数 namespace 指定的名字空间还不 存在,那么就会先创建这个名字空间。 下面是一个简单的名字空间,实现一个计数器以及相关的操作: #―――――――――――――――――――――――――――― #创建名字空间 Counter namespace eval Counter { namespace export * variable m_count 0 ;#定义名字空间内部变量 #定义过程 Reset,将计数器复位 proc Reset {} { variable m_count
;#直接引用该全局变量
名字和名字解析
前面我们看到名字空间是可以嵌套的,就好比 Unix 操作系统中的文件系统中的目录结 构一样。Unix 中表示目录的方式是用“/”作为分隔,TCL 中表示嵌套名字空间的方式比较 类似,只不过分隔符号换成了“::” ,两个连续的冒号。 每一个名字空间都必须有一个名字来标识,除了全局名字空间(global namespace) ,它 只需要用两个冒号(::)就行了,就好比文件系统根目录用“/”表示。例如,set 命令是标 准的 TCL 命令,存在于全局名字空间中,我们可以这样使用:
作者:雷雨后 Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
118
种用法! 读者可能会问,如果我使用的变量是数组,应该如何给其赋初始值?可不可以象下面这 样写? variable m_days {1 Mon 2 Tru 3 Wed} 回答是可以这样下,但是结果不是我们要的,m_days 是一个列表!正确的方法是:使 用 variable 来声明变量, 省略掉后面的 value 参数, 然后调用 arrar set 等方法来为数组变量赋 值?例如: variable m_days array set m_days {1 Mon 2 Tru 3 Wed } 还有读者会问:过程中调用 variable 关联变量的时候,这个变量必须存在吗?必须是用 variable 声明并且创建的吗?两个问题的答案都是否定的。例如下面的代码: proc t {} { variable m 100 ;#定义全局变量,初值为 100 puts "In procedure t : $m" } t ;#调用该过程 puts "Global variable m = $m" 上面代码执行如下: In procedure t : 100 Global variable m = 100 variable 命令使用很灵活,常见用法,总结一下: 1. 在名字空间中创建全局变量,并且为其赋值! 2. 在名字空间中的过程中调用 variable,可以象局部变量一样引用该变量! 总之,variable 使用非常灵活,我们要求把握的是该命令的本质!
作者:雷雨后 Email: leiyuhou010@gmail.com
TCL、Python 和软件测来自百度文库自动化
::set myage 23
119
不过这样总有画蛇添足的嫌疑。 例如前面代码中,为了在全局名字空间中调用 Print 过程,我们写出如下的代码: ::Counter::Operator::Print 如果我们直接写 Print 来调用,TCL 解释器是无法找到这个过程的。同样,如果需要在 全局名字空间中引用 Counter 中的变量 m_counter,也需要这样写: puts $::Counter::m_counter 名字空间中变量名和过程名的解析,有一定的规则。如果它们有名字空间来修饰,那么 就按照指定的名字空间来定位寻找,否则,先在当前名字空间中寻找,如果没有找到,就到 全局名字空间中寻找,如果还没有找到,那么就报告错误。下面是一个嗲行的例子: set m “Global ::m” ;#全局的变量 m
TCL、Python 和软件测试自动化
115
名字空间
TCL 为什么要引入名字空间的概念呢?这是因为随着程序规模的增加, 参与代码编写的 人员数量的增加,就有可能在全局变量名字,过程名字的命名上产生重复和冲突。对于 C 这样的语言还好,编译器会报告重复定义的编译错误或者链接错误;对于 TCL 而言,命令 是可以重复定义的,这样一旦程序运行产生错误,查找起来将会非常的困难。 可能有人会说了,脚本语言开发程序,基本上都是我一个人搞定,怎么可能会有很多人 参与呢?不要忘了,TCL 是与网络紧密结合的、开放源代码的语言,在网络上存在无数的人 来为 TCL 开发各种各样的程序库和扩展包,他们互相之间既有可能根本不认识,所以命令 同名是在太有可能发生了,比如象 Create、Initial 和 Close 等等这些都要被用滥了的名字。 为了解决这些问题,名字空间应运而生。名字空间 namespace,顾名思义,可以理解成 有名字的空间,它包含了那些属于该空间的变量名和过程名。不同名字空间里面,即使包含 同名的命令,TCL 解释器也能够区分它们,看待成不同的命令。 TCL 中所有和名字空间相关的命令只有一个,就是 namespace,命令的第一个参数决定 了完成什么功能,下面我们逐一介绍。
;#这是过程 a::print
proc print {args} { puts "I am a::b::print . Value is : $args" } variable m “m in ::a::b” ;#定义变量::a::b::m print $m ;#再次打印变量 m } } 上面的代码执行结果如下:
第一个 namespace eval 命令带有三个参数:名字为 Counter,剩下的两个参数是两块代 码, 用来在 Counter 之中执行, 分别定义了两个过程 Reset 和 Increase。 因为该 namesapce eval
作者:雷雨后 Email: leiyuhou010@gmail.com
proc print {args} { puts "I am ::print . Value is : $args" } namespace eval a { set m “ m in ::a”
;#全局的过程 print
;创建::a::m 变量,也是全局,但是属于 a
proc print {args} { puts "I am a::print .Value is : $args" } namespace eval b { ;#定义名字空间 b print $m ;#直接打印变量 m
TCL、Python 和软件测试自动化
117
执行的时候 Counter 还不存在, 所以就创建了该名字空间。 紧接着后面的一个 namespace eval 命令,用来在名字空间 Counter 中创建过程 GetCounter。再往下就是测试代码,用来检查一 下这个计数器名字空间是否正常工作。最后的打印结果是“10” 细致的读者可能会发现,过程 Reset 定义结束后,后面跟了一个分号,有什么用处?分 号的用处在于,如果后面有多个语句块,那么在将它们连接成一个字符串之后,不会因为没 有命令分隔而出现错误。一般情况下我们不赞成象上面的代码那样后面跟多个 arg 参数,而 是把所有的代码全部放到一对花括号之中。 名字空间是可以嵌套的,一个名字空间可以包含多个孩子名字空间,例如上面的代码就 定义了嵌套的名字空间 Operator,并且在其中定义了过程 Print。 上面的代码中还出现了一个我们没有见过的命令 variable,下面我们详细讨论:
variable 命令
variable 一般用在 namespace eval 的语句块中,定义该名字空间内部的全局变量。这里 要区分全局和局部的概念:局部(local)表示在过程内部生成的变量,过程外部的变量都是 全局(global)的变量。但是在名字空间出现后,也存在一个全局的概念,这就是全局名字 空间,比如 TCL 内建的命令 set 等命令都属于全局名字空间。这里不要搞混淆了。 每一个名字空间,都可以有自己的全局变量,variable 就可以用来定义以及引用这些全 局变量。该命令的语法如下: variable ?name value…? name ?value? 命令 variable 执行的时候,如果 name 指定的变量还不存在,那么就创建这个变量。在 这种情况下,如果指定了 value,那么就给变量赋值为 value,否则变量处于未定义状态;如 果变量已经存在,那么当 value 参数有的时候,就直接给该变量赋值,否则变量值保持不变。 如果 variable 是在某一个 TCL 过程中被调用, 那么就会创建一个同名的局部变量, 和对 应的全局变量关联起来。在这种情况下,variable 命令和 global 非常的类似,它们的差别在 于:global 关联的是全局名字空间(::)中的全局变量,而 varialbe 关联的是命令所在名字空 间的全局变量。 例如上面的 Counter 中,就有如下两种语句: 1. variable m_counter 0 2. variable m_counter 第一个我们是在 Counter 中任何过程外面调用的,并且给出了初始值,那么该语句就在 Counter 中创建了一个全局变量 m_counter 并且初始值未 0;第二个则是在 Counter 内部定义 的过程中调用的,没有给出 value 参数,那么它的功能就是在过程内部创建一个同名的局部 变量并且和全局变量关联起来,然后我们就可以象使用局部变量一样使用全局变量。 variable 命令的 name 参数可以带有名字空间来修饰,例如上面代码中的 Print 过程,这 个过程是在::Counter::Operator 中定义的, 其内部就使用了 variable ::Counter::m_counter 语句, 用来声明一个局部变量 m_counter,和其他名字空间中的变量关联起来。我们一般不推荐这