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

Можно ли присвоить конкретный тип аргумента функции в Python?

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

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

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

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

Присвоение типов аргументов функций в Python

Да, в Python можно явно указать типы аргументов функции с помощью type hints (аннотаций типов). Это улучшает читаемость кода, помогает инструментам статического анализа и IDE в автодополнении, но не является обязательным во время выполнения программы.

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

Постоянно используемый способ указания типов:

def greet(name: str, age: int) -> str:
    """Функция с аннотациями типов.
    
    Args:
        name: Имя пользователя (строка)
        age: Возраст пользователя (целое число)
    
    Returns:
        Приветствие (строка)
    """
    return f"Привет, {name}! Тебе {age} лет."

# Правильное использование
result = greet("Alice", 30)  # ✅
print(result)

# Python НЕ принудительно проверяет типы
result = greet("Bob", "thirty")  # ⚠️ Работает, но неправильный тип!
print(result)  # "Привет, Bob! Тебе thirty лет."

2. Встроенные типы

def process_data(
    count: int,
    name: str,
    price: float,
    is_active: bool,
    items: list,
    mapping: dict,
    unique: set,
    pair: tuple
) -> None:
    """Аннотации с встроенными типами"""
    print(f"{count} {name} {price} {is_active}")

process_data(
    count=5,
    name="Product",
    price=19.99,
    is_active=True,
    items=[1, 2, 3],
    mapping={'key': 'value'},
    unique={1, 2, 3},
    pair=(1, 2)
)

3. Типизированные коллекции (typing модуль)

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

# До Python 3.9 нужно использовать typing
def process_users(
    users: List[str],              # Список строк
    user_ids: Dict[str, int],      # Словарь {строка -> число}
    unique_names: Set[str],        # Множество строк
    coordinates: Tuple[int, int],  # Кортеж двух чисел
    optional_value: Optional[str]  # Может быть str или None
) -> List[Dict[str, any]]:
    """Сложные типы через typing модуль"""
    return [{"name": user, "id": user_ids.get(user)} for user in users]

# Python 3.9+ поддерживает встроенные обобщённые типы
def process_users_new(
    users: list[str],              # Встроенная list
    user_ids: dict[str, int],      # Встроенная dict
    unique_names: set[str],        # Встроенная set
    coordinates: tuple[int, int]   # Встроенная tuple
) -> list[dict[str, any]]:
    return [{"name": user, "id": user_ids.get(user)} for user in users]

4. Union и Optional типы

from typing import Union, Optional

# Union — может быть несколько типов
def parse_value(value: Union[str, int, float]) -> str:
    """Принимает строку, число или float"""
    return str(value)

# Optional[X] = Union[X, None]
def get_user(user_id: int) -> Optional[dict]:
    """Может вернуть словарь или None"""
    if user_id > 0:
        return {"id": user_id, "name": "User"}
    return None  # Нет пользователя

# Python 3.10+ использует синтаксис |
def process(value: str | int | None) -> str:
    """Эквивалент Union[str, int, None]"""
    if value is None:
        return "No value"
    return str(value)

5. Callable (функции как параметры)

from typing import Callable

def apply_operation(
    x: int,
    y: int,
    operation: Callable[[int, int], int]  # Функция берёт 2 числа, возвращает число
) -> int:
    """Применить операцию к двум числам"""
    return operation(x, y)

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

def multiply(a: int, b: int) -> int:
    return a * b

print(apply_operation(5, 3, add))          # 8
print(apply_operation(5, 3, multiply))     # 15

6. Generic типы (шаблонные классы)

from typing import Generic, TypeVar

T = TypeVar('T')  # Обобщённый тип

class Container(Generic[T]):
    """Обобщённый контейнер"""
    def __init__(self, value: T):
        self._value = value
    
    def get(self) -> T:
        return self._value
    
    def set(self, value: T) -> None:
        self._value = value

# Использование
int_container: Container[int] = Container[int](42)
print(int_container.get())  # 42

str_container: Container[str] = Container[str]("Hello")
print(str_container.get())  # "Hello"

# Без явного указания типа
generic_container = Container(3.14)
print(generic_container.get())  # 3.14

7. Проверка типов во время выполнения

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

# ❌ Type hints НЕ проверяются во время выполнения
def add(a: int, b: int) -> int:
    return a + b

add("5", "3")  # ❌ Работает, но неправильно!
print(add("5", "3"))  # "53" вместо 8

