I came across this neat Python hack on reddit today, a technique for defining arbitrary infix operators. Not overriding
>> 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