views:

296

answers:

2

I'm having a bit of an issue solving a problem I'm looking at. I have a specialized set of functions which are going to be in use across a program, which are basically dynamic callables which can replace functions and methods. Due to the need to have them work properly to emulate the functionality of methods, these functions override __get__ to provide a wrapped version that gives access to the retrieving object.

Unfortunately, __get__ does not work if the function is set directly on an instance. This is because only "data descriptors" call the __get__ function when the key is found in the __dict__ of an instance. The only solution to this that comes to mind is: trick python into thinking this is a data descriptor. This involves creating a __set__ function on the descriptor. Ideally, I want this __set__ function to work as a pass-through (returns control to the caller and continues evaluating as if it doesn't exist).

Is there any way to trick python into thinking that a descriptor is a data descriptor but letting a containing class/instance still be able to use its setattr command as normal?

Also, I am aware that it is possible to do this with an override of __getattribute__ for the caller. However, this is a bad solution because I would have to do this for the 'object' built-in and anything that overrides it. Not exactly a great solution.

Alternatively, if there is any alternative solution I would be happy to hear it.

Here is a problem example:

class Descriptor(object):  
    def __get__(self, obj, objtype = None):  
        return None  

class Caller(object):  
    a = Descriptor()  

print a  
>>> None  
x = Caller()  
print a
>>> None
x.a = Descriptor()
print x.a
>>> <__main__.Descriptor object at 0x011D7F10>


The last case should print 'None' to maintain consistency.

If you add a __set__ to the Descriptor, this will print 'None' (as desired). However, this messes up any command of x.a = (some value) from working as it had previously. Since I do not want to mess up this functionality, that is not helpful. Any solutions would be great.

Correction: My prior idea would still not work, as I misunderstood the descriptor handling slightly. Apparently if a descriptor is not on a class at all, it will never be called- regardless of the set. The condition I had only helps if there is a dict val and a class accessor of the same name. I am actually looking for a solution more along the lines of: http://blog.brianbeck.com/post/74086029/instance-descriptors but that does not involve having everything under the sun inherit a specialized interface.

Unfortunately, given this new understanding of the descriptor interface this may not be possible? Why oh why would python make decorators essentially non-dynamic?

A: 

I think the cleanest solution is to leave __set__ alone, and set the descriptor on the class -- wrapping the original class if needed. I.e., instead of x.a = Descriptor(), do setdesc(x, 'a', Descriptor() where:

class Wrapper(object): pass

def setdesc(x, name, desc):
  t = type(x)
  if not issubclass(t, wrapper):
    class awrap(Wrapper, t): pass
    x.__class__ = awrap
  setattr(x.__class__, name, desc)

This is the general approach I suggest when somebody wants to "set on an instance" anything (special method or descriptor) that needs to be set on the class to work, but doesn't want to affect the instance's original class.

Of course, it all works well only if you have new-style classes, but then descriptors don't really play well with old-style classes anyhow;-).

Alex Martelli
Sorry for my originally misleading description. Unfortunately this still doesn't solve the underlying issue since there are cases where an instance of a class might legitimately want a function to be set on an instance (generally as an override for an existing function).
...and by calling on the instance the setdesc function I give in my answer, that's exactly what obtains: it "sets on the instance" a function, or any other descriptor, overriding or not as the case may be. So, why not?
Alex Martelli
A: 

I think I may have one answer to my question, though it's not all that pretty- it does sidestep the issue. My current plan of attack is to do what python does- bind the functions manually. I was already using my unbound function's get command to generate bound-type functions. One possible solution is to force anybody who wants to set a new function to manually bind it. It's annoying but it's not crazy. Python actually makes you do it (if you just set a function onto an instance as an attribute, it doesn't become bound).

It would still be nice to have this happen automatically, but it's not awful to force someone who is setting a new function to use x.a = Descriptor().get(x) which in this case will give the desired behavior (as well as for the example, for that matter). It's not a general solution but it will work for this limited problem, where method binding was being emulated basically. With that said, if anybody has a better solution I'd still be very happy to hear it.