Global in multiple files

Alex Martelli aleaxit at yahoo.com
Tue Jun 12 03:55:57 EDT 2001


"Chris Barker" <chrishbarker at home.net> wrote in message
news:3B255CAC.E373C5B7 at home.net...
    ...
> As far a OO design is concerned, a module used in this way is very much
> like a class with only one instance. It's just a little less code. IN
> fact, it may make sense to put accessor functions in your globals
> module, like:
>
> globals.getUserList()
>
> rather than explicity accessing the global value itself.

Actually, and at least in Python 2.1, if one is willing to
get a LITTLE bit tricky:-), there's an alternative...

Suppose we want to obtain this effect:

D:\py21>python
Python 2.1 (#15, Apr 16 2001, 18:25:49) [MSC 32 bit (Intel)] on win32
Type "copyright", "credits" or "license" for more information.
Alternative ReadLine 1.4 -- Copyright 2001, Chris Gonnerman
>>> import fakemod
>>> fakemod.goodies.extend("batman robin superman".split())
>>> fakemod.baddies.extend("luthor penguin iceman".split())
>>> fakemod.allofem
['batman', 'robin', 'superman', 'luthor', 'penguin', 'iceman']
>>>

i.e., fakemod exposes "plain lists" -- but appends & other
mods to lists goodies and baddies of module fakemod also
become available for access through attribute allofem -- no
"accessor functions" to complicate the life of client-code
programmers.  How could we achieve this?  Well, we COULD
have in fakemod.py something like...:

class _fakemod:
    def __init__(self):
        self.goodies = []
        self.baddies = []
    def __getattr__(self, name):
        if name=='allofem':
            return self.goodies+self.baddies
        raise AttributeError, name

import sys
sys.modules[__name__] = _fakemod()

And, indeed, when this is fakemod.py we can see in the
above-copy&pasted-interactive session that:

>>> fakemod
<fakemod._fakemod instance at 007F88BC>
>>>

The trick is that an entry in sys.modules need not be a
module-object... an instance-object will do, AND if it's
a module object then we can play all of the usual tricks
with __getattr__, etc, etc.  This is just a "proof of
concept", of course, and we'd really need to expose a
UserList derivative as 'allofem', in order to catch any
attempts to modify it directly and either disallow them
or translate them appropriately into modifications to
the real underlying lists, etc, etc.  But, it's a start.

Whatever special-method trickery we want to do on an
object that client-code obtains with an import, just
like a module, we can (mostly, I guess, in __getattr__
__setattr__ and __delattr__), by using an instance as
the "fake module" in this way.


Is this "obfuscated Python" and not to be mentioned in
polite company?  I don't know.  I have not yet had any
occasion to use this in anger -- AFAIK it wasn't in 2.0,
it's a 2.1 innovation -- so I have no direct personal
experience on what effect this has on maintenance and
similar chores.  Offhand, however, this does NOT strike
me as horrid, to-be-avoided black magic.  Sanity checks
on module attributes, ease of access to them, avoidance
of cumbersome accessor-function syntax, etc -- all of
these seem to be applicable to 'modules' (or objects
accessed as such) just as much as they do to instances.

Rather than giving __setattr__, etc, to module objects
directly, the same power (or more) has been obtained by
relaxing the check that used to ensure that any entry
in sys.modules had to be a module-object (a check which
one could see as a violation of signature polymorphism
in favour of strict typechecks -- the less typechecks
we have lying around, the more the simplicity and power
of signature polymorphism will be available to us!).

I guess the main danger might be with *C* code in a
Python extension accessing "module" (ha:-) objects
with PyImport_ImportModule or thereabouts, and then
accessing the returned object directly with concrete
PyModule_* calls rather than polymorphically with
PyObject_* calls.  A danger specifically for older
existing Python extensions, I guess -- and how many
of those use PyImport_* etc, after all?  Once one is
aware of the possibility that PyImport_* yields a
non-module object (polymorphic with a module -- no
doubt an instance object in most such cases), it
should be easy enough to account for this possibility
(no personal experience yet...).


Alex






More information about the Python-list mailing list