Python:为什么全局只需要赋值而不是读取?

Ash*_*ppa 35 python global

如果函数需要修改在全局范围内声明的变量,则需要使用全局声明.但是,如果函数只需要读取全局变量,它可以在不使用全局声明的情况下执行此操作:

X = 10
def foo():
    global X
    X = 20 # Needs global declaration
def bar():
    print( X ) # Does not need global
Run Code Online (Sandbox Code Playgroud)

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

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

Ray*_*ger 27

使用嵌套作用域,变量查找很容易.它们出现在从本地开始的链中,通过封闭defs,到模块全局变量,然后是内置变量.规则是找到胜利的第一场比赛.因此,您不需要查找"全局"声明.

相反,对于写入,您需要指定要写入的范围.否则无法确定函数中的"x = 10"是否意味着"写入本地命名空间"或"写入全局命名空间".

执行摘要,写入您可以选择命名空间,但查找第一个找到的规则就足够了.希望这可以帮助 :-)

编辑:是的,就是这种方式"因为BDFL这么说",但是在没有类型声明的其他语言中,有一个首先找到的查找规则并且只需要一个非本地写入的修饰符.当你考虑它时,这两个规则导致非常干净的代码,因为范围修饰符只在最不常见的情况下需要(非本地写入).

  • LEGB +1,我总觉得它容易记住 (2认同)

Ned*_*der 17

看看这段代码:

from module import function

def foo(x):
    return function(x)
Run Code Online (Sandbox Code Playgroud)

function这里的名字是全球性的.如果我不得不说global function让这段代码工作,那将会非常繁琐.

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

  • 实际上,这就是问题标题.真正的问题是:_为什么Python设计为允许读取全局变量而不使用全局声明,但不能修改?_ (6认同)
  • 但这并没有真正回答这个问题.您可以将此参数扩展为修改全局变量.为什么显式全局修改全局变量? (4认同)
  • @jterrace:问题是"为什么全球需要*只在*任务?",不是,"为什么全局需要转让." (4认同)
  • 因为如果你不包含`global X`,语句`X = 10`会创建一个绑定到'10`的本地`X`,而不是将全局`X`重新绑定到'10`. (3认同)

Ped*_*eck 16

因为显性优于隐式.

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

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

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

  • 作为一个并不特别适合答案的旁注,我已经专业地用Python编程了8年,我从来没有**用于全球任何事情. (6认同)

Ben*_*Ben 6

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

一个选项(可能实际上被许多旧版本的Python使用,IIRC)只是说写入总是转到本地范围.然后就不需要global关键字,也没有歧义.但是你根本不能写入全局变量(不使用像globals()往返一样的东西),所以这不会很好.

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

另一种选择是x = 3仅在某个具有名称的外部作用域中没有名称时才分配给局部变量x.似乎它会直观地做正确的事情?这会导致一些严重恶劣的角落案件.目前,x = 3写入的位置由解析器静态确定; 要么global x在相同的范围内没有,这是一个本地写,或者有一个global x,它是一个全局写.但是如果它将做什么取决于全局模块范围,则必须等到运行时才能确定写入的位置,这意味着它可以在函数的调用之间进行更改.考虑一下.每次在模块中创建全局时,都会改变模块中所有函数的行为,这些函数恰好使用该名称作为局部变量名.做一些tmp用作临时变量的模块范围计算,并告别在模块tmp中的所有函数中使用.我不禁想到隐藏的错误,包括在您导入的模块上分配属性,然后从该模块调用函数.哎呀.

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


分配存在歧义,需要一些机制来解决它.global就是这样一种机制.它不是唯一可能的,但在Python的背景下,似乎所有替代机制都是可怕的.我不知道你正在寻找什么样的"更好的理由".