fictive.transform package¶
Subpackages¶
Module contents¶
transform (i.e., function) objects supporting inverse, composition, & mapping
-
fictive.transform.compose(*functions)[source]¶ creates a function that is the composition of
functionsNB: if a function’s return value is a
tuple, thattuplewill be used as thevar-positionalparameter to the next function in the composition. Otherwise, the entire return value of a function is used as the first positional argument to the next function.import typing def swap(left, right): return right, left def xor(base, variable): return base, base ^ variable def plus_one(iterable: typing.Iterable): return (item + 1 for item in iterable) def times_two(iterable: typing.Iterable): return (item * 2 for item in iterable)
>>> from fictive.transform import compose >>> # NB: functions are applied applied left-to-right >>> compose(swap, xor)(2, 3) (3, 1) >>> compose(xor, swap)(2, 3) (1, 2) >>> # NB: if the return type of a function is not a `tuple`, >>> # it will be provided as the first positional argument to >>> # the next function >>> compose(plus_one, times_two)([3, 1]) <generator object times_two.<locals>.<genexpr> ... >>> compose(plus_one, times_two, tuple)([3, 1]) (8, 4) >>> # NB: if the return type of a function is a `tuple` (e.g., >>> # `swap`), that `tuple` will be unpacked for the next >>> # function in the compositon >>> compose(swap, plus_one, tuple)(2, 3) Traceback (most recent call last): ... TypeError: plus_one() takes 1 positional argument but 2 were given >>> # adding an explicit packing function to the composition >>> # can "bridge" between functions that return `tuple`\s >>> # and functions that expect e.g. a single iterable >>> compose(swap, lambda *args: (args, ), plus_one, tuple)(2, 3) (4, 3)
-
fictive.transform.pack(*args, **kwargs)[source]¶ pack the positional and keyword arguments to a
(args, kwargs)tuple>>> from fictive.transform import pack >>> pack(1, 2, 3, four=4, five=5) == ((1, 2, 3), {'four': 4, 'five': 5}) True
-
fictive.transform.arg_slice(*args)[source]¶ perform a
sliceoperation onargsand return the sliced tuple>>> from fictive.transform import arg_slice >>> arg_slice(2, 5)(1, 2, 3, 4, 5, 6, 7) (3, 4, 5)
-
class
fictive.transform.Transform(function=None, method=None, inverse=None)[source]¶ Bases:
objectinterface for inverse, composition, and mapping operations on functions
Subclassing:
from fictive.transform import Transform class AddN(Transform): def __init__(self, n): super().__init__() self.n = n def operation(self, args): return (arg + self.n for arg in args) def inverse_operation(self, args): return (arg - self.n for arg in args)
>>> add_5 = AddN(5) >>> add_5(range(4)) <generator object AddN.operation...> >>> list(add_5(range(4))) [5, 6, 7, 8] >>> list(add_5.inverse([5, 6, 7, 8])) [0, 1, 2, 3]
Ad hoc transforms:
times_two = Transform( #: unpacked positional arguments to iterator method=lambda self, *args: (arg * self.base for arg in args), #: iterator to tuple inverse=lambda self, args: tuple(arg // self.base for arg in args), ) times_two.base = 2
>>> times_two(range(4)) <generator object <lambda>...> >>> list(times_two(range(4))) Traceback (most recent call last): ... TypeError: unsupported operand type(s) for *: 'range' and 'int' >>> list(times_two(*range(4))) [0, 2, 4, 6] >>> times_two.inverse([0, 2, 4, 6]) (0, 1, 2, 3)
Applying
mapahead of time:int_to_str = Transform(str, inverse=int)
>>> multi = int_to_str.map >>> list(multi([1, 2, 3])) ['1', '2', '3'] >>> list(multi.inverse(['1', '2', '3'])) [1, 2, 3]
Compopsition:
>>> transform = (times_two @ tuple @ int_to_str.map) >>> list(transform([1, 2, 3])) ['11', '22', '33']
plus_one = Transform( function=lambda arg: arg + 1, inverse=lambda arg: arg - 1) join_args = Transform( method=lambda self, *args: self.token.join(args), inverse=lambda self, arg: arg.split(self.token)) join_args.token = '.' tuplefy = Transform(tuple, inverse=iter)
>>> transform = \ ... join_args @ tuplefy @ (int_to_str @ plus_one).map @ tuplefy.inverse >>> transform([1, 2, 3]) '2.3.4' >>> transform.inverse('2.3.4') (1, 2, 3)
-
__init__(function=None, method=None, inverse=None)[source]¶ - Parameters
function – set the transform’s operation to a callable that will not receive the transform itself as a bound parameter (i.e., the transform instance will not be the
selfparameter). Exclusive withmethod.method – set the transform’s operation to a callable that will receive the transform itself as a bound parameter (i.e., the transform instance **will* be the
selfparameter). Exclusive withfunction.inverse – set the transform’s inverse operation to the callable. Will be treated as a “function” or a “method” (i.e., whether to provide the transform itself as the first bound parameter) based on which of
functionormethodis also set. (Requires one offunctionormethodto be set.)
-
__matmul__(transform)[source]¶ use the “at” operator (i.e.,
@) to compose the transformNB: this operator uses the standard “infix” notation for function composition, e.g.
(f @ g)(x) == (f(g(x))>>> import operator >>> from fictive.transform import pack, Transform >>> int_to_str = Transform(str, inverse=int) >>> join = Transform(''.join) >>> add = Transform(operator.add) >>> sum_of_strs = add @ tuple @ int_to_str.map >>> sum_of_strs([3, 4]) '34' >>> str_of_sum = int_to_str @ add @ tuple >>> str_of_sum([3, 4]) '7'
-
classmethod
compose(*transforms)[source]¶ create a new transform that is the composition of
transformsNB: transforms are applied “left-to-right” (i.e., postfix)
>>> import operator >>> from fictive.transform import pack, Transform >>> int_to_str = Transform(str, inverse=int) >>> join = Transform(''.join) >>> add = Transform(operator.add) >>> sum_of_strs = Transform.compose(int_to_str.map, tuple, add) >>> sum_of_strs([3, 4]) '34' >>> str_of_sum = Transform.compose(tuple, add, int_to_str) >>> str_of_sum([3, 4]) '7'
-
property
inverse¶ return a transpose of
selffrom fictive.transform import Transform add_five = Transform( function=lambda arg: arg + 5, inverse=lambda arg: arg - 5, )
>>> subtract_five = add_five.inverse >>> subtract_five(8) 3 >>> subtract_five.inverse_operation(3) 8
-
property
map¶ a new transform that will “map” the current transform when called
from fictive.transform import Transform int_to_str = Transform(str, inverse=int)
>>> int_to_str(5) '5' >>> int_to_str_map = int_to_str.map >>> int_to_str_map([1, 2, 3]) <map object at ...> >>> list(int_to_str.map([1, 2, 3])) ['1', '2', '3'] >>> list(int_to_str.inverse.map(['1', '2', '3'])) [1, 2, 3]
-
property
starmap¶ a new transform that will “star map” the current transform
import operator from fictive.transform import Transform add = Transform(operator.add)
>>> add(3, 4) 7 >>> add([3, 4], [5, 6]) [3, 4, 5, 6] >>> add.starmap(([3, 4], [5, 6])) <itertools.starmap object at ...> >>> list(add.starmap(([3, 4], [5, 6]))) [7, 11]
-
classmethod
transpose(transform)[source]¶ create a new transform, swapping the function and inverse
from fictive.transform import Transform add_five = Transform( function=lambda arg: arg + 5, inverse=lambda arg: arg - 5, )
>>> add_five(3) 8 >>> add_five.inverse_operation(8) 3 >>> subtract_five = Transform.transpose(add_five) >>> subtract_five(8) 3 >>> subtract_five.inverse_operation(3) 8
-