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

Что происходит внутри Python при создании класса?

1.2 Junior🔥 301 комментариев
#DevOps и инфраструктура#Django

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

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

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

Что происходит внутри Python при создании класса

Этот вопрос о метаклассах, дескрипторах и подробном разборе процесса. Это средний-продвинутый уровень, но важно для глубокого понимания Python.

1. Основной процесс: от синтаксиса к объекту

# Когда мы пишем:
class User:
    count = 0  # Class attribute
    
    def __init__(self, name):
        self.name = name  # Instance attribute
        User.count += 1
    
    def greet(self):
        return f"Hello, {self.name}"

# Внутри Python происходит ЭТО:

2. Фаза 1: Парсинг и выполнение класс-блока

# Синтаксис class создаёт НОВЫЙ scope (пространство имён)

# Это примерно эквивалентно:
def create_class():
    # 1. Создаём новое пустое пространство имён
    namespace = {}
    
    # 2. Выполняем код класса в этом пространстве
    # (как будто это простой скрипт)
    namespace['count'] = 0
    
    def __init__(self, name):
        self.name = name
        User.count += 1
    
    namespace['__init__'] = __init__
    
    def greet(self):
        return f"Hello, {self.name}"
    
    namespace['greet'] = greet
    
    # 3. Возвращаем этот namespace
    return namespace

namespace = create_class()
print(namespace.keys())
# dict_keys(['count', '__init__', 'greet', '__module__', '__qualname__'])

3. Фаза 2: Вызов метакласса

# ЭТА фаза создаёт настоящий класс-объект

from typing import Type

class User:
    pass

# На самом деле это примерно:
namespace = {
    '__module__': '__main__',
    '__qualname__': 'User',
    # ... остальной код ...
}

# 1. Определяется метакласс (по умолчанию type)
metaclass = type  # Можно переопределить

# 2. Вызывается метакласс (как обычный класс)
User = type(
    'User',  # name
    (),  # bases (родительские классы)
    namespace  # dict (атрибуты и методы)
)

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

4. Фаза 3: Подробный процесс метакласса

# Когда вызывается type(...), происходит:

class type:
    def __new__(mcs, name, bases, namespace):
        """
        mcs: сам метакласс (type)
        name: имя класса ('User')
        bases: кортеж родителей ((object,))
        namespace: словарь атрибутов {}
        """
        
        # 1. Подготовка базовых атрибутов
        if '__module__' not in namespace:
            namespace['__module__'] = name.split('.')[0]
        
        if '__qualname__' not in namespace:
            namespace['__qualname__'] = name
        
        # 2. Обработка дескрипторов
        # (методы, property, staticmethod, classmethod)
        for key, value in namespace.items():
            if hasattr(value, '__set_name__'):
                # Это дескриптор, вызываем его __set_name__
                value.__set_name__(type, key)
        
        # 3. Создание нового объекта класса
        cls = super().__new__(mcs, name, bases, namespace)
        
        # 4. Регистрация в подклассах (для ABC)
        # ...
        
        # 5. Вызов __init_subclass__ (если определён)
        for base in bases:
            if hasattr(base, '__init_subclass__'):
                base.__init_subclass__()
        
        return cls
    
    def __init__(cls, name, bases, namespace):
        """
        Инициализация готового класса
        """
        super().__init__(name, bases, namespace)
        # Можно добавить логику здесь

5. Практический пример: трассировка создания

# Создадим метакласс, который печатает что происходит

class DebugMeta(type):
    def __new__(mcs, name, bases, namespace):
        print(f"\n🔨 Создание класса: {name}")
        print(f"  Родители: {bases}")
        print(f"  Атрибуты: {list(namespace.keys())}")
        
        # Вызываем родительский __new__
        cls = super().__new__(mcs, name, bases, namespace)
        
        print(f"  → Класс создан: {cls}")
        print(f"  → Type класса: {type(cls)}")
        
        return cls
    
    def __init__(cls, name, bases, namespace):
        print(f"\n⚙️  Инициализация класса: {name}")
        super().__init__(name, bases, namespace)
        print(f"  → Инициализация завершена")

class User(metaclass=DebugMeta):
    count = 0
    
    def __init__(self, name):
        self.name = name
    
    def greet(self):
        return f"Hello, {self.name}"

print(f"\n✅ User класс: {User}")
print(f"✅ Type User: {type(User)}")

# Output:
# 🔨 Создание класса: User
#   Родители: (<class 'object'>,)
#   Атрибуты: ['__module__', '__qualname__', 'count', '__init__', 'greet']
#   → Класс создан: <class '__main__.User'>
#   → Type класса: <class '__main__.DebugMeta'>
# 
# ⚙️  Инициализация класса: User
#   → Инициализация завершена
# 
# ✅ User класс: <class '__main__.User'>
# ✅ Type User: <class '__main__.DebugMeta'>

6. Фаза 4: Регистрация дескрипторов

# __set_name__ вызывается для каждого дескриптора

class Descriptor:
    def __set_name__(self, owner, name):
        """
        owner: класс, которому принадлежит дескриптор
        name: имя атрибута
        """
        print(f"Дескриптор {self} добавлен в {owner.__name__}.{name}")
        self.name = name
    
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return f"Получение {self.name} из {obj}"

class User:
    email = Descriptor()
    age = Descriptor()

# Output:
# Дескриптор <__main__.Descriptor object at ...> добавлен в User.email
# Дескриптор <__main__.Descriptor object at ...> добавлен в User.age

