Список типов данных в Python

Начнём с того, что все данные в Python являются объектами. Они могут создаваться нами вручную, либо быть изначально встроенными на уровне языка. Объект можно охарактеризовать, как особую область памяти, где хранятся некоторые значения и определённые для этих значений операции.

Проиллюстрировать фундаментальность объектов в разрезе Питона можно, приведя пример общего вида программы на этом языке. Итак:

  1. Программа состоит из модулей;
  2. Модуль, в свою очередь, представляет собой набор инструкций;
  3. Инструкции содержат выражения;
  4. Выражения служат для создания и обработки объектов;
Объекты – базовое понятие в Python.

Ну и вполне закономерно, что объекты можно классифицировать по их типам.

Что такое динамическая типизация

Прежде, чем мы приступим к рассмотрению наиболее употребляемых типов данных в Python, проведём небольшую параллель с другими языками программирования. Всё их множество можно разделить на две составляющие:

  • типизированные языки;
  • нетипизированные (бестиповые) языки.

Нетипизированные языки в основной своей массе сосредоточены на низком уровне, где большинство программ напрямую взаимодействует с железом. Так как компьютер "мыслит" нулями и единицами, различия между строкой и, допустим, классом для него будут заключаться лишь в наборах этих самых 0 и 1. В связи с этим, внутри бестиповых языков, близких к машинному коду, возможны любые операции над какими угодно данными. Результат на совести разработчика.

Python же — язык типизированный. А, раз в нём определено понятия "типа", то должен существовать и процесс распознания и верификации этих самых "типов". В противном случае вероятны ситуации, когда логика кода окажется нарушенной, а программа выполнится некорректно.

Таким процессом и является типизация. В ходе её выполнения происходит подтверждение используемых типов и применение к ним соответствующих ограничений. Типизация может быть статической и динамической. В первом случае, проверка выполняется во время компиляции, во втором — непосредственно во время выполнения программного кода.

Python – язык с динамической типизацией. И здесь, к примеру, одна и та же переменная, при многократной инициализации, может являть собой объекты разных типов:

a = 1 print(type(a)) <class 'int'> a = 'one' print(type(a)) <class 'str'> a = {1: 'one'} print(type(a)) <class 'dict'>

В языке со статической типизацией такой фокус не пройдёт:

// код на C++ int main() { int b = 2; cout << b << "\n"; b = "two"; cout << b << "\n"; return 0; } > error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive] b = "two";

💭 Адепты и приверженцы разных языков часто спорят о том, что лучше: динамическая типизация или статическая, но, само собой, преимущества и недостатки есть и там, и там.

👍 К плюсам динамической типизации можно отнести:

1. Создание разнородных коллекций. Благодаря тому, что в Python типы данных проверяются прямиком во время выполнения программного кода, ничто не мешает создавать коллекции, состоящие их элементов разных типов. Причём делается это легко и просто:

# список, элементами которого являются строка, целое число и кортеж variety_list = ['String', 42, (5,25)]

2. Абстрагирование в алгоритмах. Создавая на Питоне, предположим, функцию сортировки, можно не писать отдельную её реализацию для строк и чисел, поскольку она и так корректно отработает на любом компарируемом множестве.

3. Простота изучения. Не секрет, что изучать Питон с нуля гораздо легче, чем, например, Java. И такая ситуация будет наблюдаться не только для этой пары. Языки с динамической типизацией в большинстве своём лучше подходят в качестве учебного инструмента для новичков в программировании.

🙁 К минусам же динамической проверки типов можно отнести такие моменты, как:

1. Ошибки. Ошибки типизации и логические ошибки на их основе. Они достаточно редки, однако зачастую весьма сложно отлавливаемы. Вполне реальна ситуация, когда разработчик писал функцию, подразумевая, что она будет принимать числовое значение, но в результате воздействия тёмной магии или банальной невнимательности, ей на вход поступает строка и …функция отрабатывает без ошибок выполнения, однако её результат, – ошибка, сам по себе. Статическая же типизация исключает такие ситуации априори.

2. Оптимизация. Статически типизированные языки обычно работают быстрее своих динамических братьев, поскольку являются более "тонким" инструментом, оптимизация которого, в каждом конкретном случае, может быть настроена более тщательно и рационально.

Так или иначе, сказать, что "одно лучше другого" нельзя. Иначе "другого" бы не было. Динамически типизированные языки экономят уйму времени при кодинге, но могут обернуться неожиданными проблемами на этапе тестирования или, куда хуже, продакшена. Однако вряд ли кто-то будет спорить с тем, что динамический Python куда более дружелюбный для новичков, нежели статический C++.

Разница между атомарными и структурными типы данных

По одной из классификаций все типы данных в Python делятся на атомарные и ссылочные.

Атомарные:

  • числа;
  • строки;

Ссылочные:

  • списки;
  • кортежи;
  • словари;
  • функции;
  • классы;

Разница между этими двумя группами уходит глубоко в корни языка. Вкратце:

Атомарные объекты, при их присваивании, передаются по значению, а ссылочные – по ссылке

# пример присваивания атомарного объекта atom = 3 btom = atom atom = 2 print(atom) > 2 print(btom) > 3

Из результатов видно, что переменной btom было присвоено именно значение, содержащееся в atom, а не ссылка, указывающая на область памяти.

Посмотрим, как это работает для структурных типов:

# пример присваивания ссылочного объекта link = ['Ipona', 'Master Sword'] alin = link link[0] = 'Zelda' print(link) > ['Zelda', 'Master Sword'] print(alin) > ['Zelda', 'Master Sword']

Поскольку списки – это ссылочные объекты, то вполне закономерно, что после присваивания переменной link переменной alin передалась именно ссылка на объект list-а и, при печати, на экран были выведены две одинаковые надписи.

Собственно, в этом и вся разница.

Числовые типы

"Все сущее есть Число" – сказал однажды мудрый грек по имени Пифагор. Числа – важнейший и фундаментальнейший из всех типов данных для всех языков программирования. В Python для их представления служит числовой тип данных.

int (целое число)

Концепция целых чисел проста и естественна. Это числа без дробной части, которые, говоря математическим языком, являются расширением натурального ряда, дополненного нулём и отрицательными числами.

# примеры целых чисел a = -3000 b = 0 c = 9000

Там, где есть числа, есть и математика. Поэтому резонно, что целые числа используются для исчисления всевозможных математических выражений. Также int применяется в качестве описаний количественных свойств какого-либо объекта.

float (число с плавающей точкой)

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

print(0.3 + 0.3 + 0.3) > 0.8999999999999999 print(0.3 * 3 == 0.9) > False

В плане записи, float ничем не отличаются от int:

# примеры вещественных чисел zero = 0.0 pi = 3.14 e = 2.71

В плане использования – тоже, разве что в любых мало-мальски серьёзных вычислениях без float никуда.

complex (комплексное число)

Привет высшей математике! Как вещественный ряд расширяет множество рациональных чисел, так и ряд комплексных чисел расширяет множество вещественных. Показательной особенностью комплексного ряда является возможность извлечения корня из отрицательных чисел.

В Python комплексные числа задаются с помощью функции complex():

# пример комплексного числа z = complex(1, 2) print(z) > (1+2j) # вещественная часть print(z.real) > 1.0 # мнимая часть print(z.imag) > 2.0 # сопряженное комплексное число print(z.conjugate()) > (1-2j)

Помните, что операция сравнения для комплексных чисел не определена:

z1 = complex(4, 5) z2 = complex(100, 200) print(z1 > z2) > Traceback (most recent call last): print(z1> z2) TypeError: '>' not supported between instances of 'complex' and 'complex'

Комплексные числа широко применяются, например, для решения дифференциальных уравнений.

flash
Подробнее про числа в Python:

bool (логический тип данных)

В каком-то смысле наиболее простой и самый понятный из всех типов данных. У bool есть всего два значения:

  • Истина (True);
  • Ложь (False).

Однако за этой простотой кроется колоссальный пласт теории в виде булевой алгебры.

# пример bool pravda = True lozh = False

Переменные логического типа нужны для реализации ветвлений, они применяются для установки флажков, фиксирующих состояния программы, а также используются в качестве возвращаемых значений для функций, названия которых, зачастую, начинаются на "is" (isPrime, isEqual, isDigit). То есть тех, которые, на человеческом языке, отвечали бы на вопрос одним словом "Да" или "Нет".

flash
Подробнее про логический тип данных в Python:

Последовательности

Ещё одно понятие из математики. Там, последовательность – есть нумерованный набор элементов, в котором возможны их повторения, а порядок имеет значение. Определение Питона схоже с математическим: здесь последовательностью зовётся упорядоченная коллекция объектов.

str (строка)

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

строка — это последовательность односимвольных строк.
s = 'Hello, friend. You are my world' print(type(s)) > <class 'str'>

Важность строк велика в первую очередь для людей, ведь понятно, что вся письменная речь может рассматриваться, как множество строк. А так как человеку свойственно обмениваться информацией именно в виде набора слов, то можно говорить о практически неограниченном количестве областей применения строкового типа данных. Строки, строки everywhere!

flash
Больше информации по строкам в Python тут:

list (список)

Список – это ещё один вид последовательностей... Здесь стоит остановиться и отметить, что последовательности в Python бывают изменяемыми и неизменяемыми. Список – изменяемая последовательность, а строки и кортежи – нет. Таким образом, список можно определить, как упорядоченную и изменяемую коллекцию, состоящую из объектов произвольных типов.

# пример списка list_of_lists = [['code alpha', 'code beta'], [553, 434]] list_of_lists[0][1] = 'code omega' print(list_of_lists) > [['code alpha', 'code omega'], [553, 434]]

Само название списков говорит об их предназначении быть объектами для хранения наборов данных. Список покупок, подарков, результатов матчей, ip клиентов или объектов типа Student. Списки в Python – это эдакие массивы из прочих языков "на максималках".

flash
Подробнее про работу со списками читайте тут:

tuple (кортеж)

Кортежи в языке Python можно рассматривать, как неизменяемые списки со всеми вытекающими:

# пример кортежа tup = ('i', 'j') # мы можем получить первый элемент print(tup[0]) > i # но изменить его не получится tup[0] = 'k' > Traceback (most recent call last): tup[0] = 'k' TypeError: 'tuple' object does not support item assignment

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

flash
Подробнее о кортежах в Python:

dict (словарь)

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

# пример простого словаря dictionary = {'Огонёк': 'уменьш. от слова "Огонь"'}

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

flash
Про словари в Python читайте тут:

set (множество)

Ещё один "набор, но не последовательность".

# пример множества integer_num_set = {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5}

Мы хотим видеть множество, если нам не существенен порядок элементов, но важна их уникальность.

# свойство уникальности unique_set = {6, 6, 6, 5} print(unique_set) > {5, 6}
flash
Работа с множествами описана в этой статье:

Файл

Работа с файлами, хранящимися где-то на внешнем носителе, в Python реализована в виде объектов-файлов. Они относятся к объектам базового типа, но обладают весьма характерной чертой: нельзя создать экземпляр объекта-файла при помощи литералов.

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

f = open('filename.txt', 'w')

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

  • r – выбирается по умолчанию, означает открытие файла для чтения;
  • w – файл открывается для записи (если не существует, то создаётся новый);
  • x – файл открывается для записи (если не существует, то генерируется исключение);
  • a – режим записи, при котором информация добавляется в конец файла, а не затирает уже имеющуюся;
  • b – открытие файла в двоичном режиме;
  • t – ещё одно значение по умолчанию, означающее открытие файла в текстовом режиме;
  • + – читаем и записываем.

range object (a type of iterable)

Крутая особенность языка Python состоит в наличии в нём встроенной функции range(), которая способна генерировать непрерывную последовательность целых чисел:

r = range(10) print(r) > range(0, 10) for i in r: print(i, end=' ') > 0 1 2 3 4 5 6 7 8 9

Она крайне удобна для создания циклов for.

None

None – специальный объект внутри Питона. Он означает пустоту, всегда считается "Ложью" и может рассматриваться в качестве аналога NULL для языка C/С++. Помимо этого, None возвращается функциями, как объект по умолчанию.

null = None print(type(null)) > <class 'NoneType'>

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

Работа с типами в Python

Как проверить тип данных

Нет ничего проще, чем узнать тип данных объекта в Python:

# достаточно воспользоваться встроенный функции type() my_list = {'Python': 'The best!'} print(type(my_list)) > <class 'dict'>

Как поменять тип данных

В богатом арсенале Питона есть встроенные функции для приведения типов – int(), list(), set(), tuple(), str(), bin().

☝️ Обратите внимание: встроенная функция для приведения типа не модифицирует переданное значение, а возвращает новое значение другого типа.

# int() - преобразует числовую строку в целое число seven = '7' new_seven = int(seven) print(new_seven, type(new_seven)) > 7 <class 'int'># list() - приведение итерируемого объекта к списку nums = (11, 12) new_nums = list(nums) print(new_nums, type(new_nums)) > [11, 12] <class 'list'># set() - трансформация итерируемого объекта во множество vegetables = ['carrot', 'tomato', 'cucumber'] new_vegetables = set(vegetables) print(new_vegetables, type(new_vegetables)) > {'cucumber', 'carrot', 'tomato'} <class 'set'># tuple() - аналогично, но в кортеж python = 'Python' new_python = tuple(python) print(new_python, type(new_python)) > ('P', 'y', 't', 'h', 'o', 'n') <class 'tuple'># str() - приведение к строковому типу ex_dict = {'qiwi': 1453} new_ex_dict = str(ex_dict) print(new_ex_dict, type(new_ex_dict)) > {'qiwi': 1453} <class 'str'># bin() - преобразует десятичное число в двоичный формат dec = 10 new_dec = bin(dec) print(new_dec) > 0b1010

Отличие type() от isinstance()

В отличие от type(), функция isinstance() возвращает не тип данных аргумента, а булево значение, говорящее о том, принадлежит объект к определенному классу или нет:

num = 4.44 print(isinstance(num, float)) > True

А ещё isinstance() умеет проверять принадлежность объекта хотя к одному типу из кортежа, переданного в качестве второго аргумента:

name = 'Ash' print(isinstance(name, (float, int, complex))) > False print(isinstance(name, (float, int, complex, str))) > True

Важным отличием также является то, что isinstance() "знает" о наследовании. Функция воспринимает объект производного класса, как объект базового.

class BaseExample: pass class DerivedExample(BaseExample): pass test = DerivedExample() print(isinstance(test, BaseExample)) > True

А вот вывод результата работы функции type():

print(type(test)) > <class '__main__.DerivedExample'>

Здесь видно, что для type() производный класс есть производный.

😭
😕
😃
😍