We can define intrinsic operators of Python as stated here. Just for curiosity, can we define new operators like $
or ***
? (If so, then we can define ternary condition operators or rotate operators.)
As @minitech said you can't define new operators. But check this hack that allows you to define infix operators http://code.activestate.com/recipes/384122-infix-operators/
Expanding on @fasouto answer, but adding a bit more code.
While you cannot define new operators AND you cannot redefine existing operators for built-in types, what you can do is to define a class (instantiated to any valid Python name, e.g. op
) that act as an intermediate binding for two objects, thus effectively looking like a binary infix operator:
a | op | b
In short, one can define a class overriding forward and backward methods for an operator, e.g. __or__
and __ror__
for the |
operator:
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 __call__(self, value1, value2):
return self.function(value1, value2)
This can be used directly:
op = Infix(lambda a, b: a + b) # can be any bivariate function
1 | op | 2
# 3
or as a decorator:
@Infix
def op(a, b):
return a + b
1 | op | 2
# 3
The above solution works as is, but there are some issues, e.g. op | 2
expression cannot be used alone:
op = Infix(lambda a, b: a + b)
(1 | op)
#<__main__.Infix object at 0x7facf8f33d30>
# op | 2
# TypeError: <lambda>() missing 1 required positional argument: 'b'
1 | op | 2)
# 3
To get proper bindings one would need to write a bit more complex code performing an intermediate binding:
class Infix(object):
def __init__(self, func):
self.func = func
class RBind:
def __init__(self, func, binded):
self.func = func
self.binded = binded
def __call__(self, other):
return self.func(other, self.binded)
__ror__ = __call__
class LBind:
def __init__(self, func, binded):
self.func = func
self.binded = binded
def __call__(self, other):
return self.func(self.binded, other)
__or__ = __call__
def __or__(self, other):
return self.RBind(self.func, other)
def __ror__(self, other):
return self.LBind(self.func, other)
def __call__(self, value1, value2):
return self.func(value1, value2)
This is used the same way as before, e.g. either:
op = Infix(lambda a, b: a + b)
or as a decorator:
@Infix
def op(a, b):
return a + b
With this, one would get:
1 | op
# <__main__.Infix.LBind object at 0x7facf8f2b828>
op | 2
# <__main__.Infix.RBind object at 0x7facf8f2be10>
1 | op | 2
# 3
There is also a PyPI package implementing substantially this: https://pypi.org/project/infix/
Incidentally, the binding solutions seems to be also marginally faster:
%timeit [1 | op | 2 for _ in range(1000)]
# Non-binding implementation
# 1000 loops, best of 3: 626 µs per loop
# Binding implementation
# 1000 loops, best of 3: 525 µs per loop
These implementations use |
, but any binary operator could be used:
+
: __add__
-
: __sub__
*
: __mul__
/
: __truediv__
//
: __floordiv__
%
: __mod__
**
: __pow__
@
: __matmul__
(for Python 3.5 onwards)|
: __or__
&
: __and__
^
: __xor__
>>
: __rshift__
<<
: __lshift__
The **
would require the binding implementation or adjusting the non-binding one to reflect that the operator is right-associative. All the other operators from above are either left-associative (-
, /
, //
, %
, @
, >>
, <<
) or directly commutative (+
, *
, |
, &
, ^
).
Remember that these would all have the same precedence as the normal Python operators, hence, e.g.:
(1 | op | 2 * 5) == (1 | op | (2 * 5)) != ((1 | op | 2) * 5)
No, you can’t define new operators in Python.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With