2

I am trying to write a game of rock, paper, scissors, and I am getting an error about a file I imported not having an attribute of "quitGame", even though I should have that variable imported along with the others.

Here is the function that has the quitGame variable (uneeded parts filtered out):

def playRps():
    game = "".join([playerAction, computerAction])
    global quitGame

    outcomes = {
        "rr": tied,
        "pp": tied,
        "ss": tied,
        "rp": playerLoss,
        "ps": playerLoss,
        "sr": playerLoss,
        "rs": playerWin,
        "pr": playerWin,
        "sp": playerWin,
    }

    if playerAction == "q":
        quitGame = True  # TODO: Figure out why this isn't working in the main.py file
    else:
        action = outcomes.get(game)
        if action:
            action()
        else:
            print("Invalid input!")

You can also find the entire functions.py file here.

Here is the main.py file that should runs the program (uneeded parts filtered out):

import functions  # Imports the functions.py file
while True:
    playGame = str(input('Would you like to play "Rock, Paper, Scissors"? (Y/n): '))

    if playGame == "y":
        while True:
            functions.playerChoice()
            functions.computerChoice()
            functions.playRps()
            if functions.quitGame == True:
                break

    elif playGame == "n":
        print("Terminating program...")
        quit()

    else:
        print("Unknown input. Please enter a valid answer.")
        continue

You can also find the entire main.py file here.

Here is the interaction that gave me the error I am trying to fix:

(.venv) johnny@lj-laptop:rock_paper_scissors$ /home/johnny/Projects/rock_paper_scissors/.venv/bin/python /home/johnny/Projects/rock_paper_scissors/main.py
Would you like to play "Rock, Paper, Scissors"? (Y/n): y

    Rock, paper, or scissors?
    Acceptable responses are...
    "r": Chooses rock.
    "p": Chooses paper.
    "s": Chooses scissors.

    "q": Quits the game.
    r
Tied! No points awarded.
Traceback (most recent call last):
  File "/home/johnny/Projects/rock_paper_scissors/main.py", line 18, in <module>
    if functions.quitGame == True:
AttributeError: module 'functions' has no attribute 'quitGame'

I find this very odd, because other variables such as playerScore and computerScore work as expected. I have functions. added before each variable and function, so it makes no sense as to why it's telling me I don't have it.

I even added global quitGame in the function that creates the variable. I don't see why this isn't working.

My question to you guys is:

  • Why am I getting an error when I call that variable? What am I doing wrong?
  • 4
    As a programmer you should try very, *very*, **very** hard not to use globals. – quamrana Feb 01 '21 at 16:43
  • 1
    Perhaps you could do without the global and return `True` or `False` from playRps(). – quamrana Feb 01 '21 at 16:44
  • @quamrana I haven't really learned how to *not* use them yet. Do you know of another method I can use to achieve this? – LiterallyJohnny Feb 01 '21 at 16:45
  • 1
    @LiterallyJohnny You can use a class. You can return variables from functions. You cna use Google to help find answers too. Here are some: https://stackoverflow.com/questions/59330578/how-to-avoid-using-global-variables https://stackoverflow.com/questions/11462314/python-alternatives-to-global-variables – Random Davis Feb 01 '21 at 16:46
  • @quamrana I didn't realize that the second comment was also you. When I use `return False`, how would I use that to quit the game? Would I just use `if playRps() == False`? – LiterallyJohnny Feb 01 '21 at 16:46
  • 1
    @LiterallyJohnny `if playRps() == False` is indeed correct syntax. That's pretty basic Python, I'd recommend taking a basic tutorial on how to use functions. – Random Davis Feb 01 '21 at 16:47
  • @RandomDavis well, the point of me even doing this project is so that I can get better and learn. I don't really intend to use this program. – LiterallyJohnny Feb 01 '21 at 16:49
  • @LiterallyJohnny `if not playRps()` is more pythonic – BeanBagTheCat Feb 01 '21 at 16:50
  • 1
    I'd suggest making sure you understand the benefits and pitfalls of globals before just taking internet strangers at their word. Globals do have a serious cost, but if you don't know why you should avoid them, you won't know when you should _not_ avoid them. – kojiro Feb 01 '21 at 16:50
  • @kojiro that makes sense. I don't quite see why globals aren't good to use, but I'll check into it and see why. Thanks for this comment! – LiterallyJohnny Feb 01 '21 at 16:53
  • @LiterallyJohnny https://stackoverflow.com/questions/16011056/how-to-avoid-global-variables is a good read -- in fact, I like the comments there better than the answers. The point about mutability is embedded in the top-voted answer, but not clearly called out IMO. Also, the point about "global constants" is a good one, if you can be sure they're constant. – kojiro Feb 01 '21 at 16:56

2 Answers2

1

You have to use global before the variables is "requested" not "declared".

So if you want quitGame to be global you have to declare it were do you prefer (for example in main.py) and then were do you want to use it (as the example in a function) use "global quitGame".

You can tell to your function to access to a global var called quiteGame, instead of declaring quiteGame as global.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
gianoseduto
  • 188
  • 1
  • 11
  • 1
    Oh, that makes sense. Thanks for this answer! – LiterallyJohnny Feb 01 '21 at 16:51
  • No problem, I also tried to declare the var global but doesn't work like that in python ;) p.s. The suggestion of avoiding global var is a good suggest – gianoseduto Feb 01 '21 at 16:53
  • 1
    Yeah, I'll keep that in mind. I don't understand why globals aren't meant to be used, but I'll check into it and see why. I don't mind doing research. – LiterallyJohnny Feb 01 '21 at 16:56
  • I developed many functions that act on global var to modify or read from them data (on little "one-file" scripts). But a best practice is to build your function to be indipendent from the enviroment, so the global var dipendency is a costrain. The best solution is to develop a function that can be used even outside of his enviroment. How? Simply passing as argument in the function those vars . – gianoseduto Feb 02 '21 at 09:40
0

Cloned your repository and realised that I can play the game if I first chose "q" first, followed by answering "Y" then playing the game.

Would you like to play "Rock, Paper, Scissors"? (Y/n): y

    Rock, paper, or scissors?
    Acceptable responses are...
    "r": Chooses rock.
    "p": Chooses paper.
    "s": Chooses scissors.

    "q": Quits the game.
    q
Would you like to play "Rock, Paper, Scissors"? (Y/n): y

    Rock, paper, or scissors?
    Acceptable responses are...
    "r": Chooses rock.
    "p": Chooses paper.
    "s": Chooses scissors.

    "q": Quits the game.
    r
Tied! No points awarded.
Would you like to play "Rock, Paper, Scissors"? (Y/n): 

This is because your functions.quitGame global is only initialised when functions.playRps() is run. Before this, it does not exist and thus if you answered "r" right after choosing "y" to start the game, the functions.quitGame global does not exist yet. You can fix this by initialising the variable in your functions.py file beneath the declaration of computerScore:

playerScore = 0
computerScore = 0
quitGame = False

After this, you will then be able to play the game without any issue.