43

如果函数需要修改全局范围内声明的变量,则需要使用全局声明。但是,如果函数只需要读取一个全局变量,它可以不使用全局声明来完成:

X = 10
def foo():
    global X
    X = 20 # Needs global declaration
def bar():
    print( X ) # Does not need global

我的问题是关于 Python 的设计:为什么 Python 设计为允许在不使用全局声明的情况下读取全局变量?也就是说,为什么只强制赋值具有全局,为什么不强制全局读取呢?(这会使它变得均匀而优雅。)

注意:我可以看到在阅读时没有歧义,但在分配时不清楚是否打算创建一个新的局部变量或分配给全局变量。但是,我希望 BDFL 这种不均衡的设计选择有更好的理由或意图。

4

4 回答 4

33

使用嵌套范围,变量查找很容易。它们出现在一个链中,从本地开始,通过封闭 defs,到模块全局,然后是内置。规则是找到的第一个匹配获胜。因此,查找时不需要“全局”声明。

相反,对于写入,您需要指定要写入的范围。否则无法确定函数中的“x = 10”是表示“写入本地名称空间”还是“写入全局名称空间”。

执行摘要,通过 write 您可以选择命名空间,但通过查找,首先找到的规则就足够了。希望这可以帮助 :-)

编辑:是的,是这样的“因为 BDFL 是这么说的”,但是在没有类型声明的其他语言中,对于查找有一个首先找到的规则并且只需要一个非本地写入的修饰符并不罕见。当您考虑它时,这两个规则会导致代码非常干净,因为仅在最不常见的情况下(非本地写入)才需要范围修饰符。

于 2012-04-28T03:31:22.457 回答
21

看看这段代码:

from module import function

def foo(x):
    return function(x)

这里的名字function是全局的。如果我不得不说global function让这段代码工作,那将变得非常乏味。

在你说 yourX和 myfunction不同之前(因为一个是变量,另一个是导入函数),请记住 Python 中的所有名称都被视为相同:使用时,它们的值在作用域层次结构中查找。如果你需要,global X那么你需要global function. 伊克。

于 2012-04-28T03:12:20.767 回答
21

因为显式优于隐式。

读取变量时没有歧义。从本地到全局搜索范围时,您总是会找到第一个。

当您分配时,解释器可能会明确假设您分配的范围只有两个:本地和全局。由于分配给本地是最常见的情况,实际上不鼓励分配给全局,因此这是默认设置。要分配给全局,您必须明确地执行它,告诉解释器无论您在此范围内使用该变量的任何位置,它都应该直接进入全局范围并且您知道自己在做什么。在 Python 3 上,您还可以使用“非本地”分配给最近的封闭范围。

请记住,当您在 Python 中分配一个名称时,这个新的分配与以前分配给其他东西的名称无关。想象一下,如果本地没有默认值,并且 Python 搜索所有范围,试图找到具有该名称的变量并像读取时那样分配给它。您的函数的行为不仅会根据您的参数,而且会根据封闭范围而改变。生活会很悲惨。

于 2012-04-28T03:42:44.850 回答
7

您自己说,读取没有歧义,而写入则有歧义。因此,您需要一些机制来解决写入的歧义。

一种选择(可能实际上被更旧版本的 Python,IIRC 使用)只是说写入总是进入本地范围。那么就不需要global关键字,也没有歧义。但是,您根本无法写入全局变量(不使用诸如globals()以迂回方式获取它们的东西),所以这不会很好。

静态声明变量的语言使用的另一个选项是预先与每个范围的语言实现进行通信,哪些名称是本地的(您在该范围中声明的名称)以及哪些名称是全局的(在模块范围内声明的名称) . 但是 Python 没有声明变量,所以这个解决方案不起作用。

另一种选择是x = 3仅在具有 name 的某个外部范围内没有名称时才分配给局部变量x。似乎它会直观地做正确的事情?不过,这会导致一些严重讨厌的极端情况。目前,x = 3将写入的位置由解析器静态确定;要么没有global x在同一范围内,它是一个本地写入,要么有一个global x,它是一个全局写入。但是,如果它将做什么取决于全局模块范围,则您必须等到运行时才能确定写入的位置,这意味着它可以在函数的调用之间发生变化. 考虑一下。每次在模块中创建全局变量时,都会更改模块中恰好使用该名称作为局部变量名称的所有函数的行为。做一些作为临时变量的模块范围计算,tmp并告别在模块tmp中的所有函数中使用。而且我不寒而栗地想到涉及在您导入的模块上分配属性然后从该模块调用函数的晦涩错误。

另一种选择是与每个分配的语言实现进行通信,无论它应该是本地的还是全局的。这就是 Python 所做的。鉴于有一个涵盖几乎所有情况的合理默认值(写入局部变量),我们将局部赋值作为默认值,并用global.


分配存在歧义,需要某种机制来解决它。global就是这样一种机制。这不是唯一可能的,但在 Python 的上下文中,似乎所有替代机制都是可怕的。我不知道你在寻找什么样的“更好的理由”。

于 2012-04-28T04:04:27.320 回答