【问题标题】:Python: Why is global needed only on assignment and not on reads?Python:为什么只在赋值而不是读取时才需要全局?
【发布时间】:2012-04-28 03:07:08
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: python global


    【解决方案1】:

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

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

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

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

    【讨论】:

      【解决方案2】:

      因为显式优于隐式。

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

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

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

      【讨论】:

      • Pedro:如果你正在编写一个简单的脚本,它的功能需要修改全局状态,你会怎么做?如果类对于简单的脚本来说太重了怎么办?
      【解决方案3】:

      看看这段代码:

      from module import function
      
      def foo(x):
          return function(x)
      

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

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

      【讨论】:

      • 但这并不能真正回答这个问题。您也可以将此参数扩展到修改全局变量。为什么要使用显式全局变量来修改全局变量?
      • 因为如果你不包含global X 语句X = 10 将创建一个本地X 绑定到10,而不是重新绑定全局X10。跨度>
      • @jterrace:问题是“为什么在分配时需要全局?”而不是“为什么在分配时需要全局”。
      • 其实这就是问题的标题。真正的问题是:为什么 Python 设计为允许读取全局变量而不使用全局声明,但不允许修改?
      • Predro:内德正在回答我的实际问题。如果我把读者和我的英语混淆了,我很抱歉。我现在也更正了问题文本。
      【解决方案4】:

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

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

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

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

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


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

      【讨论】:

      • Ben:这里是 Python 新手。除了源代码中已经在全局范围内列出的变量之外,是否可以在运行时“注入”一个新的全局变量?您的回答似乎表明了这一点,我想知道这是怎么可能的。
      • @Ashwin 全局范围与任何其他范围没有任何不同。它包含的内容没有单一的静态声明; Python 只是简单地执行模块中的代码,因此在全局范围内分配名称。这与在本地范围内定义名称的方式完全相同。最重要的是,任何其他引用该模块的代码(在导入它之后)都可以为其分配属性,并且模块属性只是该模块中的全局变量。模块中的Plus函数可以使用global。你指的是那种东西吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-04
      • 1970-01-01
      • 2019-07-24
      相关资源
      最近更新 更多