Skip to main content

Operator overload for function composition.

Project description

This module allows adding function composition with the | operator to any function, method, class, or other callable.

compose is used for the function composition. wrapt is used to add the operator as transparently and unintrusively as possible. This ensures that:

  1. The | composition operator does not interfere with any other functionality, such as other operators, type checks, and (optionally) Python 3.10’s | type union operator.

  2. |-composed functions still work with signature introspection, method binding, pickling, and so on.

Versioning

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

Installation

pip install compose-operator

Usage

Basics

Import composable:

>>> from compose_operator import composable

A simple inline composition:

>>> stringify_as_integer = composable(int) | str
>>> stringify_as_integer(12.3)
'12'

Either side of the operation can be marked composable:

>>> stringify_as_integer = int | composable(str)
>>> stringify_as_integer(12.3)
'12'

Of course, you can use composable as a decorator:

>>> @composable
... def foo(qux):
...     qux + 42
...
>>> (foo | str)(8)
'50'

composable is “sticky”

Of course, composable “sticks” to the result of |, so we can chain |:

>>> (composable(str) | int | float)(42)
42.0

composable will also stick to return values that are callable, so it combines out-of-the-box with currying, partial application, and so on:

>>> import functools
>>> import operator
>>> import toolz
>>>
>>> partial = composable(functools.partial)
>>> add1 = partial(operator.add, 1)
>>> (add1 | str)(2)
'3'
>>> curry = composable(toolz.curry)
>>> add = curry(operator.add)
>>> (add(2) | float)(2)
4.0

Composable Classes

If you want to decorate a class so that the class is composable, use @composable_constructor - that way, normal class functionality such as | for type unions still works:

>>> from compose_operator import composable_constructor
>>>
>>> from dataclasses import dataclass
>>>
>>> @composable_constructor
>>> @dataclass
... class MyClass:
...     x: int
...
>>> isinstance(1, int | MyClass)
True
>>> isinstance("hello!", int | MyClass)
False
>>> isinstance(MyClass(0), int | MyClass)
True
>>> (operator.add | MyClass)(3, 2)
MyClass(x=5)

You can also use this to wrap existing classes:

>>> int_ = composable_constructor(int)
>>> (int_ | (lambda x: x + 2))(4.2)
6

Of course, when you need two classes to compose, even if you’ve wrapped one or both of them with composable_constructor, you can still force them to compose with composable:

>>> (composable(int) | MyClass)("7")
MyClass(x=7)
>>> (int | composable(MyClass))("8")
MyClass(x=8)

Composable Callable Objects

If you are defining a class with a __call__ method, you can make its instances automatically composable by using composable_instances:

>>> from compose_operator import composable_instances
>>>
>>> @composable_instances
... class Greeter:
...     def __init__(self, target):
...         self._target = target
...     def __call__(self):
...         return f"Hello, {self._target}!"
...
>>> world_greeter = Greeter("world")
>>> world_greeter()
'Hello, world!'
>>> (world_greeter | list)()
['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!']

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-operator-0.5.1.tar.gz (4.8 kB view hashes)

Uploaded Source

Built Distribution

compose_operator-0.5.1-py3-none-any.whl (5.0 kB view hashes)

Uploaded Python 3

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