【问题标题】:Python overwriting variables in nested functionsPython覆盖嵌套函数中的变量
【发布时间】:2011-12-17 16:03:54
【问题描述】:

假设我有以下 python 代码:

def outer():
    string = ""
    def inner():
        string = "String was changed by a nested function!"
    inner()
    return string

我希望调用 outer() 以返回“字符串已被嵌套函数更改!”,但我得到了“”。我得出的结论是 Python 认为行 string = "string was changed by a nested function!" 是对 inner() 局部的新变量的声明。我的问题是:我如何告诉 Python 它应该使用 outer() 字符串?我不能使用global 关键字,因为该字符串不是全局的,它只是存在于外部作用域中。想法?

【问题讨论】:

标签: python scope


【解决方案1】:

在 Python 3.x 中,您可以使用 nonlocal 关键字:

def outer():
    string = ""
    def inner():
        nonlocal string
        string = "String was changed by a nested function!"
    inner()
    return string

在 Python 2.x 中,您可以使用包含单个元素的列表并覆盖该单个元素:

def outer():
    string = [""]
    def inner():
        string[0] = "String was changed by a nested function!"
    inner()
    return string[0]

【讨论】:

  • +1 至于为什么后者起作用:项目分配(连同属性分配)不是变量分配 - x[...] = ... 只是从任何范围内获取x,并调用它__setitem__ 方法。一个同样有效但最初更冗长的选项是创建一个对象(属于用户定义的类,因为object() 不允许添加属性),其唯一目的是持有一个属性。
  • 您可以使用x = type("", (), {})() 创建这样的“命名空间对象”。 :)
  • 所以,nonlocal 的意思是:“在外部作用域中查找这个变量”——如果在 outer() 作用域中找不到该变量,Python 会继续在封闭作用域中查找吗? Python 最终会尝试在全局范围内查找变量吗?
  • 另外,真的没有办法在 Python 2.x 中重新绑定 outer() 字符串吗?我可以访问在外部范围中定义的变量,并且能够调用它的方法,但不能重新分配变量以指向其他东西,这似乎很愚蠢......
  • @Ord: nonlocal 将查看除全局范围之外的所有封闭范围——毕竟这就是 global 的用途。除了上述 Python 2.x 中的解决方案之外,别无他法——这就是引入 nonlocal 的原因。 Python 语义表明,函数内部的赋值会自动使变量成为该函数的局部变量——这是一个明智的选择,因为您不在 Python 中声明变量并且编译器必须以某种方式确定变量的范围。
【解决方案2】:

你也可以通过使用函数属性来解决这个问题:

def outer():
    def inner():
        inner.string = "String was changed by a nested function!"
    inner.string = ""
    inner()
    return inner.string

澄清:这适用于 python 2.x 和 3.x。

【讨论】:

    【解决方案3】:

    这种情况经常发生在我身上,当我正在编写一个函数时,我突然意识到拥有一个较小的辅助函数可能是个好主意,但在其他任何地方都没有真正的用处。这自然让我想在内部将其定义为嵌套函数。

    但我有使用 JAVA 匿名对象的经验(即:定义一个可运行对象),并且规则是匿名对象制作其外部环境的硬拷贝,在这种情况下是外部范围的变量。因此,如果外部变量是不可变的 (int,char),它们不能被匿名对象修改,因为它们是由 value 复制的,而如果它是可变的 (collection, @ 987654324@),它们是可以改变的……因为它们是由“pointer”(它们在内存中的地址)复制的

    如果您了解编程,请将其视为按值传递和按引用传递。

    在 python 中,它非常相似。 x=123 是赋值,它们赋予变量 x 新的含义(不是修改旧的 x),list[i]/dict[key] 是对象访问操作,它们真正修改东西

    总之,你需要一个可变对象...为了修改(即使你可以使用 [] 访问一个元组,你不能在这里使用它,因为它不可变)

    【讨论】:

      【解决方案4】:

      添加到Sven's answer

      在 Python 2.x 中,您只能从内部范围读取外部范围变量。分配只会创建一个新的本地(即内部范围)变量,该变量隐藏外部范围。

      如果您想读取和修改,可以使用dict 将变量保存在外部作用域中,然后通过内部作用域中的 dict 访问它们,同时保留您的代码在存在多个外部范围变量的情况下相当干净且可读

      def outer():
          # hold some text, plus the number of spaces in the text
          vars = {'text': 'Some text.', 'num_spaces': 1}
          def inner():
              # add some more text
              more_text = ' Then some more text.'
              vars['text'] += more_text
              # keep track of the number of spaces
              vars['num_spaces'] += more_text.count(' ')
          inner()
          return vars['text'], vars['num_spaces']
      

      输出:

      >>> outer()
      ('Some text. Then some more text.', 5)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-22
        • 1970-01-01
        • 1970-01-01
        • 2018-10-21
        • 2018-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多