Skip to content
Go Back

Hacks to Python

Edit this page

Pipeline operator

Models

Miss your dear and dear pipeline operator in …

Terminal window
cat file.txt | grep "hello" | wc -l

Well, you can have it in Python too!

Implementation

from functools import update_wrapper
from typing import Any
from collections.abc import Callable
class Pipeline:
def __init__(self, func: Callable | None = None) -> None:
if func is not None:
update_wrapper(self, func)
self.funcs: list[Callable] = [func]
else:
self.funcs: list[Callable] = []
def __call__(self, *args: Any, **kwds: Any) -> Any:
"""
self(*) => self.funcs[0](self.funcs[1](self.funcs[2](...)))
"""
if len(self.funcs) == 0:
return None
res = self.funcs[-1](*args, **kwds)
for func in reversed(self.funcs[:-1]):
try:
res = func(res)
except Exception as e:
print(f"Error encountered when executing {func} with parameter {res}:")
raise e
return res
def __add__(self, func: Callable | "Pipeline") -> "Pipeline":
"""
(f + g)(*) => f(g(*))
"""
res = Pipeline()
res.funcs += self.funcs
if isinstance(func, Pipeline):
res.funcs += func.funcs
elif callable(func):
res.funcs.append(func)
else:
raise TypeError(f"Unsupported type {type(func)}")
return res
def __radd__(self, func: Callable | "Pipeline") -> "Pipeline":
res = Pipeline()
if isinstance(func, Pipeline):
res.funcs += func.funcs
elif callable(func):
res.funcs.append(func)
else:
raise TypeError(f"Unsupported type {type(func)}")
res.funcs += self.funcs
return res
def __or__(self, func: Callable | "Pipeline"):
"""
* | f | g => g(f(*))
"""
return func + self
def __ror__(self, func: Callable | "Pipeline" | Any):
if isinstance(func, Callable) or isinstance(func, Pipeline):
return self + func
else:
return self(func)
@Pipeline
def foo(a: int) -> list[int]:
return list(range(a))
@Pipeline
def bar(b: list[int]) -> int:
return max(b)
# Pipeline in Python???!!!
if __name__ == "__main__":
func = bar + foo
print(func(10)) # 9
print(10 | foo | bar) # 9

How it works

The solution is simple:

Better type hinting

Check out this project of Yixuan Wang that provides better type hinting for similar pipeline operator implementation.

Multi-statement lambda expression

Models

Multi-statement lambda expressions are commonly supported in other languages, such as:

auto func = [](auto a, auto b)
{
a++;
b++;
return a + b;
};

But wait! What about Python?

func = lambda a, b: a + b

Seems like Python only supports single-statement lambda expression… or is it?

Well, you can have it too!

Implementation

func = lambda a, b: (
a := a + 1,
b := b + 1,
a + b,
)[-1]

How it works

Well, in python, the evaluation of a tuple is done sequentially, so you can use a tuple to mimic the behavior of a multi-statement lambda expression, where the return value could be specified by the index of element.

Note that in order to assign values in a tuple, you will need to use the walrus operator := introduced in Python 3.8. It will assign the value to the variable on the left, and return the value on the right, similar to the default behavior of = in C/C++ and many other languages.


Edit this page
Share this post:

Previous Post
Migrating to a New Blog
Next Post
Speeding Up Programs by 120x