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