FAQ: Learn Python - Lists and Functions - Iterating over a list in a function

What is wrong with using the same name for the parameter and argument? Surely it just makes things easier to follow especially if you’re looking at someone else’s code? I am confused in what you mean when you say ‘the scope in which total is defined’ I understand that in order to pass an argument, within a function you need to declare one ie def add_my_list(total where total is just a place holder, but I still don’t understand why if when total is set to zero it hasn’t been defined? and why you can’t use my_list as the argument in the function?

It doesn’t make sense, and it does not make it easier to read. Remember that this function is a utility that can be called from anywhere in the program on any list object. Keep the names to the objects discrete, and the names of the parameters generic.

Parameters are not placeholders. They are locally defined variables that indicate a required positional argument.

In what scope is target defined?

But it is used as the argument (the function call).

def foo (variable):  # parameter is a local generic name
  pass

print (foo(object))  # argument is an object in outer scope

It would appear in this conversation that one is clear on the scope in which total is defined (as well as a_list ) which explains why we cannot access it outside of the function.

In your previous reply you mentioned the above, and in your last reply you said:

In what scope is target defined?

But I don’t know what a scope is, so I am not clear on how it is defined, if you could please explain what you mean by scope, so I can understand what you’re trying to say. I would also like to ask in what way is it not clear to use the same name for the argument and parameter. When I do the following:

list_1 =[1, 2, 3] def add_list(list_1): total = 0 for i in list_1: total += i return total print add_list(list_1)

print times_list(list_1) def times_list(list_1): total = 0 for i in list_1: total += i *2 return total print times_list(list_1)

Both do as expected ie the first prints 6 and the second 12. I am also confused about when using the for loop to total += i, if I do += [i] or += list_1 I get the following error: unsupported operand type(s) for +=: ‘int’ and ‘list’, if I do: += list_1[i] I get: list index out of range, but if you compare that to using the range method, the range method only works when doing += numbers [i]. Can you kindly explain what these mean and why I get them, and why when using the range method the += is different? The last question I have is when using the range method I’ve noticed that if I do either for i in range(len(numbers)): or for i in range(0,len(numbers)): I get the same outcome, but the solution writes it as the former, why is this?

There is some mention of Scope in the Learn Python 3 course, and another more in depth look at in the Learn Intermediate Python 3 course. Are you a Pro or Student subscriber? If so, track down that unit in the course.

Briefly, there are four kinds of scope in Python.

  • built-in
  • global
  • enclosing
  • local

From the bottom, look up. The scopes above are accessible to the scopes below, but not the other way around (without some help). As we can see, all scopes are accessible from local scope. That is the scope, or namespace inside a function.

def foo():
    # local scope
    pass

When a variable is declared within the function, it can only be accessed from within that function. Any variables outside of the function remain accessible from within it.

def bar():
    # enclosing scope
    pass
    def foo():
        # local scope
        pass

Variables declared in bar are accessible to foo but not the other way around. From the point of view of foo, the variables in bar are seen as non-local. In order for foo to be able to mutate those variables it will require a binding declaration…

def bar():
    # enclosing scope
    b = 40
    def foo():
        # local scope
        nonlocal b
        b += 2

Outside of a function we are in global scope. From this namespace we can access (call) all direct functions, but none of the enclosed functions. We can call bar(), but not foo(). All global variables can be accessed from anywhere except the built-in namespace. If our function is expected to mutate a global variable, then just as we declared b to be non-local in foo(), we would need to declare a binding.

a = 73
def bar():
    global a
    a -= 31

Lastly we have the built-in namespace which is where Python resides.

Anyway, I hope you can track down that unit and give it a read.

As for your other questions,

+= i

and

+= [i]

are two completely different operations. The first one can only be performed on a static number, such as,

b = 40
b += i

which will increase b by i.

b = [40]
b += [I]

will append i to the list, not add it to 40. (see list.extend()).

The multiplication function implies we are to double each value in the list, which does not involve keeping a running tally (so no total).

for x in list_1:

won’t work since we cannot mutate the list.

for i in range(len(list_1):
    list_1[i] *= 2

will work since we are iterating the range, not the list. Rule of thumb, never mutate a list you are iterating. Python simply won’t allow it.

Notice we touch on your range question, above. When the start index is omitted, Python defaults to zero, so whether we include the 0 or not, it is still the starting value. We need that positional argument so that we can start wherever we wish.

for i in range(3, int(n ** 0.5) + 1, 2):

Above the start value is 3, the end value is int(n ** 0.5) + 1, and the step, or *stride* value is 2`. Recall that a range does not include the uppermost value.

Of note

+= can also be used on strings to perform in-place concatenation (much like the list concatenates with another list).

*= can also be used on strings to perform repetition.

>>> a = '*'
>>> a *= 8
>>> a
'********'
>>> 

and similarly, on lists…

>>> a = ['0']
>>> a *= 8
>>> a
['0', '0', '0', '0', '0', '0', '0', '0']
>>> 

The other arithmetic assignment operators can only be used on numbers or bits. We cannot subtract from a string or list object. Neither can we divide, etc.

Lastly, with regard to range, we can set the direction in reverse by starting from the right side and ending on the left (remember the last value is excluded so it you wish to end on 0, it must be set to -1) and using a negative stride value…

>>> list(range(10, -1, -1))
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> 

Thankyou for informing me on scopes, unfortunately I am using a free version of codeacademy so am only learning about python 2, so not sure if scopes were relevant to python 2, if they are I haven’t got that far through the course, so will learn about them later on.

In regards to your comment, about the list iteration, if I do:

for x in list_1:

It does work, maybe this is where the differences between python 2 and python 3 are shown???

The namespaces are the same in both Python 2 and Python 3, despite the differences in other regards. Scope is scope, and functions are the simplest way to describe it. The notes above should be ample to provide a basic understanding. The unit on scope should be coming up soon. Have you got to the unit on functions?

As expected. We can iterate a list as read-only using that form. We cannot mutate the same list while iterating, though.

Above you will see I wrote list(range()). That is Python 3. In Python 2, a range is a list, so range() gives the same output.

how do you complete this part using method 1? it can’t figure it out and the code solution only shows method 2.

Can you please elaborate on your question with so extra detail?

1 Like

The instructions shows Method 1 and Method 2. Method 1 is using for item in list: Method 2 uses Iterate through indexes. I can’t figure out how to use for item in list and when I check the solution it only shows the solution using Method 2.

A list (or any iterable) can be iterated in two ways (the two methods):

  1. By index, using a range to generate a sequence that matches indices
  2. By item, which means iterating over the list, proper.
>>> a = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> r = range(len(a)
>>> print (list(r))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]   # index sequence
>>> for i in r:
    print (a[i])  # access by index
9
8
7
6
5
4
3
2
1
0
>>> for x in a:
    print (x)  # access by item
9
8
7
6
5
4
3
2
1
0
>>>
2 Likes

thank you very much!

1 Like