if not global -- then what?

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sat Feb 20 19:00:05 EST 2010


On Sat, 20 Feb 2010 11:25:46 -0800, egasimus wrote:

> Hi, newbie here. I've read on using the 'global' keyword being
> discouraged; then what is the preferred way to have something, for
> example a class containing program settings, accessible from everywhere,
> in a program spanning multiple files?

Such a thing is no different from keeping many global settings. It's not 
the global KEYWORD that is discouraged, but the over-use of global 
variables itself.

See, for example:

http://c2.com/cgi/wiki?GlobalVariablesAreBad
http://weblogs.asp.net/wallen/archive/2003/05/08/6750.aspx


In a nutshell: shared variables introduce tight coupling between parts of 
your code that should be independent. Whether they are truly global, or 
passed around in a Settings object, makes little or no difference. In 
both cases, the risks and problems are identical.

Let me give a fanciful example: suppose your refrigerator, oven and 
shower all shared a single global temperature setting instead of having 
three local temperature settings. This would be a recipe for disaster: 
every operation to any of the three would need to carefully save the 
current temperature setting, and there's the constant risk of scalding 
showers, ruined foods, and undercooked meals.

Having three different global temperature settings helps a bit, but that 
just expands the number of variables that everything has access too. Why 
do the television and the garage door need access to any temperature 
setting, let alone all three?

Python globals aren't as bad as in some other languages, because "global" 
means global to a single module, not global to the entire program, but if 
you create a Settings object and import it from module to module, you 
break the encapsulation, *and* you then have the problem of needing to 
make sure it is initialised properly.

http://archive.eiffel.com/doc/manuals/technology/bmarticles/joop/globals.html

Global constants also aren't as bad, because the coupling is weaker. 
Unfortunately Python doesn't have constants, except by convention, so 
there's always the risk that some badly-behaved function might decide to 
redefine (say) math.pi to 3.15.

Unfortunately, you probably can't get rid of globals altogether, but the 
tricks are (1) use as few of them as possible, and (2) make them local as 
quickly as possible.

For example, you might have something like this:

default_header = "My special header"  # A global

for option, argument in command_line:
    if option == "--header":
        default_header = argument

class Printer:
    def print_page(self, text, header=None):
        if header is None:
            header = default_header
        print header
        print text


I would limit the scope of default_header so that it was local to the 
printer class:


for option, argument in command_line:
    if option == "--header":
        Printer.default_header = argument

class Printer:
    default_header = "My special header"

    def print_page(self, text, header=None):
        if header is None:
            header = self.default_header
        print header
        print text

You might not be able to completely eliminate either globals, or a 
Settings object, but you should be able to make it much, much smaller.

In practice, to make testing easier, I'd split the command line 
processing into two steps: a function that reads and processes the 
command line into a single local settings object, then a separate 
function that applies those settings to the various classes. The function 
that reads the command line doesn't need to know where the settings 
eventually end up, and the function that applies the settings doesn't 
need to know where they come from. All the coupling between settings and 
classes is in one place, the second function, instead of scattered all 
over your code.



-- 
Steven



More information about the Python-list mailing list