Why is "while" ticking me off???

Alex Martelli aleaxit at yahoo.com
Tue Oct 10 04:47:49 EDT 2000


"Aahz Maruch" <aahz at panix.com> wrote in message
news:8rtlqf$72m$1 at panix3.panix.com...
    [snip]
> >was intending there. If = were an operator in Python with the same
> >meaning it has in C, then you could have written that as:
> >
> >if a = dict[k1]:
> > a.something()
>
> OIC.  Well, in Python, I'd write this as
>
> if dict[k1]:
>   dict[k1].something()
>
> assuming I were certain that k1 existed in dict, which you'd have to do
> in C (null pointer bugs are so much fun).

Here, it may be OK to evaluate the expression twice, as it's simple
enough.  More generally, however, the idiom:

    if somecomplicatedthing(a,b,c):
        somecomplicatedthing(a,b,c).something()

is untenable -- you can't even be sure that somecomplicatedthing will
return the same result both times, let alone do so efficiently.

I think a more Pythonic approach is (surprise, surprise!-) an auxiliary
object (and I nominate this as the First Python-Only Design Pattern, as
I have not met it elsewhere...:-)...

# in holdit.py...:
class HoldIt:
    def __init__(self):
        self.values = []
    def set(self, value):
        self.values.append(value)
        return value
    def get(self):
        return self.values.pop()

holdIt = HoldIt()


# in your using module
from holdit import holdIt

if holdit.set(somecomplicatedthing(a,b,c)):
    holdit.get().something()


I think this solution is quite acceptable in some cases, e.g. if you
have to translate to Python a lot of code making large use of the
idiom of assignment-within-expression that other languages afford.

If several assignments must co-exist, rather than a LIFO discipline
as here suggested, then the set and get methods will have to take
a 'variable name' argument and the class will have to use a dict
rather than a list (further syntax sugar via __getattr__ etc may
then also be warranted), but I suspect one level only will often
suffice, and I propose it in the usual spirit of "doing the simplest
thing that can possibly work".  Note that the 'holdit' object ends
up holding one value when the 'if' fails -- this needs to be
explicitly 'cleared' (I think that adding an 'implicit clear', i.e.
not actually recording in .set a value that yields false, is a bit
of an overspecialization -- although it's of course very easy to
add it if you don't mind breaking "explicit is better than implicit").

E.g., in the idiom that's probably most-asked-about in this newsgroup,
"how do I read & process a file just one line at a time", one
solution would become:

while holdit.set(fp.readline()):
    do_process(holdit.get())
# an extra call to clear the left-over null:
holdit.get()

Maybe adding a more-explicit "holdit.unset()" synonym for ".get",
for the specific purpose of removing the last-set value, might be
even better (just because it's more explicit).


With the named-values variants, one would have:

while namer.set('line', fp.readline()):
    do_process(namer.line)
namer.del('line')

or thereabouts.  Implementation is left as an easy exercise to
the reader...


Alex






More information about the Python-list mailing list