From 921cfd45ed41847e7e0cfb26d33339f636cc942d Mon Sep 17 00:00:00 2001 From: TobiasPleyer Date: Tue, 29 Jan 2019 23:10:47 +0100 Subject: [PATCH 1/3] bpo-35853: More higher order function combinators in functools Definitions added for - identity: The identity function which returns its input - compose: Create a function pipeline (threaded computation) - sequence: Use compose without binding it to a variable --- Lib/functools.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Lib/functools.py b/Lib/functools.py index 6233c30c203ea7..0770a22a54c16e 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -11,7 +11,8 @@ __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial', - 'partialmethod', 'singledispatch', 'singledispatchmethod'] + 'partialmethod', 'identity', 'compose', 'sequence', + 'singledispatch', 'singledispatchmethod'] from abc import get_cache_token from collections import namedtuple @@ -430,6 +431,36 @@ def _unwrap_partial(func): func = func.func return func + +################################################################################ +### Higher order function combinator primitives +################################################################################ + +def identity(x): + """The identity functions simply returns its argument""" + return x + + +def compose(f, *func_list): + """Compose a list of functions into one new function. The resulting + function will thread the computation results through all of its component + functions, i.e. the return value of the previous function will be the + input to the next function. For example, for f = compose(f1, f2), f(x) + would be equivalent to f2(f1(x)). + """ + def helper(*args, **kwargs): + return reduce(lambda x, f: f(x), func_list, f(*args, **kwargs)) + return helper + + +def sequence(x, f, *func_list): + """Thread the given input argument through all provided functions, + whereupon the return value of a previous function becomes the input value + to the next function and the overall value of the sequence will be the + result of the last function. For example, sequence(x, f1, f2) == f2(f1(x)). + """ + return compose(f, *func_list)(x) + ################################################################################ ### LRU Cache function decorator ################################################################################ From f3900a9ce9e8ab88fde149a44b2f9947e89e8289 Mon Sep 17 00:00:00 2001 From: TobiasPleyer Date: Wed, 30 Jan 2019 00:35:59 +0100 Subject: [PATCH 2/3] bpo-35853: Documentation added in functools.rst --- Doc/library/functools.rst | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index cd59e5bebfd565..4852b06f5fddcd 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -309,6 +309,65 @@ The :mod:`functools` module defines the following functions: See :func:`itertools.accumulate` for an iterator that yields all intermediate values. + +.. function:: identity(x) + + Returns its argument unchanged. This function can be used in situations + in which a default function argument is required. + + >>> from itertools import cycle + >>> from functools import identity + >>> def negate(x): return -x + ... + >>> def inverse_second(iterable): + ... return map(lambda t: t[0](t[1]), zip(cycle([identity, negate]), iterable)) + ... + >>> print(list(inverse_second([1,2,3,4,5,6,7,8,9]))) + [1, -2, 3, -4, 5, -6, 7, -8, 9] + + +.. function:: compose(f1[, f2[, f3[...]]]) + + Return a new *function* **f**. The resulting function **f** will be a so called + threaded computation. A threaded computation will use the return value of the + first function as the input value of the second function and the return value + of the second function will become the input o the third function until no + more functions are left. The last return value will be the final return value + of **f**. In summary ``compose(f1,f2,f3)(x) == f3(f2(f1(x)))``. + + This is also called a pipeline, because it follows the same logic as the + pipe character of the UNIX shell `echo x | f1 | f2 | f3`. + + >>> from functools import compose, partial + >>> rectify_data = compose( + ... lambda s: s.split('\n'), + ... partial(filter, lambda s: len(s)>0), + ... partial(map, lambda s: s.lower()), + ... lambda ss: '\n'.join(ss) + ... ) + >>> data = "Line1\nLiNe2\n\n\n\nlINE3" + >>> rectify_data(data) + 'line1\nline2\nline3' + + +.. function:: sequence(x, f1[, f2[, f3[...]]]) + + Apply the argument *x* to the composition of the functions. This is a + helper function and equivalent to ``compose(f1,f2,f3)(x)``. + + >>> from functools import sequence, partial + >>> data = "Line1\nLiNe2\n\n\n\nlINE3" + >>> rectified_data = sequence( + ... data, + ... lambda s: s.split('\n'), + ... partial(filter, lambda s: len(s)>0), + ... partial(map, lambda s: s.lower()), + ... lambda ss: '\n'.join(ss) + ... ) + >>> rectified_data + 'line1\nline2\nline3' + + .. decorator:: singledispatch Transform a function into a :term:`single-dispatch Date: Tue, 29 Jan 2019 23:51:40 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Library/2019-01-29-23-51-39.bpo-35853.mOvdrV.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2019-01-29-23-51-39.bpo-35853.mOvdrV.rst diff --git a/Misc/NEWS.d/next/Library/2019-01-29-23-51-39.bpo-35853.mOvdrV.rst b/Misc/NEWS.d/next/Library/2019-01-29-23-51-39.bpo-35853.mOvdrV.rst new file mode 100644 index 00000000000000..0c3bb9920c7055 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-01-29-23-51-39.bpo-35853.mOvdrV.rst @@ -0,0 +1 @@ +Higher order functions are functions which can take other functions as arguments and have functions as return value. Higher order functions allow the creation of new functions out of existing ones and support a functional programming style. The *functools* module now contains more higher order function combinators. \ No newline at end of file