Learn about higher-order functions and Python's decorator notation, which are a bit of a great version of functions.
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`,
times2(0) == 0
times2(1) == 2
times2(2) == 4
And a function from set ([0,1,2])
to a set of integers is defined.
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.
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.
times0(1) == 0
times1(1) == 1
times2(1) == 2
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(times0) == 0
apply1(times1) == 1
apply1(times2) == 2
ʻ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.
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.
>>> apply1(times0)
0
>>> apply1(times1)
1
>>> apply1(times2)
2
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
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)
.
function = decorator (function)
after defining function
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
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
* args
and ** kwargs
You can write more generic decorators using * args
and ** kwargs
.
Decorators don't necessarily have to replace a function with a new one. For example
setattr
and return the original functionEtc. are also possible.
Recommended Posts