Шаг 62.
Основы языка Python.
Списки, кортежи, множества и диапазоны. Создание списка

    На этом шаге мы рассмотрим способы создания списка.

    Создать список можно следующими способами:

    В некоторых языках программирования (например, в РНР) можно добавить элемент, указав пустые квадратные скобки или индекс больше последнего индекса. В языке Python все эти способы приведут к ошибке:

>>> arr = []
>>> arr[] = 10
SyntaxError: invalid syntax

    При создании списка в переменной сохраняется ссылка на объект, а не сам объект. Это обязательно следует учитывать при групповом присваивании. Групповое присваивание можно использовать для чисел и строк, но для списков этого делать нельзя. Рассмотрим пример:

>>> х = y = [1, 2] # Якобы создали два объекта
>>> х, y
([1, 2], [1, 2])

    В этом примере мы создали список из двух элементов и присвоили значение переменным х и у. Теперь попробуем изменить значение в переменной у:

>>> y[1] = 100 # Изменяем второй элемент
>>> х, y # Изменилось значение сразу в двух переменных
([1, 100], [1, 100])

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

>>> х, y = [1, 2], [1, 2]
>>> y[1] = 100 # Изменяем второй элемент
>>> х, y
([1, 2], [1, 100])

    Точно такая же ситуация возникает при использовании оператора повторения *. Например, в следующей инструкции производится попытка создания двух вложенных списков с помощью оператора *:

>>> arr =[[]] * 2 # Якобы создали два вложенных списа
>>> arr
[[], []]
>>> arr[0].append(5) # Добавляем элемент
>>> arr # Изменились два элемента
[[5], [5]]

    Создавать вложенные списки следует с помощью метода append() внутри цикла:

>>> arr = []
>>> for i in range(2): arr.append([])

>>> arr 
[[], []]
>>> arr[0].append(5); arr
[[5], []]

    Можно также воспользоваться генераторами списков:

>>> arr = [[] for i in range(2)]
>>> arr
[[], []]
>>> arr[0].append(5); arr
[[5], []]

    Проверить, ссылаются ли две переменные на один и тот же объект, позволяет оператор is. Если переменные ссылаются на один и тот же объект, то оператор is возвращает значение True:

>>> х = y = [1, 2]   # Неправильно
>>> х is y # Переменные содержат ссыпку на один и тот же список
True
>>> х, y = [1, 2], [1, 2] # Правильно
>>> х is y	 # Это разные объекты
False

    Но что же делать, если необходимо создать копию списка? Первый способ заключается в применении операции извлечения среза, второй - в использовании функции list(), а третий - в применении появившегося в Python 3.3 метода сору().

>>> x = [1, 2, 3, 4, 5] # Создали список
>>> # Создаем копию списка
>>> y = list(x) # или с помощью среза: y = x[:]
>>>             # или вызовом метода copy(): y = x.copy()
            
>>> y
[1, 2, 3, 4, 5]
>>> x is y # Оператор показывает, что это разные объекты
False
>>> y[1] = 100 # Изменяем второй элемент
>>> x, y
([1, 2, 3, 4, 5], [1, 100, 3, 4, 5])

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

>>> x = [1, [2, 3, 4, 5]] # Создали вложенный список
>>> y = list(x) # Якобы сделали копию списка
>>> x is y # Разные объекты
False
>>> y[1][1] = 100 # Изменяем элемент
>>> x, y # Изменение затронуло переменную x!
([1, [2, 100, 4, 5]], [1, [2, 100, 4, 5]])

    В этом примере мы создали список, в котором второй элемент является вложенным списком. Далее с помощью функции list() попытались создать копию списка. Как и в предыдущем примере, оператор is показывает, что это разные объекты, но посмотрите на результат. Изменение переменной у затронуло и значение переменной х. Таким образом, функция list() и операция извлечения среза создают лишь поверхностную копию списка.

    Чтобы получить полную копию списка, следует воспользоваться функцией deepcopy() из модуля сору:

>>> import copy # Подключаем модуль copy
>>> x = [1, [2, 3, 4, 5]]
>>> y = copy.deepcopy(x) # Делаем полную копию списка
>>> y[1][1] = 100 # Изменяем второй элемент
>>> x, y # Изменился только список в переменной y
([1, [2, 3, 4, 5]], [1, [2, 100, 4, 5]])

    Функция deepcopy() создает копию каждого объекта, при этом сохраняя внутреннюю структуру списка. Иными словами, если в списке существуют два элемента, ссылающиеся на один объект, то будет создана копия объекта, и элементы будут ссылаться на этот новый объект, а не на разные объекты. Пример:

>>> import copy # Подключаем модуль copy
>>> x = [1, 2]
>>> y = [x, x] # Два элемента ссылаются на один объект
>>> y
[[1, 2], [1, 2]]
>>> z = copy.deepcopy(y) # Сделали копию списка
>>> z[0] is x, z[1] is x, z[0] is z[1]
(False, False, True)
>>> z[0][0] = 300 # Изменили один элемент
>>> z # Значение изменилось сразу в двух элементах!
[[300, 2], [300, 2]]
>>> x # начальный список не изменился
[1, 2]

    На следующем шаге мы рассмотрим операции над списками.




Предыдущий шаг Содержание Следующий шаг