Functional programming: Functions passed as arguments are called higher-order functions. Functional programming refers to this highly abstract programming paradigm.

def add(x, y, f): 
  return f(x) + f(y)
print(add(-5, 6, abs))
11
Copy the code

###map/reduce

  • map()A function takes two arguments, one function and one可迭代.mapApply the passed function to each element of the sequence in turn, treating the result as newIteratorTo return. So for example, let’s say we have a functionf(x)=x2I’m going to apply this function to alist [1, 2, 3, 4, 5, 6, 7, 8, 9]Up, you can use itmap()The implementation is as follows:
>>> def f(x):
...  return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
The first argument passed to #map() is f, the function object itself. Since r is an Iterator, and iterators are lazy sequences, we use the list() function to evaluate the entire sequence and return a list.
Copy the code
  • reduceTo apply a function to a sequence[x1, x2, x3, ...]The function must take two arguments,reduceIf the result is cumulative with the next element in the sequence, the result is:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
Copy the code

For example, summing over a sequence can be implemented using reduce:

>>> from functools import reduce
>>> def add(x, y):
...    return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
Copy the code

Write a function that converts STR to int using Map and reduce:

>>> from functools import reduce
>>> def fn(x, y):
...    return x * 10 + y
...
>>> def char2num(s):
...        return {'0': 0.'1': 1, '2': 2.'3': 3.'4': 4.'5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
...
>>> reduce(fn, map(char2num, '13579'13579))Copy the code

Further simplified by lambda functions (anonymous functions) :

from functools import reduce
def char2num(s): 
    return {'0': 0.'1': 1, '2': 2.'3': 3.'4': 4.'5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2int(s): 
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))
Copy the code
  • Use the map() function to capitalize the user’s input of a non-canonical English name and lowercase canonical names. Input: [‘ Adam ‘, ‘LISA’, ‘barT’], output: [‘ Adam ‘, ‘LISA’, ‘barT’] :
L = ['adam'.'LISA'.'barT'.'AAAAAA']
def normalize(name):
    return name[0].upper{} + name[1:].lower()
print(list (normalize(L)))'Adam'.'Lisa'.'Bart']
Copy the code
  • Write a str2float function using map and reduce to convert string ‘123.456’ to float 123.456:
def str2Int(s):
    return {'0': 0.'1' : 1, '2': 2.'3': 3.'4': 4.'5' : 5, '6' : 6, '7' : 7, '8' : 8, '9' : 9}[s]
def str2float(name):
    if name.find(".") == -1:
        return reduce(lambda x, y: x * 10 + y, map(str2Int, name))
    else:
        n = name.index('. ')
        s = name[:n] + name[n + 1:]
        return reduce(lambda x, y: x * 10 + y, map(str2Int, s)) / (10 ** (len(name) - (n + 1)))
print(str2float('12332.1'))
# output :12332.1
Copy the code

The ###filter() function is used to filter sequences.

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

For example, in a list, if you delete the even numbers and keep only the odd numbers, you might write:

def is_odd(n):
  returnIs_odd, [1,2,3,4,5,6,7,8,9][1, 5, 9, 15]
Copy the code

A loop is a number that reads from left to right as well as from right to left, for example, 12321,909. Please use filter() to filter out non-loops:

def is_palindrome(n):
   m = str(n)
   if m == m[::-1]:
       returnN output = filter(is_palindrome, range(0,90))print(list(output))
    
Copy the code

Python’s built-in sorted() function sorts a list:

>>> 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 that can also accept a key function for custom sorting, such as sorting by absolute size:

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

Now, we propose that sorting should ignore case and sort alphabetically. To implement this algorithm, we don’t have to change much of the existing code, as long as we can use a key function to map strings to case-insensitive sorting. To compare two strings regardless of case is to make both strings uppercase (or lowercase) first and then compare.

>>> sorted(['bob'.'about'.'Zoo'.'Credit'], key=str.lower)
['about'.'bob'.'Credit'.'Zoo']
Copy the code

To reverse sort, you don’t have to change the key function; you can pass a third parameter reverse=True

>>> sorted(['bob'.'about'.'Zoo'.'Credit'], key=str.lower, reverse=True)
['Zoo'.'Credit'.'bob'.'about']
Copy the code

Given that we use a set of tuples to represent student names and grades: L = [(‘Bob’, 75), (‘Adam’, 92), (‘Bart’, 66), (‘Lisa’, 88)] please use sorted() to sort the above lists separately by name:

def by_name(t):
    return t[0]
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]print(sorted(L,key = by_name))
#[('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
Copy the code
def by_score(t):
    return t[1]
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]print(sorted(L,key = by_score))
#[('Bart', 66), ('Bob', 75), ('Lisa', 88), ('Adam', 92)]
Copy the code

### returns the function

Function as return Value In addition to accepting functions as arguments, higher-order functions can also return functions as result values.

