A Globals Class pattern for Python

I’ve gradually been evolving a technique of coding in which I put module globals in a class. Recently I stumbled across Norman Matloff’s Python tutorial in which he recommends doing exactly the same thing, and it dawned on me that this technique constitutes a truly idiomatic Python design pattern.

A pattern needs a short, catchy name. I don’t think anyone has yet given this pattern a name, so I will propose the name Globals Class pattern.

I’m sure that many experienced Python programmers are already quietly using the Globals Class pattern. They may not see much point in making a big deal about it, or in giving it a name and decking it out with the fancy title of “design pattern”. But I think a little bit of hoopla is in order. This is a useful technique, and one worth pointing out for the benefit of those who have not yet discovered it.  A bit of cheering and arm-waving is in order, simply to catch some attention.

The technique is extremely simple.

  • You define a class at the beginning of your module.  This makes the class global.
  • Then, all of the names that you would otherwise declare global, you specify as attributes of the class.

Really, there is virtually nothing class-like about this class; for instance, you probably will never instantiate it. Instead of functioning like a true class, it functions as a simple container object.

I like to use the name “mem” (in my mind, short for “GlobalMemory”) for this class, but of course you can use any name you prefer.

All you really need is a single line of code.

        class mem: pass

That is enough to create your mem container. Then you can use it wherever you like.

        def doSomething():
            mem.counter = 0
            ...
        def doMore():
            mem.counter += 1
            ...
        def doSomethingElse():
            if mem.counter > 0:
                ...

If you wish, you can initialize the global variables when you create the class. In our example, we could move the initialization of mem.counter out of the doSomething() function and put it in the definition of the mem class.

        class mem:
            counter = 0

In a more elaborate version of this technique, you can define a Mem class, complete with methods, and make mem an instance of the class. Sometimes this can be handy.

        class Mem:
            def __init__(self):
                self.stupidErrorsCount = 0
                self.sillyErrorsCount  = 0

            def getTotalErrorsCount(self):
                return self.stupidErrorsCount + self.sillyErrorsCount

        # instantiate the Mem class to create a global mem object
        mem = Mem()

What’s the point?

So, what does the Globals Class pattern buy you?

1. First of all, you don’t have to go putting “global” statements all over your code.   The beauty of using a globals class is that you don’t need to have any “global” statements in you code.

There was a time — in the past, when I still used “global” — when I might find myself in a situation where my code was evolving and I needed to create more and more global variables. In a really bad case I might have a dozen functions, each of which declared a dozen global variables. The code was as ugly as sin and a maintenance nightmare.  But the nightmare stopped when I started putting all of my formerly global variables into a global class like mem.  I simply stopped using “global” and got rid of all those “global” statements that were cluttering up my code. 

So the moral of my story is this.  Kids, don’t be like me.  I started out using “global” and had to change.  I’m a recovering “global” user. 

Don’t you even start.  Skip the section on the “global” keyword in your copy of Beginners Guide to Learning Python for Dummies.  Don’t use “global” at all.  Just use a globals class.

2. I like the fact that you can easily tell when a variable is global simply by noticing the mem. modifier.

3. The globals statement is redundant.  The Globals Class pattern relieves us of of the burden of having to worry about it.

Python has the quirk that if X is a global, and a function only reads X, then within the function, X is global. But if the function assigns a value to X, X is treated as local.

So suppose that — as your code evolves — you add an assignment statement deep in the bowels of the function. The statement assigns a value to X. Then you have — as a side-effect of the addition of that statement — converted X (within the scope of the function) from a global to a local.

You might or might not want to have done that.  You might not even realize what you’ve done.   If you do realize what you’ve done, you probably need to add another statement to the function, specifying that X is global.  That is sort of a language wart. If you use the Globals Class pattern, you avoid that wart.

4. I think the use of the Globals Class pattern makes the work of static code analyzers (e.g. PyFlakes) easier.

5. The Globals Class pattern makes it possible to create multiple, distinct groups of globals.

This can be useful sometimes. I have had modules that processed nested kinds of things: A, B, and C. It was helpful to have different groups of globals for the different kinds of things.

        class memA: pass
        class memB: pass
        class memC: pass

6. Finally, the Globals Class pattern makes it possible to pass your globals as arguments.

I have had the situation where a module grew to the point where it needed to be split into two modules. But the modules still needed to share a common global memory. With the Globals Class pattern, a module’s globals are actually attributes of an object, a globals class.  In Python, classes are first-class objects.  That means that a globals class can be passed — as a parameter — from a function in one module to a function in another module.

Is this really A Good Thing?

At this point I can hear a few stomachs churning. Mine is one of them. Because, as we all know, Global Variables are Always a Bad Thing.

But that proposition is debatable.  In any event, it is an issue that I’m not going to explore here.  For now, I prefer to take a practical, pragmatic position:

  • Sometimes globals are the best practical solution to a particular programming problem.
  • For the occasions when Globals are A Good Thing, it is handy to have a way to Do Globals in A Good Way.

So the bottom line for me is that there are occasions when some kind of globals-like technique is the best tool for the job.  And on those occasions the Globals Class pattern is a better tool for the job than globals themselves.

