0

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.

LuukV
  • 203
  • 1
  • 11
  • 4
    Why should it not be in a class/object? I assume you're making a simple game, how about making a class for the `Player` and make `health` a variable inside that class? –  May 12 '14 at 22:26
  • 1
    In terms of *why* to avoid global variables, see e.g. http://stackoverflow.com/questions/484635/are-global-variables-bad and http://c2.com/cgi/wiki?GlobalVariablesAreBad – jonrsharpe May 12 '14 at 22:30

3 Answers3

2

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
  • 115,751
  • 26
  • 228
  • 437
1

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.

kbknapp
  • 172
  • 1
  • 5
  • I would use at most a single underscore, and generally only if I wanted to use a property - name mangling is a pain, and "we're all consenting adults"! – jonrsharpe May 13 '14 at 07:36
  • Agreed, I'm just used to using a double underscore for 'truly private' variables out of habit for work. I can't remember the exact reason we do it that way. It's something along the lines of if you use a double underscore the interpreter actually remaps your variable name to something like _module_variable, where as with a single underscore it doesn't. – kbknapp May 13 '14 at 14:55
0

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.

Caleb Hattingh
  • 9,005
  • 2
  • 31
  • 44