Let’s implement a sum of variable arguments. In general, the summation function is defined as follows:

def calc_sum(*args):
   ax = 0
     for n in args:
      ax = ax + n
     return ax
Copy the code

But what if you don’t need to sum it right away, but later in the code, calculate it as needed? Instead of returning the result of the summation, we can return the summation function:

def lazy_sum(*args):
   def sum():
       ax = 0 for n in args:
           ax = ax + n
       return ax
   return sum
Copy the code

When we call lazy_sum(), instead of returning the sum, we return the summation function:

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f<function lazy_sum.<locals>.sum at 0x101c6ed90>
Copy the code

The result of the summation is actually computed when f is called:

>>> f()
25
Copy the code

### anonymous function :lambda

>>> 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

What is the difference between lambda: x+y, lambda x, y: x+y and lambda x=x, y=y: x+y?

#lambda: x+y #lambda: x+y
def build(x, y):
    return lambda: x * x + y * y
print(build (2, 4), (),# 20
# is equivalent to:
def fn(x,y):
    def lam():
        return  x * x + y * y
    return lam
print(fn (2, 4) (a))# 20
Copy the code
#lambda x, y: x+y is passed inside the anonymous function
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
# Similar to:
def f(x):
   return x * x
Copy the code

Lambda x=x, y=y: x+y #lambda x=x, y=y: x+y

>>> squares = []
>>> for x in range(5):
...  squares.append(lambda n=x: n**2)
Copy the code

Here, n=x creates a new variable n local to the lambda and computed when the lambda is defined so that it has the same value that x had at that point in the loop. This means that the value of n will be 0 in the first lambda, 1 in the second, 2 in the third, and so on. Therefore each lambda will now return the correct result: Execution creates a local n whose value is equal to x

>>> squares[2]()
4
>>> squares[4]()
16
Copy the code

# # # a decorator

>>> def now():
...  print('2015-3-25')... >>> f = now >>> f() 2015-3-25Copy the code

The function object has a __name__ attribute that takes the name of the function:

>>> now.__name__
'now'
>>> f.__name__
'now'
Copy the code

Now, suppose we want to enhance the now() function by, for example, automatically printing a log before and after a function call, but we don’t want to change the definition of the now() function. This way of dynamically adding functionality while the code is running is called a Decorator.

In essence, a decorator is a higher-order function that returns a function. So, we need to define a decorator that prints logs as follows:

import functools
def log(func):
  @functools.wraps(func)
   def wrapper(*args, **kw):#args and KW are parameters to func
     print('call %s():' % func.__name__)
     return func(*args, **kw)
   return wrapper
Copy the code

Look at the log above, because it is a decorator, so it takes a function as an argument and returns a function. We use Python’s @ syntax to place the decorator at the function definition:

@log
def now():
 print('2015-3-25')
Copy the code

Calling now() not only runs the now() function itself, but also prints a line of logs before running now() :

>>> now()
call now():
2015-3-25
Copy the code

If the decorator itself needs to pass in parameters, you need to write a higher-order function that returns the decorator, which can be more complex to write. For example, to customize the log text:

import functools
def log(text):
   def decorator(func):
     @functools.wraps(func)
     def wrapper(*args, **kw):
       print('%s %s():' % (text, func.__name__))
       return func(*args, **kw)
     return wrapper
   return decorator
Copy the code

# # # partial functions

When a function has too many arguments and needs to be simplified, use functools.partial to create a new function that holds some of the arguments of the original function, making it easier to call.

When we introduced function arguments, we said that function calls can be made easier by setting default values for the parameters. And partial functions can do the same thing. Examples are as follows:

The int() function converts a string to an integer. When only a string is passed in, the int() function converts to decimal by default:

>>> int('12345')
12345
Copy the code

But the int() function also provides an additional base argument, which defaults to 10. If we pass in the base argument, we can do the n-base conversion:

>>> int('12345', base=8)
5349
>>> int('12345'16), 74565Copy the code

Int (x, base=2); int(x, base=2); int(x, base=2);

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

Thus, it is very convenient for us to convert binary:

>>> int2('1000000')
64
>>> int2('1010101')
85
Copy the code

Functools.partial is used to create a partial function. Instead of defining int2(), we can create a new function int2 with the following code:

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
Copy the code

So the function of functools.partial is to fix some parameters of a function (that is, set defaults) and return a new function that is easier to call. When you create a partial function, you can actually take the function object, *args, and **kw.

int2 = functools.partial(int, base=2)
Copy the code

Int (); int(); int();

int2('10010')
Copy the code

Is equivalent to:

kw = { 'base': 2 }
int('10010', **kw)
Copy the code

When introduced to:

max2 = functools.partial(max, 10)
Copy the code

10 is actually automatically added to the left as part of *args, i.e. :

max2(5, 6, 7)
Copy the code

Is equivalent to:

args = (10, 5, 6, 7)
max(*args)
Copy the code

So that’s 10.