Function Composition¶
Function composition combines multiple functions into a single function. For example, we can compose two functions \(f(x)\) and \(g(x)\) into a new function \(h(x)\) such that \(h(x) = f(g(x))\).
calcpy
can convert a Python function to a component for future function compositions. The component can be passed around, just like other callable objects.
Example: Below codes combine the components calcpy.add
and calcpy.itemgetter
to a composed callable combo
. When the combo
is called with the argument [3, 4, 5]
, it returns 16
since itergetter(1)([3, 4, 5])
returns 4
, itemgetter(2, default=3)([3, 4, 5])
returns 5
, and add([4, 7, 5])
return 16
. Note that add
and itemgetter
are imported after the composition mode is enabled by calcpy.enable_composition()
. And the component itemgetter
is used twice and the two calls are executed independently.
>>> from calcpy import enable_composition
>>> enable_composition()
>>> from calcpy import add, itemgetter
>>> combo = add(itemgetter(1), 7, itemgetter(2, default=3))
>>> combo([3, 4, 5])
16
Composition Mode¶
The feature of function composition works only when the composition mode is enabled. The composition mode is a global setting that affects all functions in the current module.
By default, the composition mode is disabled, meaning that no function compositions are allowed.
To use the function composition feature, you need to enable the composition mode. The composition mode can be enabled by calcpy.enable_composition()
, and can be disabled by calcpy.disable_composition()
.
The composition mode contains three aspects:
args (bool): whether to composite positional arguments.
kwargs (bool): whether to composite keyword arguments.
force_callable (bool): whether to force the results to be callable, even when there are no child callable. If
True
, the composited function will always be a callable. Otherwise, it is a callable only when one positional argument or one keyword argument is a callable.
Composition mode can be configured by the following APIs:
- calcpy.set_composition_mode(args=None, kwargs=None, force_callable=None)[source]¶
Set composition mode.
- Parameters:
args (bool, optional) – whether to enable composition for positional arguments. The default is
None
, meaning that the current status will be kept.kwargs (bool, optional) – whether to enable composition for keyword arguments The default is
None
, meaning that the current status will be kept.force_callable (bool, optional) – whether to force return results to be callable The default is
None
, meaning that the current status will be kept.
Examples
>>> set_composition_mode(args=False, kwargs=False, force_callable=False)
- calcpy.disable_composition()[source]¶
Disable
args
composition andkwargs
composition.Alias of
set_composition_mode(args=False, kwargs=False)
- calcpy.enable_composition()[source]¶
Enable args composition and kwargs composition.
Alias of
set_composition_mode(args=True, kwargs=True)
- calcpy.disable_args_composition()[source]¶
Disable
args
composition.Alias of
set_composition_mode(args=False)
- calcpy.enable_args_composition()[source]¶
Enable
args
composition.Alias of
set_composition_mode(args=True)
- calcpy.disable_kwargs_composition()[source]¶
Disable
kwargs
composition.Alias of
set_composition_mode(kwargs=False)
- calcpy.enable_kwargs_composition()[source]¶
Enable
kwargs
composition.Alias of
set_composition_mode(kwargs=True)
- calcpy.disable_force_callable()[source]¶
Disable force callable.
Alias of
set_composition_mode(force_callable=False)
- calcpy.enable_force_callable()[source]¶
Enable force callable.
Alias of
set_composition_mode(force_callable=True)
- calcpy.args_composition_enabled()[source]¶
Return
True
whenargs
composition is enabled.- Return type:
bool
- calcpy.kwargs_composition_enabled()[source]¶
Return
True
whenkwargs
composition is enabled.- Return type:
bool
- calcpy.composition_enabled()[source]¶
Return
True
whenargs
composition orkwargs
composition or force callable is enabled.- Return type:
bool
- calcpy.composition_mode_context(args=None, kwargs=None, force_callable=None)[source]¶
Context manager to temporarily set composite mode in a
with
statement.- Parameters:
args (bool) – whether to enable composition for positional arguments
kwargs (bool) – whether to enable composition for keyword arguments
force_callable (bool) – whether to force return results to be callable
Examples
>>> force_callable_enabled() False >>> with composition_mode_context(args=False, kwargs=False, force_callable=True): ... force_callable_enabled() True >>> force_callable_enabled() False
Make Component¶
- calcpy.componentize(how, /)[source]¶
Decorate a callable function so that it becomes a building block of function composition.
Need to enable composition mode before using this function.
- Parameters:
how – Callable object that combines multiple results.
- Return type:
callable
Examples
>>> from calcpy import itemgetter >>> enable_composition() >>> max_ = componentize(max) >>> max_(itemgetter(1), 7, itemgetter(2, default=3))([3, 4, 5]) 7 >>> min_ = componentize(min) >>> min_(itemgetter(1), 7, itemgetter(2, default=3))([3, 4, 5]) 4 >>> min_(3, 4, 5) 3 >>> disable_composition()
Shorthand Functions that Support Composition¶
You need to enable the composition mode before import the shorthand functions. If those functions are imported before the composition mode is enabled, they will not be composable.
calcpy.abs_()
: adapted from operator.abs().
calcpy.acos()
: adapted from math.acos().
calcpy.acosh()
: adapted from math.acosh().
calcpy.asin()
: adapted from math.asin().
calcpy.asinh()
: adapted from math.asinh().
calcpy.atan()
: adapted from math.atan().
calcpy.atan2()
: adapted from math.atan2().
calcpy.bin_()
: adapted from bin().
calcpy.bool_()
: adapted from bool().
calcpy.ceil()
: adapted from math.ceil().
calcpy.comb()
: adapted from math.comb().
calcpy.contains()
: adapted from operator.contains().
calcpy.cos()
: adapted from math.cos().
calcpy.cosh()
: adapted from math.cosh().
calcpy.countOf()
: adapted from operator.countOf().
calcpy.degrees()
: adapted from math.degrees().
calcpy.dict_()
: adapted from dict().
calcpy.dist()
: adapted from math.dist().
calcpy.divmod_()
: adapted from divmod().
calcpy.erf()
: adapted from math.erf().
calcpy.erfc()
: adapted from math.erfc().
calcpy.exp()
: adapted from math.exp().
calcpy.exp2()
: adapted from math.exp2().
calcpy.expm1()
: adapted from math.expm1().
calcpy.factorial()
: adapted from math.factorial().
calcpy.float_()
: adapted from float().
calcpy.floor()
: adapted from math.floor().
calcpy.floordiv()
: adapted from operator.floordiv().
calcpy.format_()
: adapted from format().
calcpy.gamma()
: adapted from math.gamma().
calcpy.getitem()
: adapted from operator.getitem().
calcpy.hex_()
: adapted from hex().
calcpy.index()
: adapted from operator.index()..
calcpy.indexOf()
: adapted from operator.indexOf().
calcpy.int_()
: adapted from int().
calcpy.inv()
: adapted from operator.inv().
calcpy.is_()
: adapted from operator.is_().
calcpy.is_not()
: adapted from operator.is_not().
calcpy.isclose()
: adapted from math.isclose().
calcpy.isfinite()
: adapted from math.isfinite().
calcpy.isinf()
: adapted from math.isinf().
calcpy.isinstance_()
: adapted from isinstance().
calcpy.isqrt()
: adapted from math.isqrt().
calcpy.len_()
: adapted from len().
calcpy.lgamma()
: adapted from math.lgamma().
calcpy.list_()
: adapted from list().
calcpy.log()
: adapted from math.log().
calcpy.log2()
: adapted from math.log2().
calcpy.log10()
: adapted from math.log10().
calcpy.log1p()
: adapted from math.log1p().
calcpy.lshift()
: adapted from operator.lshift().
calcpy.max_()
: adapted from max().
calcpy.min_()
: adapted from min().
calcpy.minimial()
calcpy.mod()
: adapted from operator.mod().
calcpy.neg()
: adapted from operator.neg().
calcpy.not_()
: adapted from operator.not_().
calcpy.oct_()
: adapted from oct().
calcpy.ord_()
: adapted from ord().
calcpy.perm()
calcpy.pos()
: adapted from operator.pos().
calcpy.pow_()
: adapted from operator.pow().
calcpy.prod()
: adapted from math.prod().
calcpy.radians()
: adapted from math.radians().
calcpy.range_()
: adapted from range().
calcpy.remainder()
: adapted from math.remainder().
calcpy.round_()
: adapted from round().
calcpy.rshift()
: adapted from operator.rshift().
calcpy.set_()
: adapted from set().
calcpy.setitem()
: adapted from operator.setitem().
calcpy.sorted_()
: adapted from sorted().
calcpy.sqrt()
: adapted from math.sqrt().
calcpy.str_()
: adapted from str().
calcpy.sub()
: adapted from operator.sub().
calcpy.sum_()
: adapted from sum().
calcpy.tan()
: adapted from math.tan().
calcpy.tanh()
: adapted from math.tanh().
calcpy.truediv()
: adapted from operator.truediv().
calcpy.truth()
: adapted from operator.truth().
calcpy.tuple_()
: adapted from tuple().
calcpy.type_()
: adapted from type().
calcpy.zip_()
: adapted from zip().
Chained Composition¶
- calcpy.fun.skewer(*callables)[source]¶
Composite multiple callables into one callable.
- Parameters:
*callables (callable)
- Returns:
A callable that calls all callables in order.
- Return type:
callable
Examples
>>> def minmax(x, y): ... return min(x, y), max(x, y) >>> def mul(x, y): ... return x * y >>> skewered = skewer(minmax, mul) >>> skewered(5, 3) 15
- calcpy.fun.repeat(n=1)[source]¶
Repeat a callable n times.
- Parameters:
n (int) – Number of times to repeat.
- Returns:
a callable that swaps a callable so that it is called n times.
- Return type:
Callable[callable, callable]
Examples
Apply on a function.
>>> repeated = repeat(n=2)(abs) >>> repeated(-3) 3
Use as a decorator.
>>> @repeat(n=2) ... def g(a): ... return a + 1 >>> g(1) 3