Готов разобраться с типами данных в Python? У тебя есть два отличных варианта для изучения этой темы: посмотреть видеолекцию от преподавателя университета JavaRush или изучить детальный текстовый материал, который я специально подготовил для тебя ниже.
Коротко: в Python встроенных типов немного, и делятся они на две группы. Числа (int, float, complex), логический bool, строки str и байты bytes изменить после создания нельзя, как и кортеж tuple. А список list, словарь dict, множество set и bytearray менять можно. Тип переменной при этом нигде не объявляют: Python определяет его сам во время выполнения, а узнать его можно функцией type(). Разработку приложений можно рассматривать как работу с некоторыми данными, а точнее их хранение и обработку. Сегодня хотелось бы затронуть ключевой аспект языка Python. Как данные хранятся в Python? В отличие от многих других языков программирования, в Python все является объектом. Это не просто красивые слова, а фундаментальная особенность, которая влияет на все аспекты работы с данными. Давайте поговорим о типах данных в Python и возможностях работы с ними. Как ни крути, это фундамент наших знаний языка программирования, но в Python этот фундамент имеет свои уникальные особенности.

Кратко

  • В Python все значения это объекты, поэтому даже у числа 42 есть методы и атрибуты.
  • Тип int не ограничен разрядностью: число растет, пока хватает памяти.
  • float это те же 64 бита по стандарту IEEE 754, поэтому 0.1 + 0.2 не равно 0.3, а для денег берут Decimal.
  • Главное деление типов проходит не по содержимому, а по изменяемости: от нее зависит, увидят ли изменение другие имена, ссылающиеся на тот же объект.
  • bool наследуется от int, поэтому True + 1 дает 2.
  • Аннотации типов ничего не проверяют во время работы программы: они нужны редактору, линтеру и человеку.

Философия типов данных в Python

Прежде чем углубиться в конкретные типы, важно понять философию Python. В языках вроде Java или C++ есть четкое разделение на примитивные типы (которые хранят значения напрямую) и ссылочные типы (которые хранят ссылки на объекты). Python пошел другим путем: все данные представлены объектами. Это означает, что даже простое число 42 является полноценным объектом класса int со своими методами, атрибутами и поведением. Посмотрите сами:
number = 42
print(type(number))  # <class 'int'>
print(number.__class__)  # <class 'int'>
print(number.bit_length())  # 6 (количество бит для представления числа)
print(number.__add__(8))  # 50 (то же что и number + 8)
Немного истории. Python был создан Гвидо ван Россумом в конце 1980-х годов с целью создать язык, который был бы простым в изучении, но мощным в использовании. Одним из ключевых решений было сделать все объектами. Это решение кардинально повлияло на дизайн языка и сделало его более последовательным, но в то же время внесло некоторые особенности, которые могут удивить новичков. В Python переменные не содержат данные напрямую, они содержат ссылки на объекты. Это может привести к неожиданному поведению, которое мы разберем подробнее.

Числовые типы данных

Тип int: Целые числа произвольной точности

Начнем с самого удивительного типа в Python, это int. В отличие от большинства языков программирования, где целые числа ограничены определенным количеством бит (например, 32 или 64), в Python 3 тип int может представлять числа произвольной точности, и у него нет максимального значения. Что это означает на практике? Вы можете работать с числами настолько большими, насколько позволяет память вашего компьютера:
# Обычные числа
small_number = 42
medium_number = 1000000

# Очень большие числа
big_number = 123456789012345678901234567890
very_big_number = 2 ** 1000  # 2 в степени 1000!

print(very_big_number)  # Выведет огромное число
print(len(str(very_big_number)))  # 302 цифры!

# Факториал 100
factorial_100 = 1
for i in range(1, 101):
    factorial_100 *= i
print(f"100! = {factorial_100}")
Но как это работает внутри? Python автоматически переключается между разными внутренними представлениями чисел в зависимости от их размера. Для небольших чисел используется эффективное представление, а для больших динамические структуры данных.

Тип float: Числа с плавающей точкой и их подводные камни

Тип float в Python реализован как double в языке C, что обычно означает 64-битное представление с двойной точностью по стандарту IEEE 754.
pi = 3.14159
scientific = 1.23e-4  # Научная нотация: 0.000123
big_scientific = 1.5e308  # Очень большое число
Отсюда и главный подводный камень: не всякую десятичную дробь можно точно записать в двоичном виде, поэтому привычная арифметика иногда дает неожиданный результат.
print(0.1 + 0.2)         # 0.30000000000000004
print(0.1 + 0.2 == 0.3)  # False

