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