about iterators in python [duplicate] - python

This question already has answers here:
How to re-assign items in a list in Python?
(4 answers)
Closed 9 years ago.
If I have the following list:
a = [1, 2, 3]
And I run the following code:
for x in a:
x += 1
It seems that this does not change the list a.
However, if I do the following:
for i in range(0, len(a)):
a[i] += 1
This will modify the content of 'a'.
So I guess x and a[i] are referring to elements of a in a different way. What exactly is causing the difference? How are they each referring to elements of a?

When you iterate over a list, each element is yielded, in turn. However, there are different kinds of objects. mutable and immutable. When you do something like:
a += 1
with an immutable object, it translates roughly to:
a = a + 1
Now in this case, you take the object reference by a, add 1 to it to create a new object. Then you assign that new object the name a. Notice how if we do this while iterating, we don't touch the list at all -- We only keep creating new objects and assigning them to the name a.
This is different for a mutable object. Then a += 1 actually changes the object in place. So, the list will see the change because the object that it is holding has changed (mutated). (With the immutable object the object contained in the list wasn't changed because it couldn't be). See this question for more info.
This also makes it a little more clear what's happening when you're iterating by indices. you construct a new integer and you put it in the list (forgetting whatever was in that slot before).

When you say,
for x in [1,2,3]:
x+=1
You are saying, temporarily save x as a variable, and add one to that temporary save. When you get to the next iteration, the garbage man destroys that variable because it was temporary. x is not the spot in the list. It is the value of that spot in the list.
Edit: when I say that it gets deleted, I was not being clear with my words. What happens is that each time through the loop, x is replaced with another value, and so what happened before goes away (unless you did something else with it). With the operations you are using, though, you are not changing the values of any elements in the list. My bad for the confusion.
When you do it the other way,
for x in range(len(lst)):
lst[x] += 1
Then you are talking about the list's values. x is the index of the variable, and can therefore alter the value of the list's value at that spot.

The concept that matters here is the idea of referencing. In Python, variables are references to objects that sit somewhere in memory. Let's use the an arrow → to indicate a reference. Variable → Object. Variable on the left, object on the right.
The array can be visualized as three variables that reference three integer objects.
a[0] → int(1)
a[1] → int(2)
a[2] → int(3)
Now, integer objects are immutable. They can't be changed. When you change an integer variable you're not changing the object the variable refers to. You can't, because ints are immutable. What you can do is make the variable reference a different object.
Direct update
Let's look at the second loop first since it's simpler. What happens if you update the array directly?
for i in range(0, len(a)):
a[i] += 1
First let's unroll the loop:
a[0] += 1
a[1] += 1
a[2] += 1
For integers, a[0] += 1 is equivalent to a[0] = a[0] + 1. First, Python evaluates a[0] + 1 and gets the result int(2). Then it changes a[0] to reference int(2). The second and third statements are evaluated similarly.
a = [1, 2, 3] # a[0] → int(1)
# a[1] → int(2)
# a[2] → int(3)
a[0] += 1 # a[0] → int(2)
a[1] += 1 # a[1] → int(3)
a[2] += 1 # a[2] → int(4)
Indirect update
And what about what I'll call "indirect" updating?
for x in a:
x += 1
Unrolling the loop yields this equivalent series of statements:
x = a[0]
x += 1
x = a[1]
x += 1
x = a[2]
x += 1
What happens at each step, and why isn't the array changed?
x = a[0]
This makes x reference whatever object a[0] references. Both a[0] and x reference the same int(1) object, but x isn't directly connected to a[0]. It refers to whatever a[0] refers to, not to a[0] itself.
x += 1
This changes what x refers to. It has no effect on a[0].
The same thing happens for the second and third assignments. The result is that x is continually changed while the elements of a are merely read from but never modified. And so when the loop terminates a is unchanged.
a = [1, 2, 3] # a[0] → int(1)
# a[1] → int(2)
# a[2] → int(3)
x = a[0] # x → int(1)
x += 1 # x → int(2)
x = a[1] # x → int(2)
x += 1 # x → int(3)
x = a[2] # x → int(3)
x += 1 # x → int(4)

