Failing to understand how variable scope works in Python (compared w/ OOP)

Hello, I’m coming from a background in OOP and am trying to understand the concept of variables and scope in Python

Based on the following code, I was looking for a way to update my “money” after each iteration of “coin_flip()”…I keep getting a reference error whenever I try to call “money” inside my function

Thanks in advance…

Link to exercise…

import random

money = 100

def coin_flip(call, bet):	
  num = random.randint(1,2)
  if(call == "Heads" and num == 1):
    money += bet
    print(f"Your new balance: ${balance}")
  elif(call == "Tails" and num == 2):
    money += bet
    print(f"Your new balance: ${balance}")
  	money -= bet
  	print(f"Your new balance: ${balance}")

# Bet $20 on Heads...
coin_flip("Heads", 20)
coin_flip("Tails", 25)
coin_flip("Tails", 30)[date=2020-02-12 timezone="America/New_York"]

you can’t change global variable inside a function by default. Why do you think that is? take a second to think about it before continue reading

you could use the global keyword, but is that the best solution? Maybe for a program this small, its not so problematic, but if your program grows, it might become very difficult to understand the flow of your program and where global variable are/can be mutated.


Hello, @net2156939774, and welcome to the Codecademy Forums!

The money variable has not been declared global within the coin_flip function. Therefore, if there is any assignment to the money variable within that function, that variable is regarded as local within the function.

This assignment statement treats money as a local variable:

    money += bet

With that statement, the Python interpreter considers the name money, wherever it appears within the function, to refer to a local variable, and finds that there is no existing value associated with that variable that can be incremented. Therefore, an error is raised.

Encapsulation is an important feature of OOP. Unless there is a good reason for the variable to be global, make it completely local to the function by moving this assignment statement to right under the function header, and indenting it:

money = 100

Then you’ll have a nice self-contained function that does not depend upon global variables.

Edited on February 12, 2020 to discuss encapsulation.


The problem is a good candidate for a custom type to encapsulate an integer to create a kind of mutable integer, allowing it to be modified in one location and have the updated result show up elsewhere:

wallet = Wallet(100)
wallet -= 10
wallet += 20

Isn’t that what we do with credit card numbers anyway? (as terrifying as that is)
…speaking of which, it could be improved on by invalidating a wallet after it’s done being used. One could for example have a temporary wallet which is invalidated, perhaps by moving some money into it and then withdrawing it, or by linking it to another wallet in a way that can be severed later.

bank = Wallet(10000)
with bank.proxy() as tempwallet:
    coin_flip("Heads", 100, tempwallet)
# tempwallet now invalidated by the context manager

(obviously I’m not saying this is any kind of more secure, it’s all very silly, but the behaviour is potentially interesting to model)