Neat Python hack: infix operators

I came across this neat Python hack on reddit today, a technique for defining arbitrary infix operators. Not overriding + or >> et al., but creating keyword-style pseudo-operators that… well, the code is probably as clear as any description I could come up with:

class infix(object):
    """
    Clever hack (slightly modified) for defining infix operators. 
    Via http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/384122
    Intended for use as a decorator.
    
    >>> @infix
    ... def removing(str, chars):
    ...     for char in chars:
    ...         str = str.replace(char, '')
    ...     return str
    ...
    >>> print 'Hello, world!' |removing| 'eo'
    Hll, wrld!
    """
    def __init__(self, function):
        self.function = function
    def __ror__(self, other):
        return infix(lambda x: self.function(other, x))
    def __or__(self, other):
        return self.function(other)
    def __call__(self, value1, value2):
        return self.function(value1, value2)

Some people hate this kind of stuff. That’s why we call it a hack, to indicate that we don’t think it’s a great building block for your missile control software. Some of Those People still get their underoos in a bunch regardless. But you have to admit it’s damned clever.

This also reminds me of some of the lovely higher-order-function features in Haskell, where you can make a regular (prefix) function into infix by wrapping it in backticks – 10 `mod` 3 – and an infix function (operator) into prefix by wrapping it in parentheses – (+) 2 2.


Justin commented on Mon Feb 26 04:22:33 2007:

I totally hate it. :)


James commented on Fri Mar 2 11:20:40 2007:

I totally love it. :)


Paul commented on Tue Mar 6 16:33:51 2007:

Now that’s what I call fair and balanced.


bruce commented :

class infix2(object):
    def __init__(self, function):
        self.function = function
    def __ror__(self, other):
        self.value=other
        return self
    def __or__(self, other):
        return self.function(self.value,other)

With this version, you do not instanciate a lambda object for each __ror__ call. On my PC, it’s 45% performance gain !


Paul commented :

I love that you profiled it.


Lionel commented :

What about this one http://pypi.python.org/pypi/pipe/1.3 ? It’s simplier and have ~30 already implemented infix operators


Nicko commented :

Bruce, the problem with your solution is that it only has one place to store the left-hand value, so it fails if more than one operation is in flight at a time:

plus = infix2(lambda x,y:x+y) 1 |plus| 2 |plus| 3 6 1 |plus| (2 |plus| 3) 7

Share: