Списки (list)

У нас часто возникает необходимость собрать много разных предметов вместе. Например мы собираем вещи в дорогу и удобнее всего сначала сделать список и потом уже идти по нему и перекладывать вещи в чемодан. Как эту особенность реального мира отразить в программировании? Для случаев когда надо объединить много объектов в последовательность в Python существуют списки.

Синтаксис записи списка выглядит так:

>>> hobbits = ["Бильбо", "Фродо", "Сэмуайз"]
>>> hobbits
['Бильбо', 'Фродо', 'Сэмуайз']
>>> type(hobbits)
<class 'list'>

Список можно создавать с помощью конструктора:

>>> list('артефакт')
['а', 'р', 'т', 'е', 'ф', 'а', 'к', 'т']
>>> list(['мечь', 'корона'])
['мечь', 'корона']

Для того чтобы обратиться к элементу списка используется тот же синтаксис срезов который используется и для работы со строками:

>>> hobbits[0]
'Бильбо'

Списки могут содержать элементы разного типа:

>>> notes = ["Галадриэль", "Дракон", 7]
>>> notes
['Галадриэль', 'Дракон', 7]
>>> type(notes[0]), notes[0]
(<class 'str'>, 'Галадриэль')
>>> type(notes[2]), notes[2]
(<class 'int'>, 7)

И даже другие списки:

>>> other = [hobbits, notes, "Гендальф"]
>>> other
[['Бильбо', 'Фродо', 'Сэмуайз'], ['Галадриэль', 'Дракон', 7], 'Гендальф']
>>> type(other[1])
<class 'list'>

Можно присвоить новое значение элементу списка по его индексу, так же можно и обратиться ко вложенному списку в список:

>>> other[2] = "Серый"
>>> other[1][0] = "Артанис"
>>> other
[['Бильбо', 'Фродо', 'Сэмуайз'], ['Артанис', 'Дракон', 7], 'Серый']

Стандартные правила работы со срезами работают со списками так же как со строками:

>>> other[0:2]
[['Бильбо', 'Фродо', 'Сэмуайз'], ['Артанис', 'Дракон', 7]]
>>> other[-1]
'Серый'

Для строк не так актуальна работа с присваиванием нового значения срезу, но для списков такая операция может быть полезной:

>>> hobbits[0:2] = ["Бильбо Бэггинс", "Фродо Бэггинс"]
>>> hobbits
['Бильбо Бэггинс', 'Фродо Бэггинс', 'Сэмуайз']

Списки мутабельные

Поскольку список other мы определили как ссылку на ту же область памяти на которую ссылается hobbits то после предыдущей операции other тоже изменится:

>>> other
[['Бильбо Бэггинс', 'Фродо Бэггинс', 'Сэмуайз'], ['Артанис', 'Дракон', 7], 'Серый']

Для удаления элементов есть несколько способов:

>>> del other[2]
>>> other
[['Бильбо Бэггинс', 'Фродо Бэггинс', 'Сэмуайз'], ['Артанис', 'Дракон', 7]]
>>> other.pop()
['Артанис', 'Дракон', 7]
>>> other
[['Бильбо Бэггинс', 'Фродо Бэггинс', 'Сэмуайз']]

Метод lst.pop(n) извлекает элемент под номером n и удаляет из списка.

Если нужно удалить какой-то конкретный элемент, то можно использовать remove который найдет первое вхождение элемента и удалит из списка:

>>> x = [1, 1, 2, 3, 3]
>>> x.remove(3)
>>> x
[1, 1, 2, 3]
>>> x.remove(3)
>>> x
[1, 1, 2]

Методы списка

У объекта списка тоже есть свои методы и свойства, как и у любого объекта в Python их можно посмотреть командой dir:

>>> dir([])
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Метод Описание
append() Добавить элемент в конце списка
clear() Полностью удалить все элементы из списка
copy() Вернуть копию списка
count(e) Вернуть количество вхождений e в список
extend(lst) Добавить в список все элементы списка lst
index(e) Вернуть позицию первого найденного элемента e
insert(index, e) Вставить в позицию index элемент e
pop(index) Удалить и вернуть элемент списка в позиции index
remove(e) Удалить из списка элемент e
reverse() Поменять порядок элементов на обратный
sort() Отсортировать элементы списка используя функцию

Сортировка списка

Помимо метода списка lst.sort() еще есть встроенная функция sorted(lst) они работают похожим образом. Но метод изменяет сам список, а функция возвращает новый список не изменяя старого:

>>> l = ['e', 'a', 'u', 'o', 'i']
>>> l.sort()
>>> l
['a', 'e', 'i', 'o', 'u']
>>> n = ['e', 'a', 'u', 'o', 'i']
>>> sorted(n)
['a', 'e', 'i', 'o', 'u']
>>> n
['e', 'a', 'u', 'o', 'i']

По умолчанию список будет сортироваться по возрастанию, но если надо сделать обратную сортировку, то можно указать ключ reverse=True:

>>> n = [7, 3, 6, 9, 5, 1]
>>> sorted(n, reverse=True)
[9, 7, 6, 5, 3, 1]
>>> n.sort(reverse=True)
>>> n
[9, 7, 6, 5, 3, 1]

Внутри функции сортировки происходит процесс сравнения элементов, проще всего представить, что операторами больше-меньше. И если элемент больше всех остальных то он будет в конце списка (или в начале если reverse=True). Но что делать если вам нужно сравнить элементы по вашему собственному правилу? Что если вы сравниваете предметы по объему? Для этих целей есть дополнительная возможность, можно указать дополнительный параметр key который будет ключом сортировки. Давайте попробуем создать список из разных переменных и отсортируем его не по алфавиту, а по длине:

>>> h = ['Бильбо Бэггинс', 'Мериадок', 'Фродо Бэггинс', 'Сэм']
>>> sorted(h, key=len)
['Сэм', 'Мериадок', 'Фродо Бэггинс', 'Бильбо Бэггинс']

Для того чтобы представить себе как этот код работает внутри представьте, что когда происходит сравнение больше или меньше, то функция которая передается в параметре key получает в качестве параметров объекты и потом происходит сравнение их размера.

>>> h.sort(key=len)
>>> h
['Сэм', 'Мериадок', 'Фродо Бэггинс', 'Бильбо Бэггинс']
>>> len(h[0]) < len(h[1]) < len(h[2]) < len(h[3])
True

Функция в качестве параметра

Мы пока еще не дошли до изучения функций и возможно вам покажется странным как это мы передали в параметрах одной функции другую. Смею вас заверить это нормально и мы обязательно разберемся с этой магией подробнее дальше.

List Comprehension и генераторы

Так получилось, что у термина List Comprehension нет устоявшегося перевода. Возможно дело в том, что в разных языках программирования такой формат работы со списками работает по разному. Но как и у многих вещей в программировании у этого явления есть научные корни. В теории множеств Цермело — Френкеля есть аксиома о подмножествах. На простой язык и без должного уважения к источнику ее можно перевести так: "Если есть множество, и есть операция которую можно применить к элементам этого множества, то можно создать новое множество".

Определение

Если же вам все таки хочется точное определение, то в Википедии оно звучит так:

Схему преобразования [множеств] можно сформулировать следующим образом: «Любое множество можно преобразовать в [то же самое или другое] множество dd, высказав любое истинное математически корректное функциональное суждение ϕ\phi обо всех элементах bb данного множества aa

То есть если к каждому элементу списка мы применим какую-то операцию, то получим новый список. И для этого есть специальная краткая форма записи в Python. Поэтому иногда термин list comprehension иногда переводят как генератор списков.

Давайте разберемся со всем подробно.

Краткая форма записи которая на основе списка позволяет создавать новый список выглядит так:

# Преобразование списка
[op(element) for element in list]
# Создание генератора списка
(op(element) for element in list)

Давайте посмотрим пример:

>>> x = [1, 2, 3, 4, 5]
>>> [i*2 for i in x]
[2, 4, 6, 8, 10]

Я создал список из пяти чисел, и потом создал генератор списков который записал в квадратных скобках, он читается так "на основе списка x создать новый список путем умножения каждого элемента списка x на 2".

Причем операция которая стоит до ключевого слова for может быть разной, главное чтобы она возвращала какой-то результат (даже None, тогда результирующий список будет состоять из множества None элементов).

Давайте рассмотрим еще несколько примеров:

>>> [i**2 for i in x]
[1, 4, 9, 16, 25]
>>> h = ['Сэм', 'Мериадок', 'Фродо Бэггинс', 'Бильбо Бэггинс']
>>> [len(i) for i in h]
[3, 8, 13, 14]
>>> [i.upper() for i in h]
['СЭМ', 'МЕРИАДОК', 'ФРОДО БЭГГИНС', 'БИЛЬБО БЭГГИНС']

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

Функция range

Есть одна из функций для работы со списками с которой я хочу познакомить отдельно. Это функция range она позволяет создавать последовательности чисел. В работе с реальными данными она встречается не очень часто, но очень удобна для демонстрации работы на этапе обучения. Синтаксис работы функции:

  • range(N) — создать последовательность целых чисел от 0 до N.
  • range(start, stop, step) — для тех случаев когда требуется более точный диапазон.

Функцию очень удобно использовать внутри генераторов чтобы получить список чисел:

>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Более подробно работу с этой функцией мы разберем когда будем обсуждать работу с последовательностями.