special usage of functions

5.4 Special uses of functions

5.4.1 Anonymous functions

The so-called anonymous function is a function defined in a standard form such as the def statement. Keywords can be used in Python lambdato create anonymous functions. The function body of an anonymous function created with a lambda is simpler than defthat of a defined function. The syntax is as follows:

lambda [parameter 1[,parameter 2],...parameter n]]: expression

lam_sum = lambda arg1, arg2: arg1 + arg2
print(lam_sum(10, 20))

30

In the above code, the first line defines a lambda function that performs the sum operation of two numbers, and names the lambda function lam_sum. Then implement the summation function through the lam_sum() function.
Only limited logic can be encapsulated in the anonymous function created by Lambda.
A lambda function has its own namespace and cannot access parameters outside its own parameter list or in the global namespace.
In fact, generally when using an anonymous function, the created anonymous function will not be named. Because this loses the simplicity of anonymous functions. In some scenarios, functions need to be passed in, and the required logic is not very complicated. But I don't want to create another one, so I can use the anonymous function directly at this time. as follows:

print(list(map(lambda x: x * x, [1, 2, 3, 4, 5])))

[1, 4, 9, 16, 25]

5.4.2 Recursive calls

When defining a function in Python, the function body can call other functions, or even call itself. This way of calling yourself is called recursive calling. Here is a recursive function definition:

def recursion():
return recursion()

Obviously, for the function defined above, if you run it, you will find that after running for a while, the program crashes (throws an exception).
In theory, this program will run forever, but every time the function is called, some memory will be consumed. Therefore, after the number of function calls reaches a certain level (and the previous function calls did not return), all memory space will be exhausted, causing the program to terminate and display an error message "maximum recursion depth exceeded, the default is 1000 times )".
The maximum recursion depth can be modified by the following code:

import sys
sys.setrecursionlimit(99999)

The recursion in this function is called infinite recursion (just as a loop starting with while True and containing no break and return statements is called an infinite loop) because it theoretically never ends. What you want is a recursive function that can help you, and such a recursive function usually consists of the following two parts.
Baseline condition: A function returns a value directly when this condition is met.
Recursive Conditions: Contains one or more calls intended to solve part of the problem.
The key here is that by breaking the problem into smaller parts, you avoid endless recursion, because the problem will eventually be broken down to the smallest problem that can be solved under the baseline conditions.
So how do you make a function call itself? This is not as difficult to understand as it seems. As I said earlier, every time a function is called, a new namespace is created for it. This means that when a function calls itself, two different functions (more precisely, different versions (i.e., different namespaces) of the same function) are communicating. You can think of this as two animals of the same species communicating with each other.
递归示例1:通过递归的方式求一个数的阶乘

def factorial(p_int=0):
if p_int == 0:  # 基线条件
	    return 1
else:  #  递归条件
return p_int * factorial(p_int - 1)


print(factorial(10))

3628800

递归示例2:通过递归的方式求幂

def power(x, n):
    return 1 if n == 0 else x * power(x, n - 1)


print(power(2, 10))

1024

递归示例3:通过递归的方式解决汉诺塔问题

def move(n, a='A', b='B', c='C'):
    if n == 1:
        print('移动', a, '-->', c)
    else:
        move(n - 1, a, c, b)
        move(1, a, b, c)
        move(n - 1, b, a, c)


move(4)

Move A --> B
move A --> C
move B --> C
move A --> B
move C --> A
move C --> B
move A --> B
move A --> C
move B --> C
moves B --> A
moves C --> A
moves B --> C
moves A --> B
moves A --> C
moves B --> C

In some special problems, it can be realized by ordinary loop, but the code is simpler after using recursion. The logic is also clearer.
In theory, all recursive functions can be written as loops, but the logic of loops is not as clear as that of recursion.
When using recursive functions, care must be taken to prevent stack overflow. In a computer, a function call is implemented through a data structure called a stack. Whenever a function call is entered, a stack frame will be added to the stack, and every time the function returns, the stack will be decremented by a stack frame. Since the size of the stack is not infinite, too many recursive calls will cause the stack to overflow.

5.4.3 Partial functions

Reference: Partial function
When introducing function parameters, we mentioned that by setting the default value of the parameter, the difficulty of function calling can be reduced. Partial functions can do the same.
The int() function can convert a string to an integer. When only a string is passed in, the int() function converts by decimal by default:
>>> int('12345')

12345

But the int() function also provides an additional base parameter, the default value is 10. If you pass in the base parameter, you can do N-ary conversion:
>>> int('12345', base=8)

5349

>>> int(‘12345’, 16)

74565

