views:

63

answers:

2

The question at

http://stackoverflow.com/questions/1145722/simulating-pointers-in-python

asking how to simulate pointers in Python had a nice suggestion in the solutions, namely to do

class ref:
    def __init__(self, obj): self.obj = obj
    def get(self): return self.obj
    def set(self, obj): self.obj = obj

which can then be used to do e.g.

a = ref(1.22)
b = ref(a)

print a # prints 1.22
print b.get() # prints 1.22

The class can be modified to avoid the use of get for the print statement by adding

def __str__(self): return self.obj.__str__()

Then,

print b # prints out 1.22

Now I would like to be able to do arithmetic with b in the same way as a, which I guess would be equivelent to saying that I want a and b to behave exactly like obj. Is there anyway to do this? I tried adding methods such as

def __getattribute__(self, attribute): return self.obj.__getattribute__(attribute)
def __call__(self): return self.obj.__call__()

But regardless of this, the output of

print a + b

is always

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    print a + b
TypeError: unsupported operand type(s) for +: 'instance' and 'instance'

Does anyone have any ideas on how to modify the ref class to allow this?

Thanks for any advice!

+3  A: 

The + operator is implemented via the __add__() method on the left operand, or the __radd__() method on the right operand.

Here.

Ignacio Vazquez-Abrams
A: 

There are two potential issues.

First, you seem to be relying on your __getattribute__ implementation to let the interpreter find the right __add__ method. Unfortunately, I have noticed that the Python interpreter often has trouble finding special functions, like __add__ or __call__ if they are created on the fly (that is, not made an explicit part of the class when the class is defined). The manuals explicitly acknowledge this, at least for new-style classes:

For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

although it seems to me that I have had problems with similar tricks even with old-style classes.

Second, just redirecting __add__ won't be enough. Even if the interpreter successfully reduces

a + b

to

float.__add__( 1.22, b )

the float class still doesn't know how to add a float to a ref. So your __add__ will have to explicitly dereference the target (and dereference that if it's an indirect reference (and dereference that...) Like so:

class ref:
    def __init__(self, obj):
        self.obj = obj
    def get(self): return self.obj
    def set(self, obj): self.obj = obj
    def __str__(self): return self.obj.__str__()
    def __add__( self, other ):
        while isinstance( other, ref ):
            other = other.obj
        return self.obj.__add__( other )

a = ref(1.22)
b = ref(a)

print a
print b

print a + b

The while loop in __add__makes sure that you've unpacked all of the nested refs all the way to the base object.

If I were doing this, and I have used similar constructs to implement proxy patterns, I would refactor so that the while loop is in its own method, say getBaseObject(), and then is called from every time we need the object that is at the actual base of the chain of refs.

Dan Menes