Scope rules and closures of Python functions, and python function domain rules

Source: Internet
Author: User

Scope rules and closures of Python functions, and python function domain rules
Scope rules

The namespace is a ing from name to object. In Python, it is mainly implemented through dictionaries, mainly including the following namespaces:

  • The built-in namespace contains some built-in functions and built-in exception names. It is created when the Python interpreter is started and saved to the interpreter to exit. The built-in name actually exists in a module named _ builtins _. You can use globals () ['_ builtins _']. _ dict _ view the built-in functions and built-in exceptions.
  • The global namespace is created when the module where the function is located is read. Normally, the module namespace is saved to the interpreter and exited. You can view it through the built-in function globals.
  • A local namespace created when a function is called. It contains the name of the function parameter and the variable name assigned to the function body. Delete a function when it returns or raises an internal exception that is not handled by a function. Each recursive call has its own local namespace. You can view it through the built-in function locals.

When parsing the variable name, python first searches for the local namespace. If no matching name is found, it searches for the global namespace. If the interpreter cannot find the matching value in the global namespace, it will check the built-in namespace. If it still cannot be found, the NameError exception is thrown.

There is absolutely no relationship between names in different namespaces, such:

a = 42def foo():    a = 13    print "globals: %s" % globals()    print "locals: %s" % locals()    return afoo()print "a: %d" % a

Result:

globals: {'a': 42, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'C:\\Users\\h\\Desktop\\test4.py', '__package__': None, '__name__': '__main__', 'foo': <function foo at 0x0000000002C17AC8>, '__doc__': None}locals: {'a': 13}a: 42

It can be seen that the assignment of variable a in the function will create a new local variable a in the local scope, and the global variable a with the same name will not change.

In Python, the value assignment operation is always at the bottom of the scope. The value assignment does not copy data, but only binds the name to the object. This is also true for deletion. For example, if you run del a in a function, you only need to delete the local variable a from the local namespace. The global variable a will not change.

If you do not assign a value to a local variable, the UnboundLocalError exception is thrown:

a = 42def foo():    a += 1    return afoo()

The above function defines a local variable a. The value assignment statement a + = 1 will try to read its value before the value assignment of a, but global variable a will not assign a value to the local variable.

To operate global variables in a local namespace, you can use the global statement, which explicitly declares the variables as a global namespace:

a = 42def foo():    global a    a = 13    print "globals: %s" % globals()    print "locals: %s" % locals()    return afoo()print "a: %d" % a

Output:

globals: {'a': 13, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'C:\\Users\\h\\Desktop\\test4.py', '__package__': None, '__name__': '__main__', 'foo': <function foo at 0x0000000002B87AC8>, '__doc__': None}locals: {}a: 13

It can be seen that global variable a has changed.

Python supports nested functions (closures), but python 2 only supports assigning values to variables in the innermost layer of the scope and global namespace, internal functions cannot re-assign values to local variables in external functions. For example:

def countdown(start):    n = start    def display():        print n    def decrement():        n -= 1    while n > 0:        display()        decrement()countdown(10)

Running the command will report an UnboundLocalError. in python 2, you can put the variable in the list or dictionary to solve this problem:

def countdown(start):    alist = []    alist.append(start)    def display():        print alist[0]    def decrement():        alist[0] -= 1    while alist[0] > 0:        display()        decrement()countdown(10)

You can use the nonlocal statement in python 3 to solve this problem. The nonlocal statement searches for the definitions of the next function in the current call stack. :

def countdown(start):    n = start    def display():        print n    def decrement():        nonlocal n        n -= 1    while n > 0:        display()        decrement()countdown(10)
Closure

Closure is an important syntax structure for functional programming. Python also supports this feature. For example, a nested function:

def foo():    x = 12    def bar():        print x    return barfoo()()

Output: 12

You can see that the embedded function can access the variables in the scope defined by the external function. In fact, the embedded function first checks the local scope when parsing the name, and then starts from the scope of the function called at the innermost layer, search for the scopes of all called functions, including non-local but non-Global names.

The statements that constitute the function are packaged together with the execution environment of the statement. The resulting object is called a closure. In nested functions, closures capture the entire environment required for internal function execution.

The code object of the python function, or the bytecode contains two objects related to the closure:

  • Co_cellvars: A tuples that contain the names of local variables referenced by nested Functions
  • Co_freevars: A tuples that store variable names in the outer scope used

Let's look at the nested functions above:

>>> def foo():    x = 12    def bar():    return x    return bar>>> foo.func_code.co_cellvars('x',)>>> bar = foo()>>> bar.func_code.co_freevars('x',)

It can be seen that co_cellvars of the code object of the outer function saves the name of the variable to be referenced by the internal nested function, the co_freevars of the code object of the nested function in the inner layer saves the variable name that needs to reference the scope of the external function.

During function compilation, the internal function has a special attribute _ closure _ (func_closure) of the closure ). The _ closure _ attribute is a tuples composed of cell objects and contains variables referenced by Multiple scopes:

>>> bar.func_closure(<cell at 0x0000000003512C78: int object at 0x0000000000645D80>,)

To view the variable content in the closure:

>>> bar.func_closure[0].cell_contents12

If an internal function does not include a reference to an external function variable, the _ closure _ attribute does not exist:

>>> def foo():    x = 12    def bar():    pass    return bar>>> bar = foo()>>> print bar.func_closureNone

When a function is passed as an object to another function as a parameter, a closure and nested function are combined to return a function as a returned result, which is the application of the python modifier.

Delayed binding

Note that the scope of python functions is determined by the Code, that is, static functions, but their usage is dynamic and determined during execution.

>>> def foo(n):    return n * i>>> fs = [foo for i in range(4)]>>> print fs[0](1)

When you expect the result to be 0, the result is 3.

This is because the value of variable I will be searched only when the function foo is executed. Because the loop has ended, I points to the final value 3 and the same result will be obtained.

The same problem also exists in the closure:

def foo():    fs = []    for i in range(4):        fs.append(lambda x: x*i)    return fsfor f in foo():    print f(1)

Return Value:

3333

Solution: Set the default value for function parameters:

>>> fs = [lambda x, i=i: x * i for i in range(4)]>>> for f in fs:    print f(1)

The closure is used:

>>> def foo(i):    return lambda x: x * i>>> fs = [foo(i) for i in range(4)]>>> for f in fs:    print f(1)

Or:

>>> for f in map(lambda i: lambda x: i*x, range(4)):    print f(1)

Using closures is similar to partial functions. You can also use partial functions:

>>> fs = [functools.partial(lambda x, i: x * i, i) for i in range(4)]>>> for f in fs:    print f(1)

In this way, free variable I is first bound to the closure function.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.