There are multiple different ways to approach this answer (bold emphasis mine):
What is the reason python uses range in for loops?
This is not a for
loop. It is a foreach
loop. I.e. it is not a loop that loops over a pre-defined set of loop indices, it is an iterator that iterates over the elements of a collection.
In particular, in
for e in [2, 3, 5, 7, 11, 13, 17]:
print(e)
The result will not be
0
1
2
3
4
5
6
but
2
3
5
7
11
13
17
What is the reason python uses range in for loops?
It doesn't. It uses an arbitrary expression. More precisely, an arbitrary expression that evaluates to an iterator or to something that can be implicitly converted to an iterator (such as an iterable):
class MyIterator:
def __init__(self):
self.counter = -1
self.lost = [4, 8, 15, 16, 23, 42]
def __next__(self):
self.counter += 1
if self.counter == 6:
raise StopIteration
return self.lost[self.counter]
class MyIterable:
def __iter__(self):
return MyIterator()
my_iterable = MyIterable()
for num in my_iterable:
print(num)
# 4
# 8
# 15
# 16
# 23
# 42
Is there some philosophical reasoning behind why python uses this syntax
Yes: It is more general and thus makes the language simpler. The BASIC for
loop can do one thing and one thing only: loop over a pre-defined set of loop indices. In fact, it is even more limited than that, because there are further restrictions on the loop indices: they need to be monotonically increasing or decreasing with a fixed step size.
If you want the indices to be non-monotonic, you need a new language construct. If you want the indices to have varying step sizes, you need a new language construct. If you want to iterate over the elements of a collection, you need a new language construct.
With Python's foreach
loop, you can simply have a function that generates indices in whatever order you want, and loop over those. You can iterate over the elements of any arbitrary collection, and note that "collection" is interpreted very broadly.
Actually, you can iterate over the elements of any arbitrary iterator. An iterator can be something very general, and it doesn't even have to be finite, e.g. "all prime numbers".
As I have shown above, it is very easy to create your own custom iterators and iterables. It is in fact even more easy using generator functions:
def my_generator():
yield 4
yield 8
yield 15
yield 16
yield 23
yield 42
for num in my_generator():
print(num)
# 4
# 8
# 15
# 16
# 23
# 42
And even more easy with generator expressions.
If Python is supposed to be more human readable than most languages, in this case particular case it seems to be worse than say Sinclair BASIC.
If you are looping over loop indices in Python (or any modern language, really), you are doing it wrong.
You should be using higher-level iterators instead, such as reduce
(you may also know this one under the name fold
or more general Catamorphism), accumulate
(you may also know this one under the name scan
or prefix-sum), cycle
, chain
, groupby
, or product
. Or, you should be using list / set / dictionary comprehensions, generator expressions, or algorithms and data structures supplied by the standard library or third-party libraries.
for x in range
is the wrong thing to do in Python. You should instead just iterate the iterable (e.g. list, dict, generator) directly or useenumerate()
instead of usingrange()
.for x range(<whatever>):
, there are almost always better constructs, usually you just want to iterate over the colleciton itself, likefor item in some_list:
. Note,range
isn't a part of the for-statement syntax, it is merely an object, a sequence, that can be iterated over, like any other iterable.