Is using range() in for loops really Pythonic?

R

Roel Schroeven

John Salerno schreef:
::sigh:: No, unfortunately I don't have a strong enough grasp of Python
to give a really in-depth example. I understand what you're asking
though. Perhaps people don't use this idiom as much as I think they do,
so to give a trivial example to support my point isn't helpful.

Perhaps a function to print some text a number of times:

def print_n(text, n):
for i in xrange(n):
print text

(Though I guess you can replace that with print '\n'.join(msg * n) )

--
The saddest aspect of life right now is that science gathers knowledge
faster than society gathers wisdom.
-- Isaac Asimov

Roel Schroeven
 
I

Ivan Illarionov

Not in Python it's not. The values that come out of the iterator aren't
"in control of the loop". The state for the loop is in the *iterator*,
not the values that come out.

Having access to the values that come from the iterator is usually
useful, but regardless of whether one uses them or not, they're *not*
controlling the loop, and it's confusing to imply that they are.

I agree that "in control" was incorrect phrase.
So, when not using the values that come from the controlling iterator,
it's good to make that explicit. If Python supported it, we might prefer
to use no name at all for something that isn't used, but the 'for'
syntax doesn't allow that.

In the absence of supporting syntax, the next best thing is to choose a
name that *does* make it explicit that the values will not be used.

Name 'i' does not imply whether it's used inside the loop or not.
IMO it perfectly covers both cases.

It may have small advantage to "make it explicit that the values will not
be used", but names like "dummy", "unused", "ignored" or "junk" can be
confusing for some people and break the established conventions for
counter variable names (and these conventions are reasonable and are
taken from mathematics). "i" is still the best choice.

And let's agree to disagree. It's clear that we have different opinions
and it looks that this discussion is not going to change them.

-- Ivan
 
P

Paddy

I guess people who standardize on loop_count never nest loops. :)


And when somebody adds a nested loop things fall apart.
I don't have an example to hand. A lot of casses of repeat_X_times
inside a loop of repeat_Y_times would naturally be written as
repeat_Y*X_times.
Oh, wait a bit,
<oversimplified_example_alert>
for i in range(3):
print "Stay!"
for j in range(2):
print "Come over."
</oversimplified_example_alert>

Which could become:

for outer_stay_repetions in range(3):
print "Stay!"
for inner_come_over_repetions in range(2):
print "Come over."

But the second is daft. Nested repeats don't neccessarily pose a
problem to choosing meaningful names for repeat counters that are not
going to be referenced in the loop body, and most times i,j,k are fine
for used and un-used loop indices I find.

- Paddy
(Or were you just having a laugh ;-)
 
C

Carl Banks

Because of the precedent of those names, choosing one of those names
doesn't make it clear to the reader that the value is never used;


Why is it even necessary to document this? This is the thing that
baffles me the most about this thread.

Think about all the various aspect of a varibale:

* is it some common usage archetype
* value it represents
* scope
* (expected) type
* physical units of measure
* software package it belongs to
* whether it's used in the program or not
* scalar, structure, or array
* mutable or constant
* and so on

You cannot possibly document all of this pertinent information in the
name alone. A variable's name is a very limited resource: one can
only document a tiny number of things, so one must prioritize.

IMHO, whether a varibale is used or not has got to be one of the least
important things of all (in no small part because it's easily
discernable from nearby code). In fact, I'd say it's so irrelevant I
probably wouldn't have listed it if it wasn't the topic of discussion.