# Сравнивать такие числа лучше с допуском
import math
print(math.isclose(0.1 + 0.2, 0.3))  # True

# А там, где нужна точность до копейки, берут decimal
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))  # 0.3

Тип complex: Комплексные числа

Python имеет встроенную поддержку комплексных чисел, что довольно редко для языков программирования общего назначения:
# Создание комплексных чисел
z1 = 3 + 4j  # j или J для мнимой единицы
z2 = complex(1, 2)  # 1 + 2j
z3 = complex('5+6j')  # Из строки

print(f"z1 = {z1}")
print(f"Действительная часть: {z1.real}")  # 3.0
print(f"Мнимая часть: {z1.imag}")  # 4.0
print(f"Модуль: {abs(z1)}")  # 5.0
print(f"Сопряженное: {z1.conjugate()}")  # (3-4j)

# Арифметические операции
z4 = z1 + z2
z5 = z1 * z2
print(f"Сумма: {z4}")
print(f"Произведение: {z5}")

# Полярная форма
import cmath
magnitude, phase = cmath.polar(z1)
print(f"Модуль: {magnitude}, Фаза: {phase}")

Булевый тип: bool и концепция истинности

Тип bool в Python может принимать только два значения: True и False. Но тут есть интересные особенности.
is_python_awesome = True
is_difficult = False

# bool наследуется от int!
print(isinstance(True, int))  # True
print(True + 1)  # 2
print(False * 100)  # 0
print(True == 1)  # True
print(False == 0)  # True

Строковый тип: str и его особенности

Строки в Python это неизменяемые (immutable) последовательности символов Unicode. Это означает, что после создания строки ее нельзя изменить: любая "модификация" создает новую строку.
text = "Hello, World!"
print(id(text))  # Адрес в памяти

# "Изменение" строки
text = text.replace("World", "Python")
print(id(text))  # Другой адрес - новый объект!

Коллекции: Контейнеры для данных

Список (list): Изменяемая упорядоченная коллекция

Списки это, пожалуй, самая важная структура данных в Python. Они изменяемы, упорядочены и могут содержать элементы разных типов:
# Создание списков
empty_list = []
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, [1, 2, 3]]  # Разные типы
nested = [[1, 2], [3, 4], [5, 6]]

# Динамическое создание
dynamic = list(range(10))  # [0, 1, 2, ..., 9]
from_string = list("hello")  # ['h', 'e', 'l', 'l', 'o']

Списковые включения (List Comprehensions)

Это мощная и элегантная возможность Python:
# Базовый синтаксис
squares = [x**2 for x in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# С условием
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]

# Вложенные циклы
matrix = [[i*j for j in range(1, 4)] for i in range(1, 4)]
print(matrix)  # [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

# Более сложные примеры
words = ["hello", "world", "python", "programming"]
capitalized = [word.upper() for word in words if len(word) > 5]
print(capitalized)  # ['PYTHON', 'PROGRAMMING']

# Обработка файлов
lines = ["  line 1  \n", "  line 2  \n", "  line 3  \n"]
cleaned = [line.strip() for line in lines if line.strip()]
print(cleaned)  # ['line 1', 'line 2', 'line 3']

Кортеж (tuple): Неизменяемая упорядоченная коллекция

Кортежи похожи на списки, но неизменяемы после создания:
# Создание кортежей
empty_tuple = ()
single_item = (42,)  # Запятая обязательна!
coordinates = (10, 20)
rgb = (255, 128, 0)

# Без скобок тоже работает
point = 10, 20  # Автоматически создается кортеж
print(type(point))  # <class 'tuple'>

# Распаковка кортежей
x, y = coordinates
r, g, b = rgb

# Множественное присваивание и обмен значениями
a, b = 1, 2
a, b = b, a  # Обмен значениями переменных!
print(a, b)  # 2 1

# Именованные кортежи
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x)  # 10
print(p.y)  # 20

Словарь (dict): Ассоциативный массив

Словари в Python реализованы как хеш-таблицы и являются одной из самых оптимизированных структур данных:
# Создание словарей
empty_dict = {}
person = {
    "name": "Alice",
    "age": 30,
    "city": "Moscow",
    "hobbies": ["reading", "swimming"]
}

# Различные способы создания
dict_from_pairs = dict([("a", 1), ("b", 2), ("c", 3)])
dict_from_kwargs = dict(name="Bob", age=25)
dict_from_zip = dict(zip(["x", "y", "z"], [1, 2, 3]))