# ✅ Проверка вручную
def add_safe(a: int, b: int) -> int:
    if not isinstance(a, int) or not isinstance(b, int):
        raise TypeError(f"Ожидаю int, получил {type(a)} и {type(b)}")
    return a + b

add_safe("5", "3")  # TypeError!

8. Использование Pydantic для проверки типов

Pydantic автоматически проверяет типы:

from pydantic import BaseModel, field_validator
from typing import Optional

class User(BaseModel):
    """Модель с проверкой типов"""
    id: int
    name: str
    email: str
    age: Optional[int] = None
    
    @field_validator('age')
    @classmethod
    def age_must_be_positive(cls, v):
        if v is not None and v < 0:
            raise ValueError('age must be >= 0')
        return v

# ✅ Правильные данные
user = User(id=1, name="Alice", email="alice@example.com", age=30)
print(user)

# ❌ Неправильный тип
try:
    user = User(id="not_a_number", name="Bob", email="bob@example.com")
except ValueError as e:
    print(f"Ошибка валидации: {e}")
    # validation error for 'User' when calling '__init__': ...

# ❌ Неправильное значение
try:
    user = User(id=2, name="Charlie", email="charlie@example.com", age=-5)
except ValueError as e:
    print(f"Ошибка: {e}")

9. Mypy — статический анализ типов

Утилита для проверки типов перед запуском кода:

# main.py
def add(a: int, b: int) -> int:
    return a + b

result = add(5, 3)      # ✅ OK
result = add("5", "3")  # ❌ Ошибка типа
# Проверить типы
mypy main.py

# Вывод:
# main.py:7: error: Argument 1 to "add" has incompatible type "str"; expected "int"
# main.py:7: error: Argument 2 to "add" has incompatible type "str"; expected "int"

10. Декоратор для проверки типов

from functools import wraps
from typing import Any, Callable

def type_check(func: Callable) -> Callable:
    """Декоратор для проверки типов аргументов"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        # Получить аннотации функции
        hints = func.__annotations__
        
        # Проверить позиционные аргументы
        for i, (arg, param_name) in enumerate(zip(args, func.__code__.co_varnames)):
            if param_name in hints:
                expected_type = hints[param_name]
                if not isinstance(arg, expected_type):
                    raise TypeError(
                        f"Аргумент '{param_name}' должен быть {expected_type.__name__}, "
                        f"получил {type(arg).__name__}"
                    )
        
        # Проверить именованные аргументы
        for param_name, value in kwargs.items():
            if param_name in hints:
                expected_type = hints[param_name]
                if not isinstance(value, expected_type):
                    raise TypeError(
                        f"Аргумент '{param_name}' должен быть {expected_type.__name__}, "
                        f"получил {type(value).__name__}"
                    )
        
        return func(*args, **kwargs)
    
    return wrapper

@type_check
def divide(a: int, b: int) -> float:
    return a / b

print(divide(10, 2))      # ✅ 5.0
try:
    print(divide("10", 2))  # ❌ TypeError
except TypeError as e:
    print(f"Ошибка: {e}")

11. Лучшие практики

# ✅ ПРАВИЛЬНО: Всегда используй type hints
def calculate_total_price(
    items: list[float],
    tax_rate: float,
    discount_percent: Optional[float] = None
) -> float:
    """Расчёт итоговой цены с налогом и скидкой"""
    total = sum(items)
    total += total * tax_rate
    
    if discount_percent is not None:
        total -= total * (discount_percent / 100)
    
    return total

# ✅ Документируй сложные типы
from typing import TypeAlias

UserData: TypeAlias = dict[str, str | int]

def process_user_data(user: UserData) -> None:
    """Обработать данные пользователя"""
    pass

# ✅ Используй Pydantic для критических проверок
from pydantic import BaseModel

class Config(BaseModel):
    database_url: str
    port: int
    debug: bool = False

# ✅ Используй mypy для статического анализа
# mypy --strict myproject/

Вывод

Да, можно присвоить конкретный тип аргумента функции:

  1. Type hints — основной способ через аннотации

    def func(arg: int) -> str:
        pass
    
  2. Type hints НЕ обязательны во время выполнения (Python динамический)

  3. Для проверки во время выполнения:

    • Pydantic (автоматическая валидация)
    • isinstance() проверки вручную
    • Пользовательские декораторы
  4. Для статической проверки:

    • mypy (до запуска)
    • IDE подсказки (PyCharm, VS Code)
  5. Best practice: Всегда используй type hints для читаемости и поддерживаемости кода.