令人困惑的范围变化 - 发生了什么?

com*_*ave 3 python

def Test(value):
    def innerFunc():
        print value
    innerFunc()

def TestWithAssignment(value):
    def innerFunc():
        print value
        value = "Changed value"
    innerFunc()

Test("Hello 1")
# Prints "Hello 1"

TestWithAssignment("Hello 2")
# Throws UnboundLocalError: local variable 'value' referenced before assignment
# on the "print value" line
Run Code Online (Sandbox Code Playgroud)

为什么第二个失败,因为唯一的区别是在print语句之后的赋值?我对此非常困惑.

Sea*_*ira 8

问题是Python希望你是明确的,你想要隐含.Python使用的执行模型将名称绑定到最近的可用封闭范围.

def Test(value):
    # Local Scope #1
    def innerFunc():
        # Local Scope #2
        print value 
        # No immediate local in Local Scope #2 - check up the chain
        # First find value in outer function scope (Local Scope #1).
        # Use that.
    innerFunc()

def TestWithAssignment(value):
    # Local Scope #1
    def innerFunc():
        # Local Scope #2
        print value 
        # Immediate local variable found in Local Scope #2.
        # No need to check up the chain.
        # However, no value has been assigned to this variable yet.
        # Throw an error.
        value = "Changed value"
    innerFunc()
Run Code Online (Sandbox Code Playgroud)

没有(据我所知)走了在Python 2.x的范围的方式-你必须globals()和locals()-但全球和局部范围之间的任何范围不能被访问(如果这是不正确的,我会喜欢被纠正).

但是,您可以将局部变量传递value到内部本地范围:

def TestWithAssignment(value):
    def innerFunc(value):
        print value 
        # Immediate local **and assigned now**.
        value = "Changed value"
        # If you need to keep the changed value
        # return value
    innerFunc(value)
    # If you need to keep the changed value use:
    # value = innerFunc(value)
Run Code Online (Sandbox Code Playgroud)

在Python 3中,您可以使用新nonlocal语句来引用包含范围(Thanks @Thomas K).

def TestWithAssignment(value):
    def innerFunc():
        nonlocal value
        print value 
        value = "Changed value"
    innerFunc()
Run Code Online (Sandbox Code Playgroud)