[PYTHON] Higher-order functions and decorators

Learn about higher-order functions and Python's decorator notation, which are a bit of a great version of functions.

function

When there is a set A and a set B, the rule f that uniquely determines the elementf (a)of the set B for any element ʻa` of the set A is called a function from A to B.

Consider a function times2 that doubles its arguments and returns

def times2(i):
    return i * 2

At this time, if 0, 1, 2 is given as ʻi`,

And a function from set ([0,1,2]) to a set of integers is defined.

times2.png

Functions in programming

Functions have the following effects in programming:

By mastering the higher-order functions explained below, there are more situations where these effects can be obtained.

Function as an argument

Consider the process of applying the same value 1 to the functions that multiply the arguments by 0, multiply by 1, and double the arguments.

Compared to the definition of the function times2: set ([0, 1, 2])-> Int, the correspondence rule from the set of functionsset ([times0, times1, times2])to the set of integers I can see it.

apply1.png

ʻApply1` can be implemented as

def apply1(f):
    return f(1)

A function that takes a function as an argument (or a function that returns a function as seen in the next section) is called a higher-order function.

Function as a return value

A higher-order function that takes a number as an argument and returns a function

def times_n(n):
    def f(i):
        return n * i
    return f

If you use, you can use the times0, times1, times2 that came out earlier.

times0 = times_n(0)
times1 = times_n(1)
times2 = times_n(2)

Can be defined as.

times_n.png

>>> apply1(times0)
0
>>> apply1(times1)
1
>>> apply1(times2)
2

Functions from a set of functions to a set of functions

If you use a higher-order function that takes a function as an argument and returns the function

Etc. can be written in a reusable form.

def dot(g):
    "f -> g . f"
    def func(f):
        def composite(i):
            return g(f(i))
        return composite
    return func
>>> (lambda i: i*2)((lambda i: i+5)(1))
12
>>> f = dot(lambda i: i*2)(lambda i: i+5)
>>> f(1)
12

Decorator notation

When there is a higher-order function decorator that takes a function as an argument and returns the function

@decorator
def function(argument):
    # ...

Will replace function withdecorator (function).

Decorator example

Write a decorator that prints a log of function calls and apply it to inefficient recursive functions.

def trace(function):
    "Decorator to print call logs"
    def inner(*args):
        "Print call logs before and after function"
        print("{0}{1}".format(function.__name__, args))
        ret = function(*args)
        print("{0}{1} ==> {2}".format(function.__name__, args, ret))
        return ret
    return inner


@trace
def fib(n):
    "Find the Fibonacci number"
    if n == 0:
        return 0
    if n == 1:
        return 1
    return fib(n-2) + fib(n-1)

You can see that the same calculation has been done many times.

% python3 -c 'import fib; fib.fib(4)'
fib(4,)
fib(2,)
fib(0,)
fib(0,) ==> 0
fib(1,)
fib(1,) ==> 1
fib(2,) ==> 1
fib(3,)
fib(1,)
fib(1,) ==> 1
fib(2,)
fib(0,)
fib(0,) ==> 0
fib(1,)
fib(1,) ==> 1
fib(2,) ==> 1
fib(3,) ==> 2
fib(4,) ==> 3

TIPS: Attribute inheritance

In the previous example, the docstirng of fib and the function name would be ʻinner`.

>>> fib.__doc__
'Print call logs before and after function'
>>> fib.__name__
'inner'

→ It's painful when applying the same decorator to multiple functions

from functools import wraps


def trace(function):
    "Decorator to print call logs"
    @wraps(function)
    def inner(*args):
        "Print call logs before and after function"
    ...

If you leave

>>> fib.__doc__
'Find the Fibonacci number'
>>> fib.__name__
'fib'

And inherits the docstring and name of the original function. Useful when viewing a stack trace

TIPS: * args and ** kwargs

You can write more generic decorators using * args and ** kwargs.

TIPS: Do not replace

Decorators don't necessarily have to replace a function with a new one. For example

Etc. are also possible.

Summary

Recommended Posts

Higher-order functions and decorators
Python higher-order functions and comprehensions
Anonymous and map functions
Python 3 sorted and comparison functions
Class inheritance and super functions
Comparison of how to use higher-order functions in Python 2 and 3
About python dict and sorted functions
Formulas and functions (updated as appropriate)
[Python] What are @classmethods and decorators?
GCP: Link Functions and Pub / Sub
Understand Armijo's rules and convex functions
Tips for replacing and debugging functions
Graph trigonometric functions with numpy and matplotlib
Google Cloud Functions log output (and addicted)
Use Python and MeCab with Azure Functions
Correspondence between Python built-in functions and Rust