No "side effect" assignment!

T

Tobiah

Ok,

I miss the idiom that my other languages use,
something like:


while( foo = getmore()){
process(foo);
}


I can't seem to do this in python without gagging:

foo = getmore()
while foo:
process(foo)
foo = getmore()


Is that what ppl do? The manual sez that this is
so that newbs won't do:

if foo = bar:
process()

thinking that they are checking for equality.
I feel that in a few ways python suffers by catering
to the uninitiated. Usually however, there are
great workarounds (or even plain better ways to do the job),
which I am hopeful will be yielded by the list, as has
been it's record thus far to do.

Thanks,

Tobiah
 
D

Dave Kuhlman

Tobiah wrote:

[snip]
I can't seem to do this in python without gagging:

foo = getmore()
while foo:
process(foo)
foo = getmore()

Then test your gag reflex on the following:

while 1:
foo = getmore()
if not foo:
break
process(foo)

Slips down pretty smoothly with me.

[snip]

Dave
 
T

Tobiah

Ack!!! (slight gag reflex) :)

That's actually more lines, even if more readable.

Ok, my perl roots are showing, but I was thinking
of putting the following function into my standard
module:

def eq(thingy):
global _
_ = thingy
return _



Having done that, I can now say:

while eq(getmore()):
process(_)


It seems to me that adding support for the side effect
assignment would not break any existing scripts. Gee,
they did it for += ;)

Thanks,

Tobiah
 
J

John Roth

Tobiah said:
Ok,

I miss the idiom that my other languages use,
something like:


while( foo = getmore()){
process(foo);
}


I can't seem to do this in python without gagging:

foo = getmore()
while foo:
process(foo)
foo = getmore()


Is that what ppl do? The manual sez that this is
so that newbs won't do:

if foo = bar:
process()

thinking that they are checking for equality.
I feel that in a few ways python suffers by catering
to the uninitiated. Usually however, there are
great workarounds (or even plain better ways to do the job),
which I am hopeful will be yielded by the list, as has
been it's record thus far to do.

While one of Guido's reasons for not including assignment
as an expression was to avoid errors (and that's not always
a novice error,) Python provides similar functionality in the
'for' statement. In C, C++ and Java, 'for' is basically
syntactic sugar around a loop, in Python, it actually does
an assignment of the next element of a sequence.

Your example could be:

for foo in getmore:
process(foo)

as long as getmore is either a sequence or implements the
iterator protocol.

This isn't a total replacement for assignment as an expression,
but it does cover the overwhelming majority of use cases.

John Roth
 
A

Andrew Koenig

Tobiah> Ack!!! (slight gag reflex) :)
Tobiah> That's actually more lines, even if more readable.

Tobiah> Ok, my perl roots are showing, but I was thinking
Tobiah> of putting the following function into my standard
Tobiah> module:

Tobiah> def eq(thingy):
Tobiah> global _
Tobiah> _ = thingy
Tobiah> return _



Tobiah> Having done that, I can now say:

Tobiah> while eq(getmore()):
Tobiah> process(_)

You're right -- your Perl roots are showing.

How about this instead:

def make_generator(func):
while x = func():
yield x

for val in make_generator(getmore):
process(val)
 
A

Asun Friere

Dave Kuhlman said:
Tobiah wrote:

[snip]
I can't seem to do this in python without gagging:

foo = getmore()
while foo:
process(foo)
foo = getmore()

Then test your gag reflex on the following:

while 1:
foo = getmore()
if not foo:
break
process(foo)

Slips down pretty smoothly with me.

[snip]

Dave

I am alone in feeling some slight unease at the sight of "while 1: ...
break"? I know this is considered somewhat pythonic, but apart from
the fact that there is only a single assignment statement is there
really any advantage to be gained by doing it this way? It seems to
be trading one aesthetic disadvantage for another, and imho, a
marginally less legible one.

In terms of pandering to newbies, I think the requirement that loop
conditions be expressions is much more squarely aimed at experienced
programmers, who would otherwise be tempted to write unintelligible
code by placing too much of the programs logic withing the loop
condition.
 
D

Duncan Booth

I can't seem to do this in python without gagging:

foo = getmore()
while foo:
process(foo)
foo = getmore()


Is that what ppl do?

I don't know about ppl, but Python programmers have a variety of ways of
structuring this code. The one that I think is simplest, but is often
overlooked is to use the builtin function 'iter'. It won't exactly replace
the code you have above, since your loop terminates as soon as getmore()
returns anything false (None, 0, False, '' would all terminate it). If
however you know the exact value to indicate termination (I'll guess
getmore() returns None when its done), you can write your loop:

for foo in iter(getmore, None):
process(foo)

If instead of a simple function call you had a more complex expression (say
getmore took a file as an argument), you might consider a lambda:

for foo in iter(lambda: getmore(aFile), None):
process(foo)
 
P

Peter Hansen

Asun said:
I am alone in feeling some slight unease at the sight of "while 1: ...
break"? I know this is considered somewhat pythonic, but apart from
the fact that there is only a single assignment statement is there
really any advantage to be gained by doing it this way? It seems to
be trading one aesthetic disadvantage for another, and imho, a
marginally less legible one.

