Python 3. Списки
Свойства, индексация и срезы списков
Я уже писал про последовательности и множества
в Python 3. Список (list) — это изменяемая упорядоченная последовательность объектов. Давайте разберём это определение по частям:
- изменяемая — после создания список можно менять: добавлять, удалять и заменять элементы;
- упорядоченная — элементы хранятся в том порядке, в котором мы их добавили, и у каждого есть свой номер (индекс);
- последовательность объектов — в списке можно хранить объекты любых типов, причём в одном списке могут лежать объекты разных типов.
Списки в Python 3 записываются в квадратных скобках, элементы разделяются запятыми. Пустой список можно создать двумя способами: квадратными скобками или функцией list(). Функция list() ещё умеет превращать другие последовательности в список.
Код:
empty_1 = []
empty_2 = list()
letters = list('abc') # из строки
numbers = list(range(5)) # из диапазона
print(empty_1, empty_2)
print(letters)
print(numbers)
Исполнение:
[] []
['a', 'b', 'c']
[0, 1, 2, 3, 4]
Python — язык с динамической типизацией: тип определяется у объекта, а не у переменной, и проверяется только в момент выполнения кода. Поэтому, если, например, мы захотим из каждого элемента списка отнять 1, а там окажется строка, то ошибка появится не заранее, а только когда программа дойдёт до этой строки.
Индексация
Список, так же как и строка, нумерует свои элементы. Нумерация идёт с нуля. Кроме того, можно считать с конца: последний элемент имеет индекс -1, предпоследний -2 и так далее:

