Packages that support functional programming

Posted by andyd34 on Wed, 05 Jan 2022 00:29:06 +0100

Packages that support functional programming

Python's goal is not to become a functional language, but thanks to the support of packages such as operator and functools, the functional programming style can also be handy

operator module

In functional programming, it is often necessary to use arithmetic operators as functions. For example, it is not appropriate to use recursion to calculate factorials. sum function can be used for summation, but there is no such function for quadrature. You can use the reduce function, but you need a function to calculate the product of two elements in the sequence

# Use the reduce function and an anonymous function to calculate factorials
from functools import reduce


def fact(n):
    return reduce(lambda a, b: a * b, range(1, n + 1))

The operator module provides corresponding functions for multiple arithmetic operators to avoid writing trivial anonymous functions such as lambda a, b:a*b. Use arithmetic operators

from functools import reduce
from operator import mul


def fact(n):
    return reduce(mul, range(1, n + 1))

There is also a class of functions in the operator module that can replace lambda expressions that take elements from sequences or read object attributes: therefore, itemsetter and attrgetter actually build their own functions

metro_data = [('Tokyo', 'JP', 36.933, (35.689722, 139.69167)),
              ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889))]
from operator import itemgetter

for city in sorted(metro_data, key=itemgetter(1)):
    print(city)
# The function of itemsetter (1) is the same as that of lambda fields:fields[1]. It creates a function that accepts a collection and returns elements with index 1
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889))
('Tokyo', 'JP', 36.933, (35.689722, 139.69167))
cc_name = itemgetter(1, 0)
for city in metro_data:
    print(cc_name(city))
# If you pass multiple parameters to itemsetter, the function it builds will return tuples of the extracted values
('JP', 'Tokyo')
('IN', 'Delhi NCR')

Itemsetter uses the [] operator, so it supports not only sequences, but also mappings and any implementation__ getitem__ Class of method

Attrgetter is similar to itemsetter in that it creates a function to extract the attributes of an object by name. If multiple attribute names are passed to attrgetter, it will also return tuples of extracted values. In addition, if the parameter name contains a point number, attrgetter will go deep into the nested object to obtain the specified attribute.

# Define a namedtuple named metro_data, handle it with attrgetter
from collections import namedtuple

LatLong = namedtuple('LatLong', 'lat long')  # Defining LatLong with namedtuple
Metropolis = namedtuple('Metropolis', 'name cc pop coord')  # Define Metropolis
metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long)) for name, cc, pop, (lat, long) in
               metro_data]  # Building metro using a Metropolis instance_ Areas list. Use nested tuple unpacking extraction (lat, long), and then use them to build LatLong as the coord attribute of Metropolis
metro_areas[0]
Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLong(lat=35.689722, long=139.69167))
metro_areas[0].coord.lat  # Deep metro_areas[0] get its latitude
35.689722
from operator import attrgetter

name_lat = attrgetter('name', 'coord.lat')  # Define an attrgetter to get the name attribute and nested coord Lat attribute
for city in sorted(metro_areas, key=attrgetter('coord.lat')):  # Use attrgetter again to sort the list of cities by latitude
    print(name_lat(city))  # Using the defined attrgetter, only the city name and latitude are displayed
('Delhi NCR', 28.613889)
('Tokyo', 35.689722)
# Here are some of the functions defined in the operator module (function names starting with are omitted because they are basically implementation details)
import operator

[name for name in dir(operator) if not name.startswith('_')]
['abs',
 'add',
 'and_',
 'attrgetter',
 'concat',
 'contains',
 'countOf',
 'delitem',
 'eq',
 'floordiv',
 'ge',
 'getitem',
 'gt',
 'iadd',
 'iand',
 'iconcat',
 'ifloordiv',
 'ilshift',
 'imatmul',
 'imod',
 'imul',
 'index',
 'indexOf',
 'inv',
 'invert',
 'ior',
 'ipow',
 'irshift',
 'is_',
 'is_not',
 'isub',
 'itemgetter',
 'itruediv',
 'ixor',
 'le',
 'length_hint',
 'lshift',
 'lt',
 'matmul',
 'methodcaller',
 'mod',
 'mul',
 'ne',
 'neg',
 'not_',
 'or_',
 'pos',
 'pow',
 'rshift',
 'setitem',
 'sub',
 'truediv',
 'truth',
 'xor']

Most of these functions are self-evident. Start with i, followed by the names of another operator (such as iadd, iand, etc.), corresponding to the incremental assignment operator (such as + =, & =, etc.). If the first parameter is variable, these operators modify it in place; Otherwise, the function is the same as the function without i and returns the operation result directly

In the operator module, the function of methodcaller is similar to that of attrgetter and itemsetter. It will create its own functions. The function created by methodcaller will call the method specified by the parameter on the object

from operator import methodcaller

s = 'The time has come'
upcase = methodcaller('upper')
upcase(s)
'THE TIME HAS COME'
hiphenate = methodcaller('replace', ' ', '-')  # methodcaller can freeze some parameters
hiphenate(s)
'The-time-has-come'

Use functools Partial freeze parameters

functools module provides a series of high-order functions, of which the most well-known is the reduce function

functools.partial this higher-order function is used to partially apply a function. Partial application refers to creating a new callable object based on a function and fixing some parameters of the original function. Using this function, you can adapt a function that accepts one or more parameters into an API that requires callback, so there are fewer parameters

# Use partial to adapt two parameter functions into callable objects that require a single parameter
from operator import mul
from functools import partial

triple = partial(mul, 3) # Use mul to create a triple function and set the first positioning parameter to 3
triple(7) # Test triple function
21
list(map(triple,range(1,10))) # Using the triple function in a map
[3, 6, 9, 12, 15, 18, 21, 24, 27]
# Use partial to build a convenient Unicode normalization function
import unicodedata ,functools
nfc=functools.partial(unicodedata.normalize,'NFC')
# The first parameter of partial is the callable object, followed by any positioning parameter and keyword to be bound