How does Python iterate a for loop? - python

I tried the following code on Python, and this is what I got:
It seems like for many changes I try to make to the iterables by changing elem, it doesn't work.
lis = [1,2,3,4,5]
for elem in lis:
elem = 3
print lis
[1, 2, 3, 4, 5]
However if the iterables are objects with its own methods (like a list), they can be modified in a for loop.
lis = [[1],[2]]
for elem in lis:
elem.append(8)
print lis
[[1, 8], [2, 8]]
In the for loop what exactly is the 'elem' term? Thanks in advance!

The reason that this doesn't work is because you're misunderstanding what elem is. It's not the object itself, and it's not even correct to call it a "variable".
It's a name, kind of like a label, that points to the object. If you just directly assign over it, you're just overwriting the name to point at something else. But, you still have the original reference to the list, so assigning a different value over elem doesn't modify lis itself.
Now, in this case, since all of the objects that elem points to are integers, you can't even change them at all - because integers (and many other types, like strings or tuples) are immutable. That means, simply put, that once the object has been created it cannot be modified. It has nothing to do with whether they "have methods" or not (all Python objects have methods, integers included), but on whether or not they are immutable.
Some objects, however, are mutable, meaning that they can be changed. Lists are examples of such objects. In your second example, elem is a name that references the list objects contained within lis, which themselves are mutable. That is why modifying them in-place (using .append(), or .remove(), etc) works fine.