Assuming that a large number of binary strings need to be converted, it is very troublesome to pass int(x, base=2) every time, so we thought that we can define a function of int2(), and pass base=2 in by default:

def int2(x, base=2):
    return int(x, base)

In this way, it is very convenient for us to convert binary:

>>> int2(‘1000000’)

64

>>> int2(‘1010101’)

85

functools.partial is to help us create a partial function. We don’t need to define int2() ourselves. We can directly use the following code to create a new function int2:
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')

64

>>> int2(‘1010101’)

85

Therefore, a brief summary of the function of functools.partial is to fix some parameters of a function (that is, set default values), return a new function, and call this new function will be easier.
Note that the new int2 function above just resets the base parameter to a default value of 2, but you can also pass in other values ​​when calling the function:
>>> int2('1000000', base=10)

1000000

Finally, when creating a partial function, you can actually receive the three parameters of the function object, *args and **kw. When passing in:
>>> int2 = functools.partial(int, base=2)
actually fixes the int( ) The keyword parameter base of the function, that is:

>>> int2('10010')
is equivalent to:
>>> kw = { 'base': 2 }
>>> int('10010', **kw)
when passed in:
>>> max2 = functools.partial( max, 10)
will actually add 10 to the left automatically as part of *args, i.e.:
>>> max2(5, 6, 7)
is equivalent to:
>>> args = (10, 5, 6, 7)
>>> max(*args)

10

When the function has too many parameters and needs to be simplified, use functools.partial to create a new function. This new function can fix some of the parameters of the original function, making it easier to call.

5.4.4 Closures

Closure is a kind of nesting of functions. First, a function is defined, called 外部函数. Another one is defined in this external function body, 内部函数and the variables of the external function are used in this internal function body. The outer function finally returns the inner function. Then the external function and the internal function constitute a special object called a closure.
Closures avoid the use of global variables, making it possible for local variables to be accessed outside the function. Compared with object-oriented, there is no need to inherit so many extra methods, and closures take up less space.
闭包示例

a = 1


def out_fun(b):
    c = 3

    def in_fun(d):
        print(a + b + c + d)

    return in_fun


infun = out_fun(2)
infun(4)

10

It can be seen that the global variable a, the local variable c of the in_funouter function, and the parameter b are accessed in the inner function.out_fun

5.4.4.2 Decorators

The essence of decorator: Syntactic sugar of function closure. By decorating the function, you can enhance the function of the function or add functions that the original function does not have.
A decorator enhances the decorated function the first time it is called, and only once.
Let's start with a simple function. Suppose now there is a function to calculate the cumulative sum from 1 to 100 and output the result. In order to avoid calculation too fast, we set to wait 0.01 seconds when using loop accumulation, the function definition is as follows:

def mysum1():
    from time import sleep
    total = 0
    for _ in range(101):
        sleep(0.01)
        total += _
    print(total)

mysum1()

5050

At this point, if we want to know how long it takes to execute this function, we can obtain the time before and after execution and then calculate it, or we can modify the function to obtain the time before and after the internal function body of the function. But these two methods are cumbersome, especially the second method, which requires intrusive modification of the function code.
At this time, it can be achieved by adding a decorator to the function. Here's the general way to create a decorator:
装饰器的简单定义

def decorator1(func):

    def inner():
        print('在这里执行被装饰函数执行前的增强操作')

        func()  # 执行被装饰的函数

        print('在这里执行被装饰函数执行前的增强操作')

    return inner

As can be seen above, a decorator is also a function. It's just that the parameter received by the decorator is the function to be decorated. Then define a function inside the decorator, the internal function body executes the operation code to be enhanced and executes the decorated function. Finally, return the internal function.
The next step is to decorate a function with a decorator. We decorate with the mysum1 function defined above:
装饰器的使用

@decorator1
def mysum1():
    from time import sleep
    total = 0
    for _ in range(101):
        sleep(0.01)
        total += _
    print(total)
mysum1()

Perform the enhancement operation before the decorated function is executed here
5050 Perform the enhancement operation before the decorated function is executed here

As can be seen from the above, if you want to decorate a function, you only need to add it in the line above the def statement when defining the function @装饰器函数.
For a decorator to decorate a function:

@decorator
def myfun():
    print("hello")

The code above is equivalent to:

def myfun():
    print("hello")
   
myfun = decorator(myfun)

When a decorator decorates a function, the functionality of the function is enhanced, because when this function is called, it actually calls its internal function when the decorator function is defined. At this time, the internal function is composed of the enhanced function command and the original decorated function.
创建一个统计函数运行时长的装饰器

import time


