2016-10-26 3 views
2
>>> 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] 

У меня вопрос, когда я выполняю назначение для нескольких переменных для списка. как в примере выше, назначениеПроблема с множественным присвоением Python (список)

A[A[i] - 1], A[i] = A[i], A[A[i] - 1] 

отличается от присвоения

A[i], A[A[i] - 1] = A[A[i] - 1], A[i] 

Я действительно путают внутренний порядок вычисления в Python. Почему результаты разные? Каков наилучший способ выполнения такого рода множественных присвоений в одной строке?

+0

@Prune: Ни один из ответов там не обсуждает вид помех между назначениями, которые возникают в этом вопросе. – user2357112

+2

Я просто сломал его до той же логической последовательности и воспроизвел проблему пользователя. Короче говоря, я * * получил те же самые взаимодействия. – Prune

+0

@Prune: Вы видите что-то в тех ответах, что я нет? Ни один из них ничего не говорит о том, как присвоения и оценки чередуются в LHS, или как это может привести к тому, что задания мешают друг другу. – user2357112

ответ

0

Последовательность операций работает одинаково для любого множественного присваивания:

  1. Вычислить каждое выражение на RHS (правая сторона), слева направо, помещая каждый во временную переменную.
  2. Оцените выражения на LHS (слева) слева направо. Для каждого из них назначьте соответствующую временную переменную из RHS.

Для вашего кода, это расширяется:

# 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] 

Существенная разница, когда A [я] получает изменен в каждой последовательности. Во втором примере, когда мы оцениваем A [A [i] - 1] в окончательном присвоении, A [i] уже изменен на 1. Следовательно, это последнее присваивание помещает 4 в местоположение 0, а не в местоположение 3.

1

Как вам рекомендуется делать такие вещи, как a, b = b, a+b, вы можете подумать, что несколько назначений, подобных этому, должны всегда обходить проблемы с одним присваиванием, мешающим другому. В частности, естественно думать, что все оценки выражений происходят до любых назначений. К сожалению, это не так, как это работает.

Если у вас есть множественное присваивание формы

expr_a[expr_b], expr_c[expr_d] = expr_e, expr_f 

порядок событий выглядит следующим образом:

  1. Вычислить правую, оценивая expr_e и expr_f.
  2. Выполнение первого задания, оценивающего expr_a и expr_b.
  3. Выполнение третьего задания, оценивающее expr_c и expr_d. Первое присваивание уже произошло, когда эти выражения оцениваются.

Это означает, что если присваивание expr_a[expr_b] изменяет значение expr_c или expr_d, который изменит то, что происходит во втором задании.

В вашем случае присвоение A[i] изменяет значение A[i] - 1 в целевом назначении A[A[i] - 1].


Не используйте множественное назначение в таких случаях. Разделите присваивания на свои собственные строки и при необходимости используйте временные переменные, чтобы помнить значения, которые изменяются при назначении.

3

Per the documentation:

Python вычисляет выражения слева направо. Обратите внимание, что в то время как , оценивая назначение, правая часть оценивается до левой стороны .

Для получения более подробной информации см. this section. Хороший пример мозгового тизера, использующего такое поведение here.

Это означает, что правая часть = оценивается сначала, слева направо, а затем назначение выполняется с левой стороны, слева направо. Обязательно, скобки оцениваются наизнанку. Нарушение этого ступенчато вниз, вот первый пример:

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] 

А вот второй:

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] 

Отнесение к цели слева на левой стороне изменяет содержание A, который изменяет индексацию в правой части. 4 присваивается либо A[3], либо A[0], в зависимости от значения A[1] (который изменяется от 4 до 1) при вычислении индекса.

«Каков наилучший способ выполнения такого рода множественных присвоений в одной строке?» - Я сделаю все возможное, чтобы этого избежать. Я не могу представить ни одной ситуации, когда необходимо было бы назначить движущиеся цели таким образом.

+0

Если бы мы могли написать A.swap (i, A [i] -1), то однострочный мог бы сделать ... Может быть, причина, по которой нет такой функции swap, - это именно эта функция множественного присваивания ...как предполагается в https://www.reddit.com/r/Python/comments/3eh5p3/why_isnt_there_a_listswapi_j_function_built_in_to/ –

+0

@ aka.nice, тогда 'A [i] - 1' будет оценен до того, как функция будет вызвана, и будет зафиксирована на обе стороны назначения – jonrsharpe

+0

Да, неявное нажатие A [i] -1 в temp (или стеке) именно то, что заставит выражение работать симметрично A.swap (A [i] -1, i) будет иметь тот же эффект –

Смежные вопросы