Индексация позволяет обращаться к объектам в списке по индексу (положительному или отрицательному), а также создавать срезы.
Получение некоторых свойств списка и обращение по индексам показано в этом примере:
my_list = [1, 2, 3, 4, 'пять']
print('Тип объекта -', type(my_list))
print('Длина списка -', len(my_list))
print('Первый элемент -', my_list[0]) # Обращение по индексу
print('Последний элемент -', my_list[-1]) # Обращение по отрицательному индексу
Исполнение:
Тип объекта - <class 'list'>
Длина списка - 5
Первый элемент - 1
Последний элемент - пять
Если обратиться к индексу, которого нет в списке, то Python выдаст ошибку IndexError: list index out of range. Запомните: в списке из 5 элементов последний положительный индекс — это 4, а не 5.
Срезы
Срез позволяет получить из списка сразу несколько элементов. Записывается он так: список[старт:стоп:шаг]. Запомните, что элемент с индексом старт входит в срез, а элемент с индексом стоп — не входит. Шаг указывать необязательно, по умолчанию он равен 1. Если не указать старт, то срез начнётся с начала списка, а если не указать стоп — то закончится в конце.
Код:
my_list = [1, 2, 3, 4, 'пять']
print('Срез [1:4] - ', my_list[1:4])
print('Срез [0:5:2] - ', my_list[0:5:2])
print('Срез [:2] - ', my_list[:2])
print('Срез [3:] - ', my_list[3:])
print('Срез [-2:] - ', my_list[-2:])
print('Срез [::-1] - ', my_list[::-1]) # Список в обратном порядке
Исполнение:
Срез [1:4] - [2, 3, 4]
Срез [0:5:2] - [1, 3, 'пять']
Срез [:2] - [1, 2]
Срез [3:] - [4, 'пять']
Срез [-2:] - [4, 'пять']
Срез [::-1] - ['пять', 4, 3, 2, 1]
Срез всегда создаёт новый список, а исходный остаётся без изменений. В отличие от обращения по одному индексу, срез не выдаёт ошибку, если вышел за границы списка, он просто вернёт то, что есть.
Изменяемость списков
Так как список — это изменяемый тип данных, то мы можем изменить его, не создавая новый объект. Давайте это проверим с помощью функции id(), которая возвращает идентификатор объекта в памяти.
Код:
my_list = [1, 2, 3, 4, 'пять']
print(my_list, ' id =', id(my_list))
my_list[4] = 5
print(my_list, ' id =', id(my_list))
Исполнение:
[1, 2, 3, 4, 'пять'] id = 2308488126592
[1, 2, 3, 4, 5] id = 2308488126592
Из кода видно, что мы изменили список, при этом объект остался прежним (id не изменился). Конкретное число у вас будет другим, это нормально.
Для сравнения: строки и кортежи изменять нельзя, поэтому у них такой фокус не получится.
Работа со списками в Python
Изменение элементов списка
Чтобы изменить элемент, нужно присвоить новое значение по его индексу.
Код:
my_list = [1, 2, 3, 4, 'пять']
my_list[4] = 5
print(my_list)
Исполнение:
[1, 2, 3, 4, 5]
Также можно заменить сразу несколько элементов с помощью среза. При этом количество новых элементов может не совпадать с количеством старых, тогда список сам станет длиннее или короче.
Код:
my_list = [1, 2, 3, 4, 5]
my_list[1:3] = [20, 30, 40] # Заменили элементы с индексами 1 и 2 на три новых
print(my_list)
Исполнение:
[1, 20, 30, 40, 4, 5]
Добавление элемента в список
Мы можем добавить элемент в конец списка с помощью метода append. Или добавить элемент на определённую позицию с помощью метода insert (при этом элементы сдвигаются вправо).
Код:
my_list = [1, 2, 3, 4, 'пять']
my_list.append('шесть') # Добавили элемент в конец списка
print(my_list)
my_list.insert(1, 'два') # Добавили элемент на позицию с индексом 1
print(my_list)
Исполнение:
[1, 2, 3, 4, 'пять', 'шесть']
[1, 'два', 2, 3, 4, 'пять', 'шесть']
Обратите внимание, что в insert первым аргументом передаётся индекс, на который встанет новый элемент, а вторым — сам элемент.
Расширение списка другим списком
Можно расширить список другим списком с помощью метода extend.
Код:
my_list1 = [1, 2, 3]
my_list2 = [4, 5, 6]
my_list1.extend(my_list2)
print(my_list1)
Исполнение:
[1, 2, 3, 4, 5, 6]
Новички часто путают append и extend. Разница в том, что append добавляет один объект как есть, а extend берёт каждый элемент из переданной последовательности и добавляет их по одному.
Код:
my_list = [1, 2, 3]
my_list.append([4, 5]) # Добавили один элемент - список
print(my_list)
my_list = [1, 2, 3]
my_list.extend([4, 5]) # Добавили два элемента
print(my_list)
Исполнение:
[1, 2, 3, [4, 5]]
[1, 2, 3, 4, 5]
В первом случае мы получили список внутри списка. Это допустимо, такие списки называют вложенными, но обычно это не то, чего мы хотели.
Если нужно объединить два списка, то можно воспользоваться оператором +. Он создаёт новый список, а исходные не меняет. Оператор += работает как extend. Кроме того, список можно умножить на число, тогда он повторится указанное количество раз.
Код:
a = [1, 2]
b = [3, 4]
print(a + b)
print(a)
a += b # то же самое, что a.extend(b)
print(a)
print([0] * 5)
Исполнение:
[1, 2, 3, 4]
[1, 2]
[1, 2, 3, 4]
[0, 0, 0, 0, 0]
Удаление элемента из списка
Удалить элемент из списка можно несколькими способами:
remove(значение)— удаляет элемент по его значению, причём только первый найденный;pop(индекс)— удаляет элемент по индексу и возвращает его. Если индекс не указать, то удалится последний элемент;del список[индекс]— удаляет элемент по индексу, в том числе можно удалить срез.
Код:
my_list = [1, 2, 3, 4, 5, 6]
my_list.remove(6) # Удалили элемент со значением 6
removed = my_list.pop(0) # Удалили элемент с индексом 0 и сохранили его в переменную
print(my_list)
print('Удалённый элемент -', removed)
print('Последний элемент -', my_list.pop()) # Удалили и вывели последний элемент
print(my_list)
Исполнение:
[2, 3, 4, 5]
Удалённый элемент - 1
Последний элемент - 5
[2, 3, 4]
Если в списке несколько одинаковых значений, то remove удалит только первое из них.
Код:
my_list = [1, 2, 2, 3, 2]
my_list.remove(2)
print(my_list)
Исполнение:
[1, 2, 3, 2]
А с помощью del можно удалить сразу несколько элементов, указав срез.
Код:
my_list = [10, 20, 30, 40, 50]
del my_list[1:3] # Удалили элементы с индексами 1 и 2
print(my_list)
Исполнение:
[10, 40, 50]
Будьте внимательны: если указать значение, которого нет в списке, то remove выдаст ошибку ValueError. А pop для пустого списка выдаст IndexError. Поэтому перед удалением по значению полезно проверять, есть ли элемент в списке (как это сделать, я покажу ниже).
Очистка списка
Также можно очистить список полностью с помощью метода clear.
Код:
my_list = [1, 2, 3, 4, 5, 6]
my_list.clear()
print(my_list)
Исполнение:
[]
Проверяем, содержится ли элемент в списке
Проверить, содержится ли элемент в списке, можно с помощью оператора in. Он возвращает True или False. Противоположный оператор — not in.
Код:
my_list = [1, 3, 5, 7]
print(1 in my_list)
print(2 in my_list)
print(2 not in my_list)
Исполнение:
True
False
True
Поиск индекса элемента
Если нужно узнать, на какой позиции стоит элемент, то используйте метод index. Он возвращает индекс первого найденного элемента. Если элемента нет в списке, то будет ошибка ValueError.
Код:
my_list = [1, 3, 2, 5, 4, 6, 2, 1, 2, 1, 7, 1]
print(my_list.index(2)) # Первая двойка
print(my_list.index(2, 3)) # Поиск двойки, начиная с индекса 3
Исполнение:
2
6
Сколько раз элемент встречается в списке
Посчитать, сколько раз элемент встречается в списке, можно с помощью метода count.
Код:
my_list = [1, 3, 2, 5, 4, 6, 2, 1, 2, 1, 7, 1]
print(my_list.count(2))
Исполнение:
3
Сортировка списка
Отсортировать список с однотипными данными можно с помощью метода sort. Важно, что этот метод меняет сам список и ничего не возвращает (точнее, возвращает None). Поэтому записывать результат в переменную не нужно. А чтобы отсортировать в обратном порядке, нужно указать параметр reverse=True.
Код:
my_list = [1, 3, 2, 5, 4, 6]
my_list.sort()
print(my_list)
my_list.sort(reverse=True)
print(my_list)
Исполнение:
[1, 2, 3, 4, 5, 6]
[6, 5, 4, 3, 2, 1]
Если нужно сохранить исходный список, а отсортированный получить в виде нового, то используйте встроенную функцию sorted. Она не меняет исходный список, а возвращает новый.
Код:
my_list = [3, 1, 2]
new_list = sorted(my_list)
print(my_list)
print(new_list)
Исполнение:
[3, 1, 2]
[1, 2, 3]
Со строками нужно быть аккуратнее: Python сравнивает их по кодам символов, поэтому все слова с заглавной буквы окажутся перед словами со строчной. Чтобы этого избежать, передайте в параметр key функцию, по результату которой будет идти сортировка. Например, str.lower приведёт слова к нижнему регистру на время сравнения, а сами слова при этом не изменятся. А key=len отсортирует слова по длине.
Код
words = ['banana', 'Apple', 'cherry']
words.sort()
print(words)
words = ['banana', 'Cherry', 'apple']
words.sort(key=str.lower)
print(words)
words = ['kiwi', 'banana', 'fig']
words.sort(key=len)
print(words)
Исполнение:
['Apple', 'banana', 'cherry']
['apple', 'banana', 'Cherry']
['fig', 'kiwi', 'banana']
Теперь вернёмся к списку, с которого мы начали статью: [1, 2, 3, 4, 'пять']. Если попытаться отсортировать список с числами и строками, то получится ошибка, потому что Python не знает, как сравнить число со строкой.
Код:
my_list = [3, 'a']
my_list.sort()
Исполнение:
TypeError: '<' not supported between instances of 'str' and 'int'
Поэтому сортировать можно только списки, элементы которых можно сравнивать между собой.
Сортировка списка кортежей
Более сложный вариант сортировки — это сортировка списка кортежей.
Если сортировать список кортежей с помощью метода sort() без дополнительных параметров, то сортировка будет происходить по первому элементу кортежей. Если встретятся два одинаковых первых элемента, то Python сравнит вторые элементы, потом третьи и так далее.
Чтобы отсортировать список не по первому элементу кортежа, нужно передать в параметр key функцию, которая возвращает значение для сравнения. Проще всего написать её с помощью lambda. Запись key=lambda i: i[1] означает, что сортировка будет по элементу кортежа с индексом 1, то есть по второму элементу (индексы начинаются с нуля). Вы можете задать любой индекс, главное, чтобы элементы по этому индексу можно было сравнивать между собой.
my_list = [(23, 18, 53), (18, 51, 48), (27, 38, 24), (15, 12, 33)]
# Вывод без сортировки
print(my_list) # [(23, 18, 53), (18, 51, 48), (27, 38, 24), (15, 12, 33)]
# Сортировка по первому элементу
my_list.sort()
print(my_list) # [(15, 12, 33), (18, 51, 48), (23, 18, 53), (27, 38, 24)]
# Сортировка по второму элементу
my_list.sort(key=lambda i: i[1])
print(my_list) # [(15, 12, 33), (23, 18, 53), (27, 38, 24), (18, 51, 48)]
# Сортировка по третьему элементу
my_list.sort(key=lambda i: i[2])
print(my_list) # [(27, 38, 24), (15, 12, 33), (18, 51, 48), (23, 18, 53)]
Вместо lambda можно использовать функцию itemgetter из стандартного модуля operator. Она делает то же самое, но читается проще, а ещё немного быстрее. Параметр reverse=True работает и здесь:
from operator import itemgetter
my_list = [(23, 18, 53), (18, 51, 48), (27, 38, 24), (15, 12, 33)]
my_list.sort(key=itemgetter(2), reverse=True) # По третьему элементу, от большего к меньшему
print(my_list) # [(23, 18, 53), (18, 51, 48), (15, 12, 33), (27, 38, 24)]
Ещё одна полезная особенность: сортировка в Python устойчивая. Это значит, что элементы с одинаковым ключом сохраняют тот порядок, в котором они были до сортировки.
Разворот списка и простые вычисления
Развернуть список на месте можно методом reverse. Он, как и sort, меняет сам список. Если нужен новый развёрнутый список, то используйте срез [::-1], который мы разбирали выше.
Для списков чисел есть удобные встроенные функции: min — минимум, max — максимум, sum — сумма.
Код:
my_list = [1, 2, 3]
my_list.reverse()
print(my_list)
print('Минимум -', min(my_list))
print('Максимум -', max(my_list))
print('Сумма -', sum(my_list))
Исполнение:
[3, 2, 1]
Минимум - 1
Максимум - 3
Сумма - 6
Перебор элементов списка
Чаще всего со списком нужно проделать что-то для каждого элемента. Для этого используют цикл for.
Код:
servers = ['web-01', 'db-01', 'backup-01']
for server in servers:
print('Проверяю', server)
Исполнение:
Проверяю web-01
Проверяю db-01
Проверяю backup-01
Если в цикле нужен ещё и индекс, то используйте функцию enumerate.
Код:
servers = ['web-01', 'db-01', 'backup-01']
for i, server in enumerate(servers):
print(i, server)
Исполнение:
0 web-01
1 db-01
2 backup-01
Новый список из старого можно создать коротко, с помощью списочного выражения (list comprehension). Читается оно почти как обычное предложение: «для каждого x из диапазона взять x умножить на 2»:
doubled = [x * 2 for x in range(5)]
print(doubled) # [0, 2, 4, 6, 8]
Подробно циклы и списочные выражения я разберу в отдельных статьях.
Списки и строки: split и join
Со списками часто связаны две строковые операции. Метод строки split разбивает строку на список, а метод join собирает список строк обратно в одну строку.
Код:
line = 'a b c'
parts = line.split() # Разбиваем по пробелам
print(parts)
print(', '.join(parts)) # Склеиваем через запятую с пробелом
Исполнение:
['a', 'b', 'c']
a, b, c
Но join работает только со строками. Поэтому, если у вас список чисел, то их нужно преобразовать в строки.
Код:
numbers = [1, 2, 3]
strings = [] # Сюда будем складывать строки
for n in numbers:
strings.append(str(n)) # Превращаем каждое число в строку
print(strings)
print(', '.join(strings)) # Склеиваем через запятую с пробелом
Исполнение:
['1', '2', '3']
1, 2, 3
Тоже самое можно сделать с помощью map:
numbers = [1, 2, 3]
print(', '.join(map(str, numbers))) # map(str, numbers) - каждый элемент списка numbers преобразит в str
Исполнение:
1, 2, 3
map(str, numbers)делает то же, что цикл выше: вызываетstrдля каждого элемента. Только новый список мы не создаём,joinзабирает строки прямо изmap.
Копирование списка
Так как список относится к изменяемым типам данных, то при присваивании существующего списка новой переменной копирования не происходит. Новая переменная просто ссылается на старый список. Может случиться такая ситуация: мы думаем, что изменяем новый список, а на самом деле изменяем и старый тоже (потому что это один и тот же список).
Код:
my_list_1 = [1, 3, 5, 7]
my_list_2 = my_list_1
my_list_2.append(9)
print(my_list_1)
Исполнение:
[1, 3, 5, 7, 9]
Поэтому список нужно копировать с помощью метода copy.
Код:
my_list_1 = [1, 3, 5, 7]
my_list_2 = my_list_1.copy()
my_list_2.append(9)
print(my_list_1)
print(my_list_2)
Исполнение:
[1, 3, 5, 7]
[1, 3, 5, 7, 9]
Есть и другие способы получить копию: срез my_list_1[:] и функция list(my_list_1). Результат у них такой же.
Проверить, один и тот же это объект или нет, можно оператором is, а сравнить содержимое — оператором ==.
Код:
a = [1, 3]
b = a # Ссылка на тот же список
c = a.copy() # Копия
print(b is a) # Один и тот же объект?
print(c is a)
print(c == a) # Одинаковое ли содержимое?
Исполнение:
True
False
True
Но и у copy есть подвох: он делает поверхностную копию. Если внутри списка лежат другие списки, то копируются только ссылки на них. В итоге вложенные списки остаются общими у оригинала и копии.
Код:
matrix = [[1, 2], [3, 4]]
copy_matrix = matrix.copy()
copy_matrix[0].append(99)
print(matrix)
print(copy_matrix)
Исполнение:
[[1, 2, 99], [3, 4]]
[[1, 2, 99], [3, 4]]
Чтобы скопировать список вместе со всеми вложенными объектами, используйте функцию deepcopy() из модуля copy.
Код:
import copy
matrix = [[1, 2], [3, 4]]
deep_matrix = copy.deepcopy(matrix)
deep_matrix[0].append(99)
print(matrix)
print(deep_matrix)
Исполнение:
[[1, 2], [3, 4]]
[[1, 2, 99], [3, 4]]
Эта же особенность проявляется при умножении списка. Если создать «таблицу» так: [[0] * 3] * 3, то внешний список будет содержать три ссылки на один и тот же вложенный список. Поэтому, если изменить элемент в одном из вложенных списков, он изменится во всех трёх. Правильно создавать вложенные списки с помощью списочного выражения, тогда каждый вложенный список будет отдельным объектом.
Код:
bad = [[0] * 3] * 3
print(bad)
bad[0][0] = 1
print(bad)
good = [[0] * 3 for _ in range(3)]
print(good)
good[0][0] = 1
print(good)
Исполнение:
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]
Итоги
Подведём итог. Список — это изменяемая упорядоченная последовательность, в которой можно хранить объекты любых типов. Запомните главное:
- индексы начинаются с нуля, а отрицательные индексы считают с конца;
- срез
[старт:стоп:шаг]не включает элементстопи создаёт новый список; - методы
append,insert,extend,remove,pop,clear,sort,reverseменяют сам список; sortedи срезы возвращают новый список, а исходный не трогают;- присваивание
b = aне копирует список, для копии используйтеcopy(), а для вложенных списков —deepcopy().
Все примеры из статьи я проверил на Python 3.13, в более новых версиях Python 3 они работают так же.
Если понравилась статья, подпишись на мой канал в VK или Telegram .