There is a distinct advantage in the above, over the form with
a duplicated line, and it's nothing to do with aesthetics. Sure,
they both may be ugly to you (but they feel quite simple and thus
elegant to some experienced Python programmers), but the real issue
is that the one has the duplicated assignment and is therefore prone
to maintenance problems. This fact alone far outweighs any aesthetic
consideration, and wholly justifies the above form.

If you are really offended, use a wrapper of some kind as others have
shown.

-Peter
 
C

Christopher A. Craig

I am alone in feeling some slight unease at the sight of "while 1: ...
break"? I know this is considered somewhat pythonic, but apart from
the fact that there is only a single assignment statement is there
really any advantage to be gained by doing it this way? It seems to
be trading one aesthetic disadvantage for another, and imho, a
marginally less legible one.

While I agree with you, I don't think I'd bother with changing thel
language now to fix it. If I were writing a new language I'd prefer a
loop-and-a-half structure something like this:

repeat:
stuff
while test:
stuff

I'd really prefer something like the above without the second colon.
While it's nice to have the condition dedented for readability, it
isn't really starting a new block. At any rate, I don't think adding
this to the current language is worth it, so I'll keep using "while 1:
break" structures and translating to the above in my head.
 
J

John J. Lee

Tobiah said:
Heh! My point exactly :)

def f(): return raw_input()

for x in iter(f, ""):
print x


If you want to be more vague about the sentinel (a bad idea, and
equivalent to your Perl example), I suppose you could use something
obscure like this:

class FalsehoodSentinel:
def __cmp__(self, other): return int(bool(other))
falsehoodsentinel = FalsehoodSentinel()


and then:

def f(): return raw_input()

for x in iter(f, falsehoodsentinel):
print x


So that *any* false value from f will end the for loop. Don't do
that, though -- explicit is better...

Of course, most of the time, having written your program Pythonishly,
not Perlishly, you'll have iterators in the first place, and won't
need to turn them into an iterator by hand.


John
 
T

Terry Reedy

John J. Lee said:
def f(): return raw_input()

for x in iter(f, ""):
print x

Nice -- I keep forgetting about iter() -- except that the wrapper
adds nothing that I can see.
....
aksjf
* aksjf *
jslfkj
* jslfkj *

Terry J. Reedy
 
M

Martin Maney

Asun Friere said:
In terms of pandering to newbies, I think the requirement that loop
conditions be expressions is much more squarely aimed at experienced
programmers, who would otherwise be tempted to write unintelligible
code by placing too much of the programs logic withing the loop
condition.

Uhm, no. *Experienced* programmers know better than to take usch
things to excess, unless the code is being written for a obfuscated
code contest. As is so often the case, it's those difficult
in-betweeners, no longer innocent by virtue of ignorance, not yet
grown wise enough to eschew the merely clever, who bollix it all up for
the rest of us.
 
M

Martin Maney

Sean Ross said:
If you must use assignments in expressions, you might find the following
recipes useful:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/202234

Did I forget about this one or is it in fact new? It is, as the author
says, an ugly hack at the implementation level, but that seems to be
its only drawback. It doesn't use any hidden state that could be
altered unexpectedly, and is only a minor eyesore; those who worry
about accidentally writing an assignment in place of an equality test
will consider that last to be a feature, of course. Perhaps it could
be improved said:

The hope of obliterating this recipie is IMO the strongest argument in
favor of adding operator= to Python. I can think of no reason to
prefer this to 202234 aside from the chance that the language
implementation detail used in the latter's implementation could
conceivably go away someday.

Short form: 66061 Considered Harmful. What, you thought it was just a
coincidence it had all those sixes in its number?
 
P

Paul Paterson

Martin Maney said:
Did I forget about this one or is it in fact new? It is, as the author
says, an ugly hack at the implementation level, but that seems to be
its only drawback.

I think that the other drawback is that it only works at the top level of a
module. If you are inside a function or method then the locals() (f_locals)
dictionary is still writeable but it has no effect on the namespace it
represents.

So ...

import sys
def set(**kw):
assert len(kw)==1

a = sys._getframe(1)
a.f_locals.update(kw)
return kw.values()[0]

def main():
A=range(10)

while set(x=A.pop()):
print x

main()

.... prints 0,0,0,0,0,0 etc instead of what you want.

I guess you might call "a.f_locals.update(kw)" a no-(side)-effect assignment
;)

Paul
 
M

Martin Maney

Paul Paterson said:
I think that the other drawback is that it only works at the top level of a
module.

Yeah, it comes back to me now. So there's still nothing but ugly
and/or dangerous workarounds when you need to use a function's result
in a conditional as well as later on. Pytho is such a pleasant
language 95% of the time that the nastiness of the corner cases seems
worse than they rationally warrant - or maybe not, since these warts
tend to drop me out of flow and cost a lot more time than just
remebering the way(s) to hack around them.
I guess you might call "a.f_locals.update(kw)" a no-(side)-effect assignment

Sure 'nuff. Pity, it was otherwise quite a nice answer to what seems
to be an endlessly recurring issue.

--
Although we may never know with complete certainty the identity
of the winner of this year's presidential election, the identity
of the loser is perfectly clear. It is the nation's confidence
in the judge as an impartial guardian of the law.
- Justice John Paul Stevens, from his dissenting opinion Dec 12, 2000
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,521
Members
44,995
Latest member
PinupduzSap

Latest Threads

Top