Concise idiom to initialize dictionaries

exarkun at intarweb.us exarkun at intarweb.us
Tue Nov 9 13:27:50 EST 2004


On Tue, 09 Nov 2004 19:01:19 +0100, Gert-Jan den Besten <gj.den.besten at hccnet.nl> wrote:
>Frohnhofer, James wrote:
> > My initial problem was to initialize a bunch of dictionaries at the start of a
> > function.
> > 
> > I did not want to do
> > def fn():
> > 	a = {}
> > 	b = {}
> > 	c = {}
> > 	. . .
> > 	z = {}
> > simply because it was ugly and wasted screen space.
> > 
> > First I tried:
> > 
> > 	for x in (a,b,c,d,e,f,g): x = {}
> > 
> > which didn't work (but frankly I didn't really expect it to.)
> > Then I tried:
> > 
> > 	for x in ('a','b','c','d','e','f','g'): locals()[x]={}
> > 
> > which did what I wanted, in the interpreter.  When I put it inside a function,
> > it doesn't seem to work.  If I print locals() from inside the function, I can
> > see them, and they appear to be fine, but the first time I try to access one
> > of them I get a "NameError: global name 'a' is not defined"
> > 
> > Now obviously I could easily avoid this problem by just initializing each
> > dictionary, but is there something wrong about my understanding of locals,
> > that my function isn't behaving the way I expect?
>
> Any variable you assign within a function, is local to that function. 
> You cannot refer to it from the enclosing function or module.

  This is actually not the problem being experienced.  The actual problem is much more subtle and insidious.  Consider this example:

    >>> locals()['x'] = 'y'
    >>> print x
    y
    >>> 

  All appears well!  Mutating the object returned by locals() lets us change arbitrary names in our local scope.  Great.  Now, let's go write a program:

    exarkun at boson:~$ cat > foo.py
    def foo():
        locals()['x'] = 'y'
        print x
    foo()
    exarkun at boson:~$ python foo.py
    Traceback (most recent call last):
      File "foo.py", line 4, in ?
        foo()
      File "foo.py", line 3, in foo
        print x
    NameError: global name 'x' is not defined
    exarkun at boson:~$ 

  "_Whaaaaat_?" you say.  "It's doing exactly the same thing!" you say.  Well, not quite.

  Mutating the objects returned by both locals() and globals() is a no-no.  They are intended to provide _read only_ access to those two scopes.  Since they return dictionaries, you _can_ actually mutate them, though.  And mutating globals() even works as expected; however, mutating locals() does not.

  So why did it work in the interactive interpreter?  Well, because of this nifty little fact:

    >>> locals() is globals()
    True
    >>> 

  Oops.  At the top-level of the interactive interpreter, there is no distinct local scope, just like at the top-level of a module.  So the locals() function just returns the globals dictionary, which happens to respect modifications.

  Kind of surprising, eh?

  Luckily for the original poster, namespaces aren't the only dicts in town.  What he really wants to do is something like this:

    someDictionaries = []
    for name in ('a', 'b', ..., 'z'):
        aDict = someDictionaries[name] = {}
        aDict[foo] = bar
        ...

  Later, someDictionaries, itself a dictionary, can be used to retrieve the dictionaries which were created and given "names" of 'a', 'b', etc.

  Jp



More information about the Python-list mailing list