Как происходит создание и использование классов в Python?

Правильно ли я понимаю, что при вызове класса (включая метакласс) происходит следующий процесс: сначала осуществляется поиск атрибута <i>__call__</i> в MRO метакласса, к экземпляру которого относится <i>cls</i>, и затем этот атрибут вызывается в зависимости от его типа (например, функция или экземпляр дескриптора)? Если я ошибаюсь, пожалуйста, поправьте меня. Теперь мне нужно подробнее разобраться с семантикой следующих вызовов:

<pre><code class="python">type.__call__.__get__(cls)(*args, **kwargs)
type.__init__.__get__(cls)(*args, **kwargs)
type.__new__(cls, *args, **kwargs)
object.__new__(cls, *args, **kwargs)
object.__init__.__get__(cls)(*args, **kwargs)</code></pre>

Буду признателен за помощь!
  • 13 апреля 2025 г. 14:45
Ответы на вопрос 2
Вы на правильном пути, описывая процесс вызова класса в Python, однако давайте упорядочим и уточним некоторые моменты.

### Создание и использование классов в Python

1. **Определение класса**: Когда вы объявляете класс, Python создает его в пространстве имен, используя метакласс. По умолчанию метаклассом является `type`.

2. **Создание экземпляра класса**: Когда вы вызываете класс (например, `MyClass()`), происходит несколько этапов:

   - **Поиск `__call__`** в метаклассе: Python ищет атрибут `__call__` в метаклассе (`type` для обычных классов) и вызывает его.
   - **Поиск `__new__`**: `__new__` — это статический метод, который отвечает за создание нового экземпляра. Он вызывается первым. Если `__new__` возвращает экземпляр объекта (или другой объект), то дальнейшие процессы идут к этому экземпляру.
   - **Поиск `__init__`**: Как только экземпляр создан с использованием `__new__`, Python вызывает метод `__init__`, чтобы инициализировать атрибуты экземпляра.

### Разбор вызовов

Теперь давайте подробнее про каждый из упомянутых вами вызовов:

1. **`type.__call__.__get__(cls)(*args, **kwargs)`**:
   - Здесь `type.__call__` — это метод, который вызывается, когда вы создаете экземпляр класса. С помощью `__get__` происходит привязка метода `__call__` к экземпляру класса. Это фактически запускает процесс создания экземпляра.

2. **`type.__init__.__get__(cls)(*args, **kwargs)`**:
   - `type.__init__` — это метод, который вызывается для инициализации экземпляра класса. Здесь также используется `__get__`, чтобы обеспечить правильные ссылки на смысловую функцию.

3. **`type.__new__(cls, *args, **kwargs)`**:
   - Это метод, который используется для создания нового экземпляра. Он вызывается перед `__init__`. В большинстве случаев, вы не будете переопределять `__new__`, но если ваш класс наследует от других классов, это может быть ключевым.

4. **`object.__new__(cls, *args, **kwargs)`**:
   - Изначально происходит вызов `object.__new__`, когда класс наследует от `object` (что является стандартным для новых классов). Это создает "сырое" представление экземпляра.

5. **`object.__init__.__get__(cls)(*args, **kwargs)`**:
   - После создания нового экземпляра `__new__`, вызывается метод `__init__`. Этот метод будет инициализировать атрибуты экземпляра. Подобно `type.__init__`, также привязывается к экземпляру.

### Описание процесса

Таким образом, процесс создания экземпляра класса включает в себя последовательные вызовы методов `__call__`, `__new__`, а затем `__init__`, при этом сначала создается объект, а затем происходит его инициализация. Все эти методы взаимодействуют через механизм метапрограммирования Python, который подразумевает использование метаклассов и дескрипторов. 

Если у вас есть дополнительные вопросы или нужна дополнительная информация по какому-либо конкретному аспекту, не стесняйтесь спрашивать!
Действительно, чёт не очень понятен вопрос. Я бы описал всё так: 

1. Когда вызывается класс, интерпретатор ищет __call__ в метаклассе этого класса. По умолчанию метакласс — type . Этот type.__call__ управляет процессом создания экземпляра. Если метакласс переопределен, его __call__ будет использоваться вместо type.__call__ .
2. type.__call__ вызывает __new__ класса
3. Если класс не переопределил __new__ type.__call__ вызывает __init__ класса

Например, такой код
class Test:
    def __new__(cls, *args, **kwargs):
        print("__new__")
        return super().__new__(cls)

    def __init__(self):
        print("__init__")

test = Test()
выведет
__new__
__init__

А такой
class Test:
    def __new__(cls, *args, **kwargs):
        print("__new__")
        return 'test'

    def __init__(self):
        print("__init__")

test = Test()
только __new__
Похожие вопросы