Function scope

Scope: Refers to the scope of the identifier, which is the visible scope of the variable

def fn() :
    x = 10
    print(x)


fn()
print(x)
Copy the code
  • Print (x) {NameError: name ‘x’ is not defined;} NameError: name ‘x’ is not defined

Classification of scopes

  • Global scope: visible throughout the program, called all variables

  • Local scope: Visible only in functions, called local variables

Def fn2(): print(x) print(x)Copy the code
Def foo(): print(x) foo()Copy the code
  • Running the above code shows that local variables cannot be accessed outside the function, and global variables can generally be accessed inside the function

Nested function

def out():
    def innner():
        print(111)
        return 111

    return innner


out()
inner()
Copy the code
  • NameError: name ‘inner’ is not defined is reported after the function is run. The function name inside the function is also an identifier and cannot be accessed outside the function

Scope of nested functions

def outer():
    o = 100

    def inner():
        print(o)

    inner()
    print(o)


def outer2():
    o = 100

    def inner():
        o = 10
        print(o)

    inner()
    print(o)
Copy the code
  • As a result, an internal function can reference a variable defined externally. If an internal function defines a variable with the same name as the external variable, the internal variable is used preferentially, and the external variable is not overwritten

Easy wrong points

def fn1():
    x = 10

    def inner():
        print(x)

    return inner

a= fn1()
a()
Copy the code
  • correct
def fn1():
    x = 10

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

    return inner

a= fn1()
a()
Copy the code
  • error
def fn1():
    x = 10

    def inner():
        y =x + 1
        print(y)

    return inner

a= fn1()
a()
Copy the code
  • correct
x=10
def fn():
    x += 1
    print(x)

fn()
Copy the code
  • error

UnboundLocalError: local variable ‘x’ referenced before Assignment

  • X +=1 uses the x in the local variable, and the internal x is not defined

  • In the second case, you can add either global x or nonlocal x to inner(). Global x uses global variables, nonlocal is used only when functions are nested, and x uses the variable x defined by the external function

# x = 100 def fn1(): x = 10 def inner(): Print (x) return inner a= fn1() a() print(x) return inner a= fn1() aCopy the code
  • The fourth case can only use Global X because there are no nested functions
x=10
def fn():
   global x 
   x += 1
   print(x)

fn()
Copy the code

closure

In the case of nested functions, the inner function refers to variables defined by the outer function (also known as free variables)

def outer():
   c = [0]

   def inner():
       c[0] += 1
       return c[0]

   return inner
Copy the code
  • C [0] += 1, c[0] can not be used as an identifier, so it will not cause UnboundLocalError error
def outer():
    c = 0

    def inner():
        c += 1
        return c

    return inner
Copy the code
  • So this is going to cause an UnboundLocalError, nonlocal c
a = 5


def fn():
    nonlocal a
    a += 1
    print(a)


fn()
Copy the code
  • This is incorrect because nonlocal is only used in function nesting by reference to external variables

Function destruction

  • To define a function is to generate a function object, and the function name refers to the function object.
  • You can use the DEL statement to delete a function, reducing its reference count by one.
  • You can override the original definition with an identifier of the same name, essentially reducing its reference count by one.
  • When the Python program ends, all objects are destroyed.
  • Functions are objects, too, and whether to destroy them depends on whether the reference count is reduced to zero.

The variable name LEGB

Variable lookup order

  • Local, Local scoped Local namespace. Created when the function is called, the call ends and dies

  • If you want additional recommendations on this. Python2.2 introduced nested functions and implemented closures. This is the namespace of the outer functions of the nested functions

  • Global: the namespace of a module. Modules are created when they are imported and die when the interpreter exits

  • Build-in, a built-in module namespace whose life cycle is created when the Python interpreter starts and dies when the interpreter exits. For example, print(open), print and open are built-in variables

Higher-order functions

The mathematical concept y = f(g(x)), in Python: 1. At least one function as argument 2. Return a function

def counter():
    a = 0

    def inner():
        nonlocal a
        a += 1
        return a

    return inner


f = counter()
print(f())
print(f())
print(f())
Copy the code
  • F1 =counter() and f2=counter() are not equal, but f1()==f2(), because the comparison is based on the return value

Currie,

A function that takes two arguments becomes one that takes one argument and returns another function that takes a second argument, such as fn(x,y) converted to fn1(x)(y).

Def fn (x, y) : return x + y # conversion def f1 (x) : def fn2 (y) : return x + y return fn2Copy the code

A decorator

Reference: www.liaoxuefeng.com/wiki/101695… Personal understanding is a dynamic enhancement to the method and does not intrude into the business code

No parameter decorator

import datetime def logger(fn): def wrapper(*args, **kwargs): Print (f"{datetime.datetime.now()} execute {fn.__name__} method ") return fn(*args, **kwargs) return wrapper @logger def add(x, y): return x + y print(add(1, 5))Copy the code
  • @logger means add=logger(add) (corrification), reassigned to the old add function name, now add=wrapper function, which returns the result of the old add(closure), but printed a statement enhancement function before executing.

Parameterized constructor

import datetime def logger(do): def inner(fn): def wrapper(*args, **kwargs): Print (f" in {datetime.datetime.now()} {do} method {fn.__name__}") return fn(*args, **kwargs) return wrapper return inner @logger("execute") def add(x, y): return x + y print(add(1, 9))Copy the code
  • So at sign logger means add= Logger (“execute”)(add)

