Перейти к содержимому
Теория · Списки · урок 4 из 11

Две переменные — один список

В теме о переменных имя было биркой на нитке: бирку привязывают к объекту, и она лишь указывает на него. К одному объекту можно привязать сколько угодно бирок. Вернёмся к поезду со списком и привяжем к нему вторую бирку.

Присваивание b = a не копирует список. Оно привязывает к тому же поезду вторую бирку, и изменение через одно имя видно через другое:

Python
a = [1, 2, 3]
b = a
b.append(4)
print(a)  # [1, 2, 3, 4]
print(len(b))  # 4

Давайте разберём по строкам:

  1. a = [1, 2, 3] строит список и привязывает к нему бирку a.
  2. b = a привязывает к тому же списку вторую бирку b. Новый список не появляется.
  3. b.append(4) прицепляет вагон к поезду, на котором висит b, — к тому же, на котором висит a.
  4. 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>. Заметка на каждом шаге говорит, один это объект или два.

ВживуюВторое имя и копияшаг 1 из 9 · рамок: 1

Создан один список, и к нему привязано имя a.

Следите, какие имена меняются вместе

is и ==

Оператор is проверяет тождество: ссылаются ли два имени на один и тот же объект. Оператор == сравнивает значения. Два разных списка с одинаковыми элементами равны, но не тождественны:

Python
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

Разберём пример:

  1. c = a[:] строит новый список: a == c даёт True, а a is c — False.
  2. c.append(4) меняет только копию, и a остаётся [1, 2, 3].
  3. list(a) и a.copy() тоже дают отдельные списки: is для них возвращает False.

Числа, строки и списки сравнивают через ==. Через is проверяют, один ли это объект, а ещё сравнивают с None: x is None, потому что объект None в программе один.

Три способа скопировать

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

  • a[:] — полный срез;
  • list(a) — функция list;
  • a.copy() — метод списка.

Изменения копии не трогают исходный список. Здесь аналогия с бирками упрощает: у настоящей бирки видно, куда тянется нитка. В коде a и b выглядят как две независимые переменные, и только is показывает, что объект у них один.

Список в функции

Параметр функции — ещё одна бирка на объекте, переданном в вызове. Если функция меняет список через параметр, изменение видно и снаружи:

Python
def add_bonus(scores):
    scores.append(10)


marks = [7, 8]
add_bonus(marks)
print(marks)  # [7, 8, 10]

Присваивание параметру, наоборот, список вызывающего кода не меняет: scores = [10] внутри функции перевязывает на новый список только локальное имя scores.

Этот вопрос часто задают на собеседовании: «Как Python передаёт аргументы в функцию?» Ответ:

  • функция получает не копию, а тот же объект: параметр — ещё одно имя для него;
  • изменение объекта через параметр видно вызывающему коду;
  • присваивание параметру перевязывает только локальное имя.

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

Частая ошибка

Функция ищет медиану — средний по величине элемент — и для этого сортирует свой аргумент:

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 и подпишись на канал — откроются все уроки.

Войти другим способом