Python: неожиданное поведение, когда аргумент функции реплицируется список списков

#python #list #function #arguments

#python #Список #функция #аргументы

Вопрос:

Списки, будучи изменяемыми объектами при вызове, передают ссылку (которая, если я правильно понимаю, по сути, передает идентификаторы). В приведенном ниже коде я назначаю список списков A. Я вызываю его в функцию с именем modify(M), которая изменяет M. Как и ожидалось, A и M имеют одинаковые идентификаторы, а изменение M также изменяет A. Пока все хорошо.

Однако затем я переназначаю A в исходное значение (как и ожидалось, изменяя его идентификатор). Затем я вызываю modify(A * 1), т.Е. передаю в качестве аргумента реплицированный A. Как и ожидалось, теперь A и M имеют разные идентификаторы, а M — возвращает False . Однако изменение M все равно изменяет A, вопреки моим ожиданиям, что этого не произойдет. Почему это?

Примечание: вызов modify(A[:]) также приводит к такому же неожиданному (для меня) поведению.

Я знаю, что ответ связан с тем, что у меня есть список списков. (Когда я пытаюсь это сделать только с одноуровневыми списками, я получаю поведение, которое я ожидал.)

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

Вот код:

 def modify(M):
    print(M is A)
    print('id of M', id(M))
    print('id of A', id(A))
    print('Original list of lists before change in M =', A)
    M[0][0]=M[1][1] #some modification of M
    print('Original list of lists after change in M =', A)
    print(M is A)
    print('id of M', id(M))
    print('id of A', id(A))

A=[[1,2],[3,42]]
modify(A)    #Argument is the list 
print("n","*****What happens below is driving me crazy!*****","n")
A=[[1,2],[3,42]]
modify(A*1)  #Argument is replicated list of lists
#modify(A[:]) has the same behavior
  

РЕДАКТИРОВАТЬ: я только что сделал открытие, которое не отвечает на вопрос для меня, но, вероятно, отвечает на вопрос для кого-то более сведущего в Python, чем я.

Для этого требуется импортировать копию.

  1. Заменить modify(A*1) на modify(copy.copy(A)) . Наблюдается точно такое же странное поведение. Другими словами, идентификаторы становятся другими, но A изменяется при изменении M .
  2. Заменить modify(A*1) на modify(deepcopy.copy(A)) . Происходит то, что я ожидал. Другими словами, идентификаторы становятся другими, и A не меняется при изменении M .

Комментарии:

1. Происходит ли то же самое, если вы используете неизменяемые структуры данных в качестве аргументов (например, кортеж)

2. Хороший вопрос. Конечно, код вылетает, если A присваивается как кортеж кортежей. Но если вы назначаете A в качестве кортежа списков A=([1,2],[3,42]), A снова меняется при изменении M, но теперь A и M имеют одинаковые идентификаторы

3. Да, я имел в виду кортеж кортежей

4. Для кортежа кортежей вы пытаетесь изменить кортеж, и вы получаете TypeError: File «L:__Transfertest0008.py «, строка 13, в modify M[0][0]=M[1][1] #некоторая модификация M TypeError: объект ‘tuple’ не поддерживает назначение элемента

5. Да, вам придется создавать копии объектов

Ответ №1:

Хорошо, я нашел ответ на свой вопрос, который был:

Затем я вызываю modify(A * 1), т.Е. передаю в качестве аргумента реплицированный A. Как и ожидалось, теперь A и M имеют разные идентификаторы, а M — возвращает False . Однако изменение M все равно изменяет A, вопреки моим ожиданиям, что этого не произойдет. Почему это?

Ответ: Список списков имеет идентификатор. Но также элементы списков второго уровня имеют свои собственные идентификаторы. Я отредактировал код, чтобы также печатать идентификаторы элементов второго уровня M [0] [0] и M [1] [1]:

 def modify(M):
    print(M is A)
    print('id of M and list items', id(M), id(M[0][0]), id(M[1][1]))
    print('id of A and list items', id(A), id(A[0][0]), id(A[1][1]))
    print('Original list of lists before change in M =', A)
    M[0][0]=M[1][1] #some modification of M`
    print('Original list of lists after change in M =', A)
    print(M is A)
    print('id of M and list items', id(M), id(M[0][0]), id(M[1][1]))
    print('id of A and list items', id(A), id(A[0][0]), id(A[1][1]))

A=[[1,2],[3,42]]
modify(A)    #Argument is the list 
print("n","*****What happens below is driving me crazy!*****","n")
A=[[1,2],[3,42]]
modify(A*1)  #Argument is replicated list of lists
#modify(A[:]) has the same behavior`
  

И вот распечатка:

Истинный
идентификатор M и элементов списка 2436965208832 140725589391136 140725589392448
идентификатор A и элементов списка 2436965208832 140725589391136 140725589392448
Исходный список списков до изменения в M = [[1, 2], [3, 42]]
Исходный список списков после изменения в M = [[42, 2], [3,42]]
Истинный
идентификатор M и элементов списка 2436965208832 140725589392448 140725589392448
идентификатор A и элементов списка 2436965208832 140725589392448 140725589392448

То, что происходит ниже, сводит меня с ума!

Ложь
идентификатор M и элементов списка 2436965208832 140725589391136
140725589392448 идентификатор A и элементов списка 2436956205440 140725589391136 140725589392448

Исходный список списков до изменения в M = [[1, 2], [3, 42]]
Исходный список списков после изменения в M = [[42, 2], [3, 42]]
Ложный
идентификатор M и элементов списка 2436965208832 140725589392448 140725589392448
идентификатор A и элементов списка2436956205440 140725589392448 140725589392448

Первый вызов modify(A) , как и ожидалось, привел к M, который не только имеет тот же идентификатор, что и A, но и все его элементы имеют тот же идентификатор, что и их аналоги в A. Однако второй вызов modify(A*1) выдает M, который имеет идентификатор, отличный от A, но идентификатор элементов M[0][0] и M[1][1] остаются такими же, как у A[0][0] и A[1][1] соответственно.Вот почему присвоение нового значения M[0][0] присваивает то же значение A[0][0] . Вот почему наблюдалось странное поведение.

Извлеченный урок: два списка с разными идентификаторами могут иметь компоненты, которые совместно используют адрес памяти. Таким list1 is list2 образом, разрешение as False не гарантирует, что изменения в компонентах list1 не изменят соответствующие компоненты list2. По какой-то причине команда replicate * 1 изменяет идентификаторы списка, но не элементов второго уровня.