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

Зачем нужна интроспекция?

1.2 Junior🔥 131 комментариев
#Python Core

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

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

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

Зачем нужна интроспекция

Интроспекция (Introspection) — это способность программы исследовать самоё себя во время выполнения: узнавать о типах объектов, их атрибутах, методах и других свойствах. Python — один из самых интроспективных языков, что делает его удобным для разработки.

Основные инструменты интроспекции

1. type() — определить тип объекта

type(5)                    # <class 'int'>
type("hello")              # <class 'str'>
type([1, 2, 3])            # <class 'list'>
type({"key": "value"})    # <class 'dict'>

class MyClass:
    pass

obj = MyClass()
type(obj)                  # <class '__main__.MyClass'>

2. isinstance() — проверить является ли объект экземпляром класса

isinstance(5, int)                    # True
isinstance("hello", str)              # True
isinstance(5, (int, float))           # True — проверка нескольких типов
isinstance([1, 2], list)              # True

# Со своими классами
class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()
isinstance(dog, Dog)                  # True
isinstance(dog, Animal)               # True — работает с наследованием

3. dir() — список всех атрибутов и методов

dir("hello")
# ['__add__', '__class__', '__contains__', ..., 'capitalize', 'casefold', ..., 'upper', 'zfill']

dir([1, 2, 3])
# ['__add__', '__class__', ..., 'append', 'clear', 'copy', 'count', 'extend', ..., 'sort']

class MyClass:
    def method1(self):
        pass
    
    def method2(self):
        pass

obj = MyClass()
dir(obj)
# [..., 'method1', 'method2']

4. getattr() / setattr() / hasattr() — работа с атрибутами

class Person:
    name = "John"
    age = 30

obj = Person()

# getattr — получить значение атрибута
getattr(obj, 'name')               # "John"
getattr(obj, 'email', 'unknown')   # 'unknown' — значение по умолчанию

# hasattr — проверить наличие атрибута
hasattr(obj, 'name')               # True
hasattr(obj, 'email')              # False

# setattr — установить атрибут
setattr(obj, 'name', 'Jane')
obj.name                           # "Jane"

# delattr — удалить атрибут
delattr(obj, 'name')
hasattr(obj, 'name')               # False

5. callable() — проверить является ли объект вызываемым

def my_function():
    pass

callable(my_function)              # True
callable(lambda x: x)              # True

class MyClass:
    def __call__(self):
        return "Called"

obj = MyClass()
callable(obj)                      # True

callable(5)                        # False
callable("string")                 # False

6. inspect модуль — глубокий анализ

import inspect

def my_function(a, b, c=10):
    """Описание функции"""
    return a + b + c

# Получить сигнатуру функции
sig = inspect.signature(my_function)
print(sig)  # (a, b, c=10)

# Получить параметры
for param_name, param in sig.parameters.items():
    print(f"{param_name}: {param.default}")  # a: <class 'inspect._empty'>
                                               # b: <class 'inspect._empty'>
                                               # c: 10

# Получить исходный код
print(inspect.getsource(my_function))
# def my_function(a, b, c=10):
#     return a + b + c

# Получить информацию о классе
class MyClass:
    def method1(self):
        pass
    def method2(self):
        pass

for name, method in inspect.getmembers(MyClass, predicate=inspect.ismethod):
    print(name)  # method1, method2

Практические примеры использования

1. Динамическое создание объектов

# Создание объекта по его имени (строка)
class Dog:
    def bark(self):
        return "Woof!"

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

def create_animal(animal_name):
    """Создать животное по названию"""
    # Получить класс по имени
    animal_class = globals()[animal_name]  # Интроспекция!
    return animal_class()

dog = create_animal('Dog')
print(dog.bark())  # Woof!

cat = create_animal('Cat')
print(cat.meow())  # Meow!

2. Автоматическая валидация данных

import inspect
from typing import get_type_hints

class User:
    name: str
    age: int
    email: str

def validate(obj, data):
    """Валидировать данные по типам класса"""
    hints = get_type_hints(obj.__class__)
    
    for field, field_type in hints.items():
        if field in data:
            if not isinstance(data[field], field_type):
                raise TypeError(f"{field} must be {field_type.__name__}")
            setattr(obj, field, data[field])

user = User()
validate(user, {'name': 'John', 'age': 30, 'email': 'john@example.com'})
print(user.name)  # John
print(user.age)   # 30

# Неверный тип выбросит исключение
# validate(user, {'age': 'thirty'})  # TypeError

3. Сериализация объектов

import json
from dataclasses import asdict, dataclass

@dataclass
class Person:
    name: str
    age: int
    city: str

person = Person("Alice", 30, "New York")

# Сериализация через интроспекцию
json_str = json.dumps(asdict(person))
print(json_str)  # {"name": "Alice", "age": 30, "city": "New York"}

# Десериализация
data = json.loads(json_str)
person2 = Person(**data)
print(person2.name)  # Alice

4. Декоратор для логирования всех методов

import functools
import inspect