Think of it as in your first loop, x is just a substitute for every element in a. It's not the actual element in a (although by id() it is because they both reference the same object). When you're doing x += 1, you're just changing the value of x and not the value in the list.
In your second for-loop, you're actually modifying the list by doing a[i] += 1.

Related

What does it mean? "In x += 1 , x is only evaluated once."

I can't understand the explanation about evaluations.
What is the difference between normal and augmented assignments about this?
I know about in-place behavior.
https://docs.python.org/3/reference/simple_stmts.html#grammar-token-augmented-assignment-stmt
An augmented assignment expression like x += 1 can be rewritten as x = x + 1 to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the actual operation is performed in-place, meaning that rather than creating a new object and assigning that to the target, the old object is modified instead.
For a simple variable x, there is nothing to evaluate. The difference is seen when x is an expression. For example:
some_list[get_index()] = some_list[get_index()] + 1
some_list[get_index()] += 1
The first line will call get_index() twice, but the second one calls it once.
The second difference mentioned is when x is a mutable object like a list. In that case x can be mutated in place instead of creating a new object. So for example lst = lst + [1,2] has to copy the entire list to the new one, but lst += [1,2] will mutate the list itself which might be possible to do without having to copy it. It also affects whether other references to lst see the change:
lst = [1,2,3]
lst2 = lst
lst += [4] # Also affects lst2
lst = lst + [5] # lst2 is unchanged because lst is now a new object

What does this construct do? [duplicate]

This question already has answers here:
Is there a standardized method to swap two variables in Python?
(8 answers)
Closed 6 years ago.
Example
num = [0,3,5,3,0,0,9,8]
Output should be
[3,5,3,9,8,0,0,0]
The solution to this in Python is
def moveZeroes(self, nums):
zero, cur = -1, 0
while cur < len(nums):
if nums[cur] != 0:
# update zero index when current is not zero
zero += 1
nums[cur], nums[zero] = nums[zero], nums[cur]
cur += 1
My question is I have seen similar statement like this a lot
nums[cur], nums[zero] = nums[zero], nums[cur]
What is this statement in particular doing?
The idiom
>>> a, b = b, a
swaps the values hold in a and b in one step so that a now points to the value that b used to point to, et vice versa:
>>> a, b = 1, 2
>>> a
1
>>> b
2
>>> a, b = b, a
>>> a
2
>>> b
1
Technically, the right-hand side creates a throwaway tuple, and the left-hand side is a tuple-unpacking assignment.
nums[cur], nums[zero] = nums[zero], nums[cur]
then means "swap the elements at index zero and at index cur.
This code nums[cur], nums[zero] = nums[zero], nums[cur] is swapping two values in the nums array. It is the same as if you had written:
temp = nums[cur]
nums[cur] = nums[zero]
nums[zero] = temp
When you use commas on both sides of the = assignment operator, Python essentially makes all of the assignments at once, so you don't need a temp variable.
It is swapping the values.
a, b = b, a
Now a has the value of b, and b has the value of a
This is Python's tuple packing and unpacking feature.
It's functionally identical to
temp = nums[cur]
nums[cur] = nums[zero]
nums[zero] = temp
but more efficient to the computer and more readable to programmers.
Further reading: https://stackoverflow.com/a/14836456/5399734
It's a variable swap, Python-style. It constructs a tuple of two values, the values at zero and cur, then unpacks that tuple and assigns the values to the indices at cur and zero; by reversing the indices assigned to, it swaps the values.
Note that the code could be simplified dramatically (though possibly at the expense of running slightly slower) by repurposing the key argument of the sort method to let Python do the work at the C layer:
def moveZeroes(self, nums):
nums.sort(key=lambda x: x == 0)
# Or more efficiently but obscurely:
# nums.sort(key=(0).__eq__)
# Or with an operator import to get speed with less obscurity:
# nums.sort(key=operator.not_)
Since Python's TimSort algorithm is stable, this preserves the order for all the non-zero values while moving the zeroes to the end.
It is first evaluating both of the values on the right, then it is substituting them to the matching slots on the left. So basically:
a, b = b, a
is like:
c = b
d = a
a = c
b = d
It is particularly useful to swap values or perform some operations on them without creating additional variables.

