views:

705

answers:

2

Here's the gist of what I'm trying to do. I have a list of objects, and I know they have an instance method that looks like:

def render(self, name, value, attrs)
   # Renders a widget...

I want to (essentialy) decorate these functions at runtime, as I'm iterating over the list of objects. So that their render functions become this:

def render(self, name, value, attrs)
   self.attrs=attrs
   # Renders a widget...

Two caveats: 1. The render function is part of django. I can't put a decorator inside their library (well I could, but then I have to maintain and migrate this change). 2. It's an instance method.

An example here: http://wiki.python.org/moin/PythonDecoratorLibrary

Shows how to add a new instance method to a class. The difference here is I want to fall through to the original method after I've memorized that attrs parameter.

+6  A: 
def decorate_method(f):
  def wrapper(self, name, value, attrs):
    self.attrs = attrs
    return f(self, name, value, attrs)
  return wrapper

def decorate_class(c):
  for n in dir(c):
    f = getattr(c, n)
    if hasattr(f, 'im_func'):
      setattr(c, n, decorate_method(f.im_func))

You'll probably need some other test to skip methods with a different signature, but, apart from that, decorate_class(whatever) should do what you want on any given class whatever.

Alex Martelli
You wrote that code from the top of your head, didn't you? :)
Stephan202
@Stephan202, yep, was there some typo you had to edit?
Alex Martelli
@Alex: yes, see the revision history. `setattr` was passed `f` instead of `n` as its second argument.
Stephan202
(Recently I found a typo in another code snippet you posted, http://stackoverflow.com/questions/1653500/permutations-of-a-given-set-of-numbers/1653618#1653618. I must say, myself I usually do not feel confident enough to post code without first testing it in the interpreter :)
Stephan202
+2  A: 

The "classic" way is to subclass. This way you don't have to mess with other peoples classes.

class someclass(object):
    def render(self, name, value, attrs):
        print hasattr(self, 'attrs')

class my_render(object):
    def render(self, name, value, attrs):
        self.attrs = attrs # kind of decorating the function here
        return super(my_render, self).render(name, value, attrs)

class my_class(my_render, someclass): 
    pass    

someclass().render(1,2,3) # -> False
my_class().render(1,2,3) # -> True

The reason for MI is that all classes can inherit from my_render. I like the mixin concept ;-)

class my_otherclass(my_render, someotherclass): pass
class my_thirdclass(my_render, thirdclass): pass

# or less explicit
classlist = [ someclass, someotherclass ]
newclasses = [ type('my_'+cls.__name__, (my_render,cls), {}) for cls in classlist ]
THC4k
Interesting that you chose to use MI for a simple example instead of having my_render inherit from someclass.
jamessan
Yep this is usually how I'd do it, but I'd like to be able to take advantage of decoration simply so I don't have to subclass all of the various types of fields Django offers and MI/decorate on the fly regardless if it's an InputField, DateTimeField, etc. Django's doing a lot of black magic when it renders fields. I've had an adventure figuring out the the fact that each field gets wrapped in a 'BoundField' before it's rendered...
Koobz