【问题标题】:Why does global keyword required to access variable defined in outer scope?为什么访问外部范围中定义的变量需要全局关键字?
【发布时间】:2019-12-08 18:20:40
【问题描述】:

另一个问题没有解释为什么我不能在示例 (2) 中不使用“全局”的情况下更改变量 time_verf,但仍然可以对示例 (4) 中的列表执行此操作。

On the resource我发现我无法从函数内部更改全局变量,这些示例清楚地说明了这一点:

from datetime import datetime, timedelta
time_verf = datetime.now()

我想我明白为什么以下内容有效 (1):

def check_global():
    global time_verf
    clean_list = time_verf + timedelta(hours=12)  # время очистки листа
    if clean_list < datetime.now():
        list_verf.clear()
        time_verf = datetime.now()
    print('ok')

>> check_global()
<< ok

接下来,当我用 global 关键字 (2) 注释掉行时,它会抛出异常:

def check_global():
    # global time_verf
    clean_list = time_verf + timedelta(hours=12)  # время очистки листа
    if clean_list < datetime.now():
        list_verf.clear()
        time_verf = datetime.now()
    print('ok')

>> check_global()
<< Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 3, in check_global
UnboundLocalError: local variable 'time_verf' referenced before assignment

当赋值行被注释掉(3)时,可以再次在没有“全局”的情况下再次引用:

def check_global():
    # global time_verf
    clean_list = time_verf + timedelta(hours=12)  # время очистки листа
    if clean_list < datetime.now():
        list_verf.clear()
        # time_verf = datetime.now()
    print('ok')

>> check_global()
<< ok

但是为什么允许我在不使用全局 (4) 的情况下更新外部范围中定义的列表?

list = []

def check_global_list():
    list.append('check')
>> check_global_list()
>> list
<< ['check']

【问题讨论】:

  • 我已经在 SO 上看到了这个答案......现在找不到问题......这个想法是,通过在名称下方的代码中添加分配行,名称“time_verf”是添加到本地(甚至在执行第一行代码之前),我将搜索带有详细信息的帖子
  • 注释掉行的函数只是调用变量,它不会尝试修改。我认为这是关于可变性与不变性(列表是可变的,这就是您的底部示例有效的原因(也适用于例如字典))
  • @VladimirKolenov 我的意思是,您可以在函数内部修改可变对象,例如 list,而无需将其声明为 global 变量。这就是您在 (4) 中显示的内容。另一方面,像datetime 这样的不可变对象,您必须将其声明为global 才能在函数内部对其进行修改(或将其作为参数/关键字传递),请参见您的示例(1)-(3)。
  • @VladimirKolenov,是的,我认为 BoarGules 对此给出了很好的解释。它是关于定义变量的时间顺序(代码从上到下)。我添加了一些关于可变性的字眼,因为这可能会在脚本中引起一些混乱。

标签: python python-3.x global


【解决方案1】:

编辑:我同意 Roel Schroeven 的观点,这里的核心问题是任务

尽管如此,BoarGules 给出了一个关于可变寿命恕我直言的好答案。此外,我认为这里需要注意的重要一点是可变性与不变性。这特别是指问题的(4)。虽然以下工作正常

a = 2.72 # global scope, defined BEFORE the function
def modify():
    b = a + 3.14 # no assignmnet made to a (but to b in this case)
    return b # scope of b is local -> 'return' needed to make it available in the outer scope

In [106]: modify()
Out[106]: 5.86

这失败了:

a = 2.72
def modify(): # assignment!
    a += 3.14 # you would need 'global a' before this to make it work
    return a

In [108]: modify()
UnboundLocalError: local variable 'a' referenced before assignment

虽然a 可以在modify() 内部调用,但它不能被修改,因为它是float 类型,因此是一个不可变对象。

另一方面,如果你对像 list 这样的可变对象做同样的事情,你会得到

a = [2.72]
def modify():
    a[0] = 3.14 # change the reference to another value...
                   # no return needed

In [110]: modify()
In [110]: a
Out[110]: [3.14]

它不会失败并且a 改变了,即使在函数范围之外!如果您在函数中调用以前未定义的变量,它将再次失败。请注意,a[0] = 3.14 不是将a 分配给某个值,而是将a 中存储的引用更改为另一个值。如果您在脚本中有多个函数并传递东西,这一点非常重要。 为了进一步阅读,那里有关于该主题的好资源,例如this 作为初学者,也可能在文档中的 python data model 上。

【讨论】:

  • IMO 可变性与不变性不是问题的核心。要点是 assignment 需要 global,对于可变和不可变变量都是如此。 a = a + [3.14] 需要 global 即使是列表。
  • @RoelSchroeven,我同意你的观点,这是问题的核心问题。进行了编辑以澄清。
【解决方案2】:

您找到的资源声明“如果我们需要为全局变量分配新值,那么我们可以通过将变量声明为全局变量来实现。”,关键字是“assign”。您可以访问全局变量、调用它们的方法、改变它们而不将它们声明为全局变量。

当您需要为它们分配时,您需要将它们声明为全局。

【讨论】:

    【解决方案3】:

    当您注释掉第 2 行中的 global time_verf 语句时,

    1: def check_global():
    2: # global time_verf
    3: clean_list = time_verf + timedelta(hours=12)  # время очистки листа
    4: if clean_list < datetime.now():
    5:     list_verf.clear()
    6:     time_verf = datetime.now()
    

    第 6 行为局部变量 time_verf 赋值。它是本地的,因为分配它会将其创建为局部变量。第 3 行出现错误,因为它引用了代码在第 6 行创建的 local 变量。如果您没有该赋值,则默认情况下time_verf 将是全局的。

    但是一个变量是否是全局的并不取决于执行的顺序。您似乎期望因为第 3 行本身会使 time_verf 默认为全局,然后它变为全局并在第 6 行中保持全局。但这不是局部变量的工作方式。第 6 行的出现改变了第 3 行的含义(和正确性)。解释器检查函数的整个范围,并为代码赋予值的任何名称创建局部变量。因此,由于第 6 行,time_verf 是本地的,即使第 3 行本身会使它成为全局。

    这种行为有很好的理由。假设第 3 行包含在 if-test 中。然后,根据您似乎期望的行为,如果if-test 为真,则该变量将是全局的,但如果if-test 为假,则该变量将是局部的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-01
      • 2012-05-26
      • 2016-08-19
      • 1970-01-01
      • 1970-01-01
      • 2013-12-25
      • 2016-10-11
      相关资源
      最近更新 更多