A double preceding function with bug

It is a homework question that I am stuck on:
Your classmate claims to have written a function that replaces each value in a list with twice the preceding value (and the first value with 0). For example, if the list [1, 3, 7, 11] is passed as a parameter, the function is supposed to return [0, 2, 6, 14] -- Note: 22 is not part of the output. Here's the code:
def double_preceding(values):
if (values != []):
temp = values[0]
values[0] = 0
for i in range(1, len(values)):
values[i] = 2 * temp
temp = values[i]
Analyse this function and rewrite it so that it works as intended.
I couldn't even follow what the code was doing, can someone explain to me please
Here's an explanation of the given code, and the errors I see within it:
def double_preceding(values):
if (values != []): # this if checks for an empty list, to avoid exceptions
temp = values[0]
values[0] = 0 # but this line is not indented, so you may get an IndexError anyway
for i in range(1, len(values)): # this loops over all indexes but the first (0)
values[i] = 2 * temp # this replaces the current value with double temp
temp = values[i] # this line however saves the same doubled value as the new temp
So, the two errors I see is incorrect handling of empty lists, and a logic error in the assignment code that will cause the loop to replace values after the first with the list's original first value times successive powers of two.
A good way to solve the second issue is to do both of the assignments in the loop with a single statement. This is a neat thing that Python can do that many other languages cannot. Here's what a basic version would look like:
values[i], temp = temp*2, values[i]
The commas are the key things to pay attention to. The one on the right side of the assignment makes a tuple out of temp*2 and values[i]. The comma on the left hand side tells Python to unpack the tuple being assigned into the variables values[i] and temp. And the two parts are evaluated in that order (first the expression on the right side, then the unpacking and assignments). This means that the "old" values of temp and values[i] are used to build the tuple and it doesn't matter that they're both reassigned later.
If we're doing the assignments that way, we can solve the empty list situation elegantly too. Rather than treating the first value specially and needing a check to make sure values[0] is a valid expression, why not just set temp to 0 at the start and let the loop handle the first value as well as the later ones? Here's a fully fixed function:
def double_preceeding(values):
temp = 0
for i in range(len(values)): # loop over all indexes, not skipping the first
values[i], temp = temp*2, values[i]
The loop will do nothing if values is an empty list, since len([]) is 0 and range(0) is empty itself.
Example output:
>>> L=[]
>>> double_preceeding(L)
>>> L
[]
>>> L=[1, 3, 7, 11]
>>> double_preceeding(L)
>>> L
[0, 2, 6, 14]
If I guessed the indentation of the program correctly. See comments below:
Code:
def double_preceding(v):
values = v[:] # Make a copy of the list passed as argument
if (values != []): # If list is not empty, store the first value in 'temp'
temp = values[0]
else:
return
v[0] = 0 # Set the first value of the list as '0' (as the problem says it)
for i in range(1, len(values)): # Iterate 'n - 1' times, where n is the length of the list
v[i] = 2 * temp # Set the corresponding value to twice the precedent (the precedent is stored in 'temp')
temp = values[i]
Test:
v = [1, 3, 7, 11]
double_preceding(v)
print v
Output:
[0, 2, 6, 14, 22]

What is the term to describe this property of how lists work?

