Типы данных в Python: полный разбор для новичка

Типы данных в Python: полный разбор для новичка


Коротко. Тип данных в Python — это категория, которая определяет, что за значение хранит объект и какие операции с ним можно выполнять. Основных встроенных типов девять: числа int, float и complex, строки str, логический bool, списки list, кортежи tuple, множества set и словари dict, плюс особый тип NoneType для пустого значения. Все типы делятся на изменяемые и неизменяемые — это деление важнее, чем кажется новичку, потому что именно на нем строятся самые частые ошибки.


Зачем новичку разбираться в типах данных


Тип — это не формальность, а набор правил, по которым Python обращается со значением. От типа зависит три вещи: какие операции разрешены, сколько памяти занимает объект и как он себя ведет при изменении.


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


print(2 + 3) # 5


print("2" + "3") # '23'


print("2" + 3) # TypeError


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


Что такое тип данных в Python


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


a = 10


print(a) # 10 — значение


print(type(a)) # <class 'int'> — тип


print(id(a)) # 140717895746096 — идентификатор


Переменная в Python — это не коробка со значением, а имя-указатель. Строка a = 10 не кладет десятку в переменную, а создает объект «число 10» в памяти и привязывает к нему имя a. Понимание этой детали снимает половину вопросов о том, почему списки ведут себя странно.


Именно поэтому одна и та же переменная может по очереди указывать на объекты разных типов:


x = 10


print(type(x)) # <class 'int'>


x = "десять"


print(type(x)) # <class 'str'>


Типизация в Python: динамическая и строгая


У Python динамическая и при этом строгая типизация. Два слова, за которыми стоят два разных свойства языка.


Динамическая означает, что тип определяется во время выполнения программы, а не заранее. Указывать тип при объявлении переменной не нужно, и менять его по ходу можно свободно — пример выше это показывает. В языках со статической типизацией, таких как C++, Java или C#, тип задается заранее и меняться не может.


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


result = 1 + "1"


# TypeError: unsupported operand type(s) for +: 'int' and 'str'


JavaScript в такой ситуации молча вернет строку '11'. Python останавливает программу и сообщает об ошибке. На первый взгляд это менее удобно, но в больших проектах строгость экономит часы отладки: программа падает сразу и в понятном месте, а не выдает бессмысленный результат где-то дальше.


Чтобы код заработал, преобразование надо написать явно:


result = 1 + int("1")


print(result) # 2


Какие типы данных есть в Python


Встроенные типы удобно разложить на четыре группы.


Числовые — int (целые), float (с плавающей точкой), complex (комплексные), а также bool, который формально относится к числам.


Текстовый — str (строки).


Коллекции — list (списки), tuple (кортежи), set и frozenset (множества), dict (словари).


Особые — NoneType (пустое значение), bytes и bytearray (двоичные данные), а также файловые объекты, итераторы и генераторы.


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


Числа: int, float и complex


int хранит целые числа: положительные, отрицательные и ноль. Важная особенность Python — целые числа не ограничены по размеру. Там, где в C++ или Java переменная переполнится после определенной величины, Python спокойно посчитает число длиной в сотни знаков, пока хватает оперативной памяти.


print(2 ** 200)


# 1606938044258990275541962092341162602522202993782792835301376


float хранит числа с дробной частью. Отличить его от целого можно по точке: 10 — это int, а 10.0 — уже float. Точность у float ограничена, о чем ниже отдельный раздел.


complex хранит комплексные числа вида a + bj, где j — мнимая единица. В обычных задачах он почти не встречается, но нужен в инженерных расчетах и обработке сигналов.


a = 42 # int


b = 3.14 # float


c = 2 + 3j # complex


print(a / 4) # 10.5 — деление всегда дает float


