Часто __init__ называют конструктором класса, но на самом деле это его инициализатор. Новые объекты класса по-факту создаются методом __new__, который в свою очередь вызывает метод __init__.
Первый аргумент метода __new__ – cls. Он представляет сам класс, подобно тому, как self представляет экземпляр класса. Это также делает __new__ немного другим видом метода, поскольку он не требует экземпляра класса. И это имеет смысл, поскольку предполагается, что он создает эти экземпляры! Метод возвращает новый экземпляр класса, который затем передается методу __init__.
Обычно нет необходимости самостоятельно определять метод __new__, но он может быть полезен, если мы хотим возвращать инстансы (экземпляры) других классов или ограничивать количество объектов в нашем классе.
Представьте себе, например, что мы хотим создать класс Earth и убедиться, что мы создаем только один объект этого класса. Нам нужно было бы определить переменную класса, которая отслеживала бы количество экземпляров в классе и запрещала бы создание новых, если предел был достигнут.
class Earth:
n = 0
def __new__(cls):
if cls.n == 0:
cls.n += 1
return object.__new__(cls)
Приведенный выше код может быть не сразу понятным, поэтому давайте проанализируем его. Сначала мы проверяем, что переменная класса n имеет нулевое значение. Если это так, это означает, что никакие экземпляры класса не были созданы, и мы можем это сделать. Затем мы обновляем переменную класса и вызываем метод __new__, который позволяет нам создать новый экземпляр. Затем n увеличился и перестал равняться нулю. Поэтому создание следующего нового объекта не получится.
Покажу на примере, после которого станет всё понятно:
earth1 = Earth()
earth2 = Earth()
print(earth1)
>>> <__main__.Sun object at 0x1106884a8>
print(earth2)
>>> None