Изменчивость списков, так как она связана с аргументом функции - PullRequest
0 голосов
/ 05 июня 2018

Я изучаю Python, используя книгу Лутца.Я использую Python 3.6.5 из дистрибутива Anaconda.Я исследовал эту проблему на SO и не нашел ни одной ветки, которая отвечает на мой вопрос. Изменчивость списков в python говорит о append, а не о том, как мы можем передать изменяемый объект в функцию.

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

В частности, я создал два списка L1 и L2.Для L1 я бы сделал назначение с использованием индекса, но для L2 я бы сделал прямое назначение внутри функции.

L1=[2]
L2=['a']
print("Before, L1:",L1)
print("Before, L2:",L2)
def f(a,b):
    a[0] =[3] #Using index-based assignment
    b = ['b'] #Direct assignment

#Pass L to f
f(L1,L2)
print("After, L1:",L1)
print("After, L2:",L2)

Вывод:

Before, L1: [2]
Before, L2: ['a']
After, L1: [[3]]
After, L2: ['a']

Asмы видим, что L1 изменилось, но не L2.

Вопрос: Может кто-нибудь объяснить, почему значение L2 не изменилось на'b'?

Если вы считаете, что этот пост является дубликатом, будет замечательно, если вы пометите соответствующий пост.


В качестве отдельного случая я провел небольшой эксперимент, чтобы выяснить,здесь есть какое-то отношение к индексному или прямому присваиванию.

l=[2]
id(l)
l[0] = 3 #Index assignment
id(l) # Memory location doesn't change

l = 3 # direct assignment
id(l) #Memory location changes.

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

1 Ответ

0 голосов
/ 05 июня 2018

Если мы немного изменим ваш код, мы можем использовать id, чтобы увидеть, как ссылки изменяются (или не изменяются):

L1=[2]
L2=['a']
print("Before, L1:", L1, id(L1))
print("Before, L2:", L2, id(L2))
def f(a,b):
    print("Inside, Before, a:", id(a))
    print("Inside, Before, b:", id(b))
    a[0] =[3] #Using index-based assignment
    b = ['b'] #Direct assignment
    print("Inside, After, a:", id(a))
    print("Inside, After, b:", id(b))

#Pass L to f
f(L1,L2)
print("After, L1:", L1, id(L1))
print("After, L2:", L2, id(L2))

Вывод:

Before, L1: [2]     1870498294152  # L1
Before, L2: ['a']   1870498294280  # L2
Inside, Before, a:  1870498294152  # L1
Inside, Before, b:  1870498294280  # L2
Inside, After, a:   1870498294152  # L1
Inside, After, b:   1870498239496  # Something different, not L2
After, L1: [[3]]    1870498294152  # L1
After, L2: ['a']    1870498294280  # L2

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

С a вы изменяете / изменяете a, но не пытаетесь переназначить ссылку.Все в порядке.

С b вы переназначаете ссылку.Это будет работать внутри функции (как показывает вызов печати «Inside, After, b:»), но это изменение не будет отражаться за пределами функции.b будет восстановлено для ссылки на исходный объект, ['a'].

Что касается , почему ...

означаетЯ не уверен, почему прямое назначение изменяет местоположение в памяти.

Внутри вашей функции a и b являются просто ссылками на объекты.Первоначально они ссылаются (на объекты, на которые ссылаются) L1 и L2 соответственно, потому что, вызывая f, вы передаете ссылки на эти объекты.

a[0] = [3] first dereferences a (илиL1 в данном случае), затем индекс [0] и устанавливает это значение.

Фактически, если вы посмотрите на id(a[0]) до и после этого вызова, то , что будетменять.a список литературы.Попробуйте:

print(id(a[0]))   # One thing
a[0] =[3] #Using index-based assignment
print(id(a[0]))   # Something different

Это нормально.При выходе из функции L1 по-прежнему ссылается на объект, на который ссылается функция, используя a, и его мутация с индексом 0 сохраняется.

При b = ['b'] вы переназначаете или перепривязываете b к новому объекту.Старый объект все еще существует (для последующего использования вне функции).

Наконец, я часто использую термин «ссылка», но Python - это не просто язык «передачи по ссылке», , а имена переменных связаны с объектами .Во втором случае вы перепривязываете b, теряя связь с первоначально указанным объектом L2 навсегда.

...