1, the closure

This is the 9th day of my participation in the More text Challenge. For details, see more text Challenge

1.1 Function conceptual reference

def test1() :
    print("--- in test1 func----")

# Call function
test1()

# Reference function
ret = test1

print(id(ret))
print(id(test1))

Call functions by reference
ret()
Copy the code

Running result:

--- in test1 func----
140212571149040
140212571149040
--- in test1 func----
Copy the code

As we can see from the figure above, just like variable names, function names are just references to the code space of the function, and are passed by reference when the function name is assigned to an object.

1.2 the closure

# Define a function
def test(number) :

    If you define a function inside a function that uses variables from an outside function, you call the function and the variables you use closures
    def test_in(number_in) :
        print("In test_in function, number_in is %d" % number_in)
        return number+number_in
    # return the result of the closure
    return test_in


# assign a value to the test function. This 20 is for the parameter number
ret = test(20)

Note that the 100 is actually given to the parameter number_in
print(ret(100))

# note that 200 is actually given to the parameter number_in
print(ret(200))
Copy the code

Running result:

inTest_in function, number_inis 100
120

inTest_in function, number_inis 200
220
Copy the code

1.3 Look at a practical closure example:

def line_conf(a, b) :
    def line(x) :
        return a*x + b
    return line

line1 = line_conf(1.1)
line2 = line_conf(4.5)
print(line1(5))
print(line2(5))
Copy the code

In this example, the function line forms a closure with the variables A and b. When we create the closure, we specify the values of these two variables as line_conf arguments a and b, thus determining the final form of the function (y = x + 1 and y = 4x + 5). All we need to do is change the parameters A and b, and we can get different linear expression functions. Thus, we can see that closures can also improve code reusability.

If there are no closures, we need to specify a,b, and x each time we create a line function. As a result, we need to pass more parameters and reduce the portability of the code.

Note:

Because the closure refers to the local variable of the external function, the local variable of the external function is not released in time and consumes memory

1.4 Modifying variables in external functions

def counter(start=0) :
    def incr() :
        nonlocal start
        start += 1
        return start
    return incr

c1 = counter(5)
print(c1())
print(c1())

c2 = counter(50)
print(c2())
print(c2())

print(c1())
print(c1())

print(c2())
print(c2())
Copy the code

1.5 summarize

  • The function name is just a reference to the code space of the function. It is passed by reference when the function name is assigned to an object
  • A closure is a nested function that starts defining the inner function when the outer function is running, and then passes the reference to the inner function to the object outside the function
  • The overall composition of the internal function and the variables provided by the external function used is called a closure

2. Decorators

Let’s start with a piece of code

def show() :
    print("Let us not be afraid of any suffering!! Smile at it!!")
    
# show is the name of the current function object, because everything in Python is an object, and the function itself is an object
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
func = show  
func()   Print statement in show function
Copy the code

From the above code, you can determine that the name of a function is just an object, and like a normal object, this object can refer to the code of another function

2.1 a decorator

Concept: to extend a function without changing its function, this is a decorator.

A decorator is essentially a function. Is to add functionality to an old function by using another function function

Use format:

@ Function that extends functionalityFunctions to be extended ():pass
The extended function is a decorator, but it is essentially a function, and the function must be a closure function, and the outer function must receive the memory reference to be extended
Copy the code

There is a requirement to have it type ‘go, Ollie’ without changing the show function in the above code.

# say is a decorator for the show function.
def say(func) : 
    For closures, the outer function needs to receive more memory references than the decorator function
    def inner() :
        print("Ollie to")
        func()
    return inner

@say   The current decorator function is equivalent to say(show). Note that the current decorator function does not take any arguments
def show() :
    print("Let us not be afraid of any suffering!! Smile at it!!")

show()
Copy the code

Decorator execution:

# Interpret the above decorator code as a normal function call
def say(func) :
    def inner() :
        print("Ollie to")
        func()
    return inner

def show() :
    print("Let us not be afraid of any suffering!! Smile at it!!")

< show > < say > < say > < say > < say > < say > < say > < say > < say > < say > < say > < say > < say > < say > < say > < say > < say > < say
show= say(show)
# Call show(), which is equivalent to calling inner()
# The inner function is referenced, so the func variable (free variable) of the outer function is not freed
# func holds the original show function object
        
Copy the code

2.2 Decorated functions have arguments

*args and **kwargs can also be used if you are not sure how many arguments the decorated function has
def say(func) :
    def inner(a, b) :
        print(a, b)
        print("Ollie to")
        func(a, b)
    return inner

@say
def show(a, b) :
    print("Let us not be afraid of any suffering!! Smile at it!!")
    print(a + b)

show(1.2)
Copy the code

2.3 Decorated functions have return values

If the function has a return value, return the result of the function's run in the inner function
def say(func) :
    def inner() :
        print("-- -- -- -- -- -- -- -- -- -- 2")
        return func()
    return inner

@say
def show() :
    return "-- -- -- -- -- 1 -- -- -- -- --"


print(show())
Copy the code

2.4 Use multiple decorators to decorate the same function

# Define function: complete the package data
def makeBold(fn) :
    def wrapped() :
        return "<b>" + fn() + "</b>"
    return wrapped

# Define function: complete the package data
def makeItalic(fn) :
    def wrapped() :
        return "<i>" + fn() + "</i>"
    return wrapped

@makeBold
def test1() :
    return "hello world-1"

