Типы данных в Python
Дальше: Работа с типами — преобразования и операции по типам · Переменные · Справочник Python
Обобщения (typing): Обобщения и обобщённое программирование — теория; ниже и в typing — синтаксис Python.
Типизация в Python
Основы типизации в Python
Теория типов и осей "статика/динамика", "сильная/слабая", типобезопасность — в статьях Типы данных и Типизация.
Python — язык, в котором всё является объектом — у каждого значения есть тип, и тип задаёт допустимые операции (можно ли складывать, вызывать метод, что будет при несовместимости).
Python описывают как динамически и сильно типизированный — тип принадлежит объекту, проверка идёт в runtime, неявные преобразования между несовместимыми типами не выполняют ("5" + 2 → TypeError). Аннотации (age: int = 25) необязательны; статический анализ дают mypy, pyright и IDE — это постепенная типизация, не смена модели интерпретатора.
Термин "неявная типизация" в русскоязычных текстах часто путают со слабой — в Python это разные вещи.
Динамическая типизация
Тип принадлежит объекту, а не "ящику" переменной. Имя в коде — это ссылка на объект; тип проверяется во время выполнения.
x = 42 # x ссылается на int(42)
x = "привет" # x теперь ссылается на str — другой объект
x = [1, 2, 3] # x ссылается на list
Разбор:
- Оператор
=в Python связывает имя с объектом, а не "меняет тип переменной". - После каждого присваивания
xуказывает на новый объект в памяти. - Типом управляет сам объект (
int,str,list), поэтому проверка типов происходит в runtime. Одно имя может указывать на объекты разных типов в разные моменты времени. Аннотации (x: int = 1) необязательны и не меняют поведение интерпретатора по умолчанию. Подробнее о модели имён — в главе про переменные.
Типы без объявления в коде
При присваивании не нужно писать int age = 30 — интерпретатор создаёт объект нужного типа из литерала или выражения:
age = 30 # int
name = "Алиса" # str
Это не "слабая типизация": Python не склеивает произвольные типы в арифметике и конкатенации без явного преобразования.
Сильная типизация
Между несовместимыми типами нет "магического" приведения:
"возраст: " + 25 # TypeError
Разбор:
- Оператор
+дляstrожидает справа строку. - Попытка сложить
strиintвызываетTypeError, так как автоматического склеивания разных типов нет. - Для корректной конкатенации нужно явное преобразование через
str(25).
В языках со слабой типизацией (например, JavaScript) такое выражение часто даёт "возраст: 25". В Python нужно явно:
"возраст: " + str(25)
Узкие допустимые преобразования в выражениях всё же есть — это фиксирует правила:
True + 1 # 2 — bool наследует int
3 + 4.5 # 7.5 — int с float даёт float
bool("text") # True — явное преобразование через bool()
Play ITЗагрузка интерактивного демо…
Play ITЗагрузка интерактивного демо…
Типы данных в Python
Категории типов данных
Все типы данных в Python можно разделить на две большие категории:
- Изменяемые (mutable, мутабельные) - объект можно изменить после создания (как раз list, dict, set);
- Неизменяемые (immutable, иммутабельные) - значение фиксируется при создании (int, str, tuple, frozenset).
Неизменяемые объекты безопасны при передаче в функции — их нельзя случайно изменить. Они могут использоваться как ключи в словарях (потому что хэшируемы). При "изменении" неизменяемого объекта создаётся новый объект:
s = "hello"
s = s + " world" # Это НОВАЯ строка, старая удаляется сборщиком мусора
Изменяемые объекты позволяют добавлять, удалять или изменять элементы "на месте":
my_list = [1, 2]
my_list.append(3) # my_list изменился, id остаётся тем же
Python — динамически и сильно типизированный язык: тип известен у объекта в runtime, а int + str без преобразования вызовет ошибку.
Python использует модель ссылок на объекты: имена указывают на объекты в памяти, а не копируют значение в "ячейку" переменной.
Неизменяемые (immutable) объекты нельзя изменить после создания — числа (int, float, complex), строки (str), кортежи (tuple), frozenset, bool, None. Слово "атомарный" в других контекстах означает неделимость операции в потоках — не путайте с immutability. При "изменении" строки или числа создаётся новый объект, имя переназначается на него.
a = 42
b = a # b ссылается на тот же объект
a = a + 1 # Создаётся новый int(43), a указывает на него
print(b) # 42 — значение b не изменилось
== сравнивает значения; is — один ли это и тот же объект в памяти (id совпадает). Для проверки на отсутствие значения используйте x is None, а не x == None.
x = "hello"
y = "hello"
print(x == y) # True — одинаковое содержимое
print(x is y) # может быть True для коротких строк (интернирование)
print(id(x), id(y))
Изменяемые (mutable) объекты могут менять содержимое, сохраняя тот же id: К ним относятся:
- Списки (list)
- Словари (dict)
- Множества (set)
- Экземпляры пользовательских классов
Пример:
lst1 = [1, 2, 3]
lst2 = lst1 # lst2 ссылается на тот же объект
lst2.append(4)
print(lst1) # [1, 2, 3, 4] — изменился и lst1!
Разбор:
lst2 = lst1копирует ссылку, обе переменные смотрят на одинlist.append(4)меняет объект "на месте", поэтому изменение видно через оба имени.- Для независимых коллекций нужен
copy()илиdeepcopy()в случае вложенных структур. Чтобы избежать неожиданного поведения, нужно создавать копии:
lst2 = lst1.copy() # Поверхностная копия
# или
lst2 = lst1[:] # Для списков
# или
import copy
lst2 = copy.deepcopy(lst1) # Глубокая копия (для вложенных структур)
copy() и срез [:] — поверхностная копия: вложенные списки внутри dict/list останутся общими. Даже неизменяемые объекты живут в куче как полноценные объекты; разница в том, что append меняет список на месте, а a = a + 1 для int создаёт новый int.
А теперь рассмотрим все встроенные типы подробнее.
Число
Число (int, float, complex) поддерживает целые (int), вещественные (float) и комплексные числа (complex). int не ограничен по размеру (в отличие от многих других языков).
<переменная> = <целое_число>
<переменная> = <вещественное_число>
<переменная> = <вещественное_число>e<степень>
<переменная> = <вещественное_число>j
<переменная> = <целое_число> + <вещественное_число>j
Простой пример:
age = 30
Сложный пример:
import math
def calculate_compound_interest(principal, rate, times_per_year, years):
return principal * (1 + rate / times_per_year) ** (times_per_year * years)
final_amount = round(calculate_compound_interest(1000, 0.05, 12, 10), 2)
Выше мы видим результат функции, вычисляющей сложные проценты, округляется до двух знаков. Переменная final_amount содержит число, полученное через математическую формулу и округление — типичный сценарий финансовых вычислений.
int — целые числа. Могут быть любого размера (ограничены только памятью). Поддерживают отрицательные значения.
x = 100
big_number = 10**100 # Очень большое число — работает!
float — вещественные числа. Представлены в формате с плавающей точкой (IEEE 754). Имеют ограниченную точность (~15–17 знаков).
pi = 3.1415926535
scientific = 6.02e23 # 6.02 × 10²³
Из-за особенностей представления 0.1 + 0.2 != 0.3 — классическая проблема всех float. Для финансовых вычислений лучше использовать модуль decimal.
complex — комплексные числа. Формат: a + bj, где j — мнимая единица.
z = 3 + 4j
print(z.real) # 3.0
print(z.imag) # 4.0
Используются в научных расчётах, сигнал-обработке и математике.
Примеры:
count = 42price = 199.99avogadro = 6.02e23imaginary = 5jcomplex_num = 3 + 4j
Булево
Булево (bool) это подтип int, где True == 1, False == 0. Возникает как результат сравнений, логических выражений.
<переменная> = True
<переменная> = False
<переменная> = <выражение_сравнения>
<переменная> = <логическое_выражение>
Простой пример:
is_active = True
Сложный пример:
is_eligible = (
user['age'] >= 18 and
user['verified'] and
any(role in user['roles'] for role in ['admin', 'moderator']) and
not user.get('blocked', False)
)
Здесь условие включает проверку возраста, флага верификации, наличие одной из ролей через генератор выражения (any) и безопасное извлечение поля с помощью get. Результат — булево значение, вычисленное на основе структурированных данных.
Два значения: True и False. Является подклассом int: True == 1, False == 0.
is_ready = True
if is_ready:
print("Готов!")
Любое значение можно привести к булеву через bool():
bool(0) # False
bool("") # False
bool([1, 2]) # True
Полезно в условиях и проверках.
Примеры:
is_valid = Truehas_permission = user.role == "admin"ready = (x > 0) and (y < 100)
Строка
Строка (str) это неизменяемая последовательность символов Unicode. Поддерживает f-строки, форматирование, методы, индексацию, срезы.
Строки неизменяемы — любое "изменение" создаёт новую строку. Разворот за одну запись — срез s[::-1] (однострочные приёмы). Для списков тот же срез даёт новый list, а разворот на месте — lst.reverse() (подробнее). Частые методы (upper, find, replace, split и проверки is…) — в Работа с типами — методы строк.
<переменная> = "<текст>"
<переменная> = '<текст>'
<переменная> = """<многострочный текст>"""
<переменная> = f"<шаблон {выражение}>"
<переменная> = "<шаблон {}>".format(<значение>)
Простой пример:
name = "Тимур"
Сложный пример:
from datetime import datetime
greeting = f"""
Добро пожаловать, {user.get('name', 'Гость').title()}!
Сегодня: {datetime.now().strftime('%d.%m.%Y')}
Время: {datetime.now().strftime('%H:%M')}
Ваш баланс: {user.get('balance', 0):,.2f} ₽.
""".strip()
Здесь F-строка с вложенными вызовами методов, форматированием даты, заглавными буквами имени и денежного формата (:,.2f). Использовано безопасное извлечение значений и удаление лишних пробелов.
F-строки (форматированные строковые литералы) появились в Python 3.6 — самый удобный способ форматирования строк.
name = "Мария"
age = 28
greeting = f"Привет, {name}! Тебе {age} лет."
В фигурные скобки можно вставлять переменные, вызовы функций, выражения.
result = f"Квадрат 5: {5 ** 2}, длина имени: {len(name)}"
F-строки поддерживают форматирование:
price = 1234.5678
formatted = f"Цена: {price:,.2f} ₽" # "Цена: 1,234.57 ₽"
date = f"Дата: {datetime.now():%d.%m.%Y}"
Примеры:
name = "Алиса"message = 'Привет, мир!'doc = """Это многострочная строка."""greeting = f"Здравствуйте, {user.name}!"report = "Баланс: {:.2f}".format(balance)
None
None — специальное значение, обозначающее отсутствие значения.
<переменная> = None
Примеры:
result = Nonecache = Nonecallback = None
result = None
У него есть собственный тип: NoneType, и None — единственный экземпляр типа NoneType. В системе типов Python — аналог null (JavaScript), nil (Ruby), NULL (SQL).
Используется как значение по умолчанию, возврат из функций, метка отсутствия данных.
def find_user(user_id):
if user_id in database:
return database[user_id]
return None # пользователя нет
None не то же самое, что False, 0 или пустая строка. При проверке в if ведёт себя как False, но не равен ему:
bool(None) # False
None == False # False!
Правильно проверять: if value is None:
Последовательности (Sequences)
Последовательности — упорядоченные коллекции, доступные по индексу.
Это str, list, tuple.
Есть также диапазон range:
<переменная> = range(<конец>)
<переменная> = range(<начало>, <конец>)
<переменная> = range(<начало>, <конец>, <шаг>)
Примеры:
indices = range(5)pages = range(1, 11)odds = range(1, 20, 2)
Кортежи (tuple)
Кортежи (tuple) представляют собой неизменяемые списки и часто используются для группировки данных.
<переменная> = (<элемент>, <элемент>, ...)
<переменная> = <элемент>,
<переменная> = tuple(<итерируемый_объект>)
Примеры:
point = (10, 20)singleton = (42,)rgb = tuple([255, 128, 0])
Пусть вас не пугает это слово, оно означает "несколько значений":
point = (10, 20)
rgb = (255, 128, 0)
Они могут использоваться как ключи в словарях, но быстрее и легче, чем списки.
Play ITЗагрузка интерактивного демо…
Словарь
Словарь (dict) это упорядоченная (начиная с Python 3.7) коллекция пар "ключ-значение". Аналог Object в JavaScript или HashMap в Java.
Ключи должны быть хэшируемыми (обычно неизменяемыми). Эффективны для поиска по ключу (O(1)).
Простой пример:
person = {"name": "Иван", "age": 28}
Сложный пример:
config = {
key: value.upper() if isinstance(value, str) and key.endswith('_KEY') else value
for key, value in os.environ.items()
if key.startswith('APP_')
}
Разбор:
- Это dictionary comprehension: новая
dictсобирается в одном выражении. for key, value in os.environ.items()перебирает пары переменных окружения.if key.startswith('APP_')фильтрует только нужные ключи.- Тернарное выражение
A if condition else Bподнимает регистр для строковых значений ключей, заканчивающихся на_KEY. Это генератор словаря, который фильтрует переменные окружения по префиксуAPP_, и если ключ заканчивается на_KEY, преобразует строковое значение в верхний регистр. Демонстрирует мощь компактного синтаксиса и условной логики.
<переменная> = {<ключ>: <значение>, <ключ>: <значение>, ...}
<переменная> = dict(<пары_ключ_значение>)
<переменная> = {<ключ_выражение>: <значение_выражение> for <элемент> in <коллекция> if <условие>}
Примеры:
person = {"name": "Иван", "age": 28}config = dict(host="localhost", port=8080)squares = {x: x**2 for x in range(5)}
Операции dict:
| Действие | Синтаксис / метод |
|---|---|
| Добавить или заменить | d[key] = value, d.update(other), d.setdefault(key, default) |
| Прочитать | d[key], d.get(key, default) |
| Удалить | del d[key], d.pop(key), d.popitem(), d.clear() |
| Проверить ключ | key in d |
| Ключи / значения / пары | d.keys(), d.values(), d.items() |
Шпаргалка с примерами для get, update, setdefault, popitem и остальных методов — в разделе Методы словаря главы про коллекции.
Подробнее о преобразованиях — Работа с типами.
Множества
Множества (set и frozenset) хранят уникальные элементы без повторений. Неупорядоченные (до Python 3.7), сейчас порядок зависит от версии.
unique_tags = {"python", "web", "backend"}
set — изменяемое множество.
frozenset — неизменяемое, можно использовать как ключ в словаре.
Их используют для удаления дубликатов, операций типа объединения, пересечения, разности. Пример:
a = {1, 2, 3}
b = {3, 4, 5}
a & b # {3} — пересечение
a | b # {1, 2, 3, 4, 5} — объединение
Изменяемое множество (set):
<переменная> = {<элемент>, <элемент>, ...}
<переменная> = set(<итерируемый_объект>)
<переменная> = {<выражение> for <элемент> in <коллекция> if <условие>}
Примеры:
tags = {"python", "web"}unique = set([1, 2, 2, 3])squares = {x**2 for x in range(5)}
Неизменяемое множество (frozenset):
<переменная> = frozenset({<элемент>, <элемент>, ...})
<переменная> = frozenset(<итерируемый_объект>)
Примеры:
constants = frozenset({"pi", "e"})immutable_keys = frozenset(user.roles)
Операции set (изменяемый):
| Действие | Синтаксис / метод |
|---|---|
| Добавить | add(value) |
| Удалить | remove(value), discard(value) |
| Проверить наличие | value in s |
| Объединение / пересечение | |, &, - или union, intersection, difference |
frozenset — те же операции чтения и теоретико-множественные, без add/remove.
Play ITЗагрузка интерактивного демо…
Список
Список (list) это изменяемая упорядоченная коллекция. Аналог массива в JS.
<переменная> = [<элемент>, <элемент>, ...]
<переменная> = list(<итерируемый_объект>)
<переменная> = [<выражение> for <элемент> in <коллекция> if <условие>]
Примеры:
items = [1, 2, 3]chars = list("hello")evens = [x for x in range(10) if x % 2 == 0]
Простой пример:
numbers = [1, 2, 3]
Сложный пример:
filtered_logs = [
{**log, 'timestamp': format_timestamp(log['ts']), 'level_name': LOG_LEVELS[log['level']]}
for log in logs
if log['level'] >= MIN_LEVEL and is_valid_source(log['source'])
]
Это списковое включение с распаковкой словаря (**log), добавлением новых полей, преобразованием уровня логирования и фильтрацией. Результат — новый список с расширенной информацией.
Пошагово:
for log in logsитерирует исходные записи лога.- Условие после
ifотбрасывает сообщения ниже минимального уровня и из неподходящих источников. {**log, ...}копирует исходные поля словаря и добавляет/переопределяет новые ключи.format_timestamp(log['ts'])иLOG_LEVELS[log['level']]выполняют преобразование "сырого" лога в читаемый формат.
Операции list:
| Действие | Синтаксис / метод |
|---|---|
| Добавить в конец | append(value) |
| Вставить по индексу | insert(index, value) |
| Прочитать | lst[index] |
| Заменить | lst[index] = value |
| Удалить по индексу | pop(index), del lst[index] |
| Удалить по значению | remove(value) |
| Срез | lst[start:stop:step] |
| Развернуть порядок | reverse() на месте; копия — lst[::-1], list(reversed(lst)) — подробнее |
Функция
Функция (function) — объект первого класса. Поддерживают декораторы, замыкания, лямбды.
Простой пример:
def greet(name):
return f"Привет, {name}!"
Сложный пример:
Код ITЗагрузка примера кода…
Здесь приведён в пример декоратор высшего порядка, реализующий повторные попытки выполнения функции при ошибках. Позволяет добавить устойчивость к временным сбоям сети. Широко используется в системах, взаимодействующих с внешними API.
Обёртка wrapper(*args, **kwargs) принимает любые аргументы и передаёт их оригинальной функции — подробнее в разделе произвольное число аргументов. Функции мы изучим отдельно.
def <имя_функции>(<параметры>):
return <выражение>
<переменная> = lambda <параметры>: <выражение>
<переменная> = <имя_функции>
Примеры:
def square(x): return x * xdouble = lambda x: x * 2— см. анонимные функции и map/filter/reducehandler = process_request
Бинарные данные
Бинарные данные нужны для работы с сырыми байтами (например, файлы, сеть, шифрование).
bytes — неизменяемая последовательность байтов
data = b"Hello"
print(data[0]) # 72 (ASCII-код 'H')
bytearray — изменяемая версия
b = bytearray(b"Hello")
b[0] = 104 # заменяем 'H' на 'h'
print(b) # bytearray(b'hello')
Их используют при чтении/записи файлов в бинарном режиме (open(..., 'rb')), работе с сетевыми протоколами, в криптографии и сериализации.
Например, чтобы перевести строку в байты: text.encode('utf-8'), а чтобы перевести байты в строку: data.decode('utf-8').
Байты (bytes):
<переменная> = b"<последовательность_байтов>"
<переменная> = bytes(<итерируемый_объект_целых>)
<переменная> = "<строка>".encode("<кодировка>")
Примеры:
data = b"Hello"raw = bytes([72, 101, 108, 108, 111])encoded = "привет".encode("utf-8")
Массив байтов (bytearray):
<переменная> = bytearray(b"<последовательность_байтов>")
<переменная> = bytearray(<итерируемый_объект_целых>)
<переменная> = bytearray("<строка>", "<кодировка>")
Примеры:
buffer = bytearray(b"Hello")mutable = bytearray([1, 2, 3])text_bytes = bytearray("текст", "utf-8")