28

Adding a pipe operator to Python

 5 years ago
source link: https://www.tuicool.com/articles/hit/VJZRbe3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

The other day, I was looking at some Python code that was made up of a sequence of deeply-nested function calls and a thought immediately got pushed up brain’s stack:

Wouldn’t it be cool if Python had an operator akin to Elixir’s pipe operator ?

Pipe operator?

For those of you not familiar with Elixir, the pipe operator |> can be used to turn code that looks like this:

3iInmyR.png!web
Nested function calls.

Into code that looks like this:

VjU3InE.png!web
Refreshing!

In essence, the pipe operator takes the expression on its left-hand side and moves it into the first argument position of the expression on its right-hand side, assuming the expression on the right is a function call.

Doing it in Python

Although adding an actual pipe operator to the language isn’t possible without changing the Python interpreter, there’s nothing preventing us from re-purposing an existing operator! That’s exactly what I set out to do.

Borrowing from shells, I figured the “bitwise or” (aka “pipe”) operator would be a good fit for this sort of functionality:

eQBNfue.png!web

The simplest way I could come up with to repurpose the pipe operator was to rewrite functions using a decorator. Python’s built-in ast module makes this particularly easy.

All I had to do was

inspect.getsource
ast.parse

That sounds a lot more complicated than it turns out to be in practice.

The ast module provides a class called NodeTransformer that implements the visitor pattern: its visit method does a depth-first search through an AST and calls any declared methods of the form visit_NODETYPE on itself for each node in the tree. As the name implies, you can use a node transformer to visit and manipulate the nodes of an AST:

Whenever the transformer encounters a binary operator, it recurses so that any transformations that need to be made on its left-hand and right-hand nodes are made first, then it checks if the current operator is “bitwise or”.

If the current node’s operator is “bitwise or” and if the right-hand side is a function call node, then it inserts the left-hand side into the first argument position of the function call and then returns the node on the right-hand side, replacing the binary operator node with the call node in the tree.

The transformer also kicks in when it sees a function definition so that it may remove the enable_threadop decorator. This’ll make sense once you take a look at the decorator itself:

The decorator takes a function as an argument, grabs its source code and removes any indentation (this is important! otherwise, decorating class methods results in a SyntaxError ), parses the code and transforms the AST and, finally, it compiles and executes the function definition, returning the resulting function object.

If the transformer hadn’t removed the decorator from the final tree, then we’d have an infinite loop on our hands because enable_threadop would be called over and over again when the function definition is executed (line 13).

With all that in place, the enable_threadop decorator may be used to selectively change the behaviour of the pipe operator:

zyaYva3.png!web
If that’s not a realistic example, I don’t know what is!

Limitations

As you would expect, there are a couple limitations with this approach.

First off, inspect.getsource grabs functions’ source code off of the file system, meaning that the decorator won’t work in the Python interpreter.

Secondly, the transformer requires the right-hand side of the pipe operator to be a function call.

Time to use it in production!

Whoa there, slow down! This is just a neat little experiment and is most definitely not something you should inflict upon your coworkers!

That said, if you want to play around with it, you can find the full code (50 lines with comments!) on GitHub .


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK