Python Library: pipe
C#/.NET 有个很迷人的特征 —— LINQ。别管因为什么,我们试着在 Python 里面 "发明" 一个类似的东西吧。
首先,我们可能要抛弃 "from x in datas..." 这样的想法,改用 LINQ method 方式。先写个伪码做目标。
range(10).sum().out()
以前写过一篇博客《Python Expression》,提到用重载运算符的方式来实现 Expression。"." 有特殊含义,还是改用其他的吧。比如 "|",这个符号在 Unix-Shell 里面通常用作数据管道,用来将数据依次传递处理,正好符合我们的需求。
range(10) | sum() | out()
好,开始简单一点,先写 sum。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Sum(object):
def __ror__(self, iterator):
return sum(iterator)
print range(10) | Sum()
执行:
$ ./main.py
45
很好,我们达成了第一步目标,由于和内置函数 sum 冲突,我们改用 "Sum" 大写。不过我们需要写很多很多类似的函数,用 "class + __ror__" 太麻烦也太啰嗦了。嗯,用一个装饰器来处理吧,这样就不用写那么多重复代码了,而且还可以用真正的函数来代替类。
装饰器的名字就用 Pipe 吧,符合我们上面说的思想。
class Pipe(object):
def __init__(self, function):
self.function = function
def __ror__(self, iterator):
return self.function(iterator)
@Pipe
def Sum(iterator):
return sum(iterator)
@Pipe
def out(x):
print "result =", x
range(10) | Sum | out
这回好看多了,基本实现了原定目标,那个伪码变成现实了。(关于装饰器,请参考《Python Essential: 9. Decorators (1)》)
别着急,事情还没完。函数不会总像 sum、out 这么简单,比如 "append(x)" 这样附带参数的函数怎么办呢?首先,这类函数肯定要先执行的,为了照顾 "|" 还得 "override __ror__" 一下。继续在 Pipe 上折腾吧,调用 "append(x)" 无非是 "Pipe.__call__(x)",然后 "return" 一个新的 Pipe 对象,不就有 "__ror__" 了。
class Pipe(object):
def __init__(self, function):
self.function = function
def __ror__(self, iterator):
return self.function(iterator)
def __call__(self, *args, **kwargs):
return Pipe(lambda iterator: self.function(iterator, *args, **kwargs))
@Pipe
def Sum(iterator):
return sum(iterator)
@Pipe
def out(x):
print "result =", x
@Pipe
def append(iterator, x):
iterator.append(x)
return iterator
range(10) | append(100) | Sum | out
执行:
$ ./main.py
result = 145
由于 "append(iterator, x)" 附带额外的参数,与 __ror__ 里面的调用 "self.function(iterator)" 不符,因此我们用一个 lambda 生成一个新的包装函数。托闭包的福,可以持有其调用参数 x。
到此,我们总算完成了一个很 Happy 的功能。当然,剩下来的工作就是写好多好多的函数了⋯⋯ 郁闷不?别着急,接下来有请我们今天的主角:
$ sudo easy_install pipe
看看它的介绍:
Help on module pipe:
NAME
pipe - Infix programming toolkit
FILE
/home/mandark/doc/code/python/pipe/pipe.py
DESCRIPTION
Module enablig a sh like infix syntax (using pipes).
= Introduction =
As an exemple, here is the solution for the 2nd Euler Project exercise :
"Find the sum of all the even-valued terms in Fibonacci
which do not exceed four million."
Given fib a generator of fibonacci numbers :
euler2 = fib() | where(lambda x: x % 2 == 0)
| take_while(lambda x: x < 4000000)
| add
= Vocabulary =
* a Pipe: a Pipe is a pipeable function, somthing that you can pipe to,
In the code [1, 2, 3] | add add is a Pipe
* a Pipe function: A standard function returning a Pipe so it can be used like
a normal Pipe but called like in : [1, 2, 3] | concat("#")
= Syntax =
The basic symtax is to use a Pipe like in a shell :
>>> [1, 2, 3] | add
6
A Pipe can be a function call, for exemple the Pipe function where :
>>> [1, 2, 3] | where(lambda x: x % 2 == 0) #doctest: +ELLIPSIS
<generator object <genexpr> at ...>
A Pipe as a function is nothing more than a function returning
a specialized Pipe.
= Constructing your own =
You can construct your pipes using Pipe classe initialized with lambdas like :
stdout = Pipe(lambda x: sys.stdout.write(str(x)))
select = Pipe(lambda iterable, pred: (pred(x) for x in iterable))
Or using decorators :
@Pipe
def stdout(x):
sys.stdout.write(str(x))
= Existing Pipes in this module =
stdout
Outputs anything to the standard output
>>> "42" | stdout
42
lineout
Outputs anything to the standard output followed by a line break
>>> 42 | lineout
42
as_list
Outputs an iterable as a list
>>> (0, 1, 2) | as_list
[0, 1, 2]
as_tuple
Outputs an iterable as a tuple
>>> [1, 2, 3] | as_tuple
(1, 2, 3)
concat()
Aggregates strings using given separator, or ", " by default
>>> [1, 2, 3, 4] | concat
1, 2, 3, 4
>>> [1, 2, 3, 4] | concat("#")
1#2#3#4
average
Returns the average of the given iterable
>>> [1, 2, 3, 4, 5, 6] | average
&n
补充:Web开发 , Python ,