为什么Python中的函数可以在封闭范围内打印变量但不能在赋值中使用它们?

Sha*_*ank 25 python scope

如果我运行以下代码:

x = 1

class Incr:
    print(x)
    x = x + 1
    print(x)

print(x)
Run Code Online (Sandbox Code Playgroud)

它打印:

1
2
1
Run Code Online (Sandbox Code Playgroud)

没问题,这正是我的预期.如果我做以下事情:

x = 1

class Incr:
    global x
    print(x)
    x = x + 1
    print(x)

print(x)
Run Code Online (Sandbox Code Playgroud)

它打印:

1
2
2
Run Code Online (Sandbox Code Playgroud)

也是我所期待的.没有问题.

现在,如果我开始按如下方式创建增量函数:

x = 1

def incr():
    print(x)

incr()
Run Code Online (Sandbox Code Playgroud)

它按照我的预期打印1.我假设它这样做是因为它无法x在其本地范围内找到,因此它搜索其封闭范围并找到x它.到目前为止没有问题.

如果我这样做:

x = 1

def incr():
    print(x)
    x = x + 1

incr()
Run Code Online (Sandbox Code Playgroud)

这在回溯中给出了以下错误:

UnboundLocalError:赋值前引用的局部变量'x'.

为什么Python不只是搜索封闭空间,x因为它找不到x像我class Incr一样用于赋值的值?请注意,我不是在问这个功能如何工作.我知道如果我执行以下操作,该功能将起作用:

x = 1

def incr():
    global x
    print(x)
    x = x + 1
    print(x)

incr()
Run Code Online (Sandbox Code Playgroud)

这将正确打印:

1
2
Run Code Online (Sandbox Code Playgroud)

正如我所料.我要问的是,为什么x当关键字global不存在时,它不仅仅是从封闭范围中拉出来,就像上面我的类一样.为什么解释器觉得有必要报告这个问题,因为UnboundLocalError它清楚地知道某些x存在.由于该函数能够读取x打印时的值,我知道它具有x封闭范围的一部分......所以为什么这不像类示例那样有效?

为什么使用xprint 的值与使用赋值的值不同?我只是不明白.

Ash*_*ary 35

类和函数是不同的,类中的变量实际上被赋予类的名称空间作为其属性,而在函数内部,变量只是不能在其外部访问的普通变量.

函数内部的局部变量实际上是在第一次解析函数时决定的,而python不会在全局范围内搜索它们,因为它知道你将它声明为局部变量.

因此,只要python看到x = x + 1(赋值)并且没有global为该变量声明,那么python就不会在全局或其他范围中查找该变量.

>>> x = 'outer'
>>> def func():
...     x = 'inner'  #x is a local variable now
...     print x
...     
>>> func()
inner
Run Code Online (Sandbox Code Playgroud)

常见问题:

>>> x = 'outer'
>>> def func():
...     print x       #this won't access the global `x`
...     x = 'inner'   #`x` is a local variable
...     print x
...     
>>> func()
...
UnboundLocalError: local variable 'x' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

但是当你使用一个global语句然后python来查找global范围内的那个变量.

阅读:当变量有值时,为什么我得到UnboundLocalError?

nonlocal:对于嵌套函数,您可以使用nonlocalpy3.x中的语句来修改封闭函数中声明的变量.


但是类的工作方式不同,x在类中声明的变量A实际上变为A.x:

>>> x = 'outer'
>>> class A:
...    x += 'inside'  #use the value of global `x` to create a new attribute `A.x`
...    print x        #prints `A.x`
...     
outerinside
>>> print x
outer
Run Code Online (Sandbox Code Playgroud)

您还可以直接从全局范围访问类属性:

>>> A.x
'outerinside'
Run Code Online (Sandbox Code Playgroud)

global在课堂上使用:

>>> x = 'outer'
>>> class A:
...     global x
...     x += 'inner' #now x is not a class attribute, you just modified the global x
...     print x
...     
outerinner
>>> x
'outerinner'
>>> A.x
AttributeError: class A has no attribute 'x'
Run Code Online (Sandbox Code Playgroud)

函数的问题不会引发类中的错误:

>>> x = 'outer'
>>> class A:
...     print x                      #fetch from globals or builitns
...     x = 'I am a class attribute' #declare a class attribute
...     print x                      #print class attribute, i.e `A.x`
...     
outer
I am a class attribute
>>> x
'outer'
>>> A.x
'I am a class attribute'
Run Code Online (Sandbox Code Playgroud)

LEGB规则:如果没有global并且nonlocal使用,那么python按此顺序搜索.

>>> outer = 'global'
>>> def func():
        enclosing = 'enclosing'
        def inner():
                inner = 'inner'
                print inner           #fetch from (L)ocal scope
                print enclosing       #fetch from (E)nclosing scope
                print outer           #fetch from (G)lobal scope
                print any             #fetch from (B)uilt-ins
        inner()
...         
>>> func()
inner
enclosing
global
<built-in function any>
Run Code Online (Sandbox Code Playgroud)

  • 这个答案应该有更多的赞成.尽管我已经使用了很长时间的这些功能,但我从这篇文章中学到了很多关于Python的知识. (2认同)

Mad*_*Air 5

从 Python范围和命名空间:

重要的是要认识到范围是通过文本确定的:模块中定义的函数的全局范围是该模块的命名空间,无论该函数从何处调用或通过什么别名调用。另一方面,实际的名称搜索是在运行时动态完成的——然而,语言定义正在向静态名称解析发展,在“编译”时,所以不要依赖动态名称解析!(实际上,局部变量已经是静态确定的。)

这意味着x = x + 1,在调用函数之前静态确定范围。由于这是一个赋值,那么 'x' 就变成了一个局部变量,而不是全局查找。

这也是from mod import *函数中不允许使用的原因。因为解释器不会在编译时为您导入模块以了解您在函数中使用的名称。即,它必须知道在编译时函数中引用的所有名称。