На уровне машинного кода любые данные – это последовательности нулей и единиц. Но работать с такими последовательностями – крайне неудобно, поэтому разработчики придумали абстракцию – типы данных, каждый – со своим поведением. Вы пользуетесь типами данных с первых же строк, написанных вами на Python, поэтому знать, как они устроены и какие особенности у них есть – крайне полезно. Ниже – гайд по основным типам данных в Python, бонусом опишем специализированные (редкие) типы данных и расскажем, какие встроенные методы в Python есть для работы с данными.
Для начала рассмотрим, как Python вообще управляется с данными. Python – язык со строгой динамической типизацией – вы можете хранить в переменных любые значения (динамическая типизация), но Python практически никогда не преобразовывает типы за вас – все преобразования типов вам нужно делать явно (строгая типизация). В дополнение к этому любая переменная в Python – это объект, соответственно, любой тип данных – это класс.
Если вы переходите из языка со статичной типизацией или просто любознательны, то у вас может возникнуть вопрос: «А как динамическая типизация работает внутри Python?». Со статичной типизацией все понятно: каждый тип имеет заранее определенный размер в байтах, и компилятор/интерпретатор выделяет нужное количество места под переменную в стэке, а если место заканчивается – место выделяется уже в куче (что снижает скорость работы, к сожалению). В Python же ничего не понятно: тип данных может меняться во время исполнения (в рантайме), плюс в список можно поместить разные типы данных.
Все дело как раз в том, что любая переменная является объектом, что существенно упрощает работу со ссылками: на любую переменную, даже на число, мы всегда имеем эту самую ссылку. Поскольку на все есть ссылка – интерпретатору не нужно думать, как расширить память в стэке, если в рантайме разработчик присваивает сложный объект переменной, в которой раньше хранилось число – интерпретатор просто создает новый объект в памяти и меняет ссылку переменной, а число, если на него больше никто не ссылается, затем подберет сборщик мусора – так это работает с объектами и в других ЯП, Java как пример. Что касается списков в которые можно помещать любые типы – в списках на самом деле хранятся ссылки, а не сами переменные, поэтому положить в список можно что угодно.
Проблема такого подхода – в скорости работы, потому что любую mutable (изменяемую) переменную Python помещает в кучу – специальную произвольную область памяти. В стэк (быструю область памяти) попадают только immutable (неизменяемые) объекты, которых обычно в приложении не так уж и много. Да, Python имеет некоторые оптимизации, которые его ускоряют – вместо двух переменных с числовым значением «5», например, Python может создать 1 область памяти, в которой хранится «5», и 2 ссылки на нее; но полностью это проблему не решает, динамическая типизация – причина медленной скорости работы Python.
Полную информацию обо всех типах данных в Python вы можете почитать в соответствующем разделе официальной документации. Читать там – очень много (экранов 200-300), и большинство типов данных вам вообще никогда в жизни не пригодится, поэтому для начала можете ограничиться теми, которые мы опишем в этом разделе – в 99.9% случаев вы будете пользоваться только ими в своей работе.
Самый простой тип – в переменных этого типа могут храниться числа. Numbers подразделяются на integral, real и complex; integral, в свою очередь, содержит integers и booleans:
Последовательность – это некоторый набор значений, проиндексированный неотрицательными числами. Отличительная черта любой последовательности – доступ к элементу последовательности seq с индексом i можно получить по обращению к seq[i]. Последовательности бывают изменяемыми и неизменяемыми – как понятно из названия, первые можно изменять после создание, вторые – нельзя. Изменяемые последовательности в Python – это:
Если этих изменяемых последовательностей вам не хватает – можете подключить встроенные библиотеки с массивами и коллекциями.
Все неизменяемые последовательности хэшируются – если не вдаваться в подробности, то это значит, что они работают намного быстрее, чем изменяемые. 3 основных вида неизменяемых последовательностей в Python – это:
К слову, неизменяемость строк – частый вопрос на собеседованиях. Фишка – в том, что со строками разработчику приходится работать часто, поэтому нужно следить за тем, в каких случаях используются операции добавления/удаления символов из строк – при каждой такой операции создается новая строка, что может съедать много ресурсов.
Множество (set) – отдельный тип данных. Во множество можно поместить любое количество уникальных неизменяемых объектов. Если попытаться добавить во множество объект, который там уже есть – ничего не произойдет. У множества, в отличие от последовательностей, нет индексации – если вы последовательно добавите во множество числа «1», «2», «3», «4» и «5», после чего выведете множество в консоль, вы можете получить результат вида «5, 2, 3, 1, 4» – или любую другую комбинацию. При этом по множеству можно проходить с помощью for. Множества бывают изменяемыми (set) и неизменяемыми (frozenset).
Те же списки, но вы сами задаете индексы, и не обязательно использовать числа – в качестве индекса можно задать любой неизменяемый объект, часто для этого используются строки. Словари хранят данные в виде пар «ключ:значение», ключ – это индекс. С версии языка 3.7 словари – упорядоченные, до этого они были неупорядоченными.
Перечисленные выше типы данных – основные, с которыми вы столкнетесь в работе (и то не все – массивы байт используются редко). Но есть еще и специализированные типы данных – они нужны совсем редко, хотя знать об их существовании все же стоит. Очень кратко о некоторых:
У каждого типа данных есть свои встроенные методы для работы с ним – полный список методов смотрите в документации к конкретному виду. При этом есть операции, которые поддерживаются большинством типов данных:
Если вы будете создавать список прямо в объявлении параметра по умолчанию – он создастся только один раз, во время объявления функции, и будет использоваться при каждом ее вызове. Получается, что в список могут попасть данные, которые были внесены в него во время предыдущего вызова функции – такое поведение нежелательно, потому что вы не можете быть уверены в том, что знаете, что сейчас находится в списке.
Воспользуйтесь встроенной функцией type().
Поскольку Python – язык со строгой типизацией, вам нужно напрямую изменить тип переменной (casting).
Тезисно: