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

Что такое type identity?

2.0 Middle🔥 111 комментариев
#Python Core

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

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

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

Type Identity в Python

Определение

Type Identity — это способность системы типов отличать один тип от другого и гарантировать, что операция работает с правильным типом. В Python это означает:

  1. id() функция — уникальный идентификатор объекта в памяти
  2. type() функция — определение типа объекта
  3. isinstance() функция — проверка принадлежности к типу
  4. Type Hints — статическая типизация для IDE и mypy

1. id() — Идентичность объекта

Каждый объект в памяти имеет уникальный идентификатор (address in memory):

# id() возвращает адрес объекта в памяти
a = [1, 2, 3]
b = [1, 2, 3]

print(id(a))  # 140320849201344
print(id(b))  # 140320849201412  <- разные объекты!

print(a == b)   # True (равное содержимое)
print(a is b)   # False (разные объекты)

# Одна переменная = один и тот же объект
c = a
print(id(a))  # 140320849201344
print(id(c))  # 140320849201344
print(a is c)  # True (один объект)

2. type() — Определение типа

# type() возвращает класс объекта
print(type(42))          # <class 'int'>
print(type("Hello"))     # <class 'str'>
print(type([1, 2, 3]))   # <class 'list'>
print(type({"a": 1}))    # <class 'dict'>

class User:
    pass

user = User()
print(type(user))  # <class '__main__.User'>

# Проверка типа
if type(user) == User:
    print("user is User")

3. isinstance() — Проверка типа (ЛУЧШЕ)

# isinstance() лучше type(), потому что работает с наследованием

class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()

# ❌ type() не учитывает наследование
print(type(dog) == Animal)  # False (неправильно!)

# ✅ isinstance() учитывает наследование
print(isinstance(dog, Dog))     # True
print(isinstance(dog, Animal))  # True (наследник!)
print(isinstance(dog, str))     # False

4. Type Hints — Статическая типизация

Это самая важная часть Type Identity в современном Python:

# ❌ Без типов — IDE не может проверить
def calculate_price(quantity, price):
    return quantity * price

# Что если кто-то передаст неправильный тип?
print(calculate_price("5", 10))  # "55555" <- ошибка!

# ✅ С типами — IDE предупредит и mypy поймает ошибку
def calculate_price(quantity: int, price: float) -> float:
    return quantity * price

print(calculate_price("5", 10))  # mypy ERROR: Argument 1 has incompatible type "str"; expected "int"

Базовые Type Hints

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

# Простые типы
def get_name(user_id: int) -> str:
    pass

def is_valid(email: str) -> bool:
    pass

# Collections
def process_numbers(numbers: List[int]) -> int:
    return sum(numbers)

def get_user(user_id: int) -> Dict[str, str]:
    return {"id": str(user_id), "name": "John"}

# Optional (может быть None)
def find_user(user_id: int) -> Optional[str]:
    if user_id == 1:
        return "John"
    return None

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

# Tuple с точными типами
def get_coordinates() -> Tuple[float, float]:
    return (40.7128, -74.0060)

Type Hints для классов

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

@dataclass
class User:
    id: int
    name: str
    email: str
    created_at: datetime
    bio: Optional[str] = None  # Может быть None
    
    def update_bio(self, new_bio: str) -> None:
        """Обновляет биографию"""
        self.bio = new_bio
    
    def get_full_info(self) -> Dict[str, any]:
        return {
            "id": self.id,
            "name": self.name,
            "email": self.email,
            "bio": self.bio
        }

5. Проверка Type Identity с mypy

mypy — это статический type checker для Python. Проверяет типы БЕЗ выполнения кода:

# file: main.py

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

result1 = greet("Ivan")     # ✓ OK
result2 = greet(123)        # ✗ ERROR: Argument 1 has incompatible type "int"; expected "str"

class DataProcessor:
    def process(self, data: List[int]) -> int:
        return sum(data)

processor = DataProcessor()
result3 = processor.process([1, 2, 3])    # ✓ OK
result4 = processor.process(["a", "b"])   # ✗ ERROR
# Запуск mypy
mypy main.py
# Результат:
# main.py:8: error: Argument 1 to "greet" has incompatible type "int"; expected "str"
# main.py:18: error: List item 0 has incompatible type "str"; expected "int"

6. Type Guards (проверка типов в runtime)

from typing import Union, TypeGuard

class Dog:
    def bark(self):
        return "Woof!"

class Cat:
    def meow(self):
        return "Meow!"

# Type guard функция
def is_dog(animal: Union[Dog, Cat]) -> TypeGuard[Dog]:
    return isinstance(animal, Dog)

# Использование
def make_sound(animal: Union[Dog, Cat]) -> str:
    if is_dog(animal):  # Type guard сказал, что это Dog
        return animal.bark()  # IDE знает, что у animal есть bark()
    else:
        return animal.meow()

7. Generic типы (параметризованные типы)

from typing import Generic, TypeVar

T = TypeVar('T')  # Тип-переменная

class Container(Generic[T]):
    """Контейнер, который может содержать любой тип T"""
    def __init__(self, value: T):
        self.value = value
    
    def get(self) -> T:
        return self.value

# Использование
int_container: Container[int] = Container(42)
str_container: Container[str] = Container("Hello")

print(int_container.get())  # 42 (type: int)
print(str_container.get())  # "Hello" (type: str)

# mypy будет проверять типы
result: int = int_container.get()      # ✓ OK
result2: str = int_container.get()     # ✗ ERROR: expected str, got int

8. Protocol — структурная типизация

from typing import Protocol

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(shape: Drawable) -> None:
    shape.draw()

# Не нужно наследоваться от Drawable!
# Просто нужно иметь метод draw()
render(Circle())   # ✓ OK (имеет draw)
render(Square())   # ✓ OK (имеет draw)
render(42)         # ✗ ERROR (нет draw)

9. Литеральные типы (Literal Types)

from typing import Literal

def set_status(status: Literal["active", "inactive", "pending"]) -> None:
    """Status может быть только одним из этих значений"""
    print(f"Status set to {status}")

set_status("active")     # ✓ OK
set_status("inactive")   # ✓ OK
set_status("deleted")    # ✗ ERROR: Literal["active", "inactive", "pending"]

10. Best Practices для Type Identity

# ✅ Правило 1: Всегда типизируйте публичные функции
def fetch_user(user_id: int) -> Optional[Dict[str, any]]:
    pass

# ✅ Правило 2: Используйте isinstance, не type
if isinstance(value, str):
    pass

# ✅ Правило 3: Используйте dataclass для структур данных
from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str
    email: str

# ✅ Правило 4: Запускайте mypy в CI/CD
# в Makefile или pre-commit hook
run_mypy:
    mypy app/ --strict

# ✅ Правило 5: Используйте Union для нескольких типов
def process(value: Union[int, str]) -> str:
    return str(value)

# ❌ Правило 6: НЕ используйте any
def bad_function(value: any):
    pass  # Теряется смысл типизации

Итог

Type Identity — это система, которая гарантирует, что код работает с правильными типами:

  1. id() — адрес объекта в памяти
  2. type() — получить тип объекта
  3. isinstance() — проверить тип (учитывает наследование)
  4. Type Hints — статическая типизация (для IDE и mypy)
  5. mypy — проверка типов без выполнения кода

Модерный Python требует типизации. Это не обязательно, но это:

  • Делает код понятнее
  • Предотвращает ошибки
  • Помогает IDE давать лучшие подсказки
  • Улучшает поддерживаемость

Если код без типов — это как вождение без фар ночью. Технически возможно, но очень опасно.

Что такое type identity? | PrepBro