Pipelines and partial application of functions, why is it in Python



One of the main advantages of Python is its expressiveness. The functionality of the language allows you to concisely describe transformations over data. In my opinion, Python lacks some tools that would help describe data transformations more conveniently and supplement the functional component of the language, in particular, "function pipelines" and their partial application. Therefore, in this post I pour water on the possibility and necessity of these funds with experiments for their implementation. I came in many ways for criticism. Enjoy reading!



Briefly about FP in Python and why there are not enough pipelines for example



Python has some pretty handy basic tools like map (), reduce (), filter (), lambda functions, iterators, and generators. I advise everyone unfamiliar with this article . In general, it all allows you to quickly and naturally describe transformations over lists, tuples, and so on. Very often (with me and my friends pythonists) what turns outone-liner- essentially a set of sequential transformations, filters, for example:

Kata with CodeWars : Find



∀n∈[a,b]:n=∑0len(n)nii, ni - i-th bit of number n



The task is quite simple, unfortunately (but fortunately for this post), there are no better solutions than head-on.



My decision:



def sum_dig_pow(a, b): # range(a, b + 1) will be studied by the function
    powered_sum = lambda x: sum([v**(i+1) for i,v in enumerate(map(lambda x: int(x), list(str(x))))])
    return [i for i in range(a,b+1) if powered_sum(i)==i]


Using the means of FP as it is, we get a parenthesis hell "from the inside out". The pipeline could fix that.



Function pipelines



By sim I mean in the ideal case (the "|" operator is personal preference):



# f3(f2(f1(x)))
f1 | f2 | f3 >> x
pipeline = f1 | f2 | f3 
pipeline(x)
pipeline2 = f4 | f5
pipeline3 = pipeline | pipeline2 | f6
...


powered_sum ( ):



powered_sum = str | list | map(lambda x: int(x), *args) | enumerate | [v**(i+1) for i,v in *args] | sum


, . args . , ( ):



from copy import deepcopy

class CreatePipeline:
    def __init__(self, data=None):
        self.stack = []
        if data is not None:
            self.args = data

    def __or__(self, f):
        new = deepcopy(self)
        new.stack.append(f)
        return new

    def __rshift__(self, v):
        new = deepcopy(self)
        new.args = v
        return new

    def call_logic(self, *args):
        for f in self.stack:
            if type(args) is tuple:
                args = f(*args)
            else:
                args = f(args)
        return args

    def __call__(self, *args):
        if 'args' in self.__dict__:
            return self.call_logic(self.args)
        else:
            return self.call_logic(*args)


, , , kwargs, .



pipe = CreatePipeline()
powered_sum = pipe | str | list | (lambda l: map(lambda x: int(x), l)) | enumerate | (lambda e: [v**(i+1) for i,v in e]) | sum


, , , , , .





( ):



def f_partitial (x,y,z):
    return x+y+z
v = f_partial(1,2)
# type(v) = -    f_partial,  : ['z']
print(v(3))
# 
print(f_partial(1,2,3))


( ). pipe :



powered_sum = pipe | str | list | map(lambda x: int(x)) | enumerate | (lambda e: [v**(i+1) for i,v in e]) | sum
# map       
# map(lambda x: int(x))()  


map(lambda x: int(x)) .

:



from inspect import getfullargspec
from copy import deepcopy

class CreatePartFunction:
    def __init__(self, f):
        self.f = f
        self.values = []

    def __call__(self, *args):
        args_f = getfullargspec(self.f)[0]
        if len(args) + len(self.values) < len(args_f):
            new = deepcopy(self)
            new.values = new.values + list(args)
            return new
        elif len(self.values) + len(args) == len(args_f):
            return self.f(*tuple(self.values + list(args)))


:



#     inspect   map
m = lambda f, l: map(f, l)
#        
pmap = CreatePartFunction(m)
powered_sum = pipe | str | list | pmap(lambda x: int(x)) | enumerate | (lambda e: [v**(i+1) for i,v in e]) | sum


( ), , , , :



def f (x,y,z):
    return x+y+z
f = CreatePartFunction(f)
# 
print(f(1,2,3))
# 
print(f(1,2)(3))
print(f(1)(2,3))
#  
# 2(3) - int  callable
print(f(1)(2)(3))
# 
print((f(1)(2))(3))




, , , , , , , .




All Articles