Jack Jack - 6 months ago 9
Python Question

Use of 'global' in python across multiple scripts in Python 3.5

[To start with, I am truly sorry about my code as it I know it is probably hard to understand. I only really started learning Python last year and my teacher hardly understands it himself.]

I am attempting to make a text-based game in Python 3.5.1, and have it spread across 4 separate python files: 1 with the main game, 1 with the inventory system, 1 with other functions and the last with a character creation.

I was wondering how I am supposed to use global variables and functions, and have found in my searches that global variables are seen as a bad way to fix problems, but have not found an alternative that would suit my game. In How to make a save/load game for a text based python rpg?, they use global to define game_state but nothing else, but I have tens of variables defined in functions for ease of re-using the same code. For example:

def expadd(amount):
global level, totalxp, exp, expbound, p1, p2, p3, p4, p5
print("\nYou gained",str(amount),"exp!")
exp = exp + amount
totalxp = exp
while exp >= expbound:
addpoints()
charchange()
thingymjig()
expbound = int(p4 / p5 + 20)
exp = exp - expbound
level += 1
time.sleep(1)
print("\nLevel up! You are now level",str(level)+"!")
time.sleep(1)
expbound = int(p4 / p5 + 20)
nextlevel = expbound - exp
print("\nYou are",str(nextlevel),"exp away from the next level!")


Is this a good way to go about a function? I wouldn't have thought that you would have to type out all of this code any time the player may level up, though from what I have seen elsewhere nobody has actually used so many globals as I have here. (the p1-5 variables are from a separate function so they can be redefined as the player levels, should they be in this function too?)

def addpoints():
global charpoints
charpoints = charpoints + int(level * 2)- int(level / 2) - 1


In this section, charpoints (character points) are added after the player levels up, and is called for ease of use instead of typing out all of the code every time I need it. I use this sort of function many times throughout my code, is this wrong? Furthermore, is there an simpler way of making easily-readable code than having it across multiple scripts and using a lot of functions and global variables?

I am currently being given an NameError for the player's inputted gender, as it is from a function in another script, though it is globalised and imported into the main file how I thought is properly:

from GAMEFUNCS import gender, charname, charpoints, charstrength, chardefence, charspeed


I need to import it to the main file so I can set it to be saved, and even though it is globalised in the following code (and in the imported function), I still get the error.

def savewrite():
global qr, check, gender, charname, title, money, exp, totalxp, level
global health, hp, lives, charpoints, charstrength, chardefence
global charspeed, charintel
with open("save.csv",'w',newline='')as s:
csv_s = csv.writer(s)
csv_s.writerow(["Quickrun"]+[qr])
csv_s.writerow(["Checkpoint"]+[check])
csv_s.writerow(["Gender"]+[gender])

Answer

Globals are a bad idea for the reasons you are currently encountering. Take the following example:

a.py:

a = "a"

def print_a():
    print(a)

b.py

from a import a, print_a
a = "b"
print_a()

What will print if you execute python b.py? It will output "a", because the variable a is only set to "b" in the context of the b module. Globals make it very hard to control access and scopes of variables. It is better to put such state into some datastructures and pass them into the function. That way you are also more generic and you can reuse functions for different objects.

Example:

def addpoints(character):

    character.charpoints = character.charpoints + character.level * 2 - (character.level / 2) - 1

(This assumes that character is something like a namedtuple or something with the appropriate attributes)

That way you not only avoid globals but your function will also work for other characters like sidecharacters or teammember if you want to have multiple persons in the team or even enemies.

Without the stacktrace and the code that produces it it's hard to say why you get a NameError. I still hope that my answer gives you some insights why globals might be a bad idea.