def decorator1(func):
    def inner():
        begin = time.time()

        func()  # 执行被装饰的函数

        end = time.time()
        print(f"函数`{
      
      func.__name__}`运行的总时间为:{
      
      end - begin:.3} 秒")

    return inner


@decorator1
def mysum1():
    from time import sleep
    total = 0
    for _ in range(101):
        sleep(0.01)
        total += _
    print(total)


mysum1()

The total time the 5050
function mysum1ran was: 1.59 seconds

5.4.4.2.2 The decorated function receives parameters

In the above example, the function decorated by decoratorthe decorator function cannot have input parameters, which is not very convenient in actual use.
This can be avoided by retrofitting the decorator, making the decorator function more widely useful.
装饰器定义:让被装饰函数接收参数

import time


def decorator2(func):
    def inner(*args, **kwargs):
        begin = time.time()

        func(*args, **kwargs)  # 执行被装饰的函数

        end = time.time()
        print(f"函数`{
      
      func.__name__}`运行的总时间为:{
      
      end - begin:.3}")

    return inner

Places that need to be modified:
1. Add collection position parameters and collection keyword parameters decoratorto the internal function of the decorator function when defining 2. In the internal function body of the decorator function , when executing the function decorated by the decorator, Pass in parameters by unpacking them.inner
decoratorinnerfunc
装饰带有参数的函数:

@decorator2
def mysum2(a, b):
    from time import sleep
    total = a
    for _ in range(total + 1, b + 1):
        sleep(0.01)
        total += _
    print(total)


mysum2(1, 100)

The total time for the 5050
function mysum1to run is: 1.56

5.4.4.2.3 Decorator function receives parameters

By modifying the decorator above, the decorated function can input parameters. The above decorator function decorator2can calculate the execution time of the decorated function, but it can only get the execution time once. If you want to get the time of execution any time through parameters, you need to make the decorator able to receive parameters.
装饰器定义:装饰器接收参数

import time


def decorator3(n):
    def inner(func):
        def wrapper(*args, **kwargs):
            begin = time.time()
            for _ in range(iteration):
                func(*args, **kwargs)
            end = time.time()
            print(f"函数`{
      
      func.__name__}`运行的总时间为:{
      
      end - begin:.3}")

        return wrapper

    return inner

Things that need to be improved:
1. The decorator function does not pass parameters to the decorated function at this time, but defines the parameters of the decorator itself. A positional parameter n is used in the demonstration. If it is more complicated in the future In the case of keyword parameters, *args, **kwargs, etc. can also be used to collect parameters.
2. Internal functions are innerused to collect decorated functions.
3. The inner function innerof the inner function is wrapperused to collect the parameters of the decorated function. And write commands that need to be enhanced. Finally, to execute the decorated function is actually to execute this wrapperfunction.
装饰器接收参数:

import time


def decorator3(n):
    def inner(func):
        def wrapper(*args, **kwargs):
            begin = time.time()
            for _ in range(n):
                func(*args, **kwargs)
            end = time.time()
            print(f"函数`{
      
      func.__name__}`运行的总时间为:{
      
      end - begin:.3}")

        return wrapper

    return inner


@decorator3(10)
def mysum3(a, b):
    from time import sleep
    total = a
    for _ in range(total + 1, b + 1):
        sleep(0.01)
        total += _
    print(total)


mysum3(1, 10)
# 等价于:mysum3 = decorator3(10)(mysum3)

55
55
55
55
55
55
55
55
55
55 The total time the
function mysum3ran was: 1.41

5.4.4.2.4 The return value of the decorator

If you understand the content of the previous section, it is easy to think that as long as wrapperreturn is in the function, it is the return value of the decorated function.
装饰器定义:接收被装饰函数的返回值

import time


def decorator3(n):
    def inner(func):
        def wrapper(*args, **kwargs):
            begin = time.time()
            for _ in range(n):
                func(*args, **kwargs)
            end = time.time()
            print(f"函数`{
      
      func.__name__}`运行的总时间为:{
      
      end - begin:.3}")
            return end - begin
        return wrapper

    return inner

1. In the above code, return end – begin is the return value of the decorator. Can be received via a variable.

@decorator3(3)
def mysum3(a, b):
    from time import sleep
    total = a
    for _ in range(total + 1, b + 1):
        sleep(0.01)
        total += _
    print(total)


total_time = mysum3(1, 10)
print(total_time)

The total time the function mysum3took to run was: 1.42
1.4218323230743408

5.4.4.2.5 Multiple decorators decorate the same function

For a function, you can use multiple decorators to decorate it, written as follows:

@decorator1
@decorator2
def 被装饰函数():
	pass

For functions decorated by multiple decorators, the decoration order is from nearest to far, that is, decorator2 will be decorated first, and then decorator1.

Guess you like

Origin blog.csdn.net/crleep/article/details/128514886