Arent these snippets equivalent?

J

John Gordon

In said:
while True:
data = fp.read(4096)
if not data:
break
...
not equivalent to this:
data = fp.read (4096)
while data:
...{handle the chunk here}
data = fp.read (4096)

It looks equivalent to me (in terms of control flow).

But in the second example the fp.read() statement is duplicated, which is
undesirable. It would be all too easy for a maintenance programmer to go
into the code a year from now and change the first one but miss the second
one.
 
C

Chris Angelico

Is this:

while True:
data = fp.read(4096)
if not data:
break
...

not equivalent to this:

data = fp.read (4096)
while data:
...{handle the chunk here}
data = fp.read (4096)

They should do the same thing, but there's one critical difference in
the second: Edits to something that's really part of the loop now have
to be done twice, at the bottom of the loop and *before the loop*.
It's a violation of the principle Don't Repeat Yourself.

Personally, I'd much rather have a 'while' condition that does
assignment, but that's something Python's unlikely ever to do.
There've been various proposals to make that possible, but ultimately
the only way to make that work is for assignment to be an expression,
which is right up there alongside braces defining blocks.

(Wonder when we'll see "from __future__ import assignment_expression"
implemented...)

The 'break' method is the most common. Assuming you're doing something
as simple as the above, with a single function call and a clear
condition, it's pretty readable. Compare:

while (data = fp.read(4096))
{
... imagine about 20 lines here
}

and

while True:
data = fp.read(4096)
if not data: break
... imagine those same 20 lines, ported to Python

The critical parts of your while loop are in those first three lines.
It's the same goal as a C-style for loop - you can see everything you
need right up there at the loop header. All you have to do is
understand that the "loop header" is three lines long now.

With the second form of the loop, though, the loop header is down at
the bottom of the loop too. It's less clear. Granted, this might be
how a compiler lays it out in memory, but programmers shouldn't have
to read it that way.

ChrisA
 
R

Roy Smith

Chris Angelico said:
Personally, I'd much rather have a 'while' condition that does
assignment, but that's something Python's unlikely ever to do.
There've been various proposals to make that possible, but ultimately
the only way to make that work is for assignment to be an expression,
which is right up there alongside braces defining blocks.

while getchar() as c:
putchar(c)

That would give people (including me) the use case they're after most of
the time (call a function, assign the return value, and test it). It's
way less klunky than:

while True:
c = getchar()
if c:
break
putchar()

It wouldn't require assignment as an expression, or braces, or any new
keywords. It would also be quite analogous to

try:
blah()
except BogusThing as ex:
whatever()

in both cases, the effect is "perform some action, grab a value which
resulted from that, and if it passes some test, make it available in the
next block bound to a name".
 
E

Evan Driscoll

Is this:

while True:
data = fp.read(4096)
if not data:
break
...

not equivalent to this:

data = fp.read (4096)
while data:
...{handle the chunk here}
data = fp.read (4096)

Heres the article that sparked this question:
http://wordaligned.org/articles/pythons-lesser-known-loop-control

There is at least one potentially-critical difference: what happens if
there is a 'continue' statement in the "..." part. The upper loop will
set data again, while the lower one will not.

So if what you mean is "are they equivalent no matter what legal Python
code you put in the ...", no, they aren't.

Evan


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.14 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJRAG+jAAoJEAOzoR8eZTzgfqcH/jkzORjA64IPtyrPTPn61Owh
Ng7xsU4pNxUKiJR0qFZXfmG5XTIgmKqBq9+so4ZcuL0c1xqlKNCTbFj+xngd20pZ
av3UT65cqxnmaw8E5FNQliUFCm+d07ewMoFRyEM0c8J7xMTN8q3mo5WEC9XfQIJ9
km59y2CV363Cs7DA5nAKTTn/sEUCmYogybnLooz6PUQInse1MBmRveuvEr6z7g/Y
o5Wb3PxNWKM1UILEltaFOFe3TXqnUfOfWyNDqUrbATDIeMWsaA8ykcUQb0YzPMN7
IeQG5xz/myalNuMgQpUh5r1LAAcwUHP5ThG2K1PCjXodrnRN4Km8jABPZKAMuzI=
=9uJn
-----END PGP SIGNATURE-----
 
C

Chris Angelico

while getchar() as c:
putchar(c)

That would give people (including me) the use case they're after most of
the time (call a function, assign the return value, and test it). It's
way less klunky than:

while True:
c = getchar()
if c:
break
putchar()

It wouldn't require assignment as an expression, or braces, or any new
keywords.

I believe this was discussed recently (on python-ideas?). It's nice in
its simplest form, but doesn't cover all possibilities. My point about
braces was that, like assignment-expressions, it's a feature that
Python will not be implementing. Fundamentally against the "push" of
the language. If you want C, you know where to get it. (There are
other options, of course; ECMAScript and Pike come to mind. But you
know what I mean.)

ChrisA
 
T

Tim Chase

while getchar() as c:
putchar(c)

That would give people (including me) the use case they're after most of
the time (call a function, assign the return value, and test it). It's
way less klunky than:

while True:
c = getchar()
if c:
# I presume you mean "if not c:" here.
break
putchar()

I was a pretty strong advocate early in one of these long threads,
and for the simple cases, it's some attractive syntactic sugar.
However, I found that it quickly blossomed into a lot of really ugly
edge cases (multiple tests, multiple results, checking for "is None"
vs. false'ness or some other condition such as "< 0"). I found that
it was pretty easy to create a generator-wrapper for this:

def getter(fn):
while True:
val = fn()
if not val: break
yield val

# DB example
cursor = conn.cursor()
for row in getter(lambda: cursor.fetchmany()):
do_something(row)

# your getchar example
for c in getter(getchar):
do_something_else(c)

This allowed me to have both the readability and customized tests
(and the ability to return multiple values). It could be expanded with

def getter(fn, is_at_end=lambda v: not v):
while True:
val = fn()
if is_at_end(val): break
yield val

which would even allow you to do things like

for line in getter(file("foo.txt"), lambda s: s.find("xxx") < 0):
print "This line has 'xxx' in it:"
print line

and those felt a lot more pythonic than any of the proposals I saw
on the list.

-tkc
 
T

Terry Reedy

# I presume you mean "if not c:" here.

I was a pretty strong advocate early in one of these long threads, and
for the simple cases, it's some attractive syntactic sugar. However, I
found that it quickly blossomed into a lot of really ugly edge cases
(multiple tests, multiple results, checking for "is None" vs. false'ness
or some other condition such as "< 0"). I found that it was pretty easy
to create a generator-wrapper for this:

def getter(fn):
while True:
val = fn()
if not val: break
yield val

# DB example
cursor = conn.cursor()
for row in getter(lambda: cursor.fetchmany()):
do_something(row)

# your getchar example
for c in getter(getchar):
do_something_else(c)

This allowed me to have both the readability and customized tests (and
the ability to return multiple values). It could be expanded with

def getter(fn, is_at_end=lambda v: not v):
while True:
val = fn()
if is_at_end(val): break
yield val

which would even allow you to do things like

for line in getter(file("foo.txt"), lambda s: s.find("xxx") < 0):
print "This line has 'xxx' in it:"
print line

and those felt a lot more pythonic than any of the proposals I saw on
the list.

I agree. To me, the beauty of iterators and for loops is that they
separate production of the items of a collection from the processing of
the same items. The two processes are often quite independent, and
separating them clearly allows us to mix and match. For instance, when
summing numbers, the internal details of producing the numbers does not
matter.
 
C

Coolgg

I agree. To me, the beauty of iterators and for loops is that they

separate production of the items of a collection from the processing of

the same items. The two processes are often quite independent, and

separating them clearly allows us to mix and match. For instance, when

summing numbers, the internal details of producing the numbers does not

matter.

Thanks for all the perspectives everyone. I was just curious about the functional equivalence and I got what I needed.
 
C

Coolgg

I agree. To me, the beauty of iterators and for loops is that they

separate production of the items of a collection from the processing of

the same items. The two processes are often quite independent, and

separating them clearly allows us to mix and match. For instance, when

summing numbers, the internal details of producing the numbers does not

matter.

Thanks for all the perspectives everyone. I was just curious about the functional equivalence and I got what I needed.
 

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,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top