views:

240

answers:

2

Is there any way to write decorators within a class structure that nest well? For example, this works fine without classes:

def wrap1(func):
    def loc(*args,**kwargs):
     print 1
     return func(*args,**kwargs)
    return loc

def wrap2(func):
    def loc(*args,**kwargs):
     print 2
     return func(*args,**kwargs)
    return loc


def wrap3(func):
    def loc(*args,**kwargs):
     print 3
     return func(*args,**kwargs)
    return loc

def merger(func):
    return wrap1(wrap2(wrap3(func)))


@merger
def merged():
    print "merged"


@wrap1
@wrap2
@wrap3
def individually_wrapped():
    print "individually wrapped"

merged()
individually_wrapped()

The output is:

1
2
3
merged
1
2
3
individually wrapped

which is what I want. But now let's say that I want to make merged and individually_wrapped as static or class methods. This will also work, so long as the decorators are kept out of the class namespace. Is there any good way to put the decorators within the namespace? I'd rather not enumerate all the ways that won't work, but the main problem is that if merger is a method, it can't access the wrapX methods. Maybe this is a stupid thing to want to do, but has anyone gotten something like this to work, with all the decorators and decorated methods in the same class?

+3  A: 

"Is there any good way to put the decorators within the namespace?"

There's no compelling reason for this. You have module files. Those are a tidy container for a class and some decorators.

You don't ever need decorators as methods of the class -- you can just call one method from another.

S.Lott
I think this is the way to go. There's no great reason to keep them inside the class.
David Berger
+1  A: 

Actually, there should be no problem putting them all inside of a class. When inside of a class body, you can name any variable defined so far simply by naming it:

class A(object):
    a = 1
    b = 2
    c = a + b

print A.c

That yields the result 3, because while Python is executing a class body the functions can "see" the variables a and b that have been declared. So the following also works:

class B(object):
    @staticmethod
    def wrapper(*args, **kw):
       ...

    @wrapper
    def a(...):
        ...

Now, what about your merger function? The problem is that the wrapper function gets run long after the class body is done executing, and the variables it defined are no longer in the enclosing scope. How can you refer to them, then? By using the name of the class as a prefix! Like this:

class C(object):
    @staticmethod
    def wrap1(*args, **kw):
        ...
    @staticmethod
    def wrap2(*args, **kw):
        ...
    @staticmethod
    def wrap3(*args, **kw):
        ...
    @staticmethod
    def merger(*args, **kw):
        C.wrap1(C.wrap2(C.wrap3(...)))

    @merger
    def plain(...):
        ...

So the general rule with Python is: code that runs right at the level of the class body can talk about all the variables and methods that have been defined so far inside of it, and this means you can use those class variables as wrappers. But once you are "inside" of a function in the class, whether it's a class function, a static function, or a method (and this wrapper is a class method since it takes args but no "self), then you have to use the name of the class to "get at" its contents.

Brandon Craig Rhodes
Good point. If I have to refer to the class by name to do it, there is perhaps no good reason to put them inside the class.
David Berger
Yes, there might not be a reason to do it this way, if you were just trying to make it easier to refer to them. You can just leave the wrappers outside the class, then they can be "seen" from both inside the class definition and inside any of its functions. Might make things simpler!
Brandon Craig Rhodes