在R(感谢magritrr
)中,您现在可以通过更多功能管道语法执行操作%>%
.这意味着不是编码:
> as.Date("2014-01-01")
> as.character((sqrt(12)^2)
你也可以这样做:
> "2014-01-01" %>% as.Date
> 12 %>% sqrt %>% .^2 %>% as.character
对我来说,这更具可读性,这扩展到数据框之外的用例.python语言是否支持类似的东西?
1> Dunes..:
一种可能的方法是使用一个名为的模块macropy
.Macropy允许您将转换应用于您编写的代码.因此a | b
可以转化为b(a)
.这具有许多优点和缺点.
与Sylvain Leroux提到的解决方案相比,主要优点是您不需要为您感兴趣的函数创建中缀对象 - 只需标记您打算使用转换的代码区域.其次,由于转换是在编译时而不是运行时应用的,因此转换后的代码在运行时不会产生任何开销 - 所有工作都是在首次从源代码生成字节代码时完成的.
主要的缺点是,macropy需要激活某种方式才能使其工作(稍后提到).与更快的运行时相比,源代码的解析在计算上更复杂,因此程序将花费更长的时间来启动.最后,它添加了一种语法风格,这意味着不熟悉macropy的程序员可能会发现您的代码难以理解.
示例代码:
run.py
import macropy.activate
# Activates macropy, modules using macropy cannot be imported before this statement
# in the program.
import target
# import the module using macropy
target.py
from fpipe import macros, fpipe
from macropy.quick_lambda import macros, f
# The `from module import macros, ...` must be used for macropy to know which
# macros it should apply to your code.
# Here two macros have been imported `fpipe`, which does what you want
# and `f` which provides a quicker way to write lambdas.
from math import sqrt
# Using the fpipe macro in a single expression.
# The code between the square braces is interpreted as - str(sqrt(12))
print fpipe[12 | sqrt | str] # prints 3.46410161514
# using a decorator
# All code within the function is examined for `x | y` constructs.
x = 1 # global variable
@fpipe
def sum_range_then_square():
"expected value (1 + 2 + 3)**2 -> 36"
y = 4 # local variable
return range(x, y) | sum | f[_**2]
# `f[_**2]` is macropy syntax for -- `lambda x: x**2`, which would also work here
print sum_range_then_square() # prints 36
# using a with block.
# same as a decorator, but for limited blocks.
with fpipe:
print range(4) | sum # prints 6
print 'a b c' | f[_.split()] # prints ['a', 'b', 'c']
最后是努力工作的模块.我将其称为fpipe for function pipe作为其模拟shell语法,用于将输出从一个进程传递到另一个进程.
fpipe.py
from macropy.core.macros import *
from macropy.core.quotes import macros, q, ast
macros = Macros()
@macros.decorator
@macros.block
@macros.expr
def fpipe(tree, **kw):
@Walker
def pipe_search(tree, stop, **kw):
"""Search code for bitwise or operators and transform `a | b` to `b(a)`."""
if isinstance(tree, BinOp) and isinstance(tree.op, BitOr):
operand = tree.left
function = tree.right
newtree = q[ast[function](ast[operand])]
return newtree
return pipe_search.recurse(tree)
听起来不错,但我认为它只适用于Python 2.7(而不是Python 3.4).
我创建了一个没有依赖项的较小的库,该库的作用与@fpipe装饰器相同,但重新定义了右移(>>)而不是or(|):https://pypi.org/project/pipeop/
2> shadowtalker..:
管道是Pandas 0.16.2中的新功能.
例:
import pandas as pd
from sklearn.datasets import load_iris
x = load_iris()
x = pd.DataFrame(x.data, columns=x.feature_names)
def remove_units(df):
df.columns = pd.Index(map(lambda x: x.replace(" (cm)", ""), df.columns))
return df
def length_times_width(df):
df['sepal length*width'] = df['sepal length'] * df['sepal width']
df['petal length*width'] = df['petal length'] * df['petal width']
x.pipe(remove_units).pipe(length_times_width)
x
注意:Pandas版本保留了Python的引用语义.这就是为什么length_times_width
不需要返回值; 它修改x
到位.
遗憾的是,这仅适用于数据帧,因此我无法将其指定为正确的答案.但很高兴在这里提到我想到的主要用例是将其应用于数据帧.
3> Sylvain Lero..:
python语言是否支持类似的东西?
"更多功能管道语法"这真的是一个更"功能"的语法吗?我会说它为R添加了一个"中缀"语法.
话虽这么说,Python的语法并没有直接支持标准运算符之外的中缀表示法.
如果你真的需要这样的东西,你应该从Tomer Filiba那里获取代码作为实现你自己的中缀表示法的起点:
代码示例和评论由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)
使用这个特殊类的实例,我们现在可以使用一个新的"语法"来调用函数作为中缀运算符:
>>> @Infix
... def add(x, y):
... return x + y
...
>>> 5 |add| 6
4> smci..:
PyToolz [doc]允许任意组合管道,只是它们没有用管道操作符语法定义.
请按照上面的链接进行快速入门.这是一个视频教程:http:
//pyvideo.org/video/2858/functional-programming-in-python-with-pytoolz
In [1]: from toolz import pipe
In [2]: from math import sqrt
In [3]: pipe(12, sqrt, str)
Out[3]: '3.4641016151377544'
5> shadowtalker..:
如果您只想将其用于个人脚本,您可能需要考虑使用Coconut而不是Python.
椰子是Python的超集.因此,您可以使用Coconut的管道操作员|>
,同时完全忽略椰子语言的其余部分.
例如:
def addone(x):
x + 1
3 |> addone
编译成
# lots of auto-generated header junk
# Compiled Coconut: -----------------------------------------------------------
def addone(x):
return x + 1
(addone)(3)
6> Robin Hillia..:
我错过了|>
Elixir 的管道操作符,所以我创建了一个简单的函数装饰器(大约50行代码),>>
在编译时使用ast库和compile/exec 将Python右移操作符重新解释为类似Elixir的管道:
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
它所做的只是重写a >> b(...)
为b(a, ...)
.
https://pypi.org/project/pipeop/
https://github.com/robinhilliard/pipes
7> mhsekhavat..:
您可以使用sspipe库。它暴露了两个对象p
和px
。类似于x %>% f(y,z)
,您可以写作,x | p(f, y, z)
并且类似于x %>% .^2
您可以写作x | px**2
。
from sspipe import p, px
from math import sqrt
12 | p(sqrt) | px ** 2 | p(str)
8> yardsale8..:
建筑pipe
用Infix
正如Sylvain Leroux所暗示,我们可以使用Infix
运算符来构造中缀pipe
.让我们看看这是如何实现的.
首先,这是Tomer Filiba的代码
代码示例和评论由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)
使用这个特殊类的实例,我们现在可以使用一个新的"语法"来调用函数作为中缀运算符:
>>> @Infix
... def add(x, y):
... return x + y
...
>>> 5 |add| 6
管道运算符将前一个对象作为参数传递给管道后面的对象,因此x %>% f
可以转换为f(x)
.因此,pipe
可以使用Infix
如下定义运算符:
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'
关于部分申请的说明
在%>%
从运营商dpylr
通过在函数的第一个参数推参数,所以
df %>%
filter(x >= 2) %>%
mutate(y = 2*x)
对应于
df1 <- filter(df, x >= 2)
df2 <- mutate(df1, y = 2*x)
在Python中实现类似功能的最简单方法是使用currying.该toolz
库提供了一个curry
装饰器功能,可以轻松构建curry函数.
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)
请注意,|pipe|
将参数推送到最后一个参数位置,即
x |pipe| f(2)
对应于
f(2, x)
在设计curried函数时,应该在参数列表的前面放置静态参数(即可能用于许多示例的参数).
请注意,toolz
其中包括许多预先计算的功能,包括operator
模块中的各种功能.
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]
其大致对应于R中的以下内容
> library(dplyr)
> add2 <- function(x) {x + 2}
> 0:4 %>% sapply(add2)
[1] 2 3 4 5 6
使用其他中缀分隔符
您可以通过覆盖其他Python运算符方法来更改围绕Infix调用的符号.例如,交换__or__
和__ror__
到__mod__
和__rmod__
将改变|
操作者的mod
操作者.
In [5]: 12 %pipe% sqrt %pipe% str
Out[5]: '3.4641016151377544'
9> Eli Korvigo..:
添加我的2c.我个人使用package fn进行功能样式编程.你的例子转化为
from fn import F, _
from math import sqrt
(F(sqrt) >> _**2 >> str)(12)
F
是一个包装类,具有功能风格的语法糖,用于部分应用和组合._
是一个用于匿名函数的Scala样式构造函数(类似于Python lambda
); 它表示一个变量,因此您可以_
在一个表达式中组合多个对象以获得具有更多参数的函数(例如_ + _
,相当于lambda a, b: a + b
).F(sqrt) >> _**2 >> str
导致一个Callable
对象可以根据需要多次使用.