3

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.

2 Answers 2

7
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.

Sign up to request clarification or add additional context in comments.

3 Comments

You wrote that code from the top of your head, didn't you? :)
@Alex: yes, see the revision history. setattr was passed f instead of n as its second argument.
(Recently I found a typo in another code snippet you posted, stackoverflow.com/questions/1653500/…. I must say, myself I usually do not feel confident enough to post code without first testing it in the interpreter :)
3

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 ]

2 Comments

Interesting that you chose to use MI for a simple example instead of having my_render inherit from someclass.
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...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.