Tip: clearing up Python environment

Alex Martelli aleaxit at yahoo.com
Mon Nov 20 05:14:09 EST 2000


"Tim Roberts" <timr at probo.com> wrote in message
news:emae1t8d7oqd26qht906h8b6te23o1qh6p at 4ax.com...
> "June Kim" <junaftnoon at nospamplzyahoo.com> wrote:
>
> >Without terminating Python and restart it, you can do that.
> >
> >Put the following in a file "clear.py"
> >
> >#clear.py
> >z=['__builtins__', '__doc__', '__name__','__1__']
> >for i in [i for i in globals().keys() if i not in z]:
> >    del globals()[i]
> >del i
>
> I have a question.  Is the list comprehension above really any better than
> the unrolled version?

I think it isn't, but the fact is not immediately obvious.
In your version...:

>   z=['__builtins__', '__doc__', '__name__','__1__']
>   for i in globals().keys():
>     if i not in z:
>       del globals()[i]

...one would start worrying, "but am I not perchance
altering the very sequence I'm iterating on, so that
the results might perhaps be flakey...?".  Actually,
I think the second version is just as solid as the
first one, because in fact .keys() is (I believe)
evaluated "non-lazily" -- it returns a list that is
a "snapshot" of the dictionary's keys *in that very
moment*, and therefore it's unaffected by possible
further changes to that dictionary.  The non-lazy
nature of list-comprehension's evaluation is, I believe,
more clearly and sharply documented, which is what may
make somebody feel more secure using them in this case.

Maybe the 'best' solution might sacrifice a little
speed and terseness in favour of explicitness (and
more explicit names) with (warning -- untested!):

globdict = globals()
globvars = (globals().keys())[:]
preserve = (
    '__builtins__', '__doc__', '__name__', '__1__',
    'globdict', 'globvars', 'preserve', 'globvar')
for globvar in globvars:
    if not globvar in preserve:
        del globdict[globvar]
del globdict, globvars, preserve, globvar

Or maybe using a helpful function to avoid reliance
on global variables to operate on global variables
might be the helpful step:

def aux(*preserve):
    preserve += 'aux'
    globdict = globals()
    globvars = (globdict.keys())[:]
    for globvar in globvars:
        if not globvar in preserve:
            del globdict[globvar]
    del globdict['aux']
aux('__builtins__', '__doc__', '__name__', '__1__')


> Seems to me we are building an extra list for nothing,

This is probably true (just like it is for the [:] copy
in my proposals), but are you _absolutely sure_ that
globals().keys() will take a 'snapshot' (non-lazy) in
every version of Python, now and forever...?-)

OK, this is a too-high standard, I agree.  Were the
price of this "precaution" very high (e.g., were it an
increase in complexity, or a huge slowdown), I'd tend
to agree more fully with you.  As it is, with just a
[:], it seems cheap enough to me that one might choose
to pay it.

> and it doesn't make the code any clearer.

It may not (depends on how clear the 'snapshot' issue
is to a reader of the code in either case!).  It seems
to me that taking a copy very explicitly with [:] may
be better from this stylistic viewpoint.

> Many of the list comprehension examples I've seen
> seem to REDUCE comprehension.

I haven't seen many lc examples that were using lc
basically just to replace a straight list copy, e.g.:
    x = [y for y in z]
versus
    x = z[:]
but if there were many such examples, I would agree
with you re their inappropriateness wrt clarity.  In
this case, the extra guard can also be usefully moved
to live inside the loop, as we don't need the 'filtered
list' for other purposes.

I do find list comprehensions invaluable when they
replace cumbersome idioms:
    return [y+k for y in z if y > w]
versus
    return map(lambda y,k=k: y+k,
               filter(lambda y,w=w: y>w, z))
versus
    x = []
    for y in z:
        if y > w:
            x.append(y+k)
    return x


Alex






More information about the Python-list mailing list