I am using Python descriptors to create complex interfaces on host objects.
I don't get the behaviour I would intuitively expect when I run code such as this:
class Accessor(object):
def __get__(self,inst,instype):
self._owner = inst
return self
def set(self,value):
self._owner._val = value
def get(self):
if hasattr(self._owner,'_val'):
return self._owner._val
else: return None
class TestClass(object):
acc = Accessor()
source = TestClass()
destination = TestClass()
source.acc.set('banana')
destination.acc.set('mango')
destination.acc.set(source.acc.get())
print destination.acc.get()
# Result: mango
I would expect in this case for destination.acc.get() to return 'banana', not 'mango'.
However, the intention (to copy _val from 'source' to 'destination') works if the code is refactored like this:
val = source.acc.get()
destination.acc.set(val)
print destination.acc.get()
# Result: banana
What is is that breaks down the 'client' reference passed through get if descriptors are used in a single line versus broken into separate lines? Is there a way to get the behaviour I would intuitively expect?
Many thanks in advance.
K
Your implementation ALMOST works. The problem with it comes up with
destination.acc.set(source.acc.get())
. What happens is that it first looks updestination.acc
, which will set_owner
todestination
, but before it can callset()
, it has to resolve the parameter,source.acc.get()
, which will end up settingacc
's_owner
tosource
.Since
destination.acc
andsource.acc
are the same object (descriptors are stored on the class, not the instance), you're callingset()
on it after its_owner
is set tosource
. That means you're settingsource._val
, notdestination._val
.The way to get the behavior you would intuitively expect is to get rid or your
get()
andset()
and replace them with__get__()
and__set__()
, so that your descriptor can be used for the reason a descriptor is used.Then you could rewrite your client code as
The point of descriptors is to remove explicit getter and setter calls with implicit ones that look like simple attribute use. If you still want to be using your getters and setters on
Accessor
, then don't make it a descriptor. Do this instead:Then rewrite
TestClass
to look more like this:After that, your original client code would work.