← Назад к вопросам

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

1.0 Junior🔥 221 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Что такое Python с точки зрения типизации

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

1. Динамическая типизация

# Тип определяется в runtime, а не compile time
x = 5        # int
print(type(x))  # <class 'int'>

x = "hello"  # Теперь x — это строка
print(type(x))  # <class 'str'>

x = [1, 2, 3]  # Теперь x — это список
print(type(x))  # <class 'list'>

Переменная не привязана к типу — тип может меняться.

2. Утиная типизация (Duck Typing)

# "Если ходит как утка и крякает как утка, то это утка"

class Dog:
    def make_sound(self):
        return "Гав"

class Cat:
    def make_sound(self):
        return "Мяу"

class Phone:
    def make_sound(self):
        return "Звонок"

def play_sound(something):
    # Не проверяем тип, просто вызываем метод
    print(something.make_sound())

play_sound(Dog())    # Гав
play_sound(Cat())    # Мяу
play_sound(Phone())  # Звонок
# Все работает, хоть они и разные классы

Функция работает со ВСЕМИ объектами, у которых есть метод make_sound().

3. Слабая типизация

# Python пытается конвертировать типы неявно

# Неявное преобразование в некоторых операциях
print("5" * 3)       # "555" — строка умножается
print(5 + 3.14)      # 8.14 — int + float = float
print([1, 2] + [3])  # [1, 2, 3] — конкатенация списков

# Но не всегда
print("5" + 3)  # TypeError: can only concatenate str (not "int") to str

Python позволяет гибкость, но не настолько слаб как JavaScript.

4. Type Hints (Аннотации типов)

От Python 3.5+ появились опциональные аннотации типов.

# Без аннотаций (работает, но неясно)
def add(a, b):
    return a + b

# С аннотациями (тот же код, но яснее)
def add(a: int, b: int) -> int:
    return a + b

# Аннотации не проверяются Python runtime
result = add("hello", "world")  # "helloworld" — работает!
# Python не выбросит ошибку, даже если не соблюданы типы

5. Статическая проверка типов (Type Checking)

Для проверки типов используют отдельные инструменты:

# mypy — самый популярный type checker
from typing import List, Dict, Optional

def process_users(users: List[Dict[str, int]]) -> Optional[str]:
    if not users:
        return None
    return users[0].get("name")

# mypy проверит это
# mypy check script.py

Запускается отдельно, Python это игнорирует.

Сравнение типизации

STATIЧНО ТИПИЗИРОВАННЫЕ (compile time):
- Java: int x = 5; x = "hello"; // Ошибка компиляции
- TypeScript: const x: number = 5; x = "hello"; // Ошибка
- C: int x = 5; x = "hello"; // Ошибка компиляции

ДИНАМИЧЕСКИ ТИПИЗИРОВАННЫЕ (runtime):
- Python: x = 5; x = "hello"; # Ok, работает
- JavaScript: let x = 5; x = "hello"; // Ok, работает
- Ruby: x = 5; x = "hello"; # Ok, работает

Примеры с type hints

Базовые типы

from typing import List, Dict, Tuple, Set, Optional

def greet(name: str) -> str:
    return f"Hello, {name}"

def get_user_by_id(user_id: int) -> Optional[Dict[str, str]]:
    # Может вернуть dict или None
    if user_id > 0:
        return {"id": str(user_id), "name": "John"}
    return None

def process_items(items: List[str]) -> Tuple[int, List[str]]:
    # Функция принимает список строк
    # Возвращает кортеж (количество, список)
    return (len(items), items)

def get_config() -> Dict[str, int]:
    return {"timeout": 30, "retries": 3}

Сложные типы

from typing import Union, Callable, Any

# Union — может быть один из нескольких типов
def parse_value(value: Union[int, str]) -> str:
    return str(value)

# Callable — функция
def apply_operation(x: int, y: int, op: Callable[[int, int], int]) -> int:
    return op(x, y)

apply_operation(5, 3, lambda a, b: a + b)  # 8

# Any — любой тип (используй редко)
def dangerous_function(value: Any) -> Any:
    return value

Пользовательские типы

from typing import Protocol
from abc import ABC, abstractmethod

# Протокол (структурная типизация)
class Drawable(Protocol):
    def draw(self) -> None:
        ...

class Circle:
    def draw(self) -> None:
        print("Drawing circle")

class Square:
    def draw(self) -> None:
        print("Drawing square")

def render(obj: Drawable) -> None:
    obj.draw()  # Работает для любого объекта с методом draw()

render(Circle())   # Ok
render(Square())   # Ok

# Традиционное наследование (номинативная типизация)
class Animal(ABC):
    @abstractmethod
    def sound(self) -> str:
        pass

class Dog(Animal):
    def sound(self) -> str:
        return "Гав"

Когда type hints помогают

1. IDE автодополнение

def get_user(user_id: int) -> Dict[str, str]:
    return {"name": "John", "email": "john@example.com"}

user = get_user(1)
user.  # IDE подскажет: name, email, keys, values, etc.

2. Обнаружение ошибок

from typing import List

def sum_numbers(numbers: List[int]) -> int:
    return sum(numbers)

result = sum_numbers([1, 2, "3"])  # mypy найдёт ошибку

3. Документация

# Вместо:
def create_user(name, email, age):
    # Какие типы?
    # Что возвращает?
    pass

# Пиши:
def create_user(name: str, email: str, age: int) -> Dict[str, Any]:
    # Понятно с первого взгляда
    pass

Структурная vs Номинативная типизация

# НОМИНАТИВНАЯ (Java, C++) — нужно явное наследование
class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        pass

class Circle(Shape):  # Явно наследуется
    def area(self) -> float:
        return 3.14

shape: Shape = Circle()  # Ok

# СТРУКТУРНАЯ (Python с Protocol) — "структура важнее имени"
class Drawable(Protocol):
    def draw(self) -> None:
        ...

class Square:
    def draw(self) -> None:  # Не наследуется, но совпадает структура
        pass

obj: Drawable = Square()  # mypy одобрит

Практическое применение

# Проект без type hints (сложно поддерживать)
def process_data(data):
    result = []
    for item in data:
        if item > 0:
            result.append(item * 2)
    return result

# Проект с type hints (понятнее)
from typing import List

def process_data(data: List[int]) -> List[int]:
    result = []
    for item in data:
        if item > 0:
            result.append(item * 2)
    return result

Важно помнить

✅ Type hints — это просто подсказки для инструментов (mypy, PyCharm) ✅ Python runtime их игнорирует ✅ def func(x: int) -> str: не гарантирует, что x будет int ✅ Для проверки нужен отдельный инструмент (mypy, Pydantic, pytest)

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

result = add_numbers("5", "10")  # Работает! Возвращает "510"
# Python не выбросит ошибку, даже если нарушены type hints

# mypy найдёт это:
# error: Argument 1 to "add_numbers" has incompatible type "str"; expected "int"

Вывод

Python с точки зрения типизации:

  1. Динамически типизирован — типы проверяются в runtime
  2. Слабо типизирован — позволяет некоторые преобразования
  3. Утиная типизация — важна структура, не имя типа
  4. Опционально статично типизируется — через type hints и mypy
  5. Гибкий — ты выбираешь, когда использовать type hints