RE: Introspection at the module level?

From: Robert Brewer (fumanchu_at_amor.org)
Date: 03/07/04


Date: Sun, 7 Mar 2004 12:58:38 -0800
To: "Roy Smith" <roy@panix.com>, <python-list@python.org>

Roy Smith wrote:
> I ended up with:
>
> map = {}
> for name, value in globals().items():
> if name.startswith ("OP_"):
> map[value] = name
>
> which seems to work just fine. Thanks to everybody who
> responded with suggestions.
>
> The danger here is that I'm depending on the fact that there are no
> globals defined elsewhere in the system which start with
> "OP_", but that
> seems like a pretty minimal risk. In fact, it looks like all the
> externally defined globals are of the form __xxx__.

To help avoid the global collision issue, dump all your OP_ values in a
module-level dictionary or something similar. For a good example, have a
look at the standard library module "htmlentitydefs.py", which should be
included your distribution. It does basically one thing; it provides a
name-to-codepoint dictionary like this:

name2codepoint = {
    'AElig': 0x00c6,
    'Aacute': 0x00c1,
    'Acirc': 0x00c2,
    ...
    'zeta': 0x03b6,
    'zwj': 0x200d,
    'zwnj': 0x200c,
}

...but then goes on to provide a reverse lookup dictionary a la:

# maps the Unicode codepoint to the HTML entity name
codepoint2name = {}

# maps the HTML entity name to the character
# (or a character reference if the character is outside the Latin-1
range)
entitydefs = {}

for (name, codepoint) in name2codepoint.iteritems():
    codepoint2name[codepoint] = name
    if codepoint <= 0xff:
        entitydefs[name] = chr(codepoint)
    else:
        entitydefs[name] = '&#%d;' % codepoint

del name, codepoint

This produces a number of benefits:

1. Keeps the globals() cleaner, and avoids collisions.
2. Makes your iteration code faster, since you're now iterating over a
known, limited set of values, not every possible global.
3. Most importantly, it makes those values more developer-friendly,
since they can now be overridden by consumers. That is, I can't as a
consumer of your code write:

    yourmodule.OP_fish = 'slap'

However, if you place them in a dictionary, I can write:

    yourmodule.OP_vars['fish'] = 'slap'

Even better, of course, would be to place them and their consumers
together in a class, so I could subclass it, but it's far from a perfect
world. ;)

HTH!

Robert Brewer
MIS
Amor Ministries
fumanchu@amor.org