user = User()
print(user.email)  # Получение email из <__main__.User object at ...>

7. Пример: создание через type() динамически

# Можно создавать классы в runtime!

# Вариант 1: Простой способ
User = type('User', (), {
    'count': 0,
    '__init__': lambda self, name: setattr(self, 'name', name),
    'greet': lambda self: f"Hello, {self.name}"
})

user = User("Alice")
print(user.greet())  # Hello, Alice

# Вариант 2: С наследованием
class Animal:
    def speak(self):
        return "Sound"

Dog = type('Dog', (Animal,), {
    'speak': lambda self: "Woof!"
})

dog = Dog()
print(dog.speak())  # Woof!
print(isinstance(dog, Animal))  # True

# Вариант 3: С методами
def init(self, name):
    self.name = name

def greet(self):
    return f"Hello, {self.name}"

User = type('User', (), {
    '__init__': init,
    'greet': greet
})

user = User("Bob")
print(user.greet())  # Hello, Bob

8. init_subclass — это когда подкласс создаётся

class Plugin:
    plugins = []
    
    def __init_subclass__(cls, **kwargs):
        """
        Вызывается когда СОЗДАЁТСЯ подкласс!
        """
        super().__init_subclass__(**kwargs)
        print(f"Регистрация плагина: {cls.__name__}")
        Plugin.plugins.append(cls)

class AudioPlugin(Plugin):
    pass

class VideoPlugin(Plugin):
    pass

print(f"\nЗарегистрировано плагинов: {Plugin.plugins}")
# Output:
# Регистрация плагина: AudioPlugin
# Регистрация плагина: VideoPlugin
# 
# Зарегистрировано плагинов: [<class '__main__.AudioPlugin'>, <class '__main__.VideoPlugin'>]

# Практическое применение: автоматическая регистрация
# Классы регистрируются при их определении!

9. Полная последовательность (шпаргалка)

Шаг 1: Парсинг
  ↓ Python видит 'class User:'
  ↓ Создаёт новый namespace (dict)
  ↓ Выполняет код в теле класса

Шаг 2: Подготовка
  ↓ Добавляет __module__, __qualname__
  ↓ Обрабатывает декораторы (@property, @classmethod, @staticmethod)

Шаг 3: Вызов метакласса __new__
  ↓ type.__new__(mcs, name, bases, namespace)
  ↓ Создаёт объект класса
  ↓ Регистрирует дескрипторы (__set_name__)

Шаг 4: Вызов метакласса __init__
  ↓ type.__init__(cls, name, bases, namespace)
  ↓ Инициализирует класс

Шаг 5: Вызов __init_subclass__
  ↓ Для каждого родителя вызывает base.__init_subclass__()
  ↓ Позволяет родителям реагировать на создание подкласса

Шаг 6: Возврат объекта класса
  ↓ User = <class '__main__.User'>
  ↓ User готов к использованию

10. Конкретный практический пример

from typing import Any
import inspect

class Model:
    """Базовый класс для ORM моделей"""
    
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        
        # Собираем все атрибуты класса
        cls._fields = {}
        for name, value in inspect.getmembers(cls):
            if isinstance(value, Field):
                cls._fields[name] = value
        
        print(f"Модель {cls.__name__} имеет поля: {list(cls._fields.keys())}")
    
    def __init__(self, **kwargs):
        for field_name, field in self.__class__._fields.items():
            setattr(self, field_name, kwargs.get(field_name))

class Field:
    def __init__(self, field_type):
        self.field_type = field_type
    
    def __set_name__(self, owner, name):
        self.name = name

class User(Model):
    name = Field(str)
    age = Field(int)
    email = Field(str)

# Output:
# Модель User имеет поля: ['name', 'age', 'email']

user = User(name="Alice", age=25, email="alice@example.com")
print(f"User fields: {user.__class__._fields}")

11. Что происходит при создании экземпляра

Важно: Не путать создание класса с созданием экземпляра!

# СОЗДАНИЕ КЛАССА (один раз)
class User:
    count = 0  # ← создаётся один раз
    
    def __init__(self, name):
        self.name = name  # ← создаётся для каждого экземпляра

# СОЗДАНИЕ ЭКЗЕМПЛЯРА (много раз)
user1 = User("Alice")  # Вызывается __init__
user2 = User("Bob")    # Вызывается __init__ снова

# user1 и user2 имеют свои name
print(user1.name)  # Alice
print(user2.name)  # Bob
print(User.count)  # 0 (count не изменился)

Итоговый чек-лист: что происходит при создании класса

✅ Парсинг синтаксиса class
✅ Создание namespace (dict)
✅ Выполнение кода в теле класса
✅ Добавление __module__, __qualname__
✅ Вызов metaclass.__new__() → создание объекта класса
✅ Вызов metaclass.__init__() → инициализация класса
✅ Регистрация дескрипторов (__set_name__)
✅ Вызов __init_subclass__() в родителях
✅ Присвоение переменной User (в namespace модуля)

Вывод

Когда мы пишем class User:, под капотом происходит:

  1. Парсинг — Python читает код класса
  2. Создание namespace — новое пространство имён
  3. Выполнение — код класса выполняется
  4. Вызов метакласса — создание объекта класса (type.new и type.init)
  5. Регистрация — дескрипторы, подклассы, плагины

Это позволяет Python быть очень гибким — можно перехватить любую фазу и добавить свою логику через метаклассы, дескрипторы и init_subclass.