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