问题
>>> i = 1
>>> A = [3,4,-1,1]
>>> A[A[i] - 1], A[i] = A[i], A[A[i] - 1]
>>> A
[3, 1, -1, 4]
>>> A = [3,4,-1,1]
>>> A[i], A[A[i] - 1] = A[A[i] - 1], A[i]
>>> A
[4, 1, -1, 1]
I have question when I do assignment for multiple variables for a list. Like the example above, the assignment
A[A[i] - 1], A[i] = A[i], A[A[i] - 1]
is different from the assignment
A[i], A[A[i] - 1] = A[A[i] - 1], A[i]
I am really confused the inside calculation order in Python. Why the results are different? What's the best way to do this kind of multiple assignment in one line?
回答1:
Per the documentation:
Python evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.
For more detail, see this section. There is a good example of a brain-teaser exploiting this behaviour here.
This means that the right-hand side of the =
is evaluated first, left-to-right, then the assignment is made to the left-hand side, left-to-right. Necessarily, the brackets are evaluated inside-out. Breaking that down stepwise, here's the first example:
i = 1
A = [3, 4, -1, 1]
A[A[i] - 1], A[i] = A[i], A[A[i] - 1]
= A[1], A[A[i] - 1]
= 4, A[A[i] - 1]
= 4, A[A[1] - 1]
= 4, A[4 - 1]
= 4, A[3]
= 4, 1
A[A[i] - 1], A[i] = 4, 1
A[A[1] - 1], A[i] = 4, 1
A[4 - 1], A[i] = 4, 1
A[3], A[i] = 4, 1 # A becomes [3, 4, -1, 4]
A[i] = 1
A[1] = 1 # A becomes [3, 1, -1, 4]
And here's the second:
i = 1
A = [3, 4, -1, 1]
A[i], A[A[i] - 1] = A[A[i] - 1], A[i]
= A[A[1] - 1], A[i]
= A[4 - 1], A[i]
= A[3], A[i]
= 1, A[i]
= 1, A[1]
= 1, 4
A[i], A[A[i] - 1] = 1, 4
A[1], A[A[i] - 1] = 1, 4 # A becomes [3, 1, -1, 1]
A[A[1] - 1] = 4
A[1 - 1] = 4
A[0] = 4 # A becomes [4, 1, -1, 1]
The assignment to the left-hand target on the left-hand side alters the content of A
, which changes the indexing in the right-hand target. The 4
is assigned to either A[3]
or A[0]
, depending on the value of A[1]
(which changes from 4
to 1
) when the index is calculated.
"What's the best way to do this kind of multiple assignment in one line?" - I'd do my very best to avoid it. I can't think of any situation where it would be necessary to assign to moving targets like this.
回答2:
With how you're encouraged to do things like a, b = b, a+b
, you might think that multiple assignments like this are supposed to always bypass problems with one assignment interfering with another. Particularly, it's natural to think that all expression evaluations happen before any assignments. Unfortunately, that's not how it works.
When you have a multiple assignment of the form
expr_a[expr_b], expr_c[expr_d] = expr_e, expr_f
the order of events goes as follows:
- Evaluate the right-hand side, evaluating
expr_e
andexpr_f
. - Perform the first assignment, evaluating
expr_a
andexpr_b
. - Perform the third assignment, evaluating
expr_c
andexpr_d
. The first assignment has already happened when these expressions are evaluated.
That means that if assigning to expr_a[expr_b]
changes the value of expr_c
or expr_d
, that will change what happens in the second assignment.
In your case, assigning to A[i]
changes the value of A[i] - 1
in the assignment target A[A[i] - 1]
.
Don't use a multiple assignment in cases like this. Separate the assignments onto their own lines, and use temporary variables if necessary to remember values that get changed by assignments.
回答3:
The sequence of operations works the same for any multiple assignment:
- Evaluate every expression on the RHS (right-hand side), left to right, placing each into a temporary variable.
- Evaluate the expressions on the LHS (left), left to right. For each of these, assign the corresponding temporary variable from the RHS.
For your code, this expands to:
# A[A[i] - 1], A[i] = A[i], A[A[i] - 1]
A = [3,4,-1,1]
t1 = A[i]
t2 = A[A[i] - 1]
# t1 = 4, t2 = 1
A[A[i] - 1] = t1
A[i] = t2
print A
# Result: [3, 1, -1, 4]
# A[i], A[A[i] - 1] = A[A[i] - 1], A[i]
A = [3,4,-1,1]
t2 = A[A[i] - 1]
t1 = A[i]
# As before, t1 = 4, t2 = 1
A[i] = t2
# A[i] is now 1 !
A[A[i] - 1] = t1
print A
# Result: [4, 1, -1, 1]
The critical difference is when A[i] gets changed in each sequence. IN the second example, when we evaluate A[A[i] - 1] in the final assignment, A[i] has already changed to 1. Therefore, this last assignment puts 4 into location 0, rather than location 3.
来源:https://stackoverflow.com/questions/40272594/python-multiple-assignment-issue-list