if not global -- then what?

Jonathan Gardner jgardner at jonathangardner.net
Sun Feb 21 02:34:15 CET 2010


On Sat, Feb 20, 2010 at 11:25 AM, egasimus <fallenblood at gmail.com> 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?

One of the powerful concepts to come out of Lisp was "dynamic scope".
This is the ideal solution for configuration and logging.

"Lexical scope" is where the value of the variables are found in the
file it was defined in. This is what Python does, and it's what you're
used to in most other languages.

"Dynamic scope" is where the value of the variables are found by
looking up the call stack. This is similar to perl's "local" keyword.
It's also similar to environment variables, if you consider programs
that run programs that run programs as similar to functions. (If
you're familiar with neither, then that's fine.)

This is useful for a program where you know it needs configuration or
logging, but you don't want to have to specify it as part of the
program. Whoever calls the program needs to configure it and setup
their logger before calling it.

Unfortunately, there is no built-in way to deal with dynamic variables
in Python. But that doesn't mean you can't emulate it explicitly and
clearly!

In order to emulate it, you simply pass the configuration and such
down to the functions you call. Each function or class instance needs
to pass it further along. So, rather than:

  def foo():
      bar()

  def bar()
     ...

you need to have:

  def foo(config):
     bar(config)

  def bar(config):
     ...

Obviously, only certain functions care for the config and logging
setup. Others don't. And obviously, you can modify the value on the
way down. Just be sure to make a copy or you'll change the entire
stack's value for that configuration.

There are a couple of systems that do something close to this. I don't
know of any, except SQLAlchemy, that does this exactly right. WSGI is
also a system that does this right, although I don't know that many
people realize it.

Usually, what people end up doing is setting the config values to some
global in some module. Then everyone is expected to look in that
module for the config. This ends up being messy, particularly for
testing where you'd like to mess with the config without messing with
THE config. You can tell it's messy because people end up writing code
like this:

  old_value = get_config(...)
  set_config(... some new value ...)
  ... do your stuff ...
  set_config(...back to old_value...)

This kind of code is hard to get right. (What happens if there is an
exception before you set the old config back? That's right, you need a
try-finally block.)

In terms of "global", you should only really use "global" when you are
need to assign to a lexically scoped variable that is shared among
other functions. For instance:

def foo():
    i = 0
    def inc(): global i; i+=1
    def dec(): global i; i-=1
    def get(): return i
    return (inc, dec, get)

This really isn't that common, although it is useful. Note that the
above might be better organized into a class instance.

Good luck.

-- 
Jonathan Gardner
jgardner at jonathangardner.net



More information about the Python-list mailing list