The first two items I listed are the most important to me. If a
variable fills the archetypal role of counter, then it ought to be
named like a counter; this usually trumps all other concerns. (Never
mind that the varibale here isn't really a counter, it still fills the
archetypal role of one, which is more important. The name of a
variable is for the reader's benefit.) So I would have to agree with
Ivan Illarionov.

Having said that, if you do think it's most important to document
whether something is used, I would suggest not using "_" (because it's
conflicts with gettext conventions) or "dummy" (because it's
misleading) for it.


Carl Banks
 
C

Carl Banks

I couldn't disagree more.

If you're binding a name to a value that will never be used, you're
doing me (the reader of the code) a great favour if you indicate
clearly and concisely that this value is not intended to be referenced
anywhere else. Saving time for the reader is a very important job of
the writer of code.

I don't think knowing in advance whether a variable is going to be
used or not benefits readability.


Carl Banks
 
T

Terry Reedy

| So, when not using the values that come from the controlling iterator,
| it's good to make that explicit. If Python supported it, we might
| prefer to use no name at all for something that isn't used, but the
| 'for' syntax doesn't allow that.

But I presume it could if [target 'in'] were optional.

for range(n): <stuff to do n times>

It is possible this has been proposed and rejected,
but I have no specific memory.

tjr
 
D

Dave Parker

It seems somewhat
artificial to use the for loop to do something a certain number of
times, like above.

I agree; it's a common flaw with lots of languages, not just Python.

I'd be inclined to use something like:

FOR 8 TIMES DO <statement>.

or:

REPEAT <statement> FOR 8 TIMES.

as a compromise between readability and machine-parsability.

If anyone has other suggestions, please post them here and I'll
implement one of them next weekend in Flaming Thunder.
 
G

George Sakkis

I couldn't disagree more.

If you're binding a name to a value that will never be used, you're
doing me (the reader of the code) a great favour if you indicate
clearly and concisely that this value is not intended to be referenced
anywhere else. Saving time for the reader is a very important job of
the writer of code.

If you push this logic too far, you should del every name immediately
after the last statement it is used in the scope. I would generally
find less readable some code spread with del every few lines, micro-
managing the effective scope of each name. YMMV.

George
 
J

John Salerno

Ben said:
I think that the idiom

for unused in xrange(10):
# do stuff with no reference to 'unused'

is quite common. Is that what you're asking about?

Yes. I was more or less asking about the specific situation of using a
for loop to do something X number of times, but I think the more
generalized problem that everyone is talking about -- using a counter
variable that is never referenced in the loop -- probably puts the point
I was trying to make in a better light.

The reason I even brought this up is because I remember someone saying a
while back (probably here on the newsgroup) that the true use of a for
loop was to iterate through a sequence (for the purpose of using that
sequence), not to do something X number of times. Once they made this
comment, I suddenly saw the for loop in a new (and I believe purer)
light. That was the first time I realized what it was really meant
to do.

Using something like:

for unused in xrange(10):
# do stuff 10 times

suddenly seemed to me like a hackish way to replace

for (int i=0; i<10; i++) {
// do stuff 10 times;
}

Not that I think the above code (C#) looks all that elegant either. But
in C# there is a distinction between the above, and this:

foreach (int i in sequence)
// do something;

which is more closely related to the Python for loop.

Now, you could easily make the argument that the Python for loop is a
much simpler tool to accomplish *both* of the above, and I suppose that
makes sense. Seems a little silly to have two separate for loops to do
these things. I just wasn't sure if the "counter" version of the Python
for loop was considered slightly unpythonic.
 
T

Terry Reedy

| The reason I even brought this up is because I remember someone saying a
| while back (probably here on the newsgroup) that the true use of a for
| loop was to iterate through a sequence (for the purpose of using that
| sequence), not to do something X number of times.

I believe the specific context was to counteract some people's tendency to
write
for i in range(len(seq)): do stuff with seq
when they would better (more Pythonically) write
for item in seq: do stuff with item
or even
for i,item in enumerate(seq): do stuff with i and item.

One subtle but real advantage is that the latter two forms work with
iterables that do not have a known-ahead length or which even continue
indefinitely.

| Once they made this
| comment, I suddenly saw the for loop in a new (and I believe purer)
| light. That was the first time I realized what it was really meant
| to do.

That is an important insight. But to me it does not negate the "do
something n times" usage when there is no iterable other than range to
iterate. Do note that range has *not* been removed from 3.0 and that its
main intended usage is for looping.

| Now, you could easily make the argument that the Python for loop is a
| much simpler tool to accomplish *both* of the above, and I suppose that
| makes sense.

Yes. Python leans toward minimalism. Proposals for various
special-purpose loopin constructs have been rejected. For-loops cover most
looping needs; while-loops cover everything else.

Terry Jan Reedy
 
M

Marc 'BlackJack' Rintsch

Why is it even necessary to document this? This is the thing that
baffles me the most about this thread.

I do it (with a "special" name) because static source checkers issue a
warning for unused variables.

Ciao,
Marc 'BlackJack' Rintsch
 
D

Dave Parker

REXX's loop construct subsumes all the common uses... And worse, it
appears that a repetition and a condition can be part of the single
statement.

Thank you for pointing out the REXX examples. I am a Kedit user, but
had forgotten about the REXX do-loops. I'll keep them in mind when I
add an easy way to "do this n times" to Flaming Thunder this weekend.
 
L

Lie

Yes. I was more or less asking about the specific situation of using a
for loop to do something X number of times, but I think the more
generalized problem that everyone is talking about -- using a counter
variable that is never referenced in the loop -- probably puts the point
I was trying to make in a better light.

The reason I even brought this up is because I remember someone saying a
while back (probably here on the newsgroup) that the true use of a for
loop was to iterate through a sequence (for the purpose of using that
sequence), not to do something X number of times. Once they made this
comment, I suddenly saw the for loop in a new (and I believe purer)
light. That was the first time I realized what it was really meant
to do.

Using something like:

for unused in xrange(10):
    # do stuff 10 times

suddenly seemed to me like a hackish way to replace

for (int i=0; i<10; i++) {
    // do stuff 10 times;

}

Not that I think the above code (C#) looks all that elegant either. But
in C# there is a distinction between the above, and this:

foreach (int i in sequence)
    // do something;

which is more closely related to the Python for loop.

Now, you could easily make the argument that the Python for loop is a
much simpler tool to accomplish *both* of the above, and I suppose that
makes sense. Seems a little silly to have two separate for loops to do
these things. I just wasn't sure if the "counter" version of the Python
for loop was considered slightly unpythonic.

What was unPythonic, I think, as most people would agree, is to use
for like this:

--
for i in xrange(len(lst)):
pass
--

In VB, my language before Python, I've never used For Each even for a
sequence (Array, in VB), it seems too messy back then. Now, with
Python that only allowed a foreach statement, I realized that I never
really needed a for i in range(10) at all. Most of the time the places
where I need to do that is where the code is just a test code that
loops a certain number of times (with randomized input data), almost
never (probably never) met that in a real program code. And even in
some of those cases, the variables are usually used too (as a test
argument to the function being tested) or for logging purpose.

I use it quite often, especially if I want to implement a fixed number of
retries on a communications channel.

-Larry

That still have semantic meaning: 'tries'. If it was me, I'll go to
the trouble of giving names, since I would use it for logging or
informational purpose ('first try' 'second try' 'third try' 'no more
try').

I've never really met a real "do something n times" case, where the
variable doesn't hold any semantic meaning that I don't want to use
and is not a test case.

For me, a _ (or any other dummy variable[1]) is good enough and it's
easy to change if later I realized that I actually need to use the
variable. I agree though, that i, j, k for unused name is a bad choice
because single letter names is in common usage in mathematics.

[1] PEP 8 is useless if a certain code base has other agreed
convention, as long as the name used for a unused name in certain code
base is consistent (and probably documented), it never becomes a
problem.
 
L

Lie

Yes. I was more or less asking about the specific situation of using a
for loop to do something X number of times, but I think the more
generalized problem that everyone is talking about -- using a counter
variable that is never referenced in the loop -- probably puts the point
I was trying to make in a better light.
The reason I even brought this up is because I remember someone saying a
while back (probably here on the newsgroup) that the true use of a for
loop was to iterate through a sequence (for the purpose of using that
sequence), not to do something X number of times. Once they made this
comment, I suddenly saw the for loop in a new (and I believe purer)
light. That was the first time I realized what it was really meant
to do.
Using something like:
for unused in xrange(10):
    # do stuff 10 times
suddenly seemed to me like a hackish way to replace
for (int i=0; i<10; i++) {
    // do stuff 10 times;

Not that I think the above code (C#) looks all that elegant either. But
in C# there is a distinction between the above, and this:
foreach (int i in sequence)
    // do something;
which is more closely related to the Python for loop.
Now, you could easily make the argument that the Python for loop is a
much simpler tool to accomplish *both* of the above, and I suppose that
makes sense. Seems a little silly to have two separate for loops to do
these things. I just wasn't sure if the "counter" version of the Python
for loop was considered slightly unpythonic.

What was unPythonic, I think, as most people would agree, is to use
for like this:

 --
    for i in xrange(len(lst)):
        pass
 --

In VB, my language before Python, I've never used For Each even for a
sequence (Array, in VB), it seems too messy back then. Now, with
Python that only allowed a foreach statement, I realized that I never
really needed a for i in range(10) at all. Most of the time the places
where I need to do that is where the code is just a test code that
loops a certain number of times (with randomized input data), almost
never (probably never) met that in a real program code. And even in
some of those cases, the variables are usually used too (as a test
argument to the function being tested) or for logging purpose.

I use it quite often, especially if I want to implement a fixed number of
retries on a communications channel.

That still have semantic meaning: 'tries'. If it was me, I'll go to
the trouble of giving names, since I would use it for logging or
informational purpose ('first try' 'second try' 'third try' 'no more
try').

I've never really met a real "do something n times" case, where the
variable doesn't hold any semantic meaning that I don't want to use
and is not a test case.

For me, a _ (or any other dummy variable[1]) is good enough and it's
easy to change if later I realized that I actually need to use the
variable. I agree though, that i, j, k for unused name is a bad choice
because single letter names is in common usage in mathematics.

[1] PEP 8 is useless if a certain code base has other agreed
convention, as long as the name used for a unused name in certain code
base is consistent (and probably documented), it never becomes a
problem.

On my last message, I'm tempted not to say: "We need to have /dev/null
for Python" though I proved to fail.
 
J

John Nagle

Matt said:
Well, you should use "xrange(10)" instead of "range(10)".

CPython really is naive. That sort of thing should be a
compile-time optimization.

John Nagle
 
M

Marc 'BlackJack' Rintsch

CPython really is naive. That sort of thing should be a
compile-time optimization.

It's not naive, it can't know at compile time what object is bound to the
name `xrange` at runtime.

Ciao,
Marc 'BlackJack' Rintsch
 
C

cokofreedom

It's not naive, it can't know at compile time what object is bound to the
name `xrange` at runtime.

Ciao,
Marc 'BlackJack' Rintsch

I think he meant you should just use xrange over range at all times.
 
G

Graham Breed

George said:
If you push this logic too far, you should del every name immediately
after the last statement it is used in the scope. I would generally
find less readable some code spread with del every few lines, micro-
managing the effective scope of each name. YMMV.

Yes, but ... how about

for i in range(10):
del i
do stuff

?

It makes it clear you aren't using the index and ensures you
get a run-time error if you clobbered an existing variable.


Graham
 
D

Dave Parker

Your point about for-loops was applicable not only to Python, but to
many other programming languages. So in response, I've added two new
for-loop variations to Flaming Thunder.

The two new variations are for-forever-do and for-expression-times-do.
For-forever allows you to explicitly create infinite loops, and for-
expression-times allows you to do something a specific number of times
without having to declare a looping variable if you don't need one.
Examples:

Write "Fa". For 8 times do write "-la".

For forever do
(
Write "Do you know the definition of insanity? ".
Read response.
).
 

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,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top