How do parameters flow between functions?

#rokpapaskizzz

import random

player_wins = 0
comp_wins = 0

def initialize():
print(‘Welcome to RokPapaSkiz!’)
player_name = input('Enter Player Name: ')
print(‘Greetings,’, player_name + ‘! Good luck!’)

def ready_up():
ready = False
ready = input('Are you ready to play? (y/n): ').lower()

if ready == 'y':
    return
elif ready == 'n':
    print('Come back soon!')
else:
    print('I don\'t understand what you mean by that.')
    ready_up()

def player_move():
global options
options = [‘r’,‘p’,‘s’]

player_choice = input('Make your move (r/p/s): ').lower()

if player_choice in options:
    return player_choice
else:
    player_move()

def comp_move():
comp_choice = random.choice(options)
return comp_choice

def compare(player_choice, comp_choice, player_wins, comp_wins):
if player_choice == comp_choice:
print(‘Tie’)
elif player_choice == ‘r’ and comp_choice == ‘p’:
print({‘You won! %s beats %s’},player_choice, comp_choice)
player_wins += 1
else:
print({‘You lose! %s beats %s’},comp_choice, player_choice)
comp_wins += 1

#def winner():

#def play_again():

initialize()
ready_up()

while True:
player_move()
comp_move()
compare(player_choice, comp_choice, player_wins, comp_wins)

#-------------- Output
NameError: name ‘player_choice’ in line 74 is not defined

How might I pass the parameters from one function to the next? Been struggling to understand this for a while now :confused:

Hi Ben,

Sorry for the lack of responses, I think it is because your question is deceptively simple to ask but hard to answer.

You are asking some fundamental questions about Python’s execution model, and it is hard to give a short answer.

I’ve had a really quick glance at the official Python tutorial, and I don’t think it answers your question about local and global variablesreally well. Maybe somebody else can have a go at answering it, otherwise I will try this evening (in about 12 hours), time permitting.

Good luck!

It appears to me that your programme has 2 characteristics contributing
to your problem:

  • you’re not passing parameters around between function - all your
    functions except for compare take no parameters
  • you’re having some input-based functions call themselves to repeat the
    input if it is bad; this isn’t really doing what you might hope

Your problems passing around parameters stem in a big way from the fact
that you’re expecting many variable to be “global variables”: that
setting a variable in one function makes that setting available in
another function.

A “pure function” does not behave that way. A “pure function” is one
with no side effects. The desirable feature of a pure function is that
is it easy to combine with other functions in large programmes, as
otherwise you need to keep track of all the side effects. For example,
you have a free hand to name variables as you like because they will not
collide with the same names elsewhere.

Python functions a generally “pure functions” unless you make special
arrangements, in that any variables you set vanish when you leave the
function - information only gets out via the return statement. And
information normally only gets in as a parameter.

So, to your code. You have a couple of global variables (player_wins
and comp_wins) and this on its own puts you in a “global variables”
state of mind: that you can use the same name in different places
without connecting them. It is a natural thing, but unhelpful here.

I recommend that you get rid of all the global variables.

Move your globls and the main loop into its own function, like this:

def main():
    player_wins = 0
    comp_wins = 0
    initialize()
    ready_up()
    while True:
        player_move()
        comp_move()
        compare(player_choice, comp_choice, player_wins, comp_wins)

then right at the bottom of the programme, run that function:

main()

(This needs to be at the bottom so that all the other functions are
defined before you start running main.)

Why is this a win? Now you have no global variables and everything
must be passed around with parameters. This is less confusing.

With that change you must use parameters in your calls.

The first change is that you should collect the return values from
player_move and comp_move. Like this:

while True:
    player_choice = player_move()
    comp_choice = comp_move()
    compare(player_choice, comp_choice, player_wins, comp_wins)

The error you had before:

NameError: name 'player_choice' in line 74 is not defined

was because player_choice is a local variable in the player_move
function. That means that it is not known here, in the main function.

With the assignment statements above, you now have a completely separate
local variable player_choice in the main function which has the return
value from player_move. We give it the same name because it has the
same purpose. But the connection between the two names is the
assignment, where we copy the value from the function to the local
variable.

Now that you have done this, all the parameters you are passing to
compare are known.

Look at the compare function: is has 4 parameters. They are local
variables
. The only way they have values is because you passed these
values when you called it from the main function. In particular, they
do not have the same values as those in main because of their name.
They have the same values because that is what you supplied at the
calling end. We give them the same names only because they have the same
meaning, so that we know what we’re dealing with.

The other slightly odd thing is your input functions. Let’s look at
player_move:

def player_move():
    global options
    options = ['r','p','s']
    player_choice = input('Make your move (r/p/s): ').lower()
    if player_choice in options:
        return player_choice
    else:
        player_move()

You’re returning player_choice if it is valid, which is fine. However,
if you are not given a valid choice you call player_move() again and
do not return its value
. Because you do not return its value, if the
player chose a valid choice on the second or later inputs it will be
ignored. The direct change is to change the if-statement like this:

    if player_choice in options:
        return player_choice
    else:
        return player_move()

which calls player_move again, and returns the choice from that.
However, that means player_move calls player_move calls
player_move until a valid choice is made. The call stack starts to
look like this:

player_move()
  player_move()
    player_move()
      player_move()
      ...

until a valid choice is made, then the calls each unwind, with each
outer player_move returning the value from the inner one. That works,
but is … cumbersome. In other circumstances this kind of recursive
approach does not scale. Imagine searching a list of thousands of
elements this way. Computers have finite limits and at some point the
call stack above would exceed that limit.

A better way to write this kind of function is with a loop:

white True:
    player_choice = input('Make your move (r/p/s): ').lower()
    if player_choice in options:
        return player_choice

This never calls player_move again. Instead, it stays in the original
call until it gets a valid answer, then returns that answer.

Finally, note again that the player_choice above is a local
variable
, unrelated to any other player_choice variable elsewhere in
your programme. It gets out and gets used via the return player_choice
statement.

Hopefully this clarifies some of what’s going on.

Cheers,
Cameron Simpson cs@cskk.id.au