Python has the wonderful "in" operator and it would be nice to have additional infix operator like this. This recipe shows how (almost) arbitrary infix operators can be defined.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | # definition of an Infix operator class
# this recipe also works in jython
# calling sequence for the infix is either:
# x |op| y
# or:
# x <<op>> y
class Infix:
def __init__(self, function):
self.function = function
def __ror__(self, other):
return Infix(lambda x, self=self, other=other: self.function(other, x))
def __or__(self, other):
return self.function(other)
def __rlshift__(self, other):
return Infix(lambda x, self=self, other=other: self.function(other, x))
def __rshift__(self, other):
return self.function(other)
def __call__(self, value1, value2):
return self.function(value1, value2)
# Examples
# simple multiplication
x=Infix(lambda x,y: x*y)
print 2 |x| 4
# => 8
# class checking
isa=Infix(lambda x,y: x.__class__==y.__class__)
print [1,2,3] |isa| []
print [1,2,3] <<isa>> []
# => True
# inclusion checking
is_in=Infix(lambda x,y: y.has_key(x))
print 1 |is_in| {1:'one'}
print 1 <<is_in>> {1:'one'}
# => True
# an infix div operator
import operator
div=Infix(operator.div)
print 10 |div| (4 |div| 2)
# => 5
# functional programming (not working in jython, use the "curry" recipe! )
def curry(f,x):
def curried_function(*args, **kw):
return f(*((x,)+args),**kw)
return curried_function
curry=Infix(curry)
add5= operator.add |curry| 5
print add5(6)
# => 11
|
Of course this is a hack that plays with Python's ability of operator overloading. It is some sort of similar to the Ruby syntactic sugar recipe, but I think this might really be useful. One could e.g. define infix operators for set arithmetics like union, intersection and so on which would really enhance the readability of the code. I wonder whether it would be possible to use decorators for the definition of the infixes and one could omit the stars around the infix?
Addendum: Thanks for all the interest and the helpful comments for the recipe. I generated a revised version that took them into account and makes the recipe more useful. Now you can call an operator by using bars || or using << and >>. In principle it is easily possible to define different function calls for different operands. Also added an example from functional programming which might be helpful and shows how the hack greatly enhances readability. Time will tell whether the recipe stays a nice hack or whether people will start using it as a programming style.
Here is a discussion how the hack works: In python there are two ways for overloading an operator. Assume we want to overload the multiplication operator * for a certain class. i.e. Xy is redefined for all expressions containing X as the first operand where X is of a special class. Then we have to overload (i.e. redefine) the method __mul__(X,y) of the class of X. This is nice and works in many programming languages in some way. But python gives you more: You can also redefine the meaning yX with arbitrary y for all X. Then you have to overload the method __rmul__(X,y). Thus you can define a different meaning of the multiplication operator for left and right multiplication. This is also possible for +,-,/,*,<<,>>,& and | which call the methods __add__, __sub__, __div__, __pow__, __lshift__, __rshift__, __and__ and __or__. Now we define a special class "Infix" which exploits this property and by leaving out the blanks we can write it in a form a *op b.
Be careful that this is a hack of the language and not the definition of a new keyword. (as David S. pointed out in the comments). Of course the precedence and associativity rules for the operators still apply and therefore 23 x 4 means (23) x 4 while 2+3 x 4 means 2 + ( 3 x 4). (thanks to Raymond Hettinger for pointing this out).
Associativity and Precedence. This gets my vote for best hack of 2005. ;-)
Be sure to add notes on associativity and precedence so it is clear that:
To change the precedence, try other operators: | ^ & ** etc.
i believe this is not possible, unless there is a new explicit binary operator, applied when there is a whitespace between two terms.
So far this works for string literals only (("A" "B")=="AB")
Using '|' would work better for many situations. Because '|' is the operator with the lowest precedence that's still practical to use with this technique.
That having been said, I also have to say that this is probably the best Python hack ever, because the ability to do this has been in the language for many generations and nobody thought of it (or at least shared their invention) before. I ran the recipe on Python 1.5.2 and it worked once I changed the __class__ tests to type()!
adjacent strings. Adjacent string literals are treated as a single string. This is only true of literals. You can use it for stuff like:
So it's not an operator at all, and there's no concatenation -- that's parsed as a single string.
Not reentrant. I agree that this is a fascinating hack -- unfortunately the new infix operator is not reentrant. I'm using your "div" example but with __or__ and __ror__ methods instead:
With a slight modification, however, it works as expected -- just return a new (actually monadic) operator as the result of the first operation:
Now we get the conventional result:
Monad combinators.
observation from a novice. If, like me, you are just getting familiar with Python, you may be distracted, as I was, by the format of the examples. I had to realize that |myop| is not an atomic unit. It is the myop variable with the '|' operator (which is redefined in the Infix class) on either side.
The important operator overloading is happening on the instance named myop, for which the bar, |, has been redefined.
Also, notice that it is redefined such that it requires a right and left operand (almost obviously), which is why it must be sandwiched so.
1st form works on Jython, 2nd form does not :-(. C:\jython>jython
Jython 2.1 on java1.4.1_02 (JIT: null)
Type "copyright", "credits" or "license" for more information.
... 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)
...
:4:[SyntaxWarning]: local name 'other' in '__ror__' shadows use as global in nested scopes
:4:[SyntaxWarning]: local name 'self' in '__ror__' shadows use as global in nested scopes
Traceback (innermost last):
File "", line 1, in ?
NameError: object
nested scopes. I'm guessing that's because Jython doesn't have nested scopes. Use this definition instead (__ror__ changed):
Add __call__ method. I think that it would be good to add a __call__ method to the infix object so that the object can act, more or less, like the original function object.
And I agree with everyone else who said that this is an extremely cool hack.
@infix Decorator. It's easy to define a corresponding decorator @infix.
The decorator can be applied to any function definition and simply returns a new Infix object that wraps the original function definition.
Note that since the Infix class provides an appropriate __call__ method, @infix-decorated functions can still be called like "ordinary" functions.
You don't even need the 'def infix' part... ...because classes can be used as decorators. Just use @Infix, or rename the Infix class to lower case.
The class also needs __getattr__ and __setattr__ routines that delegate to self.function, as well as __class__ and __doc__ delegates (so that anInfix.__class__ returns function.__class__) in order to complete the illusion that Infix instances are functions.
Nested scopes work in Jython. You just need the line:
Unfortunately string (and sundry other) objects don't play well with this hack. They raise a TypeError which prevents the __rop__ method from getting called.
Still very cool though - and all the more reason to try and make standard classes play more nicely with others.
hah! Masterful.
try livelogix instead. Instead of using a neat hack, try requesting custom infix operators as a feature for python (good luck), or use LiveLogix which runs on top of the CPython VM and already has this feature: http://logix.livelogix.com
bindable infix.
ok. but...
And it just does'nt make sense.
If you want to bind the infix operator, you should fix the code like this:
Check out inrex, regular expressions as inline operators!
Ferdinand- In your Recipe description, your text below the recipe seems to be formatted wrong, triggering italics and bold in the Markdown syntax. I think since you use * next to alphabetic characters (such as 2 * X * 3: - which shows up as "2X3" if you don't include spaces after your stars.)
Markdown changes it to italics, and we lose the stars. (According to instructions, * * bold * *, * italic *, indent 4 spaces for a code block.)
Sage has a version of this sort of idea, but allows the user to also specify the precedence level for a few common operator precedences. In the Sage source, it uses the sage_wraps decorator, but outside of Sage, you could just as easily change it to use the wraps decorator from functools.
http://hg.sagemath.org/sage-main/file/361a4ad7d52c/sage/misc/decorators.py#l74
Wouldn't it be nice to have a unit testing framework based on this recipe? Writing self.assertEquals(foo, bar) is tedious :
And so on...
Once, you realize that
is not an atomic operation: it is actually
You can take advantage of that to perform statements that effect the global/local state between evaluating the left and right hand sides, in your operators. For example, here is a pipe operation:
Because Python evaluates left-to-right, this means that you can do stuff like this:
Or maybe, you could even do something unspeakable with exceptions to try and create short-circuit evaluation...
We can go crazy and make operators that take arguments:
That's what I call a ternary operator! :)
Compare this recipe with the infix package on PyPI.