When to use globals

I am dogmatic about never using global variables. But when people way smarter than me use them, like Brian Kernighan does when he builds a command-line interpreter in that chapter of The Unix Programming Environment, I wonder if maybe I’m being too reluctant.

I was looking at a python module vaguely like this:

def foo():
do_foo_stuff()

def bar():
do_bar_stuff()

def baz():
do_baz_stuff()

def quux():
do_quux_stuff()

I had a bunch of independent functions. I wanted to add logging. I saw two easy ways to do it:

def foo():
logger = get_logging_singleton()
logger.log_stuff()
do_foo_stuff()

def bar():
logger = get_logging_singleton()
logger.log_stuff()
do_bar_stuff()

def baz():
logger = get_logging_singleton()
logger.log_stuff()
do_baz_stuff()

def quux():
logger = get_logging_singleton()
logger.log_stuff()
do_quux_stuff()

In the above code, I would get a reference to my logger object in each function call. No globals. Maybe I am violating some tenet of dependency injection, but I’ll talk about that later. Anyhow, the point I want to make is that the above approach is the way I would do it in the past.

Here’s how I decided to write it this time:

logger = get_logging_singleton()

def foo():
logger.log_stuff()
do_foo_stuff()

def bar():
logger.log_stuff()
do_bar_stuff()

def baz():
logger.log_stuff()
do_baz_stuff()

def quux():
logger.log_stuff()
do_quux_stuff()

All the functions access the logger created in the main namespace of the module. It feels a tiny bit wrong, but I think it is the right thing to do. The other way violates DRY in a big fat way.

So, a third option would be to require the caller to pass in the logging object in every function call, like this:

def quux(logger):
logger.log_stuff()
do_quux_stuff()

This seems like the best possible outcome — it satisfies my hangup about avoiding global variables and the caller can make decisions about log levels by passing any particular logger it wants to.

There’s two reasons why I didn’t take this approach:

  1. I was working on existing code, and I didn’t have the option of cramming in extra parameters in the calling library. So, I could do something like def quux(logger=globally_defined_logger) but I’m trying to make this prettier, not uglier. The whole reason that I wanted to add logging was that I wanted some visibility into what what the heck was going wrong in my app. I didn’t have time to monkey with overhauling the whole system.
  2. I plan to control my logger behavior from an external configuration system. I don’t want to change code inside the caller every time I want to bump the log level up or down. It is the conventional wisdom in my work environment that I face less risk just tweaking a configuration file setting and restarting my app rather than editing my code*.

[*]I suspect that in the final analysis, this belief will be exposed as garbage. But for right now, it seems pretty true that bugs occur more frequently after editing code than after editing config files.

UPDATE: Apparently, I’m not just talking to myself here! Gary Bernhardt linked to this post and added some really interesting points. Also, his link to the post on the origin of the phrase now you have two problems was something I hadn’t heard of before.

22 thoughts on “When to use globals

  1. Or there’s always:


    class Frotz(object):

    def __init__(self, logger=None):
    self.logger = logger or logging.getLogger()

    def foo(self, ...):
    self.logger.log(...)
    ...

    def bar(self, ...):
    self.logger.log(...)
    ...

    Which would seem to be a little happier on the DI front and the cleanliness front. And then your module doesn’t get all fussy about importing if for some reason it can’t get its logger going.

    Though in reality I don’t think you should be swearing off globals entirely; I think perhaps the message has gotten distorted over time. If you have a really good reason, and you know what you’re doing, then it’s probably OK to have the occasional global. I suspect that we mostly shout about the badness of globals so that the first-year comp sci kids get the message that they should pay attention and not start using globals all willy-nilly.

  2. Thanks for the comment. I think I’m one of those people that got yelled at for using globals and now I have a hangup about them.

    Anyhow, you can use the code tag and it will respect whitespace.

  3. Thanks for a great post, was an insight into the global variable utility, I am new to programming and have been learning everyday.
    Nice i discovered your post…

  4. Nice post, very educating it was.. somehow I have always been avoiding Global variable my friends say they cannot do without Globals.. It was nice to read your post I might want to strat using Globals now..

  5. The entrenched aversion to globals is usually specifically about *program*-level globals; i.e. identifiers that cross module boundaries and are almost impossible to track down reliably.

    There's nothing wrong with *module*-level globals, which is the largest scope in Python.

  6. That was really informative.. I really liked the way you explained it.. Appreciate the effort.Thanks.

  7. That was really informative..I really had lots of doubts when I was using the global variable but after reading this article half my doubts have been cleared.

Comments are closed.