Подтвердить что ты не робот

Изменение порядка сортировки набора кортежей

Я думаю, что они идентичны.

nums = [1, 2, 0]    
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]    
print nums  # [2, 1, 0]

nums = [1, 2, 0]    
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]    
print nums  # [2, 2, 1] 

Но результаты разные.
Почему результаты разные? (почему это результат второй?)

4b9b3361

Ответ 1

Предпосылки - 2 важных пункта


Введение

когда вы выполняете a,b = c,d, сначала сохраняются значения c и d. Затем, начиная с левой стороны, значение a сначала изменяется на c, а затем значение b изменяется на d.

Ловушка здесь заключается в том, что если есть какие-либо побочные эффекты для местоположения b при изменении значения a, то d назначается более позднему b, что соответствует b побочным эффектом a.


Использовать регистр

Теперь приступим к вашей проблеме

В первом случае

nums = [1, 2, 0]    
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]    

nums[0] изначально 1, а nums[nums[0]] - 2, потому что он оценивает nums[1]. Следовательно, 1,2 теперь хранится в памяти.

Теперь распаковка пакетов происходит с левой стороны, поэтому

nums[nums[0]] = nums[1] = 1   # NO side Effect. 
nums[0] = 2

поэтому print nums напечатает [2, 1, 0]

Однако в этом случае

nums = [1, 2, 0]   
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]    

nums[nums[0]], nums[0] помещает 2,1 в стек, как и в первом случае.

Однако с левой стороны, то есть nums[0], nums[nums[0]], изменение nums[0] имеет побочный эффект, поскольку он используется как индекс в nums[nums[0]]. Таким образом,

nums[0] = 2
nums[nums[0]] = nums[2] = 1  # NOTE THAT nums[0] HAS CHANGED

nums[1] остается неизменным при значении 2. поэтому print nums будет печатать [2, 2, 1]

Ответ 2

Вы можете определить класс для отслеживания процесса:

class MyList(list):
    def __getitem__(self, key):
        print('get ' + str(key))
        return super(MyList, self).__getitem__(key)
    def __setitem__(self, key, value):
        print('set ' + str(key) + ', ' + str(value))
        return super(MyList, self).__setitem__(key, value)

Для первого метода:

nums = MyList([1, 2, 0])
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]

вывод:

get 0
get 0
get 1
get 0
set 1, 1
set 0, 2

В то время как второй метод:

nums = MyList([1, 2, 0])
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]

вывод:

get 0
get 1
get 0
set 0, 2
get 0
set 2, 1

В обоих методах первые три строки относятся к генерации кортежей, а последние три строки относятся к назначениям. Кортеж правой стороны первого метода (1, 2), а второй метод (2, 1).

На этапе назначения первый метод получает nums[0], который равен 1, и устанавливает nums[1] = 1, затем nums[0] = 2, второй метод присваивает nums[0] = 2, затем получает nums[0], который равен 2, и наконец, установите nums[2] = 1.

Ответ 3

Это из-за того, что приоритет назначения python оставлен вправо. Так в следующем коде:

 nums = [1, 2, 0]
 nums[nums[0]], nums[0] = nums[0], nums[nums[0]]

Сначала он назначил nums[0] to nums[nums[0]] означает nums[1]==1, а затем, поскольку списки являются изменяемыми объектами, число должно быть:

[1,1,0]

а затем nums[nums[0]] будет присвоено значение nums[0], что означает nums[0]==2 и:

nums = [2,1,0]

И, как и для второй части.

Обратите внимание, что здесь важно, что объекты списка изменяемы, а когда вы меняете его в сегменте кода, это может быть изменено на месте. таким образом, это повлияет на остальную часть кода.

Порядок оценки

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

Ответ 4

В первом примере происходит то, что nums [1] получает значение 1, а затем nums [0] получает значение 2, как вы могли ожидать.

Во втором примере nums [0] получает значение 2, а затем nums [2] получает значение 1. Это связано с тем, что в этом случае левая сторона nums [nums [0]] действительно ссылается на nums [2], когда назначение происходит, потому что nums [0] только что был установлен в 2.