The elem variable in your for loop is a reference to the current object on each iteration. Changing it won't do anything; it will just change the value of the variable elem and that's going to be changed the next time through the loop anyway. To actually change the value of the element in the list, you need a reference to the list and the index of the element to be changed, and you don't have the latter.
So what you want to do is something like this:
for index, elem in enumerate(lis):
lis[index] = 3
This way you have elem for the element's value and index for the position in the list. It saves you from writing lis[index] constantly to get values, but you must still do so to change elements.
You can also do:
for index in xrange(len(lis)):
lis[index] = 3
However, in most situations this is considered un-Pythonic (among other things, what happens if the list gets longer or shorter while it's being iterated)?

Here, you are actually modifying the list object in your second example. In the first example, you are not modifying the number, you are replacing it. This can be a complicated nuance for new users of Python.
Check this out:
>>> x = 1
>>> id(x)
4351668456
>>> x = 2
>>> id(x)
4351668432
id returns the identifier of the object. As you can see above, the object of x changes both of these times.
>>> y = [1]
>>> id(y)
4353094216
>>> y.append(2)
>>> id(y)
4353094216
Here, I modify the list, so the list is still the original object y.
So, all this means that when you are doing elem = 3, it's not modifying it, it is replacing it. And by now, it's not associated with the list anymore.
This is one of the ways you could do what you are trying to do. This grabs the index and then modifies the list, not the number.
lis = [1,2,3,4,5]
for idx, elem in enumerate(lis):
lis[idx] = 3
print lis
[1, 2, 3, 4, 5]

When you assign a new value to the name elem, you just change the local binding in the for loop. If you want to change the values stored in lis, use map or a list comprehension, like this:
lis = [3 for elem in lis]
You can, however, modify the attributes of elem (or call methods that do so), just like you can on any other value.

In your first example you are trying to modify an integer, and it's inmutable (as strings are).
Python variables should be seen as labels pointing to an object. When you iterate over a list of inmutables, elem points to an inmutable object, not to that position in the list, so you can't modify the original list.
In the second case, elem point to an object that can be modified, so you see the original list changed.

It depends on what the type() of elem is.
In your first case each elem is an int object and it does work to change it. You are changing a temporary object when you say: elem = 3, not the item in the list itself.
In the second case each elem is a list object.

Related

Why Can You Not Duplicate A List And Lose The 1st Index Of the List?

I was trying to make a list (for recursive purposes) that would not contain that list's [1] index. However, when I tried doing this, by instantiating an empty list and then appending the rest of the list to it, the output shows that the list is empty. I was wondering if there was a specific reason or concept for why it was not creating the special list that I wanted.
a = [1,2,3,4,5]
print(list().append(0).append(a[1:]))
The output should be [1,3,4,5] but is instead "None".
Sure, I could make a function and piece it all together but that doesn't feel nearly as intuitive or elegant.
Read the documentation. append is a list class method that alters the object. It has no return value; None is the default. Your expression reduces to
print(None.append([2, 3, 4, 5]))
Which, in turn, has a return value of None.
You can make your new list simply with
new_list = a[1:]
Ass #Daniel Roseman said, append function returns None.
If I understand what you want to achieve, create a little function and just do:
a = [1,2,3,4,5]
def addAndReturn(ls, elem):
ls.append(elem)
return ls
print(addAndReturn(addAndReturn(list(), 0), a[1:]))
result:
[0, [2, 3, 4, 5]]
append appends to a list and returns None. You should save this new list to a variable and then print it. E.g.
newlist = list() # or just[]
newlist.append(0)
newlist.extend(a[1:]) # OP had append, but you probably meant "extend"
print(newlist)
The append method adds an element to its object but returns None. What you want is the list addition:
print(list() + [0] + [a[1:]])
You'll need to create a new list, and extend it from there:
b = [0]
b.extend(a[1:])

Local and Global lists in Python

Please help me as I am new to python
when I call this function the original value of list1 changes
def mystery(list1):
list1[0] , list1[1] = list1[1], list1[0]
list1 = [7,82,44,23,11]
mystery(list1)
print(list1) #prints [82, 7, 44, 23, 11]
how it can change the value of global list1
if I change my function to
def mystery(list1):
list1 = list1 + list1[2:5]
then I am getting the global value of list1 and not the updated one.
lists are mutable objects in python, so if you are passing to a function a list all the changes that you make to the list inside your function will be reflected everywhere/globally
If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart's delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you're done, the outer reference will still point at the original object.
If you pass an immutable object to a method, you still can't rebind the outer reference, and you can't even mutate the object.[more details here]
The reason list1 is changing is because you're actually modifying the list object inside of the function.
It really has nothing todo with global / local variables, if you renamed the parameter inside your function to something else, and still passed in list1, list1 would be modified.
If you're wanting to return a new list, you need to first create a copy of the list, there's many ways to do this. list(list1) is one way. Then return the list at the end of the function.
If I understand your queston, you want to actually append some more values to the passed in list, use list1.append(...) to add to the end of the list.
And since you're modifying the list itself, it'll change in the larger scope.
But it's still not using the global scope, as you're just using a var with the same name in the local scope.
Maybe just return the list?
def mystery(list1):
return list1 + list1[2:5]
list1 = [7,82,44,23,11]
list1 = mystery(list1)
print(list1)
In python, assigning a value to a variable like a = 5 means you are creating an object in the memory for the value 5. Now a is a link to that object holding 5 (in Layman's terms). If you change a now, say a = "something else", this creates a new object for the string "something else" in somewhere else in the memory and now a is pointing to that. This is why you get to change the data type of python variables.
When you pass something to a python function, that link is the one that is passed on. So when you call mystery(list1), original link to the list1 is passed. Therefore changing an element in the list1 means you are assigning a new value to that particular element in the original list1. No matter you are inside or outside of the function mystery() in this case since you will be using the original link you created to access the element inside list1 to change it. The change happens inside the list1; list1 didn't get reassigned.
However when you do list1 = "something new" inside your function mystery(), you are creating a new variable list1 inside the function which is local to the function. You are not changing the original list1.
Passing mutable objects into a function and then modifying the object inside the function will have the same effect as modifying the object directly.
list1 = [1,2,3]
def func(list1):
list1[0] = 5
>>>list1
[5,2,3]
This is effectively same as directly running list1[0] = 5
However if you pass an immutable object into a function such as tuple, It will not support item assignement TypeError: 'tuple' object does not support item assignment. So you need to build a new immutable object and return it.
>>> tup1 = (1,2,3) # doing tup1[0] = 5 will cause TypeError
>>> tup2 = tup1 + (5,)
>>> tup2
(1, 2, 3, 5)
Put it in function,
>>> def func2(tup1):
return tup1 + (5,)
>>> func2(tup1=(1,2,3))
(1, 2, 3, 5)

python OOP unexpected results

I have a list containing lists of objects. More specifically,
l = [ [X1,X2],[X3,X4]]
where X1,X2,X3,X4 are objects.
Now, I have a method in the class definition, that takes a list of objects, and modifies the attribute of the present object. Thus,
Class X:
def __init__(self,value=1):
self.value = value
def func (self,l):
total = 0
for x in l:
total += x.value
self.value = total
The problem I encounter is as follows. I have to apply the function func on elements of l[1] using elements of l[0]. However, when I do so, it turns out that the elements of l[0] are also getting changed. Thus, when I input
for obj in l[1]:
obj.func(l[0])
then I see that elements of l[0] have values that should ideally be assigned to l[1].
the list lis created as follows.
l0 = []
for i in range(2):
newx= X(i)
l.append(newx)
l=[]
l.append(l0)
l.append(l0)
What am I missing?
Correction: It seems python doesn't create new list objects every time I append an existing list.I basically need to copy the list using copy command. Hence, it is modifying the existing object.
l.append(l0)
l.append(l0)
Here you are appending the same list twice. Changes to objects in one list will be seen in the other because they are the same list.
To get the result you want, you must create two lists rather than reuse the first list.

Python not referencing to same list

In code below:
a=[0,1]
b=a
for i in range(2):
for j in b:
a=a+[j]
why does a print as:
[0,1,0,1,0,1]
and b as:
[0,1]
However when executed on idle both lists change:
>>> c=[9,0]
>>> d=c
>>> d+=[7]
>>> c
[9, 0, 7]
Since a is being appended, why doesn't b change as is the property of python list assignment?
Since a is being appended why doesn't b change as is the property of python list assignment?
a is not appended. When you write:
a = a+[j]
you each time construct a list [j] and then construct a new list a+[j] that contains all the elements of a and then j.
Now you let a refer to the new list, but b still refers to the old list. Since the old list is not updated (the state is not altered, for instance through append), the list remains the same (which is good since iterating over a list you alter can have unwanted side effects).
If you would use a.append(j) or a += [j] instead of a = a + [j], then the list will be updated (in the latter case, you implicitly call a.extends([j])). Since both a and b refer to that list, b will thus also be updated. But mind that since we iterate over b at the same time, we could end up in an infinite loop. So you better do not do that anyway.
a is not appended. Appending is done with the append command like so:
a.append(1)
Every time you add (a + [j]) you construct a new object.

for statement in python

I'm confused about "x" in the python code below.
>>> # Grocery list
... grocery_list = ['apples', 'bananas', 'oranges', 'milk']
>>> for x in grocery_list:
... print(x, len(x))
I am confused about x's role in the for statement above. Is "x" a variable that is being defined within the for statement, or is it something else? It just seems different than how I am used to defining a variable, but I noticed "x" can be anything, which makes me think it is, in fact, a user-defined variable.
Please help.
Yes it's defined within the for statement. It's just a placeholder for an element in the list and can be called anything, e.g.
grocery_list = ['apples', 'bananas', 'oranges', 'milk']
for grocery in grocery_list:
print(grocery, len(grocery))
Python is a late-binding dynamic language. While Python programmers and the Python documentation frequently use the terms "variable" and "assignment" the more precise terms are "name" and "binding."
In your example x is a name of an object. At each iteration over the loop it's bound to the next object from your list. Python lists, and most other Python containers as well as many other Python object classes, feature iteration functions. That is to say that they define functions following a protocol which allows them to be used in for loops.
As you've noted a Python name (analogous to a "variable" in other languages) can be bound to any object of any type. A list can contain any mixture of object references. Thus, when you iterate over a list your "variable" (or loop name(s)) can be bound to objects of different types, potentially different types on each pass through the loop.
You can also have multiple names bound through "tuple unpacking" at each step through the iteration. For example the following snippet of code is a commonly used way to deal with dictionaries:
for key, value in some_dictionary.items():
# do something with each key and its associated value
This form isn't specific to dictionaries. The .items() method of dictionaries returns a sequence of two item tuples and this form of for loop could be used with any list or sequence which returned two-item tuples (or even two-item lists). Similarly you could see tuple unpacking used on sequence consisting of items which contain a uniform number of items:
for category, item, quantity, price in some_invoice_query():
# do something with these items, etc.
Conceptually a "variable" in most programming languages is a placeholder for data of a certain type (as well as a name by which that placeholder is referred throughout a program's source code). However, in Python (and other late-binding dynamic languages) a name is a reference to an object and conveys no constraint regarding the type or class of object to which the reference is made.
You can, rather crudely, think of Python names as if they were all void pointers in C.
x is a name in your current namespace, and the objects in grocery_list are assigned to this name one after another.
I think it is okay for you to treat x as a variable that is being defined within the for statement. Anything else is okay too. Python does not require a seperated "define" process, any variable is "defined" the first time it has been given a value.
The variable will be assigned behind the scenes each of the values of the list, in order. If the list holds references, then the reference will be assigned to the loop variable, of course.
It's almost equivalent to:
for i in xrange(len(grocery_list)):
x = grocery_list[i]
# rest of code here
But much much cleaner and (potentially) faster. The name (x) is not signicifant, it can be whatever you please.
After the loop has executed, the variable remains in scope, with the value of the last iteration that ran. So if you use a break to get out of the loop, that will show:
>>> for i in xrange(100):
... if i == 10: break
...
>>> i
10
x is a temporary variable that steps through a sequence. In lists, x will step through each item in the list.
>>> grocery_list = ['apples', 'bananas', 'oranges', 'milk']
>>> for x in grocery_list:
... print(x, len(x))
...
apples 6
bananas 7
oranges 7
milk 4
>>> print(x)
milk
EDIT: Apparently x remains defined even after the for loop exits.
A few more examples should clear things up:
>>> for x in 'some string': # x is bound to each character in the string
... print(x)
...
s
o
m
e
s
t
r
i
n
g
>>> for x in (0, 1, 2, 3): # x is bound to each number in the tuple
... print(x)
...
0
1
2
3
>>> for x in [(0,0), (1,1), (2,2)]: # x is bound to each tuple in the list
... print(x)
...
(0, 0)
(1, 1)
(2, 2)
In your example, x is the user-defined variable to which each value in grocery_list will be assigned in turn.
One must remember what a Python variable stores. It stores a location in memory where the object is pointing at is present. In other words, Python variables are basically pointers (void*s). They "know how to find their objects."
If you have
x = 5
y = 3
the assignment y = x actually hands a copy of the memory address where 5 is stored to y. Now x and y point at the copy of 3 in memory. Suppose you attempt this
x = [1,2,3]
for k in x:
k = 0
What happens? You hand k a copy of the memory address where each item is
stored. You then assign k to point a 0. The items in x are left undisturbed.
Now do this
x = [[1,2,3], [4,5,6], [7,8,9]]
for k in x:
k[0] = 0
Then x holds the list
[[0, 2, 3], [0, 5, 6], [0, 8, 9]]
You can change state of a mutable object via its memory address.
The moral: Python variables know WHERE to find their objects because they know where they are located in memory. When you assign one variable to another, you hand the recipient variable a copy of the donor variable's address. The loop variable in a for loop is just another variable.

Categories