@ wraps a decorator

When a function is decorated with a decorator, some function properties change, such as the function name, function document, and so on. Use the wraps in FuncTools

import datetime
from functools import wraps


def executeTime(fn):
    print("executeTime")

    @wraps(fn)
    def wrapper(*args, **kwargs):
        """execute wrapper"""
        print(f"---{datetime.datetime.now()}")
        return fn(*args, **kwargs)

    return wrapper


@executeTime
def add(x, y):
    """add doc"""
    return x + y


print(add(1, 9), add.__name__, add.__doc__)


Copy the code

  • In essence, @wraps returns a partial function that calls the update_wrapper method, where the attributes of the original function are copied

Multiple decorators decorate a function

import datetime from functools import wraps def executeTime(fn): @wraps(fn) def wrapper(*args, **kwargs): """execute wrapper""" print(f"---{datetime.datetime.now()}") return fn(*args, **kwargs) return wrapper def logger(do): def inner(fn): @wraps(fn) def wrapper(*args, **kwargs): """inner wrapper """ print(f" in {datetime.datetime.now()} {do} method {fn.__name__}") return fn(*args, **kwargs) return wrapper return inner @logger("execute") @executeTime def add(x, y): """add doc""" return x + y print(add(1, 9), add.__name__, add.__doc__)Copy the code
  • Get multiple decorators from the figure above to decorate a function, executed from top to bottom

  • Whatever the order of the @Logger and @ExecuteTime decorators, the final add attribute is the function attribute that has not been wrapped by @Wraps (fn). Reason: coolshell. Cn/articles / 11… To summarize: Multiple wraps decorate a function. As long as one of them is not wrapped with @wraps, the final result will be the attribute of the original function. Multiple wraps do not have @wraps, depending on the order

Execute the process

from functools import wraps


def logger(fn):
    print("enter logger")

    @wraps(fn)
    def wrapper(*args, **kwargs):
        """wrapper's doc"""
        print("enter wrapper ")
        return fn(*args, **kwargs)
    print(wrapper)
    return wrapper


@logger
def add(x, y):
    """add"""


@logger
def sub(x, y):
    """sub"""
Copy the code
  • So when this function is decorated with @logger, it’s like add= Logger (add), you’ve done logger, so if you get @wraps(fn) in logger, it’s like wrapper=wraps(fn), The source wraps(fn) return a partial function.

  • Function calls are performed separately, meaning that the Wrapper function of Add and the wrapper function of sub are not the same

  • If you delete @wraps(fn), add and sub will have the same function attributes as wrapper

Anonymous functions

  • Lambda argument list: Return value

  • You can have multiple parameter lists, but only one value can be returned. If you need to return more than one value, wrap it in a container

Print ((lambda x, y=3: x + y)(5)) print(lambda x, y=3: x + y)(5)) print(lambda x, y=3: x + y) Print ((lambda x, *, y=30: x + y)(5) print(lambda x, *, y=30: x + y)(5) print(lambda x, *, y=30: x + y) Print ((lambda *args: (x for x in args))(*range(5))) [x + 1 for x in args])(*range(5)) print(lambda *args: {x % 2 for x in args})(*range(5)) print((lambda *args: {STR (x): X for x in args})(*range(5))) # dict(dict(map(lambda x: (CHR (65 + x), 10-x), range(5)))) # high-order function, construct dictionary d = dict(map(lambda x: (CHR (65 + x), 10-x), range(5)) # Sorted (d.tems (), key=lambda x: x[1])Copy the code

Inner higher order function

Sorted function

  • Sorted (iterable, *, key=None, reverse=False) Iterable is an iterable. The key must be a function or a callable

Sorted (l) print(sorted(l)) # sorted() print(sorted(l)) None print(l) # Since sorted, l is already the sorted listCopy the code
  • The list method sort is used in the same way as sorted, except that sorted returns a new list and sort modifies the original list

  • The sorted function can sort not only lists, but also any iterable

l1 = (5, 4, 3, 2, 1) l2 = {5, 4, 3, 2, 1} print(sorted(l1)) print(sorted(l2)) a = {'c': 1, 'b': 3, 'a': 2} print(sorted(a)) print(sorted(a.keys())) print(sorted(a.values())) print(sorted(a.items())) Print (dict(sorted(a.sorted (), key=lambda x: x[1]))) # sort value and return key value progenitorCopy the code
  • Sort a dictionary by default by sorting all keys of the dictionary, and by sorting items of the dictionary by key, returns a list whose elements are the progenitors of keys and values

The filter function

  • Filter (function, iterable) Function must return True or False. If function is defined as None, elements in iterable that are False will be filtered

  • Filter returns an iterator

A = filter(lambda x: x % 3 == 0, range(5)) print(a) print(next(a)) print(next(a)) print(next(a)Copy the code

The map function

  • map(function, iterable, …) The first argument is a function object, whose arguments correspond to the number of iterables passed in. Processing is done in parallel, terminating when the shortest iterable runs out

  • Returns an iterable

print(list(map(str, range(5))))
print(dict(map(lambda x, y: (y, x), range(5), ['a', 'b', 'c', 'd'])))

Copy the code

Zip function

  • Zip (*iterables) combines elements of multiple iterables into primitives one by one

  • Returns an iterable

print(list(zip([1, 2, 3], ['a', 'b', 'c'], 'xyz')))
print(dict(zip([1, 2, 3], ['a', 'b', 'c'])))

Copy the code