Self-referencing decorator function parameters

George Sakkis george.sakkis at gmail.com
Wed Apr 2 16:31:15 CEST 2008


On Apr 2, 8:30 am, Thomas Dimson <tdim... at gmail.com> wrote:

> Hello,
>
> Originally I posted this as a bug but it was shot down pretty quickly.
> I am still mildly curious about this as I'm missing a bit of
> understanding of Python here. Why is it that the following code
> snippet:
>
> def decorator( call ):
>     def inner(func):
>         def application( *args, **kwargs ):
>             call(*args,**kwargs)
>             func(*args,**kwargs)
>         return application
>
>     return inner
>
> class DecorateMe:
>     @decorator( call=DecorateMe.callMe )
>     def youBet( self ):
>         pass
>
>     def callMe( self ):
>         print "Hello!"
>
> DecorateMe().youBet()
>
> Will not compile, giving:
> Traceback (most recent call last):
>   File "badpython.py", line 10, in <module>
>     class DecorateMe:
>   File "badpython.py", line 11, in DecorateMe
>     @decorator( call=DecorateMe.callMe )
> NameError: name 'DecorateMe' is not defined
>
> Where if you change the "call=DecorateMe.callMe" to "call=lambda x:
> DecorateMe.callMe(x)" everything goes along its merry way. Nesting the
> call in a lambda seems to allow it to recognize the class definition.
> Any ideas as to what is going on here (other than ugly code)?

The error message is pretty obvious; when the
"@decorator(call=DecorateMe.callMe)" line is reached, the DecorateMe
class has not been created yet, let alone the DecorateMe.callMe
method. One way to make it work (for some definition of "work" ;-) is
the following:

# use "new-style" classes unless you have a good reason not to:
# class DecorateMe(object):
class DecorateMe:

    def callMe(self):
        print "Hello!"

    @decorator(call=callMe)
    def youBet(self):
        pass


The reason this works is that at the point where @decorator is
executed, callMe is already in the temporary namespace to be used for
creating the DecorateMe class (although the class itself is not built
yet).

A subtle point is that in this case callMe is a plain function, not an
(unbound) method such as DecorateMe.callMe. This may or may not
matter, depending on what you do with it in the decorator. Some
decorators that work fine with plain functions break if they are used
to decorate methods (or vice versa) so it's good to have this in mind
when writing or debugging a decorator.

George



More information about the Python-list mailing list