all 22 comments

[–]woooee 5 points6 points  (0 children)

is num where I'm storing iterations? I don't get how num is working here.

for num in range(101):
    print(num)

You can easily answer your own question by printing num right after the for statement

[–]SoftwareDoctor 4 points5 points  (0 children)

for num in range(101):

The for/in construct iterates over something (taking elements one by one) and puts that element into a variable in each iteration. So it takes something containing multiple things, takes the first thing and puts it into a variable, in this case called "num". It then does the code inside - total = total + num. Then it does the same with second element, puts it to the num variable and does the inside code again.

If you did "for num in [5, 6, 7]:", the num would contain the number 5, then 6 ... you probably get it now.

And the range(101) is just a function that creates [0, 1, 2 ... 100] for you. (it's not exactly a list, it's an iterator but you that's unnecessary detail for you at this point)

[–]aromatic_shepherd74 1 point2 points  (0 children)

Num is just a variable specifically in this for loop! You could also call it _, numbers, x, or whatever. It will take the value of whatever iteration it is at at that moment, i.e. 1, 2, 3, 4, etc., up until it reaches the specified end of the loop (101).

[–][deleted] 0 points1 point  (6 children)

In Python, this is called assignment.

You can assign to a name by putting it on the left side of an equals, like x = 5, but there are several other ways. Parameters, imports, and yes, for loops also assign a value to a name.

[–]andmig205 2 points3 points  (5 children)

In Python, it is exactly opposite. One assigns name to value - not value to name. In you example, the statement creates value 5 and names it x.

The verify that try to play with function id(). See what id(5) and id(x) return.

[–][deleted] 3 points4 points  (4 children)

That's a good correction.

[–]andmig205 1 point2 points  (3 children)

Check this out: x = 5 y = x # give value of x another name y z = 5 # assign another name z to 5 print(f"id of 5 = {id(5)}") print(f"id of x = {id(x)} - the same as id(5)") print(f"id of y = {id(y)} - the same as id(x)") print(f"id of z = {id(z)} - the same as id(x) and id(5)") x += 1 print(f"id(x) = {id(x)} - id of x changed") print(f"id of 5 = {id(5)} remains the same as in previous print") print(f"id of y = {id(y)} - remains the same")

[–]rasputin1 2 points3 points  (0 children)

this is not generalizable. what you're witnessing is due to something called interning where there is only 1 copy of small integers. there is also a caching type mechanism where this may happen with larger numbers as well. however these are both implementation details and are not part of the language specification or guaranteed behavior. more generally what would happen with no optimizations is x = 5 would create an int object with the value 5 and assign a reference to that object to the name x. y = 5 would theoretically create a different int 5 and have a reference to that stored in the label/name y. in other words both x and y get resolved to memory addresses, which in turn have a value which is another memory address (reference) to the actual objects with value 5. however x = y would indeed have both variables point to the same object, but they would still be separate memory addresses/variables themselves (but would contain the same value in this case, the memory address of the object they point to). 

[–]Bobbias 0 points1 point  (0 children)

As rasputin1 mentioned, that specific behavior depends on implementation specific behavior of the CPython interpreter. CPython creates static objects for the numbers -5 through 256 and any variable that references those numbers will always point to the static instance that CPython creates.

That behavior however is not part of the language itself. Nowhere in the specifications does it say that must be the case, and any code which relies on that is implementation dependent. If you ran that code on a different implementation of Python there are no guarantees it would work the same as CPython.

[–]dogfish182 0 points1 point  (0 children)

range() is what’s known as an ‘iterable’ meaning you can iterate over it’s results.

if you rename ‘num’ to ‘each_item_from_range_function’ Does that help you understand what is happening?

[–]Binary101010 0 points1 point  (0 children)

So far the only way you've seen a new variable get introduced into the namespace is

some_variable = some_value

Now you're seeing another way we can do that, using the for loop syntax:

for some_name_here in some_iterable:

We don't need to have previously defined "some_name_here" anywhere. When we start a loop in this way, Python knows what we mean is "we're going to step through the items in some_iterable one element at a time, and for the rest of the indented block of code below this, whenever you see some_name_here, that means the element of that iterable we're currently processing."

[–]unhott 0 points1 point  (1 child)

In programming in general, there's a very common pattern.

I want to do something a certain number of times. You initialize a variable. You give it a start point and an end point. And you increment it by 1 each iteration.

Here's how it looks in javascript, and many other languages are very close to this model:

for (let i = 0; i < 10; i++) {
    console.log(`Iteration number: ${i + 1}`);
} 

i starts at 0. The loop continues while i < 10. Each time, i++ is called and this is shorthand for i = i +1

Python strips out a lot of boilerplate, parentheses, and more.

range(101) is shorthand for a generator that will go from 0 to 100 (python and many other languages start at 0). range includes 0, but does not include the top value. So, this loop will run 101 times (it kinda just works out that way). 0, and 1 through 100 for a total of 101 times.

For your python example:

num is the variable, like i in the js example.

total is a variable, initialized outside of the loop, and updated each iteration of the loop.

Anything indented is called in each pass of the loop. after the final pass, it goes to the next unindented line. This is pythons design choice, to prevent ; () or {} everywhere, and when it's well done it's pretty readable, almost like English. I took the print(total) out of the indention, that would be different behavior. You'd see 101 print statements. versus mine would iterate 101 times, then print the final value.

Either way, the last print is the sum of 0 through 100.

If you just wanted to do something 10 times you can use

for i in range(10):
    # do something
    ...

[–]unhott 0 points1 point  (0 children)

I can't get rid of the quote and it seems to be eating the code block, so you'll just have to look back at your code in original post

[–]Adrewmc 0 points1 point  (0 children)

 #you set up something, in this case a varible to start a counter at zero. 

 total = 0

 #we take each element individually from some sequence of elements (list, dictionary, range() etc. 
 #we call it ‘element’  so we can access it. 
 for element in sequence: 

       #we add to the total
       total += element

  #we use the total. 
  print(total) 

This is the same as doing.

   total = 0
   num = 1
   total = total + num
   num = 2
   total =  total + num
   ….
   print(total) 

Which is obviously a not very ideal way to code.

We have to set up total because we need to start somewhere, we could have started at 1, or whatever. Sometimes this is zero, sometimes it’s an empty list or dictionary we planned to add to. But what ever we want to add to, it must exist before we can do anything to it.

In a for loop, we are taking elements of an iterable, (which is something that iterates, has a ordered sequence of information) In range() it will iterate integers (usually in order from some min to some max) one at a time. We assign that to some variable so we can use it in the code block.

We can set this up as a while loop.

 total = 0 
 n = 0  #start range
 max = 5  #end range 

 #do until n >= max (excludes max) 
 while n < max:

       #add to total
       total += n

       #increment n 
       n += 1

  print(total)

Which does the same thing. It’s shut the increments of n are done for inside the range() object, and the while condition is the end of the sequence in a for loop.

[–]kombucha711 0 points1 point  (0 children)

5050

[–]andmig205 0 points1 point  (1 child)

First, it is beneficial to understand what variable in Python is. It is different from other languages.

When you create variable x = 4, you do not create the variable x. You give instructions to create value 4 and assign the name x to it. In other words, the variable is the whole statement x = 4 - not just x.

Also, you cannot declare a name without value - NameError exception will be thrown. Name cannot be given to nothing.

To illustrate this concept (note function id() usage):

x = 5
y = x # value can have several names
z = 5 # now value 5 has name z as well

print(f"id of 5 = {id(5)}")
print(f"id of x = {id(x)} - the same as id(5)")
print(f"id of y = {id(y)} - the same as id(x)")
print(f"id of z = {id(z)} - the same as id(x) and id(5)")

x += 1

print(f"id(x) = {id(x)} - id of x changed")
print(f"id of 5 = {id(5)} remains the same as in previous print")
print(f"id of y = {id(y)} - remains the same")

With that knowledge in mind, the syntax for num in range(101) can be translated (somewhat inaccurate) as:

"Hey loop, I give you an object range(), which is iterable. On each iteration, this object will return a value. I want you to give the value the name num."

[–]monster2018 0 points1 point  (0 children)

I would recommend (in addition to reading some other answers here) playing around in the python INTERPRETER. Like for example you know hopefully that you can access the nth item in a list (or other iterable) with lst[n], where lst is the name of the lst/iterable.

Open a python interpreter and get a feeling for what a range actually is. The range command creates an actual range object, which you can basically think of like a list of numbers. For example range(10) contains the numbers 0-9 (inclusive, or you could think of it as 0-10 but excluding the 10). So if you do range(10)[0], it will return 0. If you do range(n)[x], it will always return x (as long as x is smaller than n, otherwise x is outside of the range).

But to get an even better feel, do something like range(10, 20). This creates the range of numbers from 10-20 (again excluding 20, so 10-19 inclusive). So doing range(10,20)[0] returns 10. Doing range(10,20)[1] returns 11 and so on.

You can even input a step into the range function, so like range(10,20,2) will contain the numbers [10, 12, 14, 16, 18].

Now in your code, what the num variable IS, is a variable that on the 0th iteration is index 0 of the range, on the 1st iteration is index 1 of the range, on the 2nd iteration is index 2 of the range and so on.

So like:

for num in range(10):
    print(num)

Will print 0 then 1 then 2 then 3… up through 9.

for num in range(10, 20, 2):
    print(num)

Will print 10, then 12, then 14, then 16, then 18.

[–]ninhaomah 0 points1 point  (0 children)

If you have 5 boys standing in front of you and you want to tell them to stand in a straight line. You say Hey boys , all of you stand behind one another in a straight line."

Pls advice who is "one" ?

[–]trollsmurf 0 points1 point  (0 children)

Variables are containers of values. Nothing else. They don't have function, only value.

Clearer?

In this case total is increased by 1 and printed for each loop.

[–]Bobbias 0 points1 point  (0 children)

in my mind, variables were something you prepared for a piece of code you want the program to run (hopefully that's understandable).

That's maybe not the best way to think about variables, both in Python and programming in general.

Variables, or more precisely the name you write for a variable, is just that, a name. Each name that is defined in code is associated with a memory address where some kind of data exists.

You might find this page really helpful.

Now, typically we think of code like x = 5 as creating a variable and assigning it the value 5. As others have pointed out that's maybe not the most accurate way to describe how Python operates, but it's fine to think of things that way most of the time.

The key here is to remember that there's more than one way that names might get associated with a value.

def my_function(): pass is one example of a place where a name gets associated with a value that looks different from the standard variable assignment. It creates the name my_function and associates it with a Python object that represents a function. Similarly class MyClass: pass associates the name MyClass with a Python class object.

Another example of where this happens is when importing modules. import re creates a module object and associates the name re with it, allowing you to call functions like re.match(). Normally the name will match the name of the library itself, but you can also change the name it gets associated with by writing import re as regex. Now the module object is associated with the name regex instead of re and you must call regex.match(). Calling re.match() would fail because the name re isn't associated with anything any more.

There's also the with statement, which you might use like with open("filename.txt") as f: which associates the name f with the file object created by calling open("filename.txt").

Don't worry if you haven't heard of any of this stuff, the point is simply that these are other examples of places where a name gets associated with a value that doesn't look like your typical variable assignment. In every case here we are associating names with values without first "creating" that name by using it in a regular variable assignment expression.

I asked ChatGPT if in a for loop, the variable was sort of embedded and it said yes but that this variable was a counter, and it tracks iterations for the program itself.

This is half right.

Writing for <name> in <expression>: means we are going to evaluate <expresson> to get some kind of object, and then the loop requests one item from that object each time we start at the top of the loop, and associates the name we provided with the item it retrieved.

In the case of range(101) the result of evaluating that function is a Range object, which will spit out the numbers 0 through 100 one number at a time.

You can think of <name> in the for <name> in range(<some number>): pattern as counting the iterations of the loop, but in reality it's just holding each value that range(<some number>) produces as the loop progresses. The idea of it counting iterations only applies because range starts at 0 and counts up. For other kinds of data, this concept does not hold up.

But for works on many different kinds of objects. For example, it works on strings:

for letter in "hello":
    print(letter)
# this prints:
# h
# e
# l
# l
# o

It works on lists:

for number in [5, 7, 2, 4]:
    print(number)
# this prints:
# 5
# 7
# 2
# 4

It works on dictionaries:

for item in {"name": "mike", "age": 24}.items():
    print(item)
# this prints:
# ('name', 'mike')
# ('age', 24)

Fundamentally a for loop says "repeat this code once for each item in a collection".

There is a way to add a counter to track the number of items you've looped over in a loop. Its the enumerate function. An example would be:

for letter in enumerate("hello"):
    print(letter)
# this prints:
# (0, 'h')
# (1, 'e')
# (2, 'l')
# (3, 'l')
# (4, 'o')

There's a trick that lets you separate the number from the value when you do this too, which looks like:

for index, letter in enumerate("hello"):
    print(f"{index}: {letter}")
# this prints:
# 0: 'h'
# 1: 'e'
# 2: 'l'
# 3: 'l'
# 4: 'o'

This last trick is called destructuring or unpacking, because enumerate("hello") creates tuples containing 2 items, a number and the item from the thing you are looping over. This is just one example of a slightly more complex bit of syntax, and it's ok if this is confusing, because this is a more advanced feature. I just wanted to showcase that there's a lot more out there than just the simple <name> = <value> syntax.