Python内部函数

时间:2013-09-04 14:06:58

标签: python

在python中,我可以写:

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

    def _func():
        print x
    return _func

test = func()
test()

当我运行它时,输出是:

1

2

由于_func可以访问func中定义的“x”变量。右...

但如果我这样做:

def func():
    x = 1
    print x

    def _func():
        x+=1
        print x
    return _func

test = func()
test()

然后我收到一条错误消息: UnboundLocalError:分配前引用的局部变量'x'

在这种情况下,_func似乎无法“看到”“x”变量

问题是:为什么在第一个例子中打印x“看到”“x”变量,而数学运算符x + = 1会抛出异常?

我不明白为什么......

3 个答案:

答案 0 :(得分:8)

检查此答案:https://stackoverflow.com/a/293097/1741450

  

可以访问除本地函数变量之外的作用域中的变量,但如果没有进一步的语法,则无法将其重新转换为新参数。相反,赋值将创建一个新的局部变量,而不是影响父范围中的变量。例如:

答案 1 :(得分:3)

考虑到Python是一种“解释”语言,人们自然会假设如果一个变量已经在外部作用域中定义,那么同一个变量仍然可以在内部作用域中访问。事实上,在你的第一个例子中,内部_func只打印x它就可以了。

但是,关于Python的不明显之处在于,这并不是确定变量范围的确切方式。 Python在编译时分析哪些变量应该被视为范围的“本地”,这取决于它们是否被分配到该范围内。在这种情况下,赋值包括扩充赋值运算符。因此,当Python将第二个示例中的内部_func编译为字节码时,它会看到x += 1并确定x必须是_func的局部变量。除了当然,因为它的第一个赋值是一个增强赋值,所以本地没有x变量来增加,你得到UnboundLocalError

另一种看待这种情况的方法可能是写出:

def _func():
    print x
    x = 2

此处再次因为_func包含行x = 2,它会将x视为该函数范围内的局部变量而视为{{ 1}}在外部函数中定义。因此,x也应该产生print x

您可以使用dis模块更详细地检查这一点,以显示为该函数生成的字节码:

UnboundLocalError

>>> dis.dis(_func) 2 0 LOAD_FAST 0 (x) 3 PRINT_ITEM 4 PRINT_NEWLINE 3 5 LOAD_CONST 1 (2) 8 STORE_FAST 0 (x) 11 LOAD_CONST 0 (None) 14 RETURN_VALUE 操作码用于“快速”查找局部变量,绕过较慢,更通用的名称查找。它使用一个指针数组,其中每个局部变量(在当前堆栈帧中)与该数组中的索引相关联,而不是通过字典查找等。在上面的示例中,LOAD_FAST操作码的唯一参数是LOAD_FAST - 在这种情况下是第一个(也是唯一的)本地。

您可以检查函数本身(特别是其底层代码对​​象),该代码中使用了一个局部变量,并且与之关联的变量名称为0

'x'

这就是>>> _func.__code__.co_nlocals 1 >>> _func.__code__.co_varnames ('x',) 能够报告dis.dis的方式。同样适用于0 LOAD_FAST 0 (x)操作码。

为了理解Python中的变量范围,这些都不是必须知道的,但是知道幕后发生了什么可能会有所帮助。

正如其他一些答案中已经提到的,Python 3引入了STORE_FAST关键字,该关键字根据对本地范围内该变量的赋值,阻止了名称与局部变量的编译时绑定。

答案 2 :(得分:0)

您无法重新绑定在Python 2中关闭的变量(您可以在Python 3中使用nonlocal)。如果我必须在Python 2中执行您正在执行的操作,那么我将执行以下解决方法:

class Pointer(object):
    def __init__(self, val):
        self.val = val

def func():
    px = Pointer(1)
    print px.val
    px.val += 1

    def _func():
        print px.val
    return _func

test = func()
test()

基本上我将需要变换的任何值放到一个对象上 - 有时我使用一个包含一个元素的列表 - 然后重新编写代码,以便所有发生的是方法调用。以上实际上相当于:

class Pointer(object):
    def __init__(self, val):
        self.val = val

def func():
    px = Pointer(1)
    print px.val
    px.__setattr__('val', px.val + 1)

    def _func():
        print px.val
    return _func

test = func()
test()