I am looking for the proper term to describe this well-known property of collection objects, and more importantly, the way the stack diagram changes when variables are used to reference their elements:
>>> x = 5
>>> l = [x]
>>> x += 1
>>> l
[5]
>>> x
6
What is the name of what the list is doing to the variable x to prevent it from being bound to any changes to the original value of x? Shielding? Shared structure? List binding? Nothing is coming back from a Google search using those terms.
Here's an example with more detail (but doesn't have a definition, unfortunately).
Credit to ocw.mit.edu
What is the name of what the list is doing to the variable x to prevent it from being bound to any changes to the original value of x? Shielding? Shared structure? List binding? Nothing is coming back from a Google search using those terms.
Because the list isn't doing anything, and it isn't a property of collections.
In Python, variables are names.
>>> x = 5
This means: x shall be a name for the value 5.
>>> l = [x]
This means: l shall be a name for the value that results from taking the value that x names (5), and making a one-element list with that value ([5]).
>>> x += 1
x += 1 gets rewritten into x = x + 1 here, because integers are immutable. You cannot cause the value 5 to increase by 1, because then it would be 5 any more.
Thus, this means: x shall stop being a name for what it currently names, and start being a name for the value that results from the mathematical expression x + 1. I.e., 6.
That's how it happens with reference semantics. There is no reason to expect the contents of the list to change.
Now, let's look at what happens with value semantics, in a hypothetical language that looks just like Python but treats variables the same way they are treated in C.
>>> x = 5
This now means: x is a label for a chunk of memory that holds a representation of the number 5.
>>> l = [x]
This now means: l is a label for a chunk of memory that holds some list structure (possibly including some pointers and such), which will be initialized in some way so that it represents a list with 1 element, that has the value 5 (copied from the x variable). It cannot be made to contain x logically, since that is a separate variable and we have value semantics; so we store a copy.
>>> x += 1
This now means: increment the number in the x variable; now it is 6. The list is, again, unaffected.
Regardless of your semantics, you cannot affect the list contents this way. Expecting the list contents to change means being inconsistent in your interpretations. (This becomes more obvious if you rewrite the code to l = [5]; x = l[0]; x += 1.)
I would call it "immutability" of the contained object.
I think you compare your situation with the following one:
x = []
l = [x]
x += [1]
print l # --> [[1]]
The difference is:
In this situation (the mutable situation), you mutate your original object x which is contained in the list l.
In your situation however, you have x point to an immutable object (5), which is then added to the list. Afterwards, this reference is replaced by the one to 6, but only for x, not for the list.
So x += <something> either modifies x or replaces it with another object depending on the nature of the object type.
EDIT: It as well has nothing to do with the nature of lists. You can achieve the same with 2 variables:
x = 5
y = x
print x, y, x is y
x += 1
print x, y, x is y
vs.
x = []
y = x
print x, y, x is y
x += [1]
print x, y, x is y
The first one will change x due to the immutability of ints, resulting to x is y being false, while in the second one, x is y remains true because the object (the list) is mutated and the identity of the object referenced to by x and y stays the same.
The behavior you're describing has to do with references. If you're familiar with c, you're probably also familiar with "pointers." Pointers in c can get really complicated, and Python uses a data model that greatly simplifies things. But having a bit of background in c helps understand Python's behavior here, which is closely related to the behavior of c pointers. So "pointer," "reference," and "dereference" are all terms that are related to what you're talking about, although none of them is quite a "name" for it. Perhaps the best name for it is "indirection" -- though that's a bit too abstract and inclusive; this is a very specific kind of indirection. Perhaps "reference semantics"? Here's a slide from a talk by GvR himself that uses that term, and a google search turns up a few useful hits.
But if you don't have any background in c, here's my best explanation. In short, you can think of a Python name as a pointer to an object. So when you assign a name to a value, you're "pointing" that name at the value. Then, when you assign a new value to the name, you point it at a new value; but the old value isn't changed at all as a result. This seems natural when thinking about names as pointers here; the "value of the name" is changed, but the "value of the value" is not.
The thing that's a bit confusing is that += behaves inconsistently. When you use += on a number, the result makes perfect sense using the above metaphor:
x = 5
y = x
x += 1
print x, y
# 6 5
The behavior is exactly the same as it would be if you did x = x + 1.
But sometimes += is overloaded in a way that does in-place mutation. It's a pragmatic, but slightly inconsistent, idiom:
x = [5]
y = x
x += [6]
print x, y
# [5, 6] [5, 6]
So here, the "value of the value" is changed. This is actually not my favorite thing about Python, but there are good reasons for it.
The only thing list is doing to x in your example is reading it; it interacts with the variable in no other way. In fact the list generated by the expression [x] does not interact with the variable x at all.
If you had to introduce a piece of jargon for it, it would be value-capture, or perhaps simply independence.
I think the reason why there isn't a special term for this is that it is (a) not something that requires much discussion (b) is an aspect of strict by-value semantics where variables always hold references. Those kinds of semantics are pretty much the norm now (except where it is by reference with variables actually naming bits of memory containing objects). I think OP was expecting by-name or lazy semantics.
What you're effectively doing is constructing a list with one element that references the same object as x. Then you bind a new value to x while the list still references the old element. This is because += returns a reference to a new object (6) and leaves the old 5 object intact. Integers are immutable in Python.

