Две переменные — один список
В теме о переменных имя было биркой на нитке: бирку привязывают к объекту, и она лишь указывает на него. К одному объекту можно привязать сколько угодно бирок. Вернёмся к поезду со списком и привяжем к нему вторую бирку.
Присваивание b = a не копирует список. Оно привязывает к тому же поезду вторую бирку, и изменение через одно имя видно через другое:
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4]
print(len(b)) # 4Давайте разберём по строкам:
a = [1, 2, 3]строит список и привязывает к нему биркуa.b = aпривязывает к тому же списку вторую биркуb. Новый список не появляется.b.append(4)прицепляет вагон к поезду, на котором виситb, — к тому же, на котором виситa.print(a)печатает[1, 2, 3, 4], аlen(b)—4: обе бирки ведут к одному поезду.
В теме о переменных b = a с числом вело себя иначе: после a = 20 имя b по-прежнему ссылалось на 10. Там новое присваивание перевязывало на другой объект одну бирку. Здесь присваивания нет: b.append(4) меняет сам объект, а к нему привязаны обе бирки.
Чтобы получить отдельный список, нужна копия: a.copy() или полный срез a[:] строят второй поезд с теми же плитками. Нажмите «Следующий шаг» и следите за a, b и c в рамке <module>. Заметка на каждом шаге говорит, один это объект или два.
1a = [1, 2, 3]2b = a3b.append(4)4print(a)5c = a.copy()6c.append(5)7print(a)8print(c)9print(a is b, a is c)
Создан один список, и к нему привязано имя a.
is и ==
Оператор is проверяет тождество: ссылаются ли два имени на один и тот же объект. Оператор == сравнивает значения. Два разных списка с одинаковыми элементами равны, но не тождественны:
a = [1, 2, 3]
c = a[:]
print(a == c, a is c) # True False
c.append(4)
print(a, c) # [1, 2, 3] [1, 2, 3, 4]
d = list(a)
e = a.copy()
print(d == a, d is a, e is a) # True False FalseРазберём пример:
c = a[:]строит новый список:a == cдаётTrue, аa is c—False.c.append(4)меняет только копию, иaостаётся[1, 2, 3].list(a)иa.copy()тоже дают отдельные списки:isдля них возвращаетFalse.
Числа, строки и списки сравнивают через ==. Через is проверяют, один ли это объект, а ещё сравнивают с None: x is None, потому что объект None в программе один.
Три способа скопировать
Новый список с теми же элементами называют копией. Сделать её можно тремя способами, и результат у них одинаковый:
a[:]— полный срез;list(a)— функцияlist;a.copy()— метод списка.
Изменения копии не трогают исходный список. Здесь аналогия с бирками упрощает: у настоящей бирки видно, куда тянется нитка. В коде a и b выглядят как две независимые переменные, и только is показывает, что объект у них один.
Список в функции
Параметр функции — ещё одна бирка на объекте, переданном в вызове. Если функция меняет список через параметр, изменение видно и снаружи:
def add_bonus(scores):
scores.append(10)
marks = [7, 8]
add_bonus(marks)
print(marks) # [7, 8, 10]Присваивание параметру, наоборот, список вызывающего кода не меняет: scores = [10] внутри функции перевязывает на новый список только локальное имя scores.
Этот вопрос часто задают на собеседовании: «Как Python передаёт аргументы в функцию?» Ответ:
- функция получает не копию, а тот же объект: параметр — ещё одно имя для него;
- изменение объекта через параметр видно вызывающему коду;
- присваивание параметру перевязывает только локальное имя.
Иногда функцию пишут именно для того, чтобы изменить переданный список. Когда список вызывающего кода менять нельзя, функция работает с копией или строит новый список.
Частая ошибка
Функция ищет медиану — средний по величине элемент — и для этого сортирует свой аргумент:
def median(nums):
nums.sort() # ✗
return nums[len(nums) // 2]
data = [5, 1, 3]
print(median(data)) # 3
print(data) # [1, 3, 5]Медиана посчитана верно, но sort переставил элементы списка data. Сообщения об ошибке нет, и порядок сломается незаметно для следующего кода, которому нужен исходный data. Внутри функции нужна отсортированная копия: ordered = sorted(nums), а дальше работа с ordered.
Главное
b = aпривязывает к тому же списку второе имя: изменение черезbвидно черезa.a[:],list(a)иa.copy()создают отдельный список с теми же элементами.isпроверяет, один ли это объект, а==сравнивает значения.- Функция, которая меняет список через параметр, меняет список вызывающего кода.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.