Will
a, a = 2, 1
always result in a equal to 1? In other words, is tuple assignment guaranteed to be left-to-right?
The matter becomes relevant when we don't have just a, but a[i], a[j] and i and j may or may not be equal.
Yes, it is part of the python language reference that tuple assignment must take place left to right.
https://docs.python.org/2.3/ref/assignment.html
An assignment statement evaluates the expression list (remember that
this can be a single expression or a comma-separated list, the latter
yielding a tuple) and assigns the single resulting object to each of
the target lists, from left to right.
So all Python implementations should follow this rule (as confirmed by the experiments in the other answer).
Personally, I would still be hesitant to use this as it seems unclear to a future reader of the code.
How it works :
a, a = 2, 1
--> a does not exist, create variable a and set value to 2
--> a already exists, value of a changed to 1
When you have different variables, it works exactly the same way :
a, b, a = 1, 2, 3
--> a does not exist, create variable a and set value to 1
--> b does not exist, create variable b and set value to 2
--> a already exists, value of a changed to 3
Related
This question already has answers here:
List assignment to other list
(3 answers)
Closed 5 years ago.
I'm trying to understand (on the level of principle) the difference between assignment between (say) integers variables and list variables.
Integer case:
a=6
b=a
print(b) #prints 6
a=7
print(b) #prints 6
That makes sense to me with this logic: in the original b=a, b was given the same value as a (6), not identified with a. So if I change the value of a, the value of b does not change: b is not the same thing as a.
List case:
L=[1,2]
M = L
print(M)
L.append(6)
print(M)
This can make sense with this logic: in M=L I'm forcing M to literally be the same object as L is, I'm identifying it with L. So if L changes, so does M.
What doesn't make sense (to me) is why I need different logic in different cases. (Probably this has to do with integers being "immutable" and lists "mutable" but I don't see how that bears on this.) Can someone point me to a explanation? What is the principle behind the difference in behaviors? (I'm not looking so much for how the technical difference in implementation of integers and lists leads to the difference, but for the reason things were designed this way. What's the logic?)
Every name is a variable holding a reference to some object.
What happens in the first case is that
a = 6
b = a # (a and b point to the same object)
But here, you are changing what a points to:
a = 7
Compare this to the second/list situation, where you actually call the method on the first object. You didn't update the reference as you did in the case with the integers.
L = [1,2]
M = L # here you introduce a new name, referencing the same object as L.
L.append(6) # update that same object.
print(M) # you print that same object
You don't have different logic in different cases here. Lists and integers work in exactly the same way as far as assignment is concerned. If, in your second snippet, to assigned a different list to L in the penultimate line, the two variables would be unrelated.
But lists have an additional capability, which integers and strings don't have, which is that you can modify them. That's all you're seeing here.
Well M = L gets what L currently is and after it prints M you then append 6 to L but this will only effect L because M had only received what L previously was, but if you did M = L again after the append it would print the updated version of the list. Basically if you get a xvariable set to a yvariable and then that yvariable updates the xvariable will not update because you will have to update the xvariable again but this is usually this happens by it self if a loop is being used
This question already has answers here:
Scope of python variable in for loop
(10 answers)
Closed 5 months ago.
Is it guaranteed that the iteration variable will always follow the original sequence no matter how you modify the variable itself?
The doc doesn't mention anything about this.
I've seen similar questions elsewhere but none of them gives authoritative answers. I've tested 100 times and it worked 100 times but I'm still not sure whether this is guaranteed. So please give some references.
Yes. It's totally safe to assign to the loop variable(s) in a for loop.
From The for statement docs:
The for-loop makes assignments to the variables(s) in the target list.
This overwrites all previous assignments to those variables including
those made in the suite of the for-loop:
for i in range(10):
print(i)
i = 5 # this will not affect the for-loop
# because i will be overwritten with the next
# index in the range
This information is also available via help('for')
I agree with PM 2Ring's answer, but you ask if it is guaranteed the loop will "follow the original sequence". For an iterator defined by a generator function, this cannot be guaranteed, because the sequence may be altered in between iterations; and it is possible to alter the way a generator works by altering a mutable element in the sequence.
For example:
def strange_iter():
x = {'value': 1}
while x['value'] < 30:
x = {'value': x['value'] + 1}
yield x
for d in strange_iter():
print(d['value'])
if d['value'] == 10:
d['value'] = 15 # loop will skip from 10 to 16
I'm not sure if such generators would have any use in practice or are best considered a bad practice to be avoided.
The loop will use the original sequence, for example:
a = [1, 2,3 ]
for index,number in enumerate(a):
print(a[index])
index = 0
number = 0
Output:
1
2
3
I think the syntax
for item in stuff:
...
creates an iterator by calling iter(stuff), then values are extracted by calling next() on that iterator.
Short answer, It depends....
It depends on what you meant by safe. Technically there is nothing wrong modifying the variable used for iteration. When you iterate over a list or something, you're actually iterating over an iterator. It returns the value contained in the container (list, dict whatever)...
If you modify the variable returned by the iterator, you have to keep in mind about the mutability of the object you're trying to modify.
If you iterate over integers or strings, you're not actually modifying the variable but affecting a new value to the variable name.. Thus, you're not modifying the value contained in the container.
But if you're iterating over a container with mutable objects (let say dicts).. Modifying the variable by changing the content, will have an effect on the value contained in the container since they are the same values.
But doing something like this would have no effect at all on the value in the container as you're not modifying the value pointed by the variable name but changing to which value the variable name points to:
a = [{}, {}, {}]
for x in a:
x = {'val': x}
While this will:
a = [{}, {}, {}]
for x in a:
x['v'] = 1
Is it legal to do
a = b = 3
in python? If so, is it a bad practice?
Yes, it is legal to do so. No, it is not bad practice.
Just take into account that the right-hand side, the value expression, is evaluated first, and assignment then takes place from left to right; 3 is assigned to a first, then to b.
From the assignment statement documentation:
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
You assign the same value to all targets. That means that each variable refers to one value only. This is important when that value is mutable, like a list or a dictionary.
Yes, just watch out for stuff like this:
a = b = []
a.append(2)
print a
print b
Prints:
[2]
[2]
But other than that, it's fine. #Martijn has a lot of information in his answer, so check it out :).
I am a beginner in python and I find this about mutabilty quite confusing and non intuitive. Given a list:
lst = [[1, 2, 3], [4, 5, 6]]
And trying to change the list within a for-loop.
for i in lst:
i = "test"
I understand that this does not change the list. But:
for i in lst:
i[1] = "test"
I was surprised that referring to the sublist led to following outcome:
[[1, 'test', 3], [4, 'test', 6]]
I tried to understand with a visualizer but I don't get it. Would anybody please explain this in plain words? Thank you.
In the first case, you simply have a copied reference to the element.
i ---> lst[n]
In the latter case, you are dereferencing the reference, and changing data (not in a copy):
i[i] ---> lst[n][i]
Therefore assigning to i[n] will point to the actual mutable object, not a copy of it.
Assignment (which is what the = operator does) assigns a value to a name.
i is a variable name in the local namespace. At the time it runs inside the for loop, it refers to a list. By assigning a string to it, you cause the name to no longer refer to a list, but instead refer to the string. The list is unaffected -- the only thing that changes is the value associated with the name.
i[1] is a name that specifies a specific location inside one of the two lists, depending on what i is set to at the time. By assigning a string to it, you cause the name to no longer refer to the object that previously occupied that space inside the list (an integer, 2 or 5 depending on the list) and instead refer to the string. The integer is unaffected -- the only thing that changes is the value associated with the name.
So in each case, assignment is doing the same thing -- it's making a name refer to a new thing instead of the old thing it referred to. The difference is that the second case is a special name in that it refers to a property of a mutable object.
for does not make copies of each element it yields. As such, the yielded object retains all the properties of the original (since it is the original), including mutability.
since for loop in case of string is called in different way
like
for i in lst:
it means if lst is list or array of string then i is referenced to the value of x[i] directly that is why in first case result was "test " two times because length of "lst" was just 2
while in second case i[1] means
i ------lst[i]
i[1]--------`-lst[i][1] means
first value equal lst[0][1]---[1,'test',3]
second value equal lst[1][1]--[4,'test',6]
I was reading the assignment statements in the Python docs ( http://docs.python.org/reference/simple_stmts.html#assignment-statements).
In that it is quoted that:
If the target is a target list enclosed in parentheses or in square brackets: The object must be an iterable with the same number of items as there are targets in the target list, and its items are assigned, from left to right, to the corresponding targets.
After reading it, I thought of writing a sample like this:
a = 5
b = 4
a, b = a + b, a
print a, b
My assumption was that a and b both should have the value of 9.
However, I am proven wrong. 'a' has the value of 9 and 'b' has the value of 5.
Can some one help me understand this better? Why the older value of 'a' is assigned rather than the new one? As per the docs, a's value will be assigned first right? Am I missing something?
All the expressions to the right of the assignment operator are evaluated before any of the assignments are made.
From the Python tutorial: First steps towards programming:
The first line contains a multiple assignment: the variables a and b simultaneously get the new values 0 and 1. On the last line this is used again, demonstrating that the expressions on the right-hand side are all evaluated first before any of the assignments take place. The right-hand side expressions are evaluated from the left to the right.
Emphasis mine.
Your code is functionally equivalent to the following:
a, b = 5 + 4, 5
print a, b
Python does not have a "comma operator" as in C. Instead, the comma indicates that a tuple should be constructed. The right-hand side of
a, b = a + b, a
is a tuple with th two items a + b and a.
On the left-hand side of an assignment, the comma indicates that sequence unpacking should be performed according to the rules you quoted: a will be assigned the first element of the tuple, b the second.
You can think of the assignments happening in parallel on copies rather than sequentially and in-place.
This is why in python you dont need a swap function:
a, b = b, a
works sufficiently without requiring a temp variable, c.