C Pointer arithmetic in Python

I'm trying to convert a simple C program into Python but as I don't know anything about C and a little about Python its just difficult for me..
I'm stuck at C pointers.
There is a function that takes an unsigned long int pointer and adds its values to some variables within a while-loop:
uint32_t somename(const uint32_t *z) {
while(....) {
a += z[0]
b += z[1]
c += z[2]
z += 3
}
}
Can someone please tell me how to accomplish the same thing in python?
(The part that I didn't understand at all is " z += 3 ")
I'm aware that there aren't pointers in python. (at least not like C) But the problem is that I don't know what C pointers exactly do and therefor can't make this happen in python.
A similar code snippet in Python might be:
def somename(z):
i = 0
while (....):
a += z[i]
b += z[i+1]
c += z[i+2]
i += 3
In C, z works sort of like an array index, except it starts at whatever the address of the start of the array is, rather than starting at 0. There is no analogous concept in Python, so you need to use a list index explicitly.
Whatever is inside (....) will need modification too. I'll leave that as an exercise for you, since it's unspecified in the question.
What z += 3 means is basically advance the pointer 3 elements down. Say you have a pointer to an array in C called lst which contains [1, 2, 3, 4]. The pointer lst points to the first element such that *lst is equivalent to lst[0]. Furthermore, *(lst+1) is equivalent to lst[1].
Assuming z is passed as a list (in the corresponding python code). The z += 3 can be translated to del z[:3] which moves the element 3 to 0.
However in python, you need to copy the array before doing that, because with the del-statement, the array gets modified.
In C you are able to access the elements before the pointed index through negative indices. This can be done with an "invisible" offset nested in a class. This offset is always added onto the index when the list is accessed.
The following class demonstrates the behavior.
class pointer_like:
def __init__(self, lst):
self.lst = lst; self.offset = 0
# self[index]
def __getitem__(self, index):
return self.lst[self.offset + index]
# self += offset
def __iadd__(self, offset):
self.offset += offset
# other member functions...
# as your example above
def somename(z):
z = pointer_like(z)
while (....):
a += z[0]
b += z[1]
c += z[2]
z += 3
>>> # other example
>>> z = pointer_like([0, 1, 2, 3, 4, 5])
>>> z[0]
0
>>> z += 3
>>> z[0]
3
>>>
>>> # with normal python lists, this would mean third last element
>>> z[-3]
0
>>> z += -5
>>> z[2]
0
>>>
>>> # this is special, because z.offset is negative (-2),
>>> # when a list item is accessed through a negative index,
>>> # it is counted from the end of the array in python.
>>> # In this case, it is -2, so the second last is accessed
>>> # In C this would cause undefined behavor, on most
>>> # platforms this causes an access violation
>>> z[0]
4
Note that pyhon also has a += operator for lists, but this allows to append another list at the end.

Categories