# Методы словарей
print(person.keys())    # dict_keys(['name', 'age', 'city', 'hobbies'])
print(person.values())  # dict_values(['Alice', 30, 'Moscow', ['reading', 'swimming']])
print(person.items())   # dict_items([...])

# Безопасное получение значений
age = person.get("age", 0)  # Вернет 0, если ключа нет
occupation = person.get("occupation", "Unknown")

# Обновление
person.update({"occupation": "Developer", "age": 31})

Множество (set): Коллекция уникальных элементов

Множества полезны для операций с уникальными элементами и математических операций:
# Создание множеств
empty_set = set()  # Не {} - это пустой словарь!
numbers = {1, 2, 3, 4, 5}
from_list = set([1, 1, 2, 2, 3, 3])  # {1, 2, 3}
from_string = set("hello")  # {'h', 'e', 'l', 'o'}

# Операции с множествами
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}

print(set1 & set2)  # Пересечение: {3, 4}
print(set1 | set2)  # Объединение: {1, 2, 3, 4, 5, 6}
print(set1 - set2)  # Разность: {1, 2}
print(set1 ^ set2)  # Симметричная разность: {1, 2, 5, 6}

# Проверки
print(1 in set1)           # True
print({1, 2}.issubset(set1))  # True
print(set1.issuperset({1, 2}))  # True
print(set1.isdisjoint({7, 8}))  # True

Специальные типы

None: Представление отсутствия значения

None это специальный объект в Python, представляющий отсутствие значения:
# None как значение по умолчанию
def greet(name=None):
    if name is None:
        name = "Anonymous"
    return f"Hello, {name}!"

# None возвращается автоматически
def no_return():
    print("This function returns None")

result = no_return()
print(result)  # None

# Проверка на None
value = None
if value is None:  # Используйте 'is', не '=='
    print("Value is None")

# Список со значениями None
data = [1, None, 3, None, 5]
cleaned = [x for x in data if x is not None]
print(cleaned)  # [1, 3, 5]

bytes и bytearray: Работа с бинарными данными

Для работы с бинарными данными Python предоставляет типы bytes (неизменяемый) и bytearray (изменяемый):
# Создание bytes
data = b"Hello"  # Литерал bytes
from_list = bytes([72, 101, 108, 108, 111])  # Из списка чисел
from_string = "Hello".encode('utf-8')  # Кодирование строки

print(data)  # b'Hello'
print(data[0])  # 72 (ASCII код 'H')

# bytearray - изменяемая версия
mutable_data = bytearray(b"Hello")
mutable_data[0] = ord('h')  # Заменяем 'H' на 'h'
print(mutable_data)  # bytearray(b'hello')

# Работа с файлами
with open('example.txt', 'wb') as f:
    f.write(b"Binary data")

with open('example.txt', 'rb') as f:
    content = f.read()  # Возвращает bytes
    print(type(content))  # <class 'bytes'>

Динамическая типизация и ее последствия

В Python тип переменной определяется во время выполнения, а не во время компиляции:
# Переменная может менять тип
variable = 42        # int
print(type(variable))

variable = "hello"   # str
print(type(variable))

variable = [1, 2, 3] # list
print(type(variable))

# Это создает гибкость, но может привести к ошибкам
def process_data(data):
    return data.upper()  # Что если data не строка?

# process_data(42)  # AttributeError: 'int' object has no attribute 'upper'

Type Hints: Аннотации типов

Начиная с Python 3.5, появились аннотации типов, которые помогают сделать код более читаемым и позволяют инструментам статического анализа находить ошибки. Сам синтаксис с тех пор заметно упростился, и ниже он приведен в современном виде:
from collections.abc import Callable

# Базовые аннотации
def greet(name: str) -> str:
    return f"Hello, {name}!"

def add_numbers(a: int, b: int) -> int:
    return a + b

# Списки и словари аннотируют встроенными типами
def process_scores(scores: list[int]) -> dict[str, int | float]:
    return {
        "total": sum(scores),
        "average": sum(scores) / len(scores) if scores else 0,
        "count": len(scores)
    }

# Значение может отсутствовать: вертикальная черта вместо Optional
def find_user(user_id: int) -> dict[str, str] | None:
    # Может вернуть словарь или None
    users = {1: {"name": "Alice", "email": "alice@example.com"}}
    return users.get(user_id)

# Функция как параметр
def apply_operation(numbers: list[int], operation: Callable[[int], int]) -> list[int]:
    return [operation(num) for num in numbers]

def square(x: int) -> int:
    return x * x

