0

I've got the following problem:

I need to write a decorator that would be able to detect a situation where it is used around a method and around a regular function.

So let's consider the following code:

def somedecorator(fn):
    print "decorating function:", fn
    print "function class:", fn.__class__
    return fn

@somedecorator
def regular_func():
    print "I'm a regular function"

class SomeClass(object):

    @somedecorator
    def class_method(self):
        print "I'm a class method"

The output reads:

decorating function: <function regular_func at 0x181d398>
function class: <type 'function'>
decorating function: <function class_method at 0x181d410>
function class: <type 'function'>

However writing:

print regular_func
print SomeClass.class_method
print SomeClass().class_method

Produces output:

<function regular_func at 0x181d398>
<unbound method SomeClass.class_method>
<bound method SomeClass.class_method of <__main__.SomeClass object at 0x16d9e50>>

So as it turns out (not so surprisingly) that decorators are applied to functions before they are turned into methods.

Question: What would you suggest to distinguish the two applications from the decorator point of view?

EDIT For the curious ones - I need the described distinction because of pickling. I don't really want to go into details on this one.

4
  • "What would you suggest to overcome this?" Overcome what? It seems to work perfectly. What would you like that's somehow different from this? Commented Jun 24, 2011 at 16:01
  • @S.Lott As I've written above I need to write a decorator that can distinguish the two situation (being applied to a method and being applied to a regular function). I reformulated my question Commented Jun 24, 2011 at 16:02
  • 1
    possible duplicate of How to differentiate between method and function in a decorator? Commented Jun 24, 2011 at 16:08
  • @Ignacio It indeed is a duplicate. Couldn't find it myself so thanks for that. Commented Jun 24, 2011 at 16:20

2 Answers 2

2

May be looking at the stack?

def somedecorator(fn):
    import traceback
    print "decorating function:", fn
    print "function class:", fn.__class__
    print traceback.extract_stack()[-2][2]
    return fn

If the decorator has been called from a module outputs 'module' and from the method outputs 'SomeClass'. If called from inside a function returns also the functions name, so to distingish between both cases, you could lookup locals() and globals() to know what the hell are those names: functions or classes.

What do you think?

Edit: The objects may be out of scope so the globals() and locals() thing wouldn't work...

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

4 Comments

That's really hackinsh but it works indeed, at least until they don't change the implementation in some future version. Anyways, thanks a lot!
This seems to be the only real options for class magic when you can't (or won't) use metaclasses. Actually, can you require a metaclass on decorated classes?
@julkiewicz But you can't tell from a decorator applied over a method or over a nested function, can you?
Although I didn't use it, I'll accept your answer as it most closely addresses the question that I asked.
1

You can't. A method is a function. Inside the class, there's just a regular function. Instancemethod descriptor magic only happens when an object is instanciated, and the descriptors are part of the object, not part of the class. "Unbound methods" (which btw disappeared in Python 3, for good reasons - they serve about no purpose, are a meaningless concept and add some overhead) are only temporary wrapper objects around those functions, generated whenever the function would be accessed as attribute of the class.

The sanest way to differentiate between a method and a function would be providing two decorators and let the programmers decide which one to apply. Or even better, do away with this strange seperation - methods are functions, nothing special, you should treat them as such.

4 Comments

"Instancemethod descriptor magic only happens when an object is instantiated" - that's not exactly true, as there is also some additional magic that turns class attributes that are functions into unbound methods. As to the purpose of the whole distintion - the answer is simple - pickling.
@julkiewicz: That's not instancemethod magic though. But I already added a sentence to take care of unbound methods - tl;dr they don't exist at this point either.
Ok, thanks for the explanation. I'll probably have to find some other place in the code to make this distinction.
In the end I indeed did something that didn't include this distinction. I just programmatically added non-toplevel functions to the module under an aliased name and it lets me pickle and unpickle them freely.

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.