Skip to main content

The classic ``compose``, with all the Pythonic features.

Project description

The classic compose, with all the Pythonic features.

This compose follows the lead of functools.partial and returns callable compose objects which:

  • have a regular and unambiguous repr,

  • retain correct signature introspection,

  • allow introspection of the composed callables,

  • can be type-checked,

  • can be weakly referenced,

  • can have attributes,

  • will merge when nested, and

  • can be pickled (if all composed callables can be pickled).

This compose also fails fast with a TypeError if any argument is not callable, or when called with no arguments.

This compose even provides an acompose variant which does the right thing with async code.

Versioning

This library’s version numbers follow the SemVer 2.0.0 specification.

Installation

pip install compose

Usage

Import compose:

from compose import compose

All the usual function composition you know and love:

>>> def double(x):
...     return x * 2
...
>>> def increment(x):
...     return x + 1
...
>>> double_then_increment = compose(increment, double)
>>> double_then_increment(1)
3

Of course any number of functions can be composed:

>>> def double(x):
...     return x * 2
...
>>> times_eight = compose(douple, double, double)
>>> times_16 = compose(double, double, double, double)

We still get the correct signature introspection:

>>> def f(a, b, c=0, **kwargs):
...     pass
...
>>> def g(x):
...     pass
...
>>> g_of_f = compose(g, f)
>>> import inspect
>>> inspect.signature(g_of_f)
<Signature (a, b, c=0, **kwargs)>

And we can inspect all the composed callables:

>>> g_of_f.functions  # in order of execution:
(<function f at 0x4048e6f0>, <function g at 0x405228e8>)

When programmatically inspecting arbitrary callables, we can check if we are looking at a compose instance:

>>> isinstance(g_of_f, compose)
True

We can compose async code by using acompose:

>>> import asyncio
>>> from compose import acompose
>>>
>>> async def get_data():
...     await asyncio.sleep(0)
...     return 42
...
>>> get_and_double_data = acompose(double, get_data)
>>> asyncio.run(get_and_double_data())
84

Of course we can compose any number of async and regular functions, in any order:

>>> async def async_double(x):
...     await asyncio.sleep(0)
...     return x * 2
...
>>> async_times_16 = acompose(async_double, double, async_double, double)
>>> asyncio.run(async_times_16(1))
16

compose and acompose instances are distinct types:

>>> isinstance(async_times_16, acompose)
True
>>> isinstance(async_times_16, compose)
False
>>> isinstance(g_of_f, acompose)
False

Recipes

  • If you want composing zero functions to be the identity function:

    def identity(x):
        return x
    
    icompose = partial(compose, identity)
  • To compose arguments in reverse order:

    def rcompose(*functions):
        return compose(*reversed(functions))
  • When you need composition to return a regular Python function:

    def fcompose(*functions):
        composed = compose(*functions)
        return lambda *args, **kwargs: composed(*args, **kwargs)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

compose-1.2.8.tar.gz (5.0 kB view hashes)

Uploaded Source

Built Distributions

compose-1.2.8-py38-none-any.whl (4.2 kB view hashes)

Uploaded Python 3.8

compose-1.2.8-py35-none-any.whl (4.2 kB view hashes)

Uploaded Python 3.5

compose-1.2.8-py2.py30-none-any.whl (4.1 kB view hashes)

Uploaded Python 2 Python 3.0

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page