def log_all_methods(cls):
    """Автоматически добавить логирование ко всем методам"""
    for name, method in inspect.getmembers(cls, predicate=inspect.isfunction):
        if not name.startswith('_'):
            original = method
            
            @functools.wraps(original)
            def wrapper(self, *args, **kwargs):
                print(f"Calling {name}({args}, {kwargs})")
                result = original(self, *args, **kwargs)
                print(f"Result: {result}")
                return result
            
            setattr(cls, name, wrapper)
    
    return cls

@log_all_methods
class Calculator:
    def add(self, a, b):
        return a + b
    
    def multiply(self, a, b):
        return a * b

calc = Calculator()
calc.add(2, 3)      # Логирует вызов и результат
calc.multiply(4, 5) # Логирует вызов и результат

5. Автоматическая генерация API документации

import inspect

class API:
    def get_user(self, user_id: int) -> dict:
        """Получить информацию о пользователе"""
        return {"id": user_id, "name": "John"}
    
    def create_user(self, name: str, email: str) -> bool:
        """Создать нового пользователя"""
        return True

def generate_docs(cls):
    """Автоматически генерировать документацию"""
    print(f"API Documentation for {cls.__name__}\n")
    
    for name, method in inspect.getmembers(cls, predicate=inspect.isfunction):
        if not name.startswith('_'):
            sig = inspect.signature(method)
            doc = inspect.getdoc(method)
            print(f"- {name}{sig}")
            print(f"  {doc}\n")

generate_docs(API)
# API Documentation for API
#
# - get_user(user_id: int) -> dict
#   Получить информацию о пользователе
#
# - create_user(name: str, email: str) -> bool
#   Создать нового пользователя

6. ORM-подобная магия (как Django модели)

class Model:
    def __init__(self, **kwargs):
        # Интроспекция! Установить все переданные атрибуты
        for key, value in kwargs.items():
            setattr(self, key, value)
    
    def to_dict(self):
        # Получить все атрибуты, исключая методы
        result = {}
        for name, value in inspect.getmembers(self):
            if not name.startswith('_') and not callable(value):
                result[name] = value
        return result

class User(Model):
    pass

user = User(name="Alice", age=30, email="alice@example.com")
print(user.to_dict())
# {'age': 30, 'email': 'alice@example.com', 'name': 'Alice'}

7. Динамическое создание классов

# Создать класс динамически
MyDynamicClass = type('MyDynamicClass', (), {
    'x': 10,
    'method': lambda self: "Hello from dynamic class"
})

obj = MyDynamicClass()
print(obj.x)              # 10
print(obj.method())       # Hello from dynamic class

# Проверить что это реально класс
print(isinstance(obj, MyDynamicClass))  # True
print(type(obj))                        # <class 'MyDynamicClass'>

Встроенные функции интроспекции

# id() — уникальный идентификатор объекта
x = [1, 2, 3]
print(id(x))  # 140234567890432

# vars() — словарь атрибутов объекта
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(10, 20)
print(vars(p))  # {'x': 10, 'y': 20}

# repr() и str() — строковое представление
print(repr(5))         # 5
print(repr("hello"))   # 'hello'
print(str([1, 2, 3]))  # [1, 2, 3]

# help() — встроенная справка
help(len)  # Выведет подробную информацию о функции len()

Когда использовать интроспекцию

# ✅ Используйте интроспекцию для:
# - Фреймворков (Django, FastAPI, Flask)
# - Сериализации (JSON, YAML, Protocol Buffers)
# - Тестирования (pytest использует её массово)
# - Валидации данных
# - ORM (SQLAlchemy)
# - API документации (Swagger, OpenAPI)
# - Декораторов и метапрограммирования

# ❌ Избегайте чрезмерной интроспекции:
# - Может понизить производительность
# - Усложняет отладку
# - Сложнее для понимания

Примеры из популярных библиотек

# Django ORM
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    # Django использует интроспекцию для:
    # - Создания таблиц в БД
    # - Валидации данных
    # - Генерации админ-панели

# FastAPI
from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
    return item

# FastAPI использует интроспекцию для:
# - Валидации входных данных
# - Генерации Swagger документации
# - Автоматической сериализации/десериализации

# pytest
import pytest

def test_something():
    assert True

# pytest использует интроспекцию для:
# - Поиска тестов (функции начинающиеся с test_)
# - Сбора параметров через @pytest.mark.parametrize
# - Dependency injection через fixtures

Итоги

Интроспекция нужна для:

  1. Динамического программирования — создание кода во время выполнения
  2. Фреймворков — автоматизация типичных задач
  3. Метапрограммирования — изменение поведения кода
  4. Отладки — понимание структуры объектов
  5. Тестирования — автоматический поиск и запуск тестов
  6. Сериализации — преобразование объектов
  7. Документирования — автоматическая генерация справочников

Интроспекция — это мощный инструмент Python, который делает язык чрезвычайно гибким и выразительным. Все крупные фреймворки используют её интенсивно.

Зачем нужна интроспекция? | PrepBro