I'm writing a python program in which I need to overload the >> operator. The problem that I'm facing is that this operator need to be right associative. So if I do the following
A >> B >> C >> D >> E
I want this to be parsed as
(A >> (B >> (C >> (D >> E))))
From what I understand, in python this operator is left associative and so I would get,
((((A >> B) >> C) >> D) >> E)
Is there anyway to change the default associativity of an operator in python when you do operator overloading?
This can be done...but it will require a bit of effort. The basic idea is to use the right shift operator to create and update a new type of object that defers the actual computation.
For instance, let's say that your variables above: A, B, C, D, and E are all objects of type Actual. We'll introduce a new class Deferred that is produced by the rshift operation on an instance of Actual. Deferred also implements the rshift operator, which updates the object and returns itself.
(BTW, For the remainder of this answer, I'm assuming that A, B, C, D, and E are immutable and that the rshift operation produces a new object.)
F = A >> B >> C >> D >> E
Would be computed like ...
F = Deferred(A,B) >> C >> D >> E
F = Deferred(A,B,C) >> D >> E
F = Deferred(A,B,C,D) >> E
F = Deferred(A,B,C,D,E)
F maintains a cached instance of Actual, that is computed from the reverse sequence. Furthermore, F implements the same interface as Actual, so that methods invoked on an instance of Deferred are delegated to the cached instance of Actual.
I don't know the kind of computation you're doing, so in the following example, I make up something kind of trivial, just to demonstrate that the when the deferred computation is actually performed, they are reversed.
class Var(object):
def __init__(self):
pass
@property
def name(self):
return self._name( )
@property
def length(self):
return len(self.name)
class Actual(Var):
def __init__(self, name):
Var.__init__(self)
self._text = name
def _name(self):
return self._text
def __rshift__(self, other):
if isinstance(other, Actual):
return Deferred(self, other)
return len(self.name)
@staticmethod
def NewFromShiftComputation(sequence):
x = ' >> '.join(reversed(map(lambda actual: actual.name, sequence)))
return Actual(x)
class Deferred(Var):
def __init__(self, *args):
Var.__init__(self)
self._items = [ ]
self._actual = None #-- cached "actual"
for item in args:
self._items.append(item)
def _name(self):
self._assure_actual( )
return self._actual.name
def __rshift__(self, other):
self._actual = None #-- Invalidate the cached "actual"
self._items.append(other)
return self
def _assure_actual(self):
if self._actual is None:
self._actual = Actual.NewFromShiftComputation(self._items)
A = Actual('A')
B = Actual('B')
C = Actual('C')
D = Actual('D')
E = Actual('E')
F = A >> B >> C >> D >> E
print F.name
print F.length
This isnt an issue with the operator. Python evaluates left to right:
http://docs.python.org/reference/expressions.html#evaluation-order
So, parenthesis would be needed in this case since its all the same operator.
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