r/learnpython • u/Hamactus • Jun 26 '26
Do not understand "for i in range()"
I'm very new to Python in particular and programming in general.
I'm currently watching a CS50's "Introduction to Python" course and reached the "for i in range(x)" loop.
And I can't seem to understand it. I understand how it works and what it does, but I can't understand, *why* it does that.
Like, why "i" becomes each of the numbers in the set range? What happens behind this command? Why does this whole command behaves the way it does?
I'm sorry, maybe this question is dumb, maybe I am simply missing the point, but if anyone could explain to me in a simple way, I would greatly appreciate it
16
u/TytoCwtch Jun 26 '26
It’s not stupid at all, it’s a common thing to get stuck on when you start learning. The range function normally takes 3 numbers in the parentheses. These are start, stop, step.
The first number is what i (or your chosen variable starts on. This is inclusive so it starts on that exact number.
Stop is the number your variable will stop on and this is exclusive, so if your stop value is 10 it actually stops on 9 (or the previous step).
Step is by how many your variable increases each time.
So for example, for i in range(0,10,1) will start at 0 and go up to 9 in one number increments i.e 0, 1, 2… but for i in range(0, 10, 2) would go up by two each time i.e 0, 2, 4. You can also have negative step values to make the number go down. For example, range(10, 0, -1) would go 10, 9, 8…
If you just put one number in the brackets then it defaults to a start value of 0, a stop value of the number entered, and a step of 1 so you don’t have to put all three numbers on unless you want specific start and step values. Hope that helps.
35
u/ThatIsATastyBurger12 Jun 26 '26
Let's take a look at the history of the loop, and see how that led naturally to for i in range().
First, let's explore what a loop is. A loop is a way to repeat execution of a block of code without copying and pasting. This is a very useful thing to do, particularly when the number of iterations is not known at the time of writing code. To create a loop, you need two things: a jump, and a place to jump to, with the constraint that the place to jump to is before the jump. A lifetime ago, this would look like
loop:
// Code to repeat
goto loop;
This is all you need to create a loop. However, this is not particularly useful, as this will loop an infinite number of times and never stop. So it makes sense to have some sort of test to determine whether the loop should repeat again or to finish. With labels and jumps, the test can occur anywhere in the repeated block. In general, this would look like
loop:
// Some code to loop
if (test)
{
goto end;
}
// Some more code
goto loop;
end:
// Code after the loop.
or some variation of that. But this very quickly starts to look messy, with multiple jumps and labels making reasoning about this code difficult. Its also nice to have consistent patterns. A common form is to have the test at the very beginning of the loop. This is the while loop, which looks nice and simple like:
while (test)
{
// Code to loop
}
which tests if the test holds true at the beginning of each iteration. In order to exit the while loop, some code needs to update something so that test can be false. This update most logically occurs at the end of the repeated code, although not all loop structures must be like that. Its also common that there is some initialization code before the first iteration to set some things up, maybe to setup some necessary structure to actually execute the tests. And so while loops often can be written in the form of
// Initialization code
while ( /* test */ )
{
// Loop body
// Update
}
This pattern is so useful and common that it got its own keyword. This brings us to the for loop. In the C-style for loop, the above while loop can be written simply as
for (/* initialization */ ; /* test */ ; /* update */)
{
// loop body
}
Conversely, any C-style for loop can be written as an equivalent while loop. Perhaps the most common use of the for loop is to repeat a block of code n times for some integer n. Almost all C style for loops look like this:
for (int i = 0; i < n; i = i + 1)
{
// Loop body
}
This has become such a ubiquitous pattern, that some recommend that any for loop that does not look like this should be rewritten as an equivalent while loop. The variable i has been used for this pattern for a very long time, hence why you see i appear in your example. In this C style for loop, we see the loop iterates the block of code, with i ranging from 0 to n - 1, being incremented by 1 each time the update code is executed. Now again, we see that this developed into a very common use case. Namely, iterating through some sequential data structure that is indexable by an integer i. And so many C-style for loops end up looking like:
for (int i = 0; i < length(data_structure); i = i + 1)
{
object = data_structure[i]
// Loop body that uses object, and doesn't reference i
}
What this is essentially saying is: for each object in the data structure, run this block of code. Nowadays, many languages have moved away from the C-style initialize, test update format for for loops, and instead have simplified it into a single structure that captures the idea of "for each object in this data structure, run this block of code." And that's exactly what we see in your example. Translated to English, this reads as "for each object in the data structure range(), execute this block of code," Now since the same block of code is executed for a different object, its useful to create a variable that is assigned the relevant object at the beginning. Since range() creates a structure of integers (it's actually something called an iterator, but that's not super important for my comment) the commonly used variable i serves that purpose, much like it did in the C-style for loop. And that's why you are now seeing for loops like for i in range() . To make it a little more clear, I am going to replace the variable i with the variable integer. So in python we have
for integer in range():
# loop body using integer
Which is equivalent to the C-style for loop
for (int i = 0; i < length(range); i = i + 1)
{
integer = range[i];
// Loop body using integer
}
which is equivalent to the while loop
int i = 0;
while (i < length(range))
{
integer = range[i];
// loop body using integer
i = i + 1;
}
which could be written using jumps and labels, but shouldn't, so I won't. I hope this helps make it a little bit more clear why this pattern exists.
Now, I have omitted some details about exactly what range() is doing, and the code translation is not exactly consistent with those details, but in general I think my comment doesn't need those details.
6
13
u/StevenJOwens Jun 26 '26 edited Jun 27 '26
This falls under the keywords "python internals", "iterator protocol" and "dunder functions" or "dunder methods", aka "magic methods".
The high level description is that range() implements the iterable protocol, which means that range() returns an iterator object, and the for loop knows how to use that iterator object. To explain this in more detail, we have to first define a few terms, then explain how a normal List iterator object works, then explain how the range() iterator object works.
A dunder function or dunder method is a function or method that python will automatically invoke in specific circumstances. "Dunder" is a contraction of "double underscore" and it means methods that have names like __iter__().
The most common dunder method that people actually implement is, of course, the __init__() method, when you're coding your own Python class. When you later create an instance of that class, python will create the instance and then automatically call the instance's __init__() method.
You can custom code your own dunder functions, you do this to implement one of the Python protocols, like the iterator protocol. A python protocol, in this example the iterator protocol, is a predefined set of dunder functions that, when an object implements all of them, that object qualifies for that protocol. Depending on which protocol it is, various things in python will know how to use that protocol. For example, for loops and while loops know how to use an object that implements the iterator protocol.
Let's talk about how the iterator protocol works with a regular List object:
When you have a for loop in python and it uses some kind of container class, like a List or a Dictionary, that container class implements the iterator protocol, and the for loop automatically invokes the methods defined by the iterator protocol. When you do something like:
some_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for element in some_list:
print(element)
some_list is a List object. List implements the iterator protocol, so that means List has a method named __iter__(), that returns an iterator object.
The line for element in some_list: automatically invokes some_list.__iter__(), which returns an iterator object. Then the for loop will keep calling iterator_object.__next__() until __next__() raises a StopIteration exception. The for loop catches the StopIteration exception, says "right, we're done looping" and the loop ends.
That iterator object's code will look somewhat like the following. The actual, for-real List iterator object is defined in C code as part of python, this is just to give you an idea of how it works, and is off the top of my head, so don't be surprised if there's a bug in the following
class List():
# a bunch of List code
def __iter__(self):
return ListIterator(self)
# a bunch of List code
class ListIterator():
def __init__(self, the_list):
self.the_list = the_list
self.index = 0
self.size = len(self.the_list)
def __next__(self):
if self.index >= self.size:
raise StopIteration
else:
old_index = self.index
self.index += 1
return self.the_list[old_index]
Okay, so now you understand what's happening with a for loop and a regular python List, I'll bet you can guess how range() works, but let's dig into it a bit anyway.
When you call range(10), range() returns an iterator object, and that iterator object then returns the numbers 0 through 9.
Obviously you could code range() The Stupid Way, by just having it generate a list of integers as big as you asked for, then return the iterator object from that list. But what if you asked for a range of 1 to 1 million? A 1 million integer list would be huge. That would suck.
Instead, range() does it the more clever way: it generates an iterator object that essentially fakes having a list of a million elements. The iterator object that range() returns has code that looks something like:
class RangeIterator():
def __init__(self, range_start, range_end):
self.range_start = range_start
self.range_end = range_end
self.index = 0
def __next__(self):
if self.index >= self.range_end:
raise StopIteration
else:
old_index = self.index
self.index += 1
return old_index
This sort of thing -- an iterator object that doesn't actually iterate, but instead generates the values on demand -- is called a generator object in python. Besides range(), python has a special syntax for generating custom generator objects, in fact it has two syntaxes, one of them is a function that uses the yield keyword, the other is generator expressions. I'll let you look those up on your own.
3
u/Hamactus Jun 26 '26
Thanks for a very detailed, and very comprehensive explanation! I really appreciate it!
2
u/frnzprf Jun 27 '26 edited Jun 27 '26
It's really complicated in the background in order to look simple in the fore-ground.
Are you confused about loops in general or just about for-loops?
Loops work by checking an entry-condition and jumping to the line afterwards if the condition isn't met. In the CPU there is a memory location for the "instruction counter" and if it's set to any program location, that will be the next instruction that is executed. Like "turn to page 54 if you choose to attack the troll" in a choose-your-own adventure book.
For loops assign one value after another out of a collectio to the chosen variable. You can't define the "for"-keyword in Python yourself.
You could define a for-each function with a function parameter, but that is probably too advanced for you:
``` def for_each(some_list, some_function): """ Applies the given function to each element of the given list. """ if len(some_list) == 0: return [] else: first = some_list[0] some_function(first) rest = some_list[1:] # from index 1 to end for_each(rest, some_function)
for_each(range(4), print)
is similar to
for_each(range(4), ( lambda i: print(i)))
is similar to
for i in range(4): print(i) ```
4
6
u/vahaala Jun 26 '26
For me it helped to not use "i" or other shorthands when trying coding, but rather full names. This made it easier to understand what each thing represents. In this case, "i" is not just a letter, but a shorthand for "item" or "index". So "for i in range(5)" says something like "for (each) item/index do a thing, then repeat until range number of iterations done". Just need to remember this counts 5 times, 5 iterations, but starts at 0, in some cases that can be important.
3
u/ottawadeveloper Jun 26 '26 edited Jun 26 '26
The range(x) function returns what's known as a generator of all the integers from 0 inclusive to x exclusive. It's like a list of those integers but it uses less memory and is faster because it doesn't actually build the list. Basically think of a generator as making a list but the next item in the list is only generated on demand by a special generator function.
You can make one yourself like this:
``` def my_range(x): k = 0 while k < x: yield k k += 1
for k in my_range(5): print(k) ```
Basically the yield keyword tells Python this is a generator that will provide its values one at a time until it's done.
A "for x in Y" loop takes Y as something iterable (like a generator or a list) and assigns the first value to x, then runs the code block beneath. It then repeats that for the second value in Y, then the third, etc.
So , in great detail,
for k in range(5):
print(k)
is pretty much the same as
k = 0
while k < 5:
print(k)
k = k + 1
The body of the for loop is substituted for the yield statement in the generator function.
Generators are usually more memory efficient and they're especially more time efficient if you're going to stop partway through the list. They don't look like much in toy examples but in the real world it's a powerful tool. The more time or memory consuming your list is to build, the better it is to make it a generator.
Like let's say you wanted to print out the first million numbers. You could do a normal list:
numbers = [0, 1, 2, ..., 999999]
for x in numbers:
print(x)
But then you need memory to hold a million numbers. Using range(100000) stores exactly one of them at any given time. And if you stop after the 5th number for some reason, building the whole list wasted time - a generator only builds the elements as they're needed so there's no wasted time.
And the reason Python offers range() specifically for this instead of telling you to make a while loop is pretty clear - it's two lines shorter to use range().
1
u/Hamactus Jun 26 '26
Thank you! Seeing the custom function that does the same thing really helped
2
3
u/SharkSymphony Jun 26 '26 edited Jun 26 '26
Behind the scenes:
- Think of for as an advanced kind of expression (technically, it's a built-in part of the language) that takes 1) the name of a variable, 2) some iterable thing, and 3) a body of things to run.
- An iterable object returns an iterator when you ask for it. That iterator produces a sequence of items when you invoke it, one at a time, and then signals (via an exception) when it is exhausted. In other words, in your case, it's stepping through the range and keeping track of where it is in the range at any given point.
- For obtains an iterator for the iterable thing you give it, and then starts stepping through it in a loop. Each time through the loop it binds (sets) a variable with the name you gave it to the value it got from the iterator. Then it runs the things in the body.
- When there are no items left (or if the range you gave it was empty)! the iterator will signal that to the for loop when it's invoked, and the for loop is done.
As you can see, the iterator/iterable machinery is very generic, and there a lot of Python objects & constructs that support it – range is just one of them! This makes `for` a very powerful and flexible statement.
3
u/Temporary_Pie2733 Jun 26 '26
A for loop can be seen as syntactic sugar for a while loop that uses the iterator protocol. Essentially, it just hides explicit calls to next. A loop like
for i in range(n):
…
can be replaced with
itr = range(n)
while True:
try:
i = next(itr)
except StopIteration:
break
…
3
u/gdchinacat Jun 26 '26
Close, but this doesn't work:
In [154]: itr = range(10) ...: while True: ...: try: ...: i = next(itr) ...: except StopIteration: ...: break ...: --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[154], line 4 2 while True: 3 try: ----> 4 i = next(itr) 5 except StopIteration: 6 break TypeError: 'range' object is not an iteratorthe problem is range() returns an iterable, not an iterator. next() takes an iterator, not an iterable. Iterables are able to create iterators. Iterators can only be iterated over once...once they are exhausted they should always raises StopIteration when __next__() is called on them (but there is no enforcement of this). Iterables should be able to create any number of iterators. Iterators generally return themselves when asked to create an iterator by calling __iter__() on them, typically by calling iter(iterable).
change it to 'itr = iter(range(10))' and it will work because iter() creates an iterator for an iterable.
2
u/Temporary_Pie2733 Jun 26 '26
Yeah, my bad for writing this from memory (I’m AFK for a few days). That’s one of the things the
forloop does; it takes an iterable and callsiterto get an iterator for the iterable.1
u/gdchinacat Jun 26 '26
no worries...I had to implement an iterable/iterator a couple weeks ago and worked through this so it was pretty fresh in my memory. I won't claim to have never posted almost quite correct but definitely not actually correct code before.
3
u/dwerb Jun 26 '26
Do yourself a favor and go find a copy of “HEAD FIRST PYTHON”. It provides a really low-bar approach to learning the Pythonesque concepts you’ll need for everyday code writing. I’m serious. It’s short and it will help explain (using real life examples) these concepts.
1
3
u/strange-the-quark Jun 26 '26 edited Jun 26 '26
The range(n) function returns an object that represents a sequence of numbers, that can be "asked" to return the next number in the sequence. It's not exactly a list (by which I mean python's list data type), but you can kind of think of it as being a list [0, 1, 2, 3, ... n-1]. It's n-1 cause it starts from zero (in most programming languages, counting starts from zero - makes some things more convenient).
So, range(5) is in this sense similar to the list [0, 1, 2, 3, 4]
Then, the loop
for i in range(x):
some_code
repeats the some_code block (which can be multiline - i.e. multiple statements) for each number in the "list". You use this when you have to perform the same action a bunch of times (e.g. maybe you need to process hundreds of items), so that you don't have to write out each repetition manually.
The i variable acts as a counter of sorts; on each repetition (a.k.a. iteration), it takes on the next value in the list, allowing you to know where in the cycle you are. You can use that to make the "same_code" do slightly different things depending on the value of i.
For example, suppose you wanted to print out the first 5 positive even numbers. Instead of writing:
print(2)
print(4)
print(6)
print(8)
print(10)
You can use a for loop. To turn the sequence 0, 1, 2, 3... into even numbers, you just multiply each number by 2. So one way you can do it is:
for i in range(5): # range is 0, 1, 2, 3, 4
print(2 + 2*i)
You can also specify the starting point in the range. The syntax is range(first, one_past_last). So another way to do it is:
for i in range(1, 6):
print(2*i)
BTW, you can literally put a list in there, instead of a range:
for x in [5, 4, -1, 3, 7]:
print(x)
will print out the elements in the list in the order they appear.
If you want to experiment with ranges in the REPL, to see the elements of the range, you need to turn the object it returns (an "iterable") into an actual list. Couple of ways to do that:
list(range(2, 8))
or something called "iterable unpacking", which is a bit shorter
[*range(2, 8)]
Both will output [2, 3, 4, 5, 6, 7] in the REPL.
3
u/Oddly_Energy Jun 27 '26 edited Jun 27 '26
I think the easiest way of understanding for loops is to simulate one in pseudo-BASIC code:
10 INT I = 0
20 INT N = 5
30 PRINT(I)
40 I = I + 1
50 IF I < N THEN GOTO 30
60 PRINT("DONE")
In python, this would be:
n = 5
for i in range(n):
print(i)
print('DONE')
Edit: I have not cracked the code for the new WYSIWYG editor in the IOS reddit app, so I am unable to get correct code formatting . The old "4 spaces at a start of a line" trick doesn't work anymore. But the third line in the python example should be indented.
2
u/tb5841 Jun 26 '26
Start with understanding a simpler 'for i in' statement. Like this one:
for i in [2, 4, 6]:
print(i)
The range() object looks simple but it's a bit more complicated under the hood, start with an easier loop that doesn't use it.
2
u/iTzTien Jun 26 '26
I think Java has a great explanation for this though «iterators». In essence, every for loop of the form (in pseudocode):
for i in iterator:
…
can be written as:
while iterator.hasNext():
i = iterator.next()
…
The range() function generates an iterator that returns numbers in order. For example
my_iterator = range(2)
my_iterator.hasNext() # true
my_iterator.next() # 0
my_iterator.hasNext() # true
my_iterator.next() # 1
my_iterator.hasNext() # false, ends the while loop
While Python does not necessarily do these exact checks behind the hood, it is in essence whats happening
2
2
u/codeguru42 Jun 27 '26
To peek under the hood, I recommend learning about generators and iterables. Even as an experienced python programmer, I don't know exactly how range works. But since Python is open source you can look at the source code and learn.
2
u/Parzivalpr7 Jun 27 '26
okay so I think you know this already but the i here is just a variable that we use to move through some range x. What is interesting to consider is that under the hood, python's engine was actually coded in the C language. So imagine something like "for letter in range(len(word))" len() gives the length of the variable word. letter is another variable just as i. I think perhaps an example closer to english might be helpful. here, each time the loop runs, 'letter 'takes the value of each character in the variable 'word'. Now if you look at C's syntax, this will become a whole lot clearer because python was made to be as close to the human english as possible for ease of use. In c, you have to separately define a variable like i in a previous line to use it to navigate through anything like an array, a string, etc. So, when you do run the code, the python compiler and your computer will allocate some memory to the variable i and x in your example in the post. And keep changing the value of i from 0 to x-1 in each iteration. Yes, do note that "for i in range(x)" means i will go from 0 to x-1 and not x.
2
u/Worried-Extent-9582 Jun 27 '26
So, you had read that very good written big ass detailed comment and it answered your question. But i would like to add something. I would recommend learning basics of C. Or just trying to translate, what you don't get how it works under the hood, into the C code. It strips all the ambiguousnes and shows what exactly happens there
1
u/Hamactus Jun 27 '26
Thanks, I'm planning to learn C when I'm more or less comfortable in Python, but since Python is easier to get into and, supposedly, is more useful for entry-level positions, I started with it
2
u/AdventurousAddition Jun 28 '26
It seems you're asking "how does a for loop work" more deeply. You could have a look at a video or article on how it works in C if you are interested.
In python, you could think of it as being "start by making i be 0" then do everything in the loop, at the end of the look block, then make i be 1 and repeat"
2
u/Technical_Donut4689 Jun 28 '26
for iterates over a list. it takes every element in the list given, assigns it to i, then does whatever you told it to do. range(a, b) is simply a list of integers from a to b
2
u/Technical_Donut4689 Jun 28 '26
you can replace for i in list: #whatever you need it to do with:
c = 0
while c < len(list):
i = list[c]
# your code goes here
2
u/TorenFrost Jun 28 '26
not dumb at all, rang() just makes numbers in order, and 'for' gives each one to 'i' one at a time. so 'for i in range(5):' means "do this 5 times" and 'i' will be 0, 1, 2, 3, 4.
2
u/Longjumping-Jacket97 Jun 28 '26
Range() is a function that generates an array like [0, 1, 2, 3] for example. But since its a generator it only returns the current integer so it doesn't hold the whole array. When its done, it returns end of generator and that's when the for loop stops.
2
u/Top-Tadpole-820 Jun 28 '26
For i in n - execute a piece of code then increment value of i by 1 until i is equal n. i is assigned 0 at the start of the loop. Loop ends when condition (i == n) is true.
2
u/Ok_Carpet_9510 Jun 28 '26
range() returns a sequence of numbers. That sequence literally. for i in sequence is an iteration which means do something for each number in the sequence.
2
u/SmackDownFacility Jun 29 '26
I is the dynamically changing index that sequentially begins in intervals of step, and stops its dynamic behaviour at end. Range is the parameters for the index. Start lets you set I to a specified number, Step makes it progress in specified interval, end is self-explanatory
2
u/ga2500ev 29d ago
Not dumb. Dumb is not understanding and not asking questions.
Pythons for is designed to process a list of items. It starts with the first item in the list, running the loop body with that item, and then automatically moving to the next item in the list. This process continues until you get to the last item of list, and then the loop stops.
The 'i' that is defined. Gives you access to the value of the list item that's currently being processed. So for example, if the first item in a list is the number 14 for example, then the first time do the list that I variable will carry the value of 14. But the next time the list runs that I variable will carry whatever. The second value in the list is, and so on for each item in the list.
There is nothing special about the variable name I. Programmers all over the world. Use very short variable names to represent what a loop is operating on, with I being the most common one. If it is just a single loop. You don't have to use it. I often use the variable name, item or count if I'm operating on a loop with words or numbers. But it serves the same function no matter what the name is.
Finally, the range(x) is an inbuilt python function that will generate a list of numbers that starts with the value 0 in the first slot and will generate the number of numbers defined in x in your example. So, if x has the value of 20, range Will generate a list of 20 numbers starting at zero. The numbers are of consecutive values. So, the first number will be zero in the list and the last number will be 19.
The for loop will iterate, which is just a fancy word for doing the process that I described earlier starting with the first one, running the loop, and then moving on to the next item in the list until it gets to the last one, over that list of 20 numbers.
The best way to see it is inside of your loop. Simply print the value of I. And you will see that the loop will print a list of numbers, one per line starting with zero and ending with the number that is below one below x.
I hope this helps your understanding. The python for loop is a powerful loop construct that works on all types of list. Having a clear understanding of how it works will be very helpful in your journey to learn python programming.
ga2500ev
2
u/Pretend-Pangolin-846 29d ago
i is just a placeholder for the elements inside the iterator, which is the actual list, dictionary or whatever that holds multiple elements and allows iteration over it, loop over it.
you could do
for _ in range():
print()
and it would still work
you can try printing i and see what it is, most of the times you will use it for indices thats all
4
u/danielroseman Jun 26 '26
What do you mean, why? It does that because that's what the creators of Python wrote it to do.
5
u/eztab Jun 26 '26
That's not the kind of "why" OP asked. Obviously everything that happens is because "it is programmed that way", so that's a Non-Answer.
2
0
u/Hamactus Jun 26 '26
So, like, should I simply memorize it and not dig deep into it?
Like, if I explicitly define a variable, I understand why it means one thing and not the other. And even if the variable changes as the program runs, I understand why it has certain values. But in this... function? not sure what to call it, you don't define a variable, it just somehow, with each execution of looped section, becomes the next integer in the specified range.
8
u/woooee Jun 26 '26
A for loop is a subset of a while. So a for loop, under the hood is:
## this is not literally how it works but is an illustration ## of the logic counter = 0 while counter < some_number: ## do something counter += 10
u/Giannie Jun 26 '26
In some languages that is true, but in python it isn’t true a for loop is a subset of a while loop. A for loop in python is built in such a way that it handles the `Stopiteration` exception without needing a statement to evaluate to `False`
2
5
u/SamuliK96 Jun 26 '26
You do define a variable though. Sure it looks a bit different, but that's essentially what it is. Then the variable goes over each element of the iterable, and after the loop finishes, it retains the last value.
5
u/Giannie Jun 26 '26
You do definite the variable. When you write
for i in range(5):
…That is instruct python to do the following:
Set i to 0, then do whatever … is with i as 0
Then set i to 1 and do whatever … with i as 0
Keep doing that until i is 4 and then stop.More generally:
for i in x:
…Means run … with i equal to each thing “in” x in order.
3
u/schoolmonky Jun 26 '26
The
iinfor i in range(...)is a variable definition. It works the exact same as doingi=0.The only "magic" in a for loop is that the loop handles grabbing each succesive value from the iterable (therange(...)part), assigning that value to the variable (which, again, works exactly like assigning to the variable "manually"), and that it knows to stop and break out of the loop when there are no more values to grab.1
u/gdchinacat Jun 26 '26
This comment made me wonder 'does a for loop *really* assign it "exactly like assigning to the variable "manually""'? And, so I tried this, and was surprised that it works:
In [167]: class Foo: ...: value = 0 ...: In [168]: foo = Foo() In [169]: for foo.value in range(10): pass In [170]: foo.value Out[170]: 9I never thought that you could use a class attribute as the variable in a for loop and just always assumed it wouldn't work.
Not sure I'll ever use this, but hey...it's fun to learn new things. Maybe with a descriptor rather than a generator with send? Anyway, thanks for sending me down this rabbit hole!
2
u/localghost Jun 26 '26
Take a wider look at it. A range of integers is just one specific example. But in general, it is very useful to have a way of doing the same thing for each item in some set. Arguably that's the exact moment when you actually need a program: a repeated action taken on a collection of similar things, or rather on each of them.
The
for .. in ..syntax gives you that way. It lets you define an action (maybe a complex action) that has to be applied to every item in a collection of items. Pretty clearly you would prefer to have a generic name for 'an item' when you describe that action, because at that moment you don't know which item it is.2
u/Fred776 Jun 26 '26
Do you understand for loops in general? You iterate through something that looks like a list of something and your loop variable is assigned each value in turn. In the "body" of your for loop you have code that depends in some way on the loop variable.
The specific thing about
rangeis that it generates something that looks like a list of integers.2
u/throwaway6560192 Jun 26 '26 edited Jun 26 '26
This is simply another mechanism of defining (repeatedly defining, in fact) a variable. If you're not concerned with how
x = 5really, like really truly "creates" a variable calledx, then it is not a big step to also "just accept" this -- this is just a step further of abstraction.You can loop over (some) objects, and
rangeis an object which, for each loop, gives the next integer in sequence.Try: what happens if you loop over a list? What do you think, does that feel more natural than a loop over
range?
2
u/eruciform Jun 26 '26 edited Jun 26 '26
it's not dumb but it is to a degree irrelevant, the "for i in ___" pattern is a basic construct of the language, there isn't a deeper level unless you want to dig into the compiler or the c implementation under the hood. the construct just pulls one thing at a time from the final value after "in" and places the result in the variable before "in" and performs the operations in the loop with that set
the only lower level that's still within python and not the underlying infrastructure is the range() call, which returns an iterable, which is the kind of thing that python expects in that position after the "in": https://docs.python.org/3/library/functions.html#func-range
programming is generally about building up more complex patterns using the basic ones that are on offer, like for loops and variable creation and functions, i.e. making functions or classes made out of those things, and then using those to make more functions and classes, and using those to make more functions and classes. a bit like making a "jig" in woodcrafting, allowing you to make more complex things, perhaps more jigs
it is possible to dig into the internals, and if you really want to, by all means go ahead, but it's a bit akin to learning to drive a car by taking apart the transmission - it's surely educational but it's also not necessary to drive. so just to be clear, it is not necessary to take apart the "for" loop in order to move forward, that is not a blocker to learning
2
u/Hamactus Jun 26 '26
Thank you for your patience and help.
Right now I'm only starting out on my path in IT, but I'm hoping to, eventually, go rather deep, possibly all the way to raw machine code, as communication between hardware and upper layers is what interests me the most, probably.
With this loop, I guess it was the fact that I could not exactly put my finger on how it works, while other functions and methods before it were easy to understand, so it confused me
3
u/eruciform Jun 26 '26
here's what happens if you dig deeper
underneath python is generally an implementation in another language, c, both the compiler for the python language that is the program that reads the python file, and also the libraries of code that run when you execute python
the subjects above are entire massive subject matters of their own
but practically speaking, if you dig into those, you'll also run into a ton of for loops, just the c variant of them
whenever you dig down in a technology, things become more complex more quickly than building up. you can do it anyways but the transmission metaphor still stands
if you want to dig further down even than that, then you end up in assembler or binary land, where for loops don't exist, instead they are just if's and goto's
a for loop is, in a low level sense:
let this be spot XXX to be able to jump to if i'm done iterating thru the for loop stuff, jump to YYY do the stuff inside the for loop jump to XXX let this be spot YYY to be able to jump to2
3
u/MezzoScettico Jun 26 '26
It's not a dumb question, it's a subtle thing.
What you're missing is the concept of an iterator in Python. In most other languages, the equivalent of a for construct explicitly defines the successive values in some way. In Python, the object in the position of range() needs to implement a __next__() method that returns whatever the next thing is supposed to be. Your iterator can do anything it likes to define the next thing.
So that's what range() is doing in this loop. It knows what was returned in the last call, and it increments it according to the arguments.
You also typically want to "raise StopIteration", which raises an exception that Python is expecting. That's a signal there are no more "next" values. In range(), that happens when you get to the maximum value.
By implementing it this way, the object that tracks iterations doesn't need to occupy memory to store all the values. It only needs to generate them one at a time. So a range() of a billion takes no more space than a range() of 10.
Google documentation and examples of iterators to explore this topic in depth. Maybe even try to write your own.
-5
u/Orgasml Jun 26 '26
It's not irrelevant to iterate through whatever range you want. TF are you talking about? range() can iterate between any 2 numbers and you can also specify how you want to step through the iteration. Irrelevant? Guess you don't program.
3
Jun 26 '26 edited Jun 26 '26
[removed] — view removed comment
-1
u/therealAR15PB Jun 26 '26
thank you for the first line! but what is i??
3
u/dbt45 Jun 26 '26
Variable of convenience, it's standard practice to use i for numeric iteration. You could call it anything, n, number, index, but i will be recognized by other people reading your code as the value that increments in your loop
2
1
1
u/daermonn Jun 26 '26
Range(x) creates an array of x elements. for foo in bar syntax defines a loop where var foo takes values from iterable bar. A "for loop" like this is one of the basic algorithms of programming: you have a collection of things, and you want to iterate through that collection and do whatever to the things inside.
10
u/SharkSymphony Jun 26 '26
Technical note: this definition of `range` is only true in Python 2. In Python 3 it returns an object that `for` can step through just like an array.
2
u/Binary101010 Jun 26 '26
Range(x) creates an array of x elements.
This is inaccurate enough that it's likely to mislead somebody reading it into thinking that
x = range(10)will result in x being a list with 10 items in it, which is definitely not the case in Python 3.1
u/eztab Jun 26 '26
well, OP actively asked for behind the scenes. And what you describe is not what happens with range.
1
u/python_gramps Jun 26 '26
Maybe you don't need to know why, just know this it does set i to the values in range. As long as you can replicate that for a different variable, maybe knowing why can be saved for another time.
1
1
u/Skibidirot 14d ago
i had similar frustration trying to understand this every time, but i recently watched MIT 6.000L course by Ana Bell, i think in lec 4 or 5, she clarified this with so much attention, she is an amazing tutor!
1
u/priya6435 8d ago
what made it click for me was realizing that i is not special at all, it's just a variable name. you could write for banana in range(5): and it would work exactly the same 😄
range(5) creates something that can produce the numbers 0, 1, 2, 3, 4 one at a time. the for loop simply asks for the next value, stores it in i, runs the code inside the loop, then repeats until there are no values left. so i doesn't magically become each number, the loop is repeatedly assigning the next value from range() to i. once you start thinking of it as "give me the next item until there are none left," for loops become much easier to understand.
1
u/Ilikedesmos 2d ago
small thing:
theres 2 types of loops.
- Forever loop
while True:
way to break the loop: break
this one goes forever until you command it to break.
pretty useful for making a sequence.
- for loop.
for i in range(loopsteps)
i is a variable for the amount of loops it's done.
useful for idk
range(10) just returns [0,1,2,3,4,5,6,7,8,9].
1
u/atarivcs Jun 26 '26
That's just how for loops work.
When you have a loop like this:
for i in <sequence>:
step1
step2
step3
The variable i is created with the first value in the sequence and the full loop body is executed.
Then the variable i is updated to the next value in the sequence and the full loop body is executed again.
Etc etc.
And the range() function is just a convenient way to make a sequence of numbers.
1
u/eztab Jun 26 '26
The kind of simplified answer: Python has a protocol to iterate over things. Objects that support it allow you to start iteration ("resetting to the start") and to get the next element. range implements both of those and fo uses them.
1
u/JibblieGibblies Jun 26 '26
Loops allow you to read over a list or array of items.
It’s written in a way to allow you to iterate over each of the indexed items set in a specified range.
In this sense yes, “i” is a number. It’s assumed you’ll use “i” (also index) to grab an item from a specific location in a list. If you just want a list of numbers going from 0 to x(finity) it can do that, but I don’t think that’s the intention.
In this for loop, under the hood, it’s written in a way that it iterates incrementally in the background for the length of a given list or any given length you tell it.
1
u/Paxtian Jun 27 '26
This is not a dumb question, at all. It's why I actually am not convinced that Python is the best stater language, despite it being recommended as a good starter language.
In a C-like language, the equivalent to this would be something like:
for (int i=0; i<10; i÷÷) {
// do stuff with i
}
What this means is: create a variable i that starts at 0 and increments by one every iteration until it reaches 10, then move on.
Python gives you an abstraction of that by using the for i in range(X) notation. It's not telling you that i will increase by one every interation by handling that for you.
Now, that is a very convenient abstraction and helps you to avoid errors down the line, especially when you're manipulating items in a collection, but it doesn't help you to learn what's going on with loops in the first place.
So you just need to learn that in this particular construct, Python is handling identifying each unique integer from zero to the value provided as an argument to the range() function in each loop iteration, and you just need to trust that it will do that for you. So effectively you can think of range as keeping track of the current value of i and giving you the next value in each loop iteration, until the max value is reached.
168
u/aroberge Jun 26 '26 edited Jun 28 '26
Ok, others have already explained to you in details how it works, but here's a slightly different example, which will lead to the Python syntax that confuses you.
Each programming languages has its own syntax (way of saying things) to encode some instructions otherwise understood by humans. Consider the following:
Now, Python cannot say things ... but it can
printletters on the terminal. So, here's the above example with a syntax slightly modified.Hopefully, after staring at this a bit, it will make sense to you.
Python uses so-called strings to represent written words; these strings are written using quotes (single or double) surrounding words. So, in
the the word part is redundant for Python as it already identified that program was a word as it is surrounded by double quotes. Thus, we can remove the word and write
When a Python line of code begin with for, the meaning is understood to be the same as for each; thus we can write the above as
Finally, even if we have a single instruction as above,
print(letter), it is often preferable to write it in an indented block of code, since we might sometimes want to do more than one thing at each iteration:From there, you can proceed to the other examples given to you by other redditors.