8 thoughts on “A Globals Class pattern for Python

  1. > Sometimes, I find myself in a situation where my
    > code is evolving and I need to create more and
    > more global variables. In a really bad case I might
    > have a dozen functions, each of which declares a
    > dozen global variables. The code is as ugly as sin
    > and a maintenance nightmare.

    Can you give an example where you need all these globals?

    How about using a class in the first place:

    class MyClass(object):
        def __init__(self):
            self.counter = 0
        def do_something(self):
            self.counter += 1
        def do_something_else(self):
            if self.counter > 0:
                # this could be more useful ;)
                self.counter -= 1
    

    Then work with an instance calling its methods instead
    of the functions in the module:

     
    my_instance = MyClass()
    print(my_instance.counter)
    

    I haven’t had any need for the global statement in
    years. There was only one occasion when I embedded Python
    in C and needed to keep some state.

    Is there anything in your six points describing the advantage
    of your global class that cannot be achieved with just a simple
    class?

    • >> How about using a class in the first place

      I think you missed my point… but it is my fault because of the way that I described my situation.

      I have revised the post to make it clear that I was describing something that I did in the past and don’t do any more.

      The point that I was trying to make is exactly your point: Just use a class in the first place. Don’t use “global” at all.

      >> Is there anything in your six points describing the advantage of your global class that cannot be achieved with just a simple class?

      No, because, as I use the term, a “globals class” *is* a simple class.

      A globals class isn’t a special kind of class, different from an ordinary class. A globals class is nothing more than a simple class… used for a particular purpose. What makes it a “globals” class is that it is used as a container for variables that otherwise would have to be declared global. So your MyClass (because of the kind of work it is doing) could be considered to be a globals class.

      But note that in many cases, there *is* a difference between the ordinary use of a class and the use of a class as a globals class. In most cases, classes are made to be intantiated. Most classes that we create are tools for creating instances, which is what we really want.

      In contrast, in many cases, a globals class is not made to be instantiated. It is itself a container; the class itself does what we want and we never need to instantiate it. In your example, MyClass is not used this way; you use it to create my_instance. So in this way your MyClass is not a typical globals class.

      In cases like this, where the class itself does all the work, you can use the class itself as a container without having to instantiate it. I guess you could call that a small advantage: you save one line of code.

      >> work with an instance calling its methods instead of the functions in the module

      Code examples are dangerous. The tend to blow up in your face. An author creates some example code with the idea of illustrating X. But in the example, details which the author considered irrelevant can seem significant to some readers. Readers take the code as illustrating not X, but Y. Confusion ensues.

      Consider my example code:

              def doMore():
                  mem.counter += 1
                  ...
      

      As author, I meant the ellipses to mean “other unspecified code in the function”. But a reader can also interpret them as meaning “other unspecified code in the module”.

      If they are interpreted the second way, then doMore() is a one-line function that does nothing but increment the counter. That’s not what I intended. But if it had been, then you would be right: it would indeed be reasonable to make the functions in my example into methods of MyClass.

  2. Hi,

    Nice well done post, explanations and examples!

    You’re right about that some of us already do similar things, though in my case I wouldn’t call it “globals”” I generally in more complex systems carry around a special Environment container; an instance.

    cheers
    James

  3. Thanks for you clarification. But I still don’t
    see the need for it. You do this:

    class mem: pass
    
    def doSomething():
        mem.counter = 0
    
    def doMore():
        mem.counter += 1
    
    def doSomethingElse():
        if mem.counter > 0:
            mem.counter += 100
    
    doSomething()
    doMore()
    doSomethingElse()
    

    I would do it this way:

    class Mem: pass
    
    def doSomething(mem):
        mem.counter = 0
    
    def doMore(mem):
        mem.counter += 1
    
    def doSomethingElse(mem):
        if mem.counter > 0:
            mem.counter += 100
    
    doSomething(Mem)
    doMore(Mem)
    doSomethingElse(Mem)
    
    print(Mem.counter)
    

    The result is same for this example.
    No need for globals. Just explicitly pass your
    container. In my opinion this is much easier to understand.
    Especially when the code gets larger. I can see mem being
    handed in. Using your Globals Class I need to figure out
    were mem comes from.

    Maybe I missing something.

  4. I find it useful to use global constants in larger programs, rather than passing constants around as arguments. Since globals are contained a dictionary, here’s what I do:

    G = globals()

    G[‘SCRIPT_NAME’] = sys.argv[1] # global assignment

    print ‘END: %s’ % (SCRIPT_NAME) # global reference

    etc…

    Another convention I stick with is CAPITAL globals. There’s no mistaking them for anything else.

  5. The idiomatic way to do what you’re describing in Python is to put those needed-by-lots-of-things as attributes of a module.

    Then, whatever needs them can import that module (e.g. ‘import config’) and everything has access to the objects that way: ‘do_something_with(config.foo)’.

    That way, there’s no need for any ‘global’ statement, there’s no confusion over why a class is being defined but not instantiated, etc.

Comments are closed.