How to make a game while avoiding global variables? - python

Just read that global variables are bad, which is shocking because I use them all the time. The way I'm making games right now is by making a function for each location in a game, like so.
def menu():
#stuff
def game():
#stuff
while True:
if location=="menu":
menu()
#etc
And then I always use the global keyword at the top of each function. This way, if there's something that needs to be reset frequently, like a score, I can do it at the very top of the page, outside the game function. My question is, how is it possible to do things like that with only local variables? I can't do this.
def game():
timer+=1
There wouldn't be a way I can initialize timer without making it a global variable. I have a feeling that the way I'm doing it is the "incorrect" way of making games. If so, then what is the "correct" way? By the way I have one python page for commonly used functions such as drawing or writing, and everything else is in a game python page.

The correct way to do this would be to create classes, and then create instances of those classes.
For example a Menu class, with various methods and attributes.
class Menu():
...
menu = Menu()
Avoiding global variables makes it usually easier to debug, as you keep track of the 'format' of your data.

Related

How should I modify global variables from within a function?

I've been trying to clean up my code for a neural network evolution simulator I'm working on, because it's grown to over 1000 lines and it's very difficult to read. Part of that process involves moving blocks of code from the main loop into their own functions (event handling, drawing to the screen, etc) or their own modules. However, I'm having trouble figuring out how to deal with the global variables.
As an example, I have a variable called "selected" that keeps track of a particular neural network creature selected by the user. I want to change the value of this variable in the event handling function when the user clicks on a new creature; I also want to change its value when the creature dies, which happens in a different block of code in its own function.
I understand that I can use the "global" keyword to do this, but my impression is that it's generally poor practice to do so. I also know that I can store these variables in a separate module and import them, but when I'm dealing with 30+ variables, typing "module_name.variable_name" every time I want to modify them seems unnecessarily tedious. I was also considering storing all of these values in a global dictionary so I can modify them from within functions, but again, typing "dict_name['var_name']" each time seems unnecessary.
So essentially my question is, what would be the best practice in this situation? Is it one of these solutions, or is there a cleaner approach? I'd like to make sure I'm on the right track before I spend hours overhauling my code. Thank you for your help!
30 global variables is just a big no-no. At some point you'll forget the global statement, and you'll create a local variable and will spend hours to debug your code.
you could build a singleton object instead
instead of:
a = 12
b = 33
c = "foo"
def run():
global a
if a == 34:
# do something
a += 1
you could create a big wrapping class with all variables as members:
class Program:
def __init__(self):
self.a = 12
self.b = 33
self.c = "foo"
now you can access all your variables with the self prefix. It may be tedious but at least it's short, and most good IDEs (pycharm, pyscripter, eclipse) propose completion (much better than a dictionary, where you cannot have completion)
# let's say it's the main method
def run(self):
if self.a == 34:
# do something
self.a += 1
now:
o = Program()
o.run()
print(o.a) # without __ prefix, your data is visible from the outside
So using an object not for inheritance, polymorphism or such, but just to define the "global" context and work inside the instance, with self. prefix, and say goodbye to global variables.

What to do instead of adding/changing global variable

I'm quite new to programming and I keep reading everywhere "using methods to change global variables is not good coding. Use very little global variables and definitely don't change them". I can't seem to find a good explanation of why or what should be done.
Lets say I want to have some sort of a game. And it has variable health=100. Now I figured this should be a global variable and not inside a class/object, but that would also mean I would need to adjust it and so on by doing something like:
def minushealth():
global health
health -= 20
I can't really seem to figure this out and maybe there is just something simple I don't know here.
You could make health an explicit argument to and return value from the function:
def minus_health(health):
return health - 20
then assign the return value when you call it:
health = minus_health(health)
Even better, get rid of the "magic number" with an optional second argument:
def minus_health(health, amount=20):
return health - amount
Note that you can now test minus_health like:
assert minus_health(100) == 80
whereas if you implemented with global, you would have to do:
health = 100
minus_health()
assert health == 80
It might not seem like a huge problem in this simple example, but as your program gets more complex you have much more setup and tear-down to do, whereas a well-isolated function will still be effectively testable in one line.
jonrsharpe's answer is on point.
But for a game type answer you'd be better off using the class/attribute solution which would look like:
player_object.minus_health()
Where minus_health would look like:
class Player(object):
def __init__(self):
__health = 100
def minus_health(self):
self.__health -= 20
Of course this doesn't take into account if the health goes below 0, etc. But you should get the idea. This way allows all "Players" to have separate health attributes.
The Player class shown in another answer is probably going to be where you end up, but just an explanation of arguments to a function, you can only modify arguments that are mutable. In your specify case, an approach similar to the Player class, but simpler, is to manage your state in a dict and then pass that dict into the function to modify an attribute:
def minus_health(entity, amount=-20):
entity['health']+=amount
# Use like so
player = dict(name='John', health=100)
minus_health(player)
lists and objects are also mutable and will work in a similar way, i.e. their contents can be modified inside the function.

Sharing global variables between classes in Python

I'm relatively new to thinking of python in terms of object-oriented programming, and it's coming relatively slowly to me.
Is it possible to pass global variables between classes with multiple functions in them? I've read extensively about them here and in other sources, but I'm still a bit confused.
My ultimate goal is to define a variable globally, assign it a value in a function of one class, and then USE it in another class's function. Is that possible? I'm building a pythonaddin for ArcMap, which requires a class for each button. I want to have a button to assign a value to a variable, and then use that variable in another class in the script.
(Before I get the flak, I know it's relatively bad form to use global variables in the first place, I'm trying to learn)
For instance, as a highly simplified example:
x = []
class A():
def func_1(self):
#populate the x variable
global x
x = 1
class B():
def func_2(self):
global x
#do stuff with x
Is this acceptable (even if not pythonic)?
Yes, it can work. Here is the modified code:
x = []
class A:
def func_1(self):
#populate the x variable
global x
x.append(1)
class B:
def func_2(self):
global x
print x
a = A()
a.func_1()
b = B()
b.func_2()
It can print the list x correctly. When it's possible, you should try to avoid global variables. You can use many other ways to pass variables between classes more safely and efficiently.
PS: Notice the parameters in the functions.
A more Pythonic solution would be to have the button objects (in their method that handles the "press" event) set an attribute or call a method of whatever object needs the information.
Yes, it is possible. Your code is almost right, just missing a couple of parenthesis in the function definitions. Other than that, it works.
Now, is that acceptable? In 99% of cases, no, it's not. There're many ways of passing variables and sharing data between classes, and using global variables is one of the worse ones, if not the worst.

Avoid using global without confusing new programming students in Python?

I've been teaching 8th-9th graders basic computer programming for two weeks, and yesterday I tried to show them how they could make real simple text-adventure games in Python.
Scenes are functions, (e.g dragons_cave()) which consist of some print statements and then a call to input(), asking the player where they want to go next, which then gets passed to globals() to find the appropriate function and then called. I know it's not ideal (at what point would the huge chain of functions start becoming a problem?) but, of what crossed my mind, it seems to be the simplest for them while involving only a little handwaving.
My problem is with global state — ex. the player gets a key in one scene and only then can they unlock the gate in another scene. When I have global immutables like strings or booleans, Python wants me to use the global keyword at the beginning of the function.
global hasKey
hasKey = True
I'm pretty okay with that, but I have a vague sense (picked up from Stackoverflow among other places on the Internet) that global is frowned upon and always has a superior counterpart. I could have a global dictionary or wrap everything in a class, but I'm not sure if I could defend those options clearly to my kids (who are still thinking through the implications of variables).
Whatever I use, I want to be able to explain straightforwardly to my kids why we do it this way and why doing it this way is necessary. global seems to have both these properties, but is it bad?
I would encourage them to start learning OO
class Location:
name="a place"
description = "A dark place. there are exits to the North and East"
exits = "North","East"
def __str__(self):
return "%s\n%s"%(self.name,self.description)
class Player:
current_location = "Home"
inventory = ["Blue Key","Magic Thumbtacks"]
health = 100
name = "Unknown"
def __init__(self,name):
self.name = name
player = Player("Player 1")
loc = Location()
print loc
x = input("Input:")
To be honest a game is a difficult concept (even a text adventure). But I would start them directly on OO concepts, they will benefit more from that in the long term.
Granted this example is very small and leaves a lot of implementation details out.
An unrelated but better OO example would be:
class Animal:
voice = '...'
def speak(self):
return "A %s Says '%s'"%(self.__class__.__name__, self.voice)
class Dog(Animal):
voice = "Bark, Bark"
class Duck(Animal):
voice = "Quack, Quack"
print Dog().speak()
print Duck().speak()
Assuming you want to keep things as simple as possible (no OO) and want to avoid introducing the global keyword, you could instead use a state dictionary and assign variables in there.
state['hasKey'] = True
Since access to this dict is not a variable assignment you avoid introducing the global keyword and at the same time can teach how to use a dict (checking for keys etc.)
Of course you still use a global variable and don't really address the issue of good coding style. But on the other hand, it could serve as an introduction to scopes and namespaces.

Parameter and Function with the same name

On a Python assignment, I had to make the following two functions:
move(board, move)
undomove(board, move)
Having an argument with the same name as the function seems like a bad practice to me. I already contacted the professor to change it, but out of curiosity, is it possible to call the move function from inside the undomove, or to use recursion on the move? Inside these functions, move refers to the argument.
(Python 3, if it matters)
You can get a handle on move (the function), however it will require some additional gymnastics.
def move(move):
print(move,"inside move")
def undomove(move):
print (move,"inside undomove")
this_mod =__import__(__name__)
this_mod.move(move)
if __name__ == '__main__':
move(1)
undomove(2)
Generally though, I would definitely avoid naming a local variable with the same name as a function that I will need in that function.
As far as style is concerned, creating a function def move(move): ... is definitely a little weird, and it would make the casual reader think that you're trying to write a recursive function, so I would definitely avoid that. Writing undomove(move) when move is already defined in the module scope as a function is a little less weird, but it still might cause confusion at a quick glance (is it a local variable? is it the function?) so I would probably avoid that one as well.
There are a number of options here, but ruling out the simplest (renaming move), there are a few others.
Firstly, you could create another name for the function, so you can use that when move gets overridden:
def move(...):
...
move_ = move
def undomove(..., move):
move_(...)
This works as functions in Python are objects like any other - so you can just assign them to variables.
Another option would be to place the functions in a class, so they are in a namespace - meaning you access the method as self.move() and the parameter as move. That said, if you assignment requires that the functions be top-level, that isn't an option.
You could reach the function moveby calling globals()['move'] from within undomove (or any other function). Not very elegant...

Categories

Resources