← Назад к вопросам
Что происходит внутри 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:, под капотом происходит:
- Парсинг — Python читает код класса
- Создание namespace — новое пространство имён
- Выполнение — код класса выполняется
- Вызов метакласса — создание объекта класса (type.new и type.init)
- Регистрация — дескрипторы, подклассы, плагины
Это позволяет Python быть очень гибким — можно перехватить любую фазу и добавить свою логику через метаклассы, дескрипторы и init_subclass.