A simple syntax for piping in Python.
Compare:
from bookends import _
from toolz.curried import map
l = _| [3, 2, 1] | map(lambda n: n*2) | sorted |_ # [2, 4, 6]
with:
l = sorted(map(lambda n: n*2, [3, 2, 1]))
l = sorted([n*2 for n in [3, 2, 1]])
l = []
for n in [3, 2, 1]:
l.append(n*2)
l.sort()
For an extended comparison, see example.py.
To install:
pip install bookends
To use:
from bookends import _
For similar tools, see:
- from fn import F
- from toolz import thread_first, thread_last, pipe
- Clojure's -> and ->>
- Underscore's chain
- Unix |
Note: for multiline usage, wrap the expression in parens.
import csv
from StringIO import StringIO
(_| '40,5,10\n20,6,9\n41,10,10\n'
| StringIO
| csv.reader
| sorted
|_)
# [['20', '6', '9'], ['40', '5', '10'], ['41', '10', '10']]
Wrap lone lambdas in parens as well.
(_| ['addition', 'multiplication']
| (lambda l: l + ['exponentiation', 'tetration'])
| ', '.join
|_)
# 'addition, multiplication, exponentiation, tetration'
You can use partial or curried functions.
from functools import partial
from toolz.curried import drop
(_| ['ca', 'tx', 'ny']
| partial(map, lambda state: state.upper())
| drop(1)
| list
|_)
# ['TX', 'NY']
And/or use threading syntax, by putting each function and its arguments into a tuple.
from toolz import drop
(_| ['ca', 'tx', 'ny']
| (map, lambda state: state.upper())
| (drop, 1)
| list
|_)
# ['TX', 'NY']
If you don't like the underscore, import the bookend as B.
from bookends import B
(B| ['ca', 'tx', 'ny']
| (map, lambda state: state.upper())
| (drop, 1)
| list
|B)
To stop in the debugger before each function call, put a step
into the pipe.
from bookends import step
(_| [3, 2, 1]
| (map, lambda x: x*2)
| step # <==
| sorted
| sum
|_)
To call off the stepping, drop in an endstep
.
from bookends import step, endstep
(_| [3, 2, 1]
| (map, lambda x: x*2)
| step # <==
| sorted
| endstep # <==
| sum
|_)
To print each function and its output, drop in a verbose
.
from bookends import verbose
(_| [3, 2, 1]
| verbose # <==
| (map, lambda x: x*2)
| sorted
| sum
|_)
You can easily add these options while debugging by tacking on their first letter to the initial bookend.
(_.sv| [3, 2, 1] # <== Turn on step and verbose (_.s, _.v, and _.vs work too).
| (map, lambda x: x*2)
| sorted
| sum
|_)
Drop in a function that won't affect the operand by decorating it with passthrough.
from bookends import passthrough
@passthrough
def log(operand):
log.info('Operand was {}.'.format(operand))
(_| [3, 2, 1]
| (map, lambda x: x*2)
| log # <==
| sorted
|_)
Plays nice with Kachayev's _.
from fn import _ as __
_| [1, 2, 3] | __ + [4, 5] |_
# [1, 2, 3, 4, 5]
Here's a simplified version of the source:
class Bookend():
def __or__(self, operand):
return Pipe(operand)
class Pipe():
def __init__(self, operand):
self.operand = operand
def __or__(self, f):
if isinstance(f, Bookend):
return self.operand
else:
self.operand = f(self.operand)
return self
_ = Bookend()
Contact: @bzrry.