Why won't this decorator work?

ChasBrown cbrown at cbrownsystems.com
Sat Jul 2 15:16:48 EDT 2011


On Jul 2, 11:08 am, John Salerno <johnj... at gmail.com> wrote:
> On Jul 2, 12:33 pm, MRAB <pyt... at mrabarnett.plus.com> wrote:
>
>
>
> > On 02/07/2011 17:56, John Salerno wrote:
>
> > > I thought I had finally grasped decorators, but the error I'm getting
> > > ('str' type is not callable) is confusing me.

> > > def move(roll):
> > >     return 'You moved to space {0}.'.format(roll)

<snip>

>
> > A decorator should return a callable.

Well, should typically return a callable, but there are exceptions...

>
> > This:
>
> >      @move
> >      def roll_die():
> >          return random.randint(1, 6)
>
> > is equivalent to this:
>
> >      def roll_die():
> >          return random.randint(1, 6)
>
> >      roll_die = move(roll_die)
>
> > You should be defining a function (a callable) and then passing it to a
> > decorator which returns a callable.
>
> > As it is, you're defining a function and then passing it to a decorator
> > which is returning a string. Strings aren't callable.
>
> But why does the documentation say "The return value of the decorator
> need not be callable"?

Well, because it need not be callable: i.e., if the return value is
not callable, that is perfectly legal. You very rarely want to return
something else, but an example of this is the @property decorator: it
returns a property object, which is not callable.

> And why, if I remove the decorator and just
> leave the two functions as if, does the call to move(roll_die()) work?
> Isn't that what the decorator syntax is essentially doing?

No; instead it's doing the similar looking but quite different

    move(roll_die)()

As you wrote it, move(roll_die) returns the string 'You moved to space
<function roll_die>.' which is not callable. You typically want
instead something like:

def move(roll):
    # note that roll is a function, not a number
    def wrapper():
        result = roll()
        print 'You moved to space {0}.'.format(result)
        return result
    return wrapper # which is a function

Now move returns a callable (the function 'wrapper') so move(roll_die)
is callable, and move(roll_die)() is legal.

Cheers - Chas



More information about the Python-list mailing list