Globals and cargo culting
Matt Wilson
wants
a module’s functions to log to one logger, but he can’t change their
interface and he doesn’t want to use a global variable. This is the
kind of thing that decorators are very good at, for better or worse.
Here’s a decorator that will do the job:
def with_logger(fn):
def new_fn(*args, **kwargs):
logger = get_singleton_logger()
return fn(logger=logger, *args, **kwargs)
return new_fn
And here’s how to use it to define a function that gets a logger
instance without the caller passing it in:
>>> @with_logger
... def add(x, y, logger):
... logger.warning('x + y = %i' % (x + y))
...
>>> add(1, 2)
WARNING:foo:x + y = 3
This seems to be exactly what Matt was looking for: there are no
globals, a logger gets injected, and the function’s interface hasn’t
changed. But is it a good idea?
No, it’s a ridiculous idea! It’s just a reimplementation of global
variables! All I’ve done is come up with a complicated scheme for
injecting a single logger instance into every function in the module.
But that’s exactly what a global does! This is something that
programmers have done over and over again in the name of OO.
Everyone wants globals, but they go to great lengths to hide
it.
Here are three possible ways to solve the original logger
problem:
- Use a singleton, and have each function retrieve the instance
that way.
- Use a decorator that injects the instance into the function’s
argument list each time. In with_logger, I combined this with a
singleton.
- Just use a global.
If you choose (1) or (2), the joke’s on you. You’re still using a
global, but now you
have two problems:
global state and a complicated method for managing it. There’s
no need for that, because we already have a simple method for
injecting instances into a module’s functions: globals!
Of course, sometimes singletons or decorators like with_logging
make sense, but only when they actually do something. If all
they do is allow multiple functions to access a single long-living
instance, they’re dangerous and needlessly complex. In almost all
cases, singleton and related techniques are nothing more than
cargo
cult programming.