Why won't this decorator work?

J

John Salerno

I thought I had finally grasped decorators, but the error I'm getting
('str' type is not callable) is confusing me. Here is my code. Also,
the commented sentence is from the Python docs, which says it doesn't
even need to be callable, if that matters. I also commented out a few
things in the move method. They were just to see if it would work, but
those lines raised even more errors!

import random

#space = 0

def move(roll):
# global space
# space += roll
return 'You moved to space {0}.'.format(roll)

@move
def roll_die():
return random.randint(1, 6)

# The return value of the decorator need not be callable
# roll_die = move(roll_die)



I tried running the command "roll_die()" and I get the error message.

Thanks.
 
M

MRAB

I thought I had finally grasped decorators, but the error I'm getting
('str' type is not callable) is confusing me. Here is my code. Also,
the commented sentence is from the Python docs, which says it doesn't
even need to be callable, if that matters. I also commented out a few
things in the move method. They were just to see if it would work, but
those lines raised even more errors!

import random

#space = 0

def move(roll):
# global space
# space += roll
return 'You moved to space {0}.'.format(roll)

@move
def roll_die():
return random.randint(1, 6)

# The return value of the decorator need not be callable
# roll_die = move(roll_die)



I tried running the command "roll_die()" and I get the error message.

Thanks.

A decorator should return a callable.

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.
 
J

John Salerno

A decorator should return a callable.

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"? 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?
 
T

Tim Chase

But why does the documentation say "The return value of the decorator
need not be callable"?

I must not be looking at the same documentation you are...could
you provide a link? The only time I know of that the return value
of a decorator need not be callable is if you want to totally
break the syntax of the function. :-/
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?

Are you doing

move(roll_die()) #call roll_die() and pass results to move()

or

move(roll_die) # pass the function-object roll_die to move()

? The decorator syntax is the equivalent of

roll_die = move(roll_die)

-tkc
 
I

Ian Kelly

But why does the documentation say "The return value of the decorator
need not be callable"?

Because the language does not enforce the restriction that the return
value should be a callable. If it's not a callable, then the result
will just be that something non-callable is bound to the "roll_die"
name -- which could be useful, but is probably a bad idea in general.
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. The decorator syntax is doing:

roll_die = move(roll_die)

If you then call roll_die, that is equivalent to "move(roll_die)()",
which is not the same thing as "move(roll_die())".

Cheers,
Ian
 
C

ChasBrown

Well, should typically return a callable, but there are exceptions...
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
 
C

ChasBrown

Well, should typically return a callable, but there are exceptions...
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
 
C

ChasBrown

Well, should typically return a callable, but there are exceptions...
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
 
J

John Salerno

I must not be looking at the same documentation you are...could
you provide a link? The only time I know of that the return value
of a decorator need not be callable is if you want to totally
break the syntax of the function. :-/

http://docs.python.org/py3k/whatsnew/2.4.html?highlight=decorator

Sort of around the middle of the PEP 318 section.

But I think I understand now. I was thinking the decorator I made was
creating move(roll_die()), which I see now that it is not. Perhaps a
decorator isn't what I need in my case, although it seems like it
might be.

Basically what I want to do is this: I first to need to roll a die to
get a random number, then pass that number to the move method of a
Player class. Can this be done with a decorator, or is it better just
to do it like move(roll_die()) and be done with it?
 
S

Steven D'Aprano

John said:
But why does the documentation say "The return value of the decorator
need not be callable"?

The thing returned by a decorator does not need to be callable, but if you
want to call it, then it better be!

This is no different from this:

my_func = "hello world"
my_func() # fails because strings aren't callable

So if I do this:

def decorator(func):
# ignores the function and returns a string
return "hello world"

@decorator
def my_func():
x = 1
y = 2
return x+y

print(my_func) # prints "hello world"
my_func() # fails because strings aren't callable


If that's useful to you, decorator syntax allows it. That is all the
documentation means.


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?

The call move(roll_die()) is similar to this:

temp = roll_die()
my_string = move(temp)

which is perfectly fine, because you never call my_string. If you did, you'd
get the same error, because strings aren't callable.

The decorator syntax is completely different. It is doing this:

# replace the function roll_die with the output of move, which is a string
roll_die = move(roll_die)
# now try to call "roll_die", actually a string
roll_die()

which is more like:

move(roll_die)()

See the difference?
 
J

John Salerno

The thing returned by a decorator does not need to be callable, but if you
want to call it, then it better be!

This is no different from this:

my_func = "hello world"
my_func()  # fails because strings aren't callable

So if I do this:

def decorator(func):
    # ignores the function and returns a string
    return "hello world"

@decorator
def my_func():
    x = 1
    y = 2
    return x+y

print(my_func)  # prints "hello world"
my_func()  # fails because strings aren't callable

If that's useful to you, decorator syntax allows it. That is all the
documentation means.


The call move(roll_die()) is similar to this:

temp = roll_die()
my_string = move(temp)

which is perfectly fine, because you never call my_string. If you did, you'd
get the same error, because strings aren't callable.

The decorator syntax is completely different. It is doing this:

# replace the function roll_die with the output of move, which is a string
roll_die = move(roll_die)  
# now try to call "roll_die", actually a string
roll_die()

which is more like:

move(roll_die)()

See the difference?

Eesh, ok, I think I *still* don't quite get decorators, but I get it a
little more now. Definitely not what I needed to do here. :)
 
G

Gregory Ewing

Ian said:
If it's not a callable, then the result
will just be that something non-callable is bound to the "roll_die"
name -- which could be useful, but is probably a bad idea in general.

There are legitimate uses -- for example, the following
is a convenient way of creating a read-only property:

@property
def foo(self):
return self.calculate_value_of_foo()
 
O

OKB (not okblacke)

John said:
Basically what I want to do is this: I first to need to roll a die to
get a random number, then pass that number to the move method of a
Player class. Can this be done with a decorator, or is it better just
to do it like move(roll_die()) and be done with it?

A decorator basically modifies a function/method, so that ALL
subsequent calls to it will behave differently. If you want ALL calls
to your method to roll a die to get a random number, and then use that
random number, why not just roll the die inside the method itself:

def move(dist=None):
if dist=None:
dist = random.randint(1, 7)
# etc

If you don't want all calls to your method to roll the die, but
only some, and if the choice of whether to roll or not depends on
information outside the method, then you need to do the rolling outside
the method and pass the result in. This is more or less what other
people have mentioned (and what you seem to have also been considering):

# In code that wants to move the player. . .
if doIWantToMoveRandomly():
player.move(rollDie())
else:
player.move(2) # or whatever non-random expression you want

--
--OKB (not okblacke)
Brendan Barnwell
"Do not follow where the path may lead. Go, instead, where there is
no path, and leave a trail."
--author unknown
 
J

John Salerno

subsequent calls to it will behave differently.  If you want ALL calls
to your method to roll a die to get a random number, and then use that
random number, why not just roll the die inside the method itself:

I thought maybe it would be cleaner if the roll function was something
separate, but I suppose it could go inside the move method. It just
seemed like two different things that needed to be separated.
 
G

Gregory Ewing

OKB said:
A decorator basically modifies a function/method, so that ALL
subsequent calls to it will behave differently.

Furthermore, usually a decorator is used when for some
reason you *can't* achieve the same effect with code
inside the function itself.

For example, the classmethod() and staticmethod()
decorators return descriptors that have different
magical effects from a standard function object when
looked up in a class or instance. Since that magic
happens *before* the function is called, you can't
do the same thing using an ordinary function.

In this case, an ordinary function is quite sufficient,
and there is no need to involve a decorator.
 

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

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top