result = apply_operation([1, 2, 3, 4], square)  # [1, 4, 9, 16]

# Переменные с аннотациями
age: int = 25
name: str = "Alice"
scores: list[float] = [95.5, 87.2, 92.1]
user_data: dict[str, str | int] = {"name": "Bob", "age": 30}
Если в других материалах вы встретите записи вроде List[int] и Optional[str], знайте: это тот же смысл, но старый синтаксис. С версии 3.9 списки и словари аннотируют встроенными типами, а с 3.10 вместо Optional и Union пишут вертикальную черту. Псевдонимы из модуля typing продолжают работать, но документация помечает их как устаревшие.

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

Понимание того, какие типы изменяемы, а какие нет, критически важно в Python:
Тип Можно менять Что хранит
int, float, complex нет числа
bool нет истина и ложь, подтип int
str нет текст в Unicode
bytes нет двоичные данные
tuple нет упорядоченный набор фиксированного состава
frozenset нет множество, которое нельзя дополнить
None нет отсутствие значения
list да упорядоченный изменяемый набор
dict да пары ключ и значение
set да уникальные элементы без порядка
bytearray да изменяемые двоичные данные
пользовательские классы да по умолчанию атрибуты можно менять

Сравнение с другими языками программирования

Аспект Python Java C++ JavaScript
Типизация Динамическая, сильная Статическая, сильная Статическая, слабая Динамическая, слабая
Все объекты Да Нет (примитивы) Нет Да
Целые числа Произвольная точность Фиксированный размер Фиксированный размер Один тип Number
Строки Неизменяемые Unicode Неизменяемые Unicode Изменяемые Неизменяемые
Null/None None null nullptr null, undefined
Массивы/Списки Динамические Фиксированные/ArrayList Статические/vector Динамические
Duck typing Да Нет Нет Да

Заключение

Типы данных в Python это не просто способ хранения информации, это целая философия языка. Принцип "все есть объект" делает Python последовательным и мощным, но требует глубокого понимания особенностей поведения объектов. Ключевые принципы для запоминания:
  1. Все является объектом: даже числа имеют методы и атрибуты.
  2. Изменяемость критически важна: понимайте разницу между mutable и immutable типами.
  3. Duck typing: важно поведение объекта, а не его формальный тип.
  4. Динамическая типизация: с большой гибкостью приходит большая ответственность.
  5. Аннотации типов: используйте их для улучшения читаемости и отлова ошибок.
Практические советы:
  • Для финансовых расчетов используйте Decimal, не float.
  • Будьте осторожны с изменяемыми аргументами функций по умолчанию.
  • Используйте is для сравнения с None, == для сравнения значений.
  • Применяйте списковые включения для краткости и производительности.
  • Добавляйте аннотации типов в критически важный код.
Особенности, которые стоит помнить:
  • Интернирование небольших чисел и строк.
  • Поведение оператора += для разных типов.
  • Особенности сравнения с NaN.
  • Подводные камни с вложенными изменяемыми структурами.
Python предоставляет богатую и выразительную систему типов, которая при правильном использовании позволяет писать элегантный, читаемый и эффективный код. Понимание особенностей каждого типа данных это ключ к мастерству в Python разработке. Как говорится в Zen of Python: "Simple is better than complex" и "Explicit is better than implicit". Используйте правильные типы данных для правильных задач, и ваш код будет не только работать корректно, но и легко поддерживаться и развиваться.

Вопросы и ответы

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

Встроенных типов немного: числа int, float и complex, логический bool, строка str, двоичные bytes и bytearray, коллекции list, tuple, dict, set и frozenset, а также специальный объект None. Все остальное строится уже из них или из пользовательских классов.

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

Функцией type(x): она вернет объект класса, и при печати вы увидите запись вида class int. Если нужно именно проверить принадлежность типу, берите isinstance(x, int): в отличие от сравнения типов напрямую, эта проверка учитывает наследование.

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

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

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

Потому что float это двоичное число двойной точности по стандарту IEEE 754, а десятичную дробь 0.1 в двоичном виде точно записать нельзя. Результат получается 0.30000000000000004. Для денег и любых точных расчетов берите модуль decimal: Decimal("0.1") + Decimal("0.2") даст ровно 0.3.

Зачем нужны аннотации типов, если Python их не проверяет?

Именно поэтому и нужны: во время выполнения аннотации ничего не проверяют и не замедляют программу, но их читают редактор, линтер и статические анализаторы вроде mypy. Они же служат документацией: по сигнатуре сразу видно, что функция принимает и что возвращает.

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

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