Scope (3)
入门。
这是一段很简单的 Bash 脚本。x 初始值为 0,foo
打印出 x 的值,bar 将 x 设为 100 并调用 foo。
x=0
foo() {
echo "foo: $x"
}
bar() {
echo -n "bar: "
local x=100;
foo
}
foo
bar
foo
结果不出意外,foo 打印出 0,而 bar 打印出 100
foo: 0
bar: foo: 100
foo: 0
如果把 bar 里的 local x=100; 的 local 去掉,那全局的 x 的值会被改写,打印的结果就是
foo: 0
bar: foo: 100
foo: 100
不难理解吧?
放弃?
如果把这段代码翻译成 Python 呢?
x = 0
def foo():
print("foo: %d" % x)
def bar():
print("bar: ", end="")
x = 100
foo()
foo()
bar()
foo()
运行的结果编程了:
foo: 0
bar: foo: 0
foo: 0
这跟两次 Bash 运行的结果都不一样,foo 打印出的 x 的值为什么始终为 0?
进阶!
Scope 分两种,一种是静态的,叫 static scope 或 lexical scope,另一种是动态的,叫 dynamic scope。所谓的“静态”,就是在编译的时候就已经确定了的,而“动态”是指运行的时候再决定变量的值。Python 和大部分现代语言都采用 lexical scope,而 bash, LISP 等语言采用 dynamic scope,此外 Perl 两种都支持。
以此为例,Python 代码在编译的时候就将 foo 中的 x 绑定到全局变量 x(所谓的 early binding),所以无论 foo 何时被调用,打印出来的都是 0. 一个小实验是把最后函数调用的部分改成如下:
foo()
x = 999
foo()
bar()
foo()
结果是:
foo: 0
foo: 999
bar: foo: 999
foo: 999
可见全局变量 x 改变了,所有的 foo 的打印值都变了。
而在 Bash 的代码中,编译时 x 的值并没有被绑定,(所以也叫 late binding),当 foo 第一次被调用的时候,它才去找 x 的定义,所以采用了 x=0;当 bar 被调用的时候,foo 找到的是 bar 里定义的 x=100,所以打印出来 100;如果没用 local 关键字,那全局的 x 被改动,当下一次 foo 被直接调用的时候,它还是找到了全局变量 x,打印出来就是被更新后的值 100.