Список – это базовая структура данных в Python. Вы будете использовать списки очень часто и по самым разным причинам: хранение информации, передача данных куда-либо, сортировка данных и так далее. Кроме того, вы часто будете использовать списки, даже не зная, что вы их использовали, потому что строки – это измененный вариант списков. Поэтому вам нужно знать об этой структуре данных все: как они создаются, как их изменять, какие операторы поддерживаются списками, какие функции языка доступны спискам и какие встроенные функции у списков есть. Обо всем этом – ниже.
Начнем не со списков в Питоне, а со структур данных как таковых. Если вы работаете с набором данных одного типа, зачастую бывает удобно поместить все эти данные в какое-то одно место, из которого вы будете иметь доступ сразу ко всем этим данным. Таким «местом» во многих языках программирования выступает массив:
Концепция у массивов – очень простая: если для отдельной переменной мы выделяли в памяти, например, 4 байта (стандартный int во многих языках), то для массива мы выделяем те же 4 байта, умноженные на количество элементов массива. Каждому элементу мы назначаем индекс от 0: 0, 1, 2, 3… Почему от 0? Долгая история, если хотите, то можете почитать статью-расследование на Хабре. Важно то, что мы по итогу:
Эти свойства массива вы можете увидеть в языках, в которых обычно встречается доступ к адресам и строгая типизация – C или Golang, например. И эти свойства обеспечивают высокую скорость работы массивов, одновременно с этим добавляя проблем в разработке. Основных проблем – 2:
Некоторые языки предоставляют инструменты для создания динамических массивов (срез в Golang, например) – это решает вторую проблему, а вот с первой проблемой вы всегда остаетесь один на один.
Python решает обе проблемы: обычных массивов в языке нет вообще, есть списки. Список в Python – это динамический массив, который позволяет хранить в себе данные любых типов. «Под капотом» у списка в Python лежит все тот же массив, но – с динамическим расширением: когда вы добавляете новый элемент в массив, лимит которого исчерпан, Python автоматически создает новый массив увеличенного объема и копирует в него все данные из старого массива. Проблему элементов разных типов Python тоже решает без вашего участия – в массиве хранятся не значения, а указатели на значения, поэтому длина значения в байтах не является проблемой: само значение хранится где-то в куче (специальная «безразмерная» область памяти), в массиве же хранится указатель длиной в (обычно) 4 байта. Но вы должны понимать, что обе эти оптимизации, упрощающие вашу жизнь, уменьшают скорость работы списков в Python.
Вот как выглядит список в Python:
Списки заключены в квадратные скобки – так их можно отличить от других переменных и типов данных. Свойства списка в Python:
Списки создаются либо через встроенную конструкцию, либо через функцию list(). Начнем со встроенной конструкции – квадратных скобок. Создание пустого списка:
testList = []
Создание списка со значениями:
testList = [1, False, “sravni.ru”]
Если вам нужно много одинаковых элементов в списке, вы можете использовать операцию умножения:
testList = [42] * 5
А теперь к сложному – списочные выражения. Python позволяет вставить в инициализацию списка специальное выражение, состоящее из цикла for и, при необходимости, условия if. Форма записи: testList = [выражение цикл (условие)]. Возьмем самое простое:
testList = [x*2 for x in range(5)]
Что тут произошло? Мы создали объект range, который последовательно возвращает числа от 0 до 4, и каждое из этих чисел обрабатывается согласно выражению «x*2», то есть умножается на 2. Мы можем добавить условие:
testList = [x for x in range(10) if x // 2 == 0]
Мы добавили в список только те элементы, которые делятся на 2 без остатка – то есть являются четными. Если в списочное выражение добавить 2 цикла for – вы получите матрицу. Списочные выражения – очень мощный инструмент создания списков, но и очень сложный, поэтому ниже мы его рассматривать не будем – дальнейший разбор выходит за рамки этого материала.
Второй способ создания списков – с помощью встроенной функции list(). Создание пустого списка:
testList = list()
Функция может принимать итерируемый объект (iterable, то, что можно пересчитать) и создавать список на его основе. В этом list() уже сильно отличается от []: если мы, например, передадим строку «Hello, sravni.ru!» в [], то получим список с одним элементом-строкой; если мы передадим его в list(), то получим список, в котором каждый элемент будет отдельным символом строки:
Второй важный момент функции list() – она позволяет копировать список. В примере мы сначала создали список с помощью [], затем через «=» создали второй такой же список. Затем мы изменили элемент во втором списке, вывели первый – и оказалось, что в первом списке элемент тоже поменялся, хотя мы меняли его во втором. Далее мы создали список через list(), на его основе создали такой же дубликат, изменили во втором списке элемент – а в первом ничего не поменялось.
Почему так? Когда мы написали «listWithBracers2 = listWithBracers1», мы на самом деле копировали не список, а указатель на список – то есть у нас возникла вторая переменная, с помощью которой можно было получить доступ к тому же списку. Мы этим доступом воспользовались, поменяли значение – и когда мы запросили вывод через первую переменную, мы получили измененный список. С list() так не работает – мы создаем не указатель, а полностью новый список.
Самая базовая операция – это индексация. Получить доступ к элементу списка можно по его индексу: testList[0]. Python поддерживает отрицательную индексацию – в этом случае отсчет начинается с конца:
Для того, чтобы изменить элемент списка, нужно обратиться к конкретному элементу и присвоить этому элементу новое значение. Можно изменять элементы массово – для этого нужно указать срез, который изменяется, и задать ему новые значения, длина среза может отличаться от длины нового подсписка (о срезах поговорим ниже):
По операторам:
К слову, списки поддерживают и операторы сравнения «>»/«<», но работают они немного контр-интуитивно: Python сравнивает элементы с одинаковыми индексами в каждом списке, и как только по какому-то индексу Питон находит элемент больше/меньше – он возвращает True или False в зависимости от оператора (отсутствие значения по индексу считается самым маленьким значением из возможных):
Логика – запутанная, поэтому ей пользуются нечасто.
Что касается других интересных операций со списками – можно отметить:
Многие изучающие Python часто путаются между встроенными функциями и методами списков – например, для списка list1 нужно писать reversed(list1), но list1.reverse(). Внесем ясность:
Встроенных функций в Python – много, полный список вы можете посмотреть вот здесь. Из самого «ходового» для списков:
Заметьте, что все эти функции возвращают значение – то есть вам нужно присвоить результат в переменную или передать его куда-либо, чтобы не потерять:
Теперь перейдем к методам списков. Встроенные методы нужно было чему-то присваивать, с методами списков все проще: вызываете их для конкретного списка, и метод применяется к его содержимому (есть несколько методов, возвращающих значение, упомянем). В Python у списков есть 11 методов:
Срез – это список, представляющий собой часть другого списка. В Python срезы создаются с помощью специального синтаксиса: slice = list[start:end:step], где:
Индексы могут быть отрицательными – в этом случае они считаются с конца. Шаг тоже может быть отрицательным – тогда слайс будет создаваться от последнего элемента к первому (но нужно указать последний элемент в качестве start и первый в качестве end, иначе получим пустой срез). Start, end и step можно опускать, тогда будут использоваться стандартные значения: индекс 0 для start, индекс последнего элемента + 1 для end, 1 для step.
Собственно, выше – вся база по спискам в Python. Очень кратко пройдемся по другим основным структурам данных в Python, чтобы вы знали, какими они еще бывают:
Каких-то специализированных курсов по спискам нет: списки – это один из базовых инструментов языка, и вам нужно учить весь язык, а не просто списки. Если хотите учиться с ментором и гарантией трудоустройства – присмотритесь к курсам:
Тезисно: