In R (thanks to magritrr
) you can now perform operations with a more functional piping syntax via %>%
. This means that instead of coding this: <
Building pipe
with Infix
As hinted at by Sylvain Leroux, we can use the Infix
operator to construct a infix pipe
. Let's see how this is accomplished.
First, here is the code from Tomer Filiba
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
The pipe operator passes the preceding object as an argument to the object that follows the pipe, so x %>% f
can be transformed into f(x)
. Consequently, the pipe
operator can be defined using Infix
as follows:
In [1]: @Infix
...: def pipe(x, f):
...: return f(x)
...:
...:
In [2]: from math import sqrt
In [3]: 12 |pipe| sqrt |pipe| str
Out[3]: '3.4641016151377544'
A note on partial application
The %>%
operator from dpylr
pushes arguments through the first argument in a function, so
df %>%
filter(x >= 2) %>%
mutate(y = 2*x)
corresponds to
df1 <- filter(df, x >= 2)
df2 <- mutate(df1, y = 2*x)
The easiest way to achieve something similar in Python is to use currying. The toolz
library provides a curry
decorator function that makes constructing curried functions easy.
In [2]: from toolz import curry
In [3]: from datetime import datetime
In [4]: @curry
def asDate(format, date_string):
return datetime.strptime(date_string, format)
...:
...:
In [5]: "2014-01-01" |pipe| asDate("%Y-%m-%d")
Out[5]: datetime.datetime(2014, 1, 1, 0, 0)
Notice that |pipe|
pushes the arguments into the last argument position, that is
x |pipe| f(2)
corresponds to
f(2, x)
When designing curried functions, static arguments (i.e. arguments that might be used for many examples) should be placed earlier in the parameter list.
Note that toolz
includes many pre-curried functions, including various functions from the operator
module.
In [11]: from toolz.curried import map
In [12]: from toolz.curried.operator import add
In [13]: range(5) |pipe| map(add(2)) |pipe| list
Out[13]: [2, 3, 4, 5, 6]
which roughly corresponds to the following in R
> library(dplyr)
> add2 <- function(x) {x + 2}
> 0:4 %>% sapply(add2)
[1] 2 3 4 5 6
Using other infix delimiters
You can change the symbols that surround the Infix invocation by overriding other Python operator methods. For example, switching __or__
and __ror__
to __mod__
and __rmod__
will change the |
operator to the mod
operator.
In [5]: 12 %pipe% sqrt %pipe% str
Out[5]: '3.4641016151377544'
There is very nice pipe
module here https://pypi.org/project/pipe/
It overloads | operator and provide a lot of pipe-functions like add, first, where, tail
etc.
>>> [1, 2, 3, 4] | where(lambda x: x % 2 == 0) | add
6
>>> sum([1, [2, 3], 4] | traverse)
10
Plus it's very easy to write own pipe-functions
@Pipe
def p_sqrt(x):
return sqrt(x)
@Pipe
def p_pr(x):
print(x)
9 | p_sqrt | p_pr