@makeItalic
def test2() :
    return "hello world-2"

@makeBold
@makeItalic
def test3() :
    return "hello world-3"

print(test1())
print(test2())
print(test3())
# Execution result:
"""
<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>
"""
# Execution is the nearest principle
Copy the code

3. Anonymous functions

When we pass in functions, sometimes we don’t need to explicitly define functions. It’s easier to pass in anonymous functions.

In Python, there is limited support for anonymous functions. For example, if f(x)=x2, we can define a function of f(x), and pass in an anonymous function directly:

>>> list(map(lambda x: x * x, [1.2.3.4.5.6.7.8.9[]))1.4.9.16.25.36.49.64.81]
Copy the code

The anonymous function lambda x: x * x is actually:

def f(x) :
    return x * x
Copy the code

The keyword lambda denotes anonymous functions, and the x before the colon denotes function arguments.

Anonymous functions are limited to one expression. The return value is the result of that expression.

The advantage of using anonymous functions is that because functions don’t have names, you don’t have to worry about name conflicts. In addition, an anonymous function is also a function object. It is also possible to assign an anonymous function to a variable and then call the function from that variable:

>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25
Copy the code

It is also possible to return an anonymous function as a return value, such as:

def build(x, y) :
    return lambda: x * x + y * y
Copy the code

4. Higher-order functions

4.1 the map function

The map() function takes two arguments, a function and an Iterable. Map applies the passed function to each element of the sequence and returns the result as a new Iterator.

For example, if we have a function f(x)=x2, to apply this function to a list [1, 2, 3, 4, 5, 6, 7, 8, 9], we can use map() as follows:

F (x) = x * x │ │ ┌ ─ ─ ─ ┬ ─ ─ ─ ┬ ─ ─ ─ ┬ ─ ─ ─ ┼ ─ ─ ─ ┬ ─ ─ ─ ┬ ─ ─ ─ ┬ ─ ─ ─ ┐ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ [1 2 3 4 5 6 7 8 9] │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ 36 49 [1 4 September 16 to 25, 64, 81]Copy the code

The first argument passed to map() is f, the function object itself. Since the result r is an Iterator, and that Iterator is a generic object, we use the list() ‘function to have it compute the entire sequence and return a list.

You might think that you can write a loop to calculate the result without the map() function:

L = []
for n in [1.2.3.4.5.6.7.8.9]:
    L.append(f(n))
print(L)
Copy the code

Yes, but can you see from the above loop code that “apply f(x) to each element of the list and generate a new list as a result”?

So, map(), as a higher-order function, actually abstracts the rules, so that we can compute not only simple f(x)=x2, but also any complex function, such as converting all the numbers in this list to strings:

print(list(map(str[1.2.3.4.5.6.7.8.9)))['1', '2', '3', '4', '5', '6', '7', '8', '9']
Copy the code

4.2 the filter function

Python’s built-in filter() function is used to filter sequences.

Like map(), filter() takes a function and a sequence. Unlike map(), filter() applies the passed function to each element in turn and decides whether to keep or discard the element depending on whether the return value is True or False.

For example, in a list, delete the even numbers and leave only the odd numbers.

def is_odd(n) :
    return n % 2= =1

list(filter(is_odd, [1.2.4.5.6.9.10.15]))
# Results: [1, 5, 9, 15]
Copy the code

To delete an empty string from a sequence, write:

def not_empty(s) :
    return s and s.strip()

list(filter(not_empty, ['A'.' '.'B'.None.'C'.' ']))
['A', 'B', 'C']
Copy the code

With the higher-order filter() function, the key is to correctly implement a “filter” function.

Note that the filter() function returns an Iterator, that is, an lazy sequence, so to force filter() to complete the calculation, you need to use the list() function to retrieve all the results and return the list.

4.3 sorted function

Sorting is also an algorithm that is often used in programs. Whether you use bubble sort or quicksort, the core of sorting is to compare the size of two elements. If it’s a number, we can directly compare it, but what about strings or two dicts? It makes no sense to directly compare mathematical sizes; therefore, the process of comparison must be abstracted through functions.

Python’s built-in sorted() function can sort lists:

>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
Copy the code

In addition, the sorted() function is also a higher-order function, and it can also receive a key function for custom sorting, such as sorting by absolute value:

>>> sorted([36.5, -12.9, -21], key=abs)   # abs takes the absolute value of the number. The current sort is based on the absolute value of the number
[5.9, -12, -21.36]
Copy the code

Reverse sorting simply requires adding the reverse argument to the sorted function and setting it to True

>>> sorted([1.3.2], reverse=True)
[3.2.1]
Copy the code

4.4 the reduce function

The first argument is a function, and the second argument is an Iterable type (Iterable). The first argument must also take two arguments. Reduce passes the return value of the function and the next element in the sequence to the function for calculation.

Here’s an example of multiplying from 1 to 100

From functools import reduceDef add(x,y): return x*yprint(add,range(1, 100)))
Copy the code

So we can visualize the equivalent description of this method:

reduce(fn, [x1, x2, x3, x4,……] ) = f(f(f(f(x1, x2), x3), x4) ……)

conclusion

This is a long article, give a big thumbs-up to those who see it! Due to the limited level of the author, it is inevitable that there will be mistakes in the article, welcome feedback and correction.

If you think the article is helpful to you, please like, comment, and collect

Your support is my biggest motivation!!