[Tutor] global variables

Steven D'Aprano steve at pearwood.info
Thu Aug 22 17:35:53 CEST 2013


On 22/08/13 22:36, Matthew Ngaha wrote:
> I'm always told to avoid using them. I read discussions on the python
> irc channel about them but honestly i feel there are some times where
> i can't avoid using them. Like where i want to keep track of a state
> variable in many different functions that may or may not alter its
> value and also not wanting any of the functions to return it to the
> caller.
>
> My question is how many global variables did your last decent sized
> program have? Also please share any insight you have about them. I do
> try to avoid them, but is this always possible?

Good question! And one that needs a long answer.

Yes, it is possible to avoid global variables. The proof of this, if you need any, is programming languages like Haskell which are purely functional languages, which means they don't have variables at all! Everything is a fixed value, usually created on the fly as needed, and thrown away when no longer required, but never modified[1]. Need to change something? You create a new value rather than modify an existing one. Haskell programmers are capable of writing large programs, and they do it without global variables.

Functional programming is a very different paradigm, and one which takes a while to get used to. Haskell, which is pure functional, can be hard to wrap your head around, but Python is strongly influenced by Haskell and Lisp and could be considered a semi-functional language. If you've ever used list comprehensions or generator expressions, you've got a small taste of functional programming.

Truth be told, you *could* write a large, non-trivial Python program in entirely functional style, but without a clever Lisp or Haskell compiler behind it, it would probably be just as slow to run as it would be hard to write. In Python, you should consider global variables to be something to be minimized rather than entirely avoided.

In my experience, global variables are well-suited to dealing with user preferences and command-line options, and otherwise best avoided. But notice that user prefs and cmd line options aren't exactly *variables*, since they are typically set once, when the program starts up, and then never -- or at least hardly ever -- changed. Your functions may read their value, but they will not usually change their value.

That brings us to why global variables are unsafe. They're not unsafe because they are global. Global constants, or almost-constants, are perfectly fine. They're unsafe because they vary unpredictably. They introduce coupling between functions which otherwise should be independent.

For example, suppose we have a global variable and three functions, f, g, h, all of which read and write to the global under various situations. Now suppose you call f() -- the result you get depends on whether or not g() or h() have been called first, since they may change the global. And likewise, the result g() gives depends on whether or not f() or h() have been called, and similarly for h(). So instead of the result of f() depending in a nice clean way only on the arguments passed to it, instead it depends in a messy, convoluted, confusing way on the history of calls to separate functions that may have nothing to do with f(). You now have a tangle of couplings between functions:

f depends on g and h
g depends on f and h
h depends on f and g

Now imagine ten globals and twenty functions.

Globals introduce "action at a distance". A function in one part of your program can reach out over a great distance and effect another function's result, just by changing a global. That's a bad thing and should be minimized or eliminated.

So the real problem here is *state*. If your functions didn't depend on mutable state, but only on arguments passed to them, the problem of action at a distance would go away. This is where functional programming comes in: they eliminate mutable state altogether. Once a value is created, you can't change it, only throw it away and create a new one. In Python we don't necessarily go quite to that extreme, but it's still a good ideal to work towards.

One mistake people have is to create a class just to hold global variables, and then think that they are virtuous because "it's not a global". So then they end up with something like this:

class Everything_I_Need:
     def __init__(self):
         self.a = 1
         self.b = 2
         self.c = 3
         # etc.

everything = Everything_I_Need()


f(everything)
g(everything)
h(everything)


but of course this is just global variables in disguise, and still suffers from the same problem with strong coupling and action at a distance.



[1] Never say never. Haskell actually does have a way to create modifiable values, but to get an idea of how people consider it, it is often called the "unsafePerformIO hack".


-- 
Steven


More information about the Tutor mailing list