print(a // 4) # 10 — целочисленное деление


print(a % 4) # 2 — остаток от деления


print(a ** 2) # 1764 — возведение в степень


Отдельно стоит знать про интернирование : Python заранее создает объекты для небольших целых чисел от -5 до 256 и переиспользует их. Поэтому две переменные с одинаковым маленьким числом ссылаются на один объект, а с большим — на разные. Это внутренняя оптимизация, но она объясняет неожиданное поведение оператора is, о котором спрашивают на собеседованиях.


Почему 0.1 плюс 0.2 не равно 0.3


Ловушка, на которой спотыкается каждый новичок:


print(0.1 + 0.2) # 0.30000000000000004


print(0.1 + 0.2 == 0.3) # False


Дело не в баге Python, а в устройстве компьютеров. Тип float хранит числа в двоичной системе, а дробь 0.1 в двоичной записи бесконечна — примерно как одна треть в десятичной. Компьютер обрезает ее, и накапливается крошечная погрешность.


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


print(abs(0.1 + 0.2 - 0.3) < 1e-9) # True


А для денег, где ошибка в сотых долях недопустима, берите тип Decimal из стандартного модуля:


from decimal import Decimal


print(Decimal("0.1") + Decimal("0.2")) # 0.3


Это правило стоит запомнить сразу: float для расчетов, Decimal для денег.


Строки: str


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


name = 'PROWEB'


city = "Ташкент"


text = """Первая строка


Вторая строка"""


К строкам применимы операции последовательностей: обращение по индексу, срезы, длина, склеивание, повторение.


s = "программирование"


print(len(s)) # 16 — длина


print(s[0]) # п — первый символ


print(s[-1]) # е — последний символ


print(s[0:5]) # прогр — срез


print(s[::-1]) # еинавориммаргорп — разворот строки


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


s = "Привет"


s[0] = "п" # TypeError: 'str' object does not support item assignment


s = "п" + s[1:] # так правильно


print(s) # привет


Из-за неизменяемости склеивать строки в цикле через += неэффективно: на каждой итерации создается новый объект. Правильный способ — собрать список и объединить его методом join():


cities = ["Ташкент", "Самарканд", "Бухара"]


print(", ".join(cities))


# Ташкент, Самарканд, Бухара


Самые ходовые методы строк: upper() и lower() меняют регистр, strip() срезает пробелы по краям, replace() заменяет подстроку, find() ищет позицию, split() разбивает строку на список. Подробный разбор последнего есть в отдельной статье блога.


Отдельно стоит f-строка — удобный способ подставить значения переменных в текст:


name = "Азиз"


age = 19


print(f"Меня зовут {name}, мне {age} лет")


# Меня зовут Азиз, мне 19 лет


Логический тип: bool


bool хранит всего два значения: True и False. Он нужен для условий, циклов и любых проверок.


is_student = True


has_paid = False


print(10 > 5) # True


print(type(10 > 5)) # <class 'bool'>


Формально bool — подтип целых чисел: True равен единице, False равен нулю, и их можно складывать. На этом основан удобный прием подсчета:


answers = [True, False, True, True]


print(sum(answers)) # 3 — сколько ответов верных


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


Ложными считаются: False, 0, 0.0, пустая строка "", пустой список [], пустой кортеж (), пустой словарь {}, пустое множество и None. Все остальные значения истинны.


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


items = []


if not items:


print("Список пуст")


Списки: list


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


courses = ["Python", "Frontend", "QA"]


mixed = [1, "два", 3.0, True, [4, 5]]


print(courses[0]) # Python


print(courses[-1]) # QA


print(courses[0:2]) # ['Python', 'Frontend']


Списки меняются на месте — это их главное отличие от строк и кортежей:


courses = ["Python", "Frontend", "QA"]


courses.append("Data Science") # добавить в конец


courses.insert(0, "AI") # вставить по индексу


courses.remove("QA") # удалить по значению


courses.sort() # отсортировать


print(len(courses)) # длина списка


Списки берут, когда набор данных будет меняться: пополняться, сортироваться, фильтроваться. Из минусов — поиск элемента по значению идет перебором, поэтому в очень больших списках проверка «есть ли элемент» работает медленно. Для этой задачи лучше подходят множества.


Кортежи: tuple


Кортеж — тот же список, только неизменяемый. Записывается круглыми скобками.


coordinates = (41.31, 69.28)


print(coordinates[0]) # 41.31


coordinates[0] = 0 # TypeError: 'tuple' object does not support item assignment


Логичный вопрос новичка: зачем нужен урезанный список? У кортежа три практических преимущества.


Первое — защита данных. Если набор значений не должен меняться (координаты, настройки, дни недели), кортеж гарантирует, что никакая функция его случайно не испортит. Второе — кортеж может быть ключом словаря или элементом множества, а список не может, потому что для этого объект обязан быть неизменяемым. Третье — кортежи занимают чуть меньше памяти и работают немного быстрее.


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


not_tuple = (5) # это просто число 5


real_tuple = (5,) # а это кортеж


Множества: set и frozenset


Множество — неупорядоченная коллекция уникальных элементов. Дубликаты в нем исчезают автоматически.


numbers = {1, 2, 2, 3, 3, 3}


print(numbers) # {1, 2, 3}


У множеств две сильные стороны. Первая — мгновенное удаление дубликатов из списка:


values = [1, 2, 1, 3, 2, 1]


print(list(set(values))) # [1, 2, 3]


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


Множества поддерживают операции из школьной математики: объединение, пересечение, разность.


group_a = {"Азиз", "Дилноза", "Тимур"}


group_b = {"Тимур", "Малика"}


print(group_a | group_b) # объединение


print(group_a & group_b) # пересечение: {'Тимур'}


print(group_a - group_b) # разность


Ограничения тоже есть: элементы множества не упорядочены и не имеют индексов, поэтому обратиться к numbers[0] не получится. И хранить в множестве можно только неизменяемые объекты — списки и словари туда положить нельзя.


Отдельно существует frozenset — неизменяемая версия множества. Она нужна, когда множество само должно стать ключом словаря или элементом другого множества.


frozen = frozenset(["a", "b", "c"])


Словари: dict


Словарь хранит пары «ключ — значение». Это самый используемый тип для структурированных данных: карточек товаров, профилей пользователей, настроек, ответов API.


student = {


"name": "Дилноза",


"age": 21,


"course": "Data Science",


"is_active": True


}


print(student["name"]) # Дилноза


student["city"] = "Ташкент" # добавить пару


del student["is_active"] # удалить пару


print(student.get("phone")) # None вместо ошибки


Обращение через квадратные скобки к несуществующему ключу вызывает ошибку KeyError, а метод get() в такой ситуации вернет None. Для данных из внешних источников безопаснее get().


Ключом может быть только неизменяемый объект: строка, число, кортеж. Значением — что угодно, включая списки и другие словари, глубина вложенности не ограничена.


Отдельно стоит развеять устаревшее утверждение, которое до сих пор встречается в статьях: словари в Python неупорядочены. Это перестало быть правдой начиная с версии 3.7 — теперь словарь гарантированно сохраняет порядок добавления ключей.


NoneType: значение «ничего»


None — специальный объект, который обозначает отсутствие значения. Это не ноль, не пустая строка и не False, а именно «ничего».


result = None


print(type(result)) # <class 'NoneType'>


None возвращают функции, которые ничего не возвращают явно, и методы, которые меняют объект на месте. Из-за этого возникает классическая ошибка новичка:


numbers = [3, 1, 2]


numbers = numbers.sort() # ошибка: sort() меняет список и возвращает None


print(numbers) # None


Правильно — вызвать метод отдельно или использовать функцию sorted(), которая возвращает новый список.


Проверять None принято через оператор is, а не через равенство:


if result is None:


print("Значения нет")


Изменяемые и неизменяемые типы


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


Неизменяемые типы: числа, строки, кортежи, frozenset, None. Их содержимое нельзя поменять после создания — любая операция создает новый объект.


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


Увидеть разницу помогает функция id():


number = 10


print(id(number))


number += 5


print(id(number)) # идентификатор изменился — это новый объект


items = [1, 2, 3]


print(id(items))


items.append(4)


print(id(items)) # идентификатор тот же — объект изменился на месте


Две ловушки изменяемых типов


Ловушка первая: копирование, которого не было. Присваивание списка другой переменной не создает копию, а добавляет второе имя тому же объекту.


a = [1, 2, 3]


b = a


b.append(4)


print(a) # [1, 2, 3, 4] — изменился и a


Чтобы получить настоящую копию, используйте срез, метод copy() или конструктор списка:


b = a.copy() # или a[:], или list(a)


Для вложенных списков этого мало — нужен deepcopy из модуля copy, который копирует и внутренние объекты.


Ловушка вторая: изменяемый аргумент по умолчанию. Значение по умолчанию вычисляется один раз при определении функции, а не при каждом вызове. Поэтому список «запоминает» данные между вызовами:


def add_item(item, basket=[]):


basket.append(item)


return basket


print(add_item("хлеб")) # ['хлеб']


print(add_item("молоко")) # ['хлеб', 'молоко'] — сюрприз


Правильный вариант — передавать None и создавать список внутри:


def add_item(item, basket=None):


if basket is None:


basket = []


basket.append(item)


return basket


Как узнать тип данных: type и isinstance


Есть две функции, и они решают разные задачи.


type() возвращает точный тип объекта:


print(type(5)) # <class 'int'>


print(type("текст")) # <class 'str'>


print(type([1, 2])) # <class 'list'>


isinstance() проверяет, принадлежит ли объект указанному типу, и возвращает True или False. Он же понимает наследование и умеет проверять сразу несколько типов:


print(isinstance(5, int)) # True


print(isinstance(5, (int, float))) # True — подходит любой из двух


На практике для проверок берут isinstance(), а type() используют, когда нужно именно посмотреть тип, например при отладке.


Преобразование типов


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


print(int("42")) # 42 — строка в число


print(int(3.9)) # 3 — дробная часть отбрасывается, без округления


print(float("3.14")) # 3.14


print(str(42)) # '42'


print(bool(0)) # False


print(list("abc")) # ['a', 'b', 'c']


print(tuple([1, 2])) # (1, 2)


print(set([1, 1, 2])) # {1, 2}


Чаще всего преобразование нужно после ввода данных: функция input() всегда возвращает строку, даже если пользователь ввел число.


age = input("Ваш возраст: ")


print(age + 1) # TypeError


age = int(input("Ваш возраст: "))


print(age + 1) # работает


Помните, что int() не округляет, а отбрасывает дробную часть: int(3.9) даст 3. Для округления по правилам математики есть функция round().


Преобразование срабатывает не всегда. Если строка не похожа на число, Python выдаст ValueError:


int("сто") # ValueError: invalid literal for int() with base 10: 'сто'


Аннотации типов


С версии 3.5 в Python можно подсказывать типы прямо в коде. Это не меняет поведение программы — интерпретатор аннотации не проверяет, — но делает код понятнее и позволяет редактору находить ошибки до запуска.


def greet(name: str, age: int) -> str:


return f"{name}, вам {age} лет"


price: float = 45000.0


courses: list[str] = ["Python", "QA"]


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


Какой тип выбрать под задачу


  • Целое количество, счетчик, возраст — int
  • Цена, вес, проценты — float, но для денег Decimal
  • Любой текст, ввод пользователя, ответ API до разбора — str
  • Флаг «да или нет», результат проверки — bool
  • Набор данных, который будет меняться и сортироваться — list
  • Фиксированный набор, который менять нельзя, или ключ словаря — tuple
  • Уникальные значения и быстрая проверка наличия — set
  • Данные с именованными полями, карточки, настройки — dict

Значение, которого пока нет — None


Коротко о главном


Типы данных в Python — это категории, определяющие, что хранит объект и что с ним можно делать. Основных встроенных типов девять: int, float, complex, str, bool, list, tuple, set, dict, плюс особое значение None. Типизация в языке динамическая — тип определяется при выполнении, и строгая — неявных преобразований Python не делает.


Ключевое для практики деление — изменяемые типы против неизменяемых. Списки, словари и множества меняются на месте, из-за чего присваивание не копирует их, а изменяемый аргумент по умолчанию сохраняет состояние между вызовами. Числа, строки и кортежи неизменяемы, поэтому любая операция создает новый объект.


Три вещи, которые стоит запомнить сразу: input() всегда возвращает строку, 0.1 + 0.2 не равно 0.3, а int() не округляет, а отбрасывает дробную часть.


Типы данных — это фундамент, на котором дальше строятся функции, классы и работа с базами. Освоить Python с нуля до уровня реальных проектов можно на курсе Python в PROWEB — 8 месяцев, с 14 лет, с практикой и поддержкой в трудоустройстве. Если вы еще выбираете направление в IT, пройдите бесплатный IT-тест — он подскажет, что вам ближе.


Часто задаваемые вопросы


Какие типы данных есть в Python?


Основные встроенные типы: числа int, float и complex, строки str, логический bool, списки list, кортежи tuple, множества set и frozenset, словари dict. Отдельно стоит NoneType — тип единственного значения None, обозначающего отсутствие данных.


Чем отличаются изменяемые и неизменяемые типы?


Изменяемые объекты можно менять на месте, их идентификатор при этом не меняется. К ним относятся списки, словари и множества. Неизменяемые объекты изменить нельзя — любая операция создает новый объект. Это числа, строки, кортежи, frozenset и None.


Как узнать тип переменной в Python?


Функцией type(), которая возвращает точный тип объекта, или функцией isinstance(), которая проверяет принадлежность к типу и возвращает True или False. Для проверок в коде обычно берут isinstance().


Чем список отличается от кортежа?


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


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


Это когда тип переменной определяется во время выполнения программы, а не задается заранее. Одной и той же переменной можно последовательно присвоить число, а потом строку — Python не возражает.


Почему 0.1 + 0.2 не равно 0.3?


Тип float хранит числа в двоичной системе, где дробь 0.1 записывается бесконечно и обрезается. Из-за этого накапливается погрешность. Сравнивайте дробные числа через допустимое отклонение, а для денежных расчетов используйте тип Decimal.


Как преобразовать строку в число?


Функциями int() для целых и float() для дробных: int("42") вернет число 42. Если строку нельзя интерпретировать как число, Python выдаст ошибку ValueError.


Что такое None и чем оно отличается от нуля?


None — особое значение, обозначающее отсутствие данных. Это не ноль и не пустая строка, а отдельный объект типа NoneType. Проверять его принято оператором is, а не знаком равенства.


Нужны ли новичку аннотации типов?


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



Другие статьи