Python 变量作用域

2 投票
3 回答
628 浏览
提问于 2025-04-15 13:54

我刚开始学习Python,现在正在阅读别人写的一个脚本。我发现全局变量到处都是(我不太喜欢这样)。除此之外,我还注意到当我有这样的代码时

def some_function():
    foo.some_method()
    # some other code

if __name__ == '__main__' :
    foo = Some_Object()

    some_function()

即使我没有把foo传递给some_function(),但some_function仍然可以操作foo(??!)。我对此不太喜欢,虽然这有点像JavaScript中的闭包(?)。我想知道是否可以阻止some_function()访问foo,如果foo没有作为函数参数传入的话?还是说这是Python中推荐的做法呢??!(我现在在Ubuntu Hardy上使用Python 2.5)

3 个回答

1

在Python中,像foo这样的东西并不是一个值,而是一个名字。当你试图访问它时,Python会尝试找到与这个名字相关联的值(就像解引用一个指针一样)。它首先会在本地作用域(也就是函数内部)查找,然后逐层向外查找,直到找到模块作用域(也就是全局作用域),最后再查找内置的内容,直到找到一个匹配这个名字的东西。

这就是为什么下面的代码可以正常工作的原因:

def foo():
    bar()

def bar():
    pass

尽管在你定义foo的时候bar并不存在,但这个函数仍然可以正常工作,因为你后来在一个包含foo的作用域中定义了bar

你发布的代码中发生的事情也是一样的,只不过fooSome_Object()的输出,而不是一个函数定义。

正如Alex所说,你可以写出这样的代码,并不意味着你应该这样做。

2

据我所知,阻止 some_function 访问 foo 的唯一方法就是把 foo 这个变量从 some_function 的作用域中去掉,可能像这样:

tmp = foo
del foo
some_function()
foo = tmp

当然,这样做会导致你当前的代码崩溃,因为 foosome_function 的作用域中已经不存在了。

在 Python 中,变量的查找是先在本地进行,然后往上查找作用域,直到全局范围,最后再查找内置变量。

另外一个选择 可能 是:

with some_object as foo:
    some_function()

但是这样的话,你至少需要声明 some_object.__exit__,可能还需要 some_object.__enter__。最终的结果是你可以控制在 some_function 的作用域中使用哪个 foo

关于 "with" 语句的更多解释可以在 这里 找到。

4

这个脚本在风格和组织上有很严重的问题——比如说,如果有人想用它,他们得自己猜出在调用 some_function 之前,得把 thescript.foo 设置成一个 Some_Object 的实例……真让人无奈!

很遗憾你是通过一个写得不好的脚本来学习 Python,但我不太明白你的问题。Python 中的变量作用域分为本地(包括参数)、非本地(也就是嵌套函数的外层函数的本地变量)、全局和内置变量。

你想要阻止访问全局变量吗? some_function.func_globals 是只读的,但你可以创建一个新的函数,使用空的全局变量:

import new
f=new.function(some_function.func_code, {})

这样调用 f() 时就会抛出一个异常 NameError: global name 'foo' is not defined。你可以在模块中用 some_function 的名字把这个设置回来,或者通过装饰器系统地做到这一点,例如:

def noglobal(f):
    return new.function(f.func_code, {})
...
@noglobal
def some_function(): ...

这样每次调用 some_function 时都会确保抛出异常。不过,我不太清楚你希望从中得到什么好处,也许你可以再解释一下……?

撰写回答