Self-referencing decorator function parameters

T

Thomas Dimson

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)?

Thank you,
Thomas Dimson
 
G

George Sakkis

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
 
T

Thomas Dimson

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- Hide quoted text -

- Show quoted text -

Thanks George, that was helpful.

I guess my real question is: why does wrapping the call to be
"call=lambda x: DecorateMe.callMe(x)" somehow fix the issue with this
temporary namespace? It seems strange to me that defining an
additional function (through lambda) would allow me to see/add more
members to the namespace.
 
C

castironpi

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)?

def decorator( call ):
def inner(func):
def application( *args, **kwargs ):
call(*args,**kwargs)
func(*args,**kwargs)
return application


return inner


class DecorateMe:


def callMe( self ):
print( "Hello!" )
@decorator( call=callMe )
def youBet( self ):
pass

DecorateMe().youBet()
 
E

Erich

I guess my real question is: why does wrapping the call to be
"call=lambda x: DecorateMe.callMe(x)" somehow fix the issue with this
temporary namespace? It seems strange to me that defining an
additional function (through lambda) would allow me to see/add more
members to the namespace.

This works because of the very same mechanism that allows decorators
to work. When the decorator in your example is called, it then
evaluates the inner statements, including the creation of the function
inner (the def ...). Similarly, when you do the lambda above, python
points call to the lambda function, but does not evaluate it until
later, when the youBet method is run. By the time youBet is run, the
DecorateMe class exists, so this will properly evaluate.

I hope the above is clear, I haven't had my coffee yet.

regards,
Erich
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top