-1

I have the following program and the variable(dictionary) in question is player_info that stores player information (name and goals). In order to solve the error that results currently, I simply need to make player_info a global variable, but I was wondering if stackoverflow experts could suggest or discuss the possibility of alternate ways of solving this problem WITHOUT the use of global variables.

Code

#FOOTBALL COACH app

#The program allows a user to enter a number of players (their names and goals scored) and then search for a player, returning their average goals for the three matches


import sys 
def main():
 mainmenu()

def mainmenu():

  print("=====WELCOME to the MAIN MENU=============")
  print("""
  1..........Add New Players & Goals
  2..........Search by Players
  3..........Quit

  =========================================
  """)

  choice=int(input("Enter choice:"))
  if choice==1:
    addplayers()
  elif choice==2:
    searchplayer(player_info)
  elif choice==3:
    sys.exit()
  else:
    print("You must make a valid choice - 1, 2 or 3")


def addplayers():


  player_info= {} #create a dictionary that stores the player name: player goals

  num_players = int(input("Please enter number of players you wish to enter:"))
  print ("You are entering %s players" %num_players)
  player_data = ['Match 1 goals : ', 'Match 2 goals : ', 'Match 3 goals : ']
  for i in range(0,num_players):
      player_name = input("Enter Player Name :")
      player_info[player_name] = {}
      for entry in player_data:
          player_info[player_name][entry] = int(input(entry)) #storing the marks entered as integers to perform arithmetic operations later on.


  mainmenu()


def searchplayer():  
   print("===============SEARCH by player: Calculate average goals==================")
   name = input("Player name : ")
   if name in player_info.keys():
     #print student_info
      print ("Average player goals : ", str(sum(player_info[name].values())/3.0))
   else:
      print("Please enter a valid player name:")

main()

As mentioned, I am aware that re-writing this in the addplayer() sub would fix the problem:

global player_info
player_info = {} #create a dictionary that stores the player name: player goals

...I am looking for ways to solve the problem WITHOUT the use of global variables.

Update:

One answer below using return player_info is what I would like to go with, but it doesn't quite work yet. Also, I need to return to the main menu each time a player is added, not quite sure how to do this, without a mainmenu call each time. Any suggestions? https://repl.it/JRl5/1

Compoot
  • 2,227
  • 6
  • 31
  • 63
  • [`return`](https://docs.python.org/3/reference/simple_stmts.html#grammar-token-return_stmt) the dictionary – DavidG Jul 07 '17 at 09:46

3 Answers3

2

You can use return inside your function to avoid using global variables. A simple example is shown below:

def addplayers():

    player_info= {} 

    name = input("Enter Name: ")
    test = int(input("Enter a number: "))

    player_info[name] = test

    return player_info


player_info = addplayers()

If you then wanted to use this in another function you would just pass in the dictionary as an argument to that function:

def searchplayers(player_info):

    print (player_info)

Note: An interesting answer on "Why are global variables evil?"

Edit:

Your addplayers() was calling mainmenu() which itself was being calling within mainmenu(). This is a recursive function and it might be best to avoid these unless there's a good reason for having it. I would put the contents of mainmenu inside a while loop until some condition is met. The complete code is shown below (I have removed the main function as it wasn't really doing anything):

def mainmenu():

    stop = False

    while stop == False:

        print("=====WELCOME to the MAIN MENU=============")
        print("""
        1..........Add New Players & Goals
        2..........Search by Players
        3..........Quit

        =========================================
        """)

        choice=int(input("Enter choice:"))
        if choice==1:
            player_info = addplayers()
        elif choice==2:
            searchplayer(player_info)
        elif choice==3:
            print ("Exit the main menu")
            stop = True
        else:
            print("You must make a valid choice - 1, 2 or 3")


def addplayers():


    player_info= {} #create a dictionary that stores the player name: player goals

    num_players = int(input("Please enter number of players you wish to enter:"))
    print ("You are entering %s players" %num_players)
    player_data = ['Match 1 goals : ', 'Match 2 goals : ', 'Match 3 goals : ']

    for i in range(0,num_players):
        player_name = input("Enter Player Name :")
        player_info[player_name] = {}

        for entry in player_data:
            player_info[player_name][entry] = int(input(entry)) #storing the marks entered as integers to perform arithmetic operations later on.

    return player_info


def searchplayer(player_info):  
    print("===============SEARCH by player: Calculate average goals==================")
    name = input("Player name : ")
    if name in player_info.keys():
        #print student_info
        print ("Average player goals : ", str(sum(player_info[name].values())/3.0))
    else:
        print("Please enter a valid player name:")


mainmenu()
DavidG
  • 24,279
  • 14
  • 89
  • 82
  • Thank you - as a valid learning question, I'm not sure why this question was downvoted!!!!! :( – Compoot Jul 07 '17 at 10:30
  • I didn't downvote, but there is a quite a lot of code in your question. You could have probably made it a bit shorter with a small bit of code which reproduces the issue you are having rather than posting all of it. – DavidG Jul 07 '17 at 10:32
  • Can I ask what player_info=addplayers() does please? – Compoot Jul 07 '17 at 10:32
  • `addplayers()` is the function we have defined. As there is a return statement within the function itself we need to create a variable outside of the function which will be equal to whatever was returned when we call this function. – DavidG Jul 07 '17 at 10:34
  • okay - I just tried it and it doesn't quite work. player_info=addplayers() ...is this to be declared outside the function as you have shown? – Compoot Jul 07 '17 at 10:37
  • Thank you. Unfortunately, trying that: NameError: name 'player_info' is not defined. Also I need it to keep returning to the mainmenu after each player is added. https://repl.it/JRl5/1 (I'd like to award you the answer, but just need it to work!) – Compoot Jul 07 '17 at 11:01
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/148597/discussion-between-misscomputing-and-davidg). – Compoot Jul 07 '17 at 11:06
  • Two questions: 1. Why can't we say: While stop==False,. rather than While stop != true? 2. Why is recursion undesirable? – Compoot Jul 07 '17 at 12:06
  • 1) You can actually, that's more readable and so I will update my answer. 2) See [this](https://stackoverflow.com/questions/3021/what-is-recursion-and-when-should-i-use-it) question/answers. – DavidG Jul 07 '17 at 12:12
0

Store everything related to the game in a data structure, for example a dictionary, and pass it along in all functions where it can be updated as needed. Write a function "newgame" that creates this structure and initialises it.

In a way, this is object-oriented programming without using Python's syntax for classes and objects. Probably, you will learn these later in your class / tutorial.

Joachim Wagner
  • 860
  • 7
  • 16
0

Firstly, it is always possible to avoid using global variables. Secondly, global variables are possibly a misnomer in Python; global stores the variable in the local globals, which is typically the local module. That avoids a large part of the problem languages like C have with globals, in that they collide; Python has a namespace per module. For a simple script, where there is only one context, that might be fine.

Another namespace you might use is that of a particular object, using a class. This might look like:

class Game:
    def mainmenu(self,...):
        self.addplayers()
    def addplayers(self):
        self.player_info = {}

With that sort of code, whoever instantiates Game can make multiple instances, each passed as self when used. This is in large part syntactic sugar for a similar form of mutable state passing:

def mainmenu():
    state={}
    addplayers(state)
def addplayers(state):
    state['player_info'] = {}

For some forms of programming, immutable state is far preferable (in particular, multithreading where data is shared, or to keep a log where you can undo steps). That's done similarly but you make a new state for each call:

def mainmenu():
    state = {}
    state = addplayers(state)
def addplayers(oldstate):
    newstate = oldstate.copy()
    newstate['player_info'] = {}
    return newstate

Python isn't designed for this and doesn't really have a mode to keep you from inadvertently modifying mutable types. Some types can be converted to similar types that are immutable, like frozenset or tuple.

One of the weirder hacks we can do is calling a Python function with a different set of globals than it normally has. This can be abused to take your existing functions, global statements and all, and have them use a different variable:

fakeglobals = mainmenu.__globals__.copy()
exec(addplayers.__code__, fakeglobals)

Your original code has calls back and forth between functions, though, and each of those will reset their globals per that __globals__ attribute.

Your code also implements a loop using tail recursion. This is not optimized in Python and will run out of stack space eventually. In languages where tail recursion is optimized, you can pass along state as arguments continously and need not return it.

Yann Vernier
  • 15,414
  • 2
  • 28
  • 26