Functional pipes in python like %>% from R's magritrr

后端 未结 14 2185
春和景丽
春和景丽 2020-11-29 16:17

In R (thanks to magritrr) you can now perform operations with a more functional piping syntax via %>%. This means that instead of coding this: <

相关标签:
14条回答
  • 2020-11-29 16:43

    You can use sspipe library. It exposes two objects p and px. Similar to x %>% f(y,z), you can write x | p(f, y, z) and similar to x %>% .^2 you can write x | px**2.

    from sspipe import p, px
    from math import sqrt
    
    12 | p(sqrt) | px ** 2 | p(str)
    
    0 讨论(0)
  • 2020-11-29 16:46

    Does the python language have support for something similar?

    "more functional piping syntax" is this really a more "functional" syntax ? I would say it adds an "infix" syntax to R instead.

    That being said, the Python's grammar does not have direct support for infix notation beyond the standard operators.


    If you really need something like that, you should take that code from Tomer Filiba as a starting point to implement your own infix notation:

    Code sample and comments by Tomer Filiba (http://tomerfiliba.com/blog/Infix-Operators/) :

    from functools import partial
    
    class Infix(object):
        def __init__(self, func):
            self.func = func
        def __or__(self, other):
            return self.func(other)
        def __ror__(self, other):
            return Infix(partial(self.func, other))
        def __call__(self, v1, v2):
            return self.func(v1, v2)
    

    Using instances of this peculiar class, we can now use a new "syntax" for calling functions as infix operators:

    >>> @Infix
    ... def add(x, y):
    ...     return x + y
    ...
    >>> 5 |add| 6
    
    0 讨论(0)
  • 2020-11-29 16:48

    There is no need for 3rd party libraries or confusing operator trickery to implement a pipe function - you can get the basics going quite easily yourself.

    Lets start by defining what a pipe function actually is. At its heart, it is just a way to express a series of function calls in logical order, rather than the standard 'inside out' order.

    For example, lets look at these functions:

    def one(value):
      return value
    
    def two(value):
      return 2*value
    
    def three(value):
      return 3*value
    

    Not very interesting, but assume interesting things are happening to value. We want to call them in order, passing the output of each to the next. In vanilla python that would be:

    result = three(two(one(1)))
    

    It is not incredibly readable and for more complex pipelines its gonna get worse. So, here is a simple pipe function which takes an initial argument, and the series of functions to apply it to:

    def pipe(first, *args):
      for fn in args:
        first = fn(first)
      return first
    

    Lets call it:

    result = pipe(1, one, two, three)
    

    That looks like very readable 'pipe' syntax to me :). I don't see how it is any less readable than overloading operators or anything like that. In fact, I would argue that it is more readable python code

    Here is the humble pipe solving the OP's examples:

    from math import sqrt
    from datetime import datetime
    
    def as_date(s):
      return datetime.strptime(s, '%Y-%m-%d')
    
    def as_character(value):
      # Do whatever as.character does
      return value
    
    pipe("2014-01-01", as_date)
    pipe(12, sqrt, lambda x: x**2, as_character)
    
    0 讨论(0)
  • 2020-11-29 16:48

    Adding my 2c. I personally use package fn for functional style programming. Your example translates into

    from fn import F, _
    from math import sqrt
    
    (F(sqrt) >> _**2 >> str)(12)
    

    F is a wrapper class with functional-style syntactic sugar for partial application and composition. _ is a Scala-style constructor for anonymous functions (similar to Python's lambda); it represents a variable, hence you can combine several _ objects in one expression to get a function with more arguments (e.g. _ + _ is equivalent to lambda a, b: a + b). F(sqrt) >> _**2 >> str results in a Callable object that can be used as many times as you want.

    0 讨论(0)
  • 2020-11-29 16:50

    There is dfply module. You can find more information at

    https://github.com/kieferk/dfply

    Some examples are:

    from dfply import *
    diamonds >> group_by('cut') >> row_slice(5)
    diamonds >> distinct(X.color)
    diamonds >> filter_by(X.cut == 'Ideal', X.color == 'E', X.table < 55, X.price < 500)
    diamonds >> mutate(x_plus_y=X.x + X.y, y_div_z=(X.y / X.z)) >> select(columns_from('x')) >> head(3)
    
    0 讨论(0)
  • 2020-11-29 16:50

    I missed the |> pipe operator from Elixir so I created a simple function decorator (~ 50 lines of code) that reinterprets the >> Python right shift operator as a very Elixir-like pipe at compile time using the ast library and compile/exec:

    from pipeop import pipes
    
    def add3(a, b, c):
        return a + b + c
    
    def times(a, b):
        return a * b
    
    @pipes
    def calc()
        print 1 >> add3(2, 3) >> times(4)  # prints 24
    

    All it's doing is rewriting a >> b(...) as b(a, ...).

    https://pypi.org/project/pipeop/

    https://github.com/robinhilliard/pipes

    0 讨论(0)
提交回复
热议问题