Метод __new__() в Python — как использовать

В этом руководстве вы узнаете о методе __new__() и поймете, как Python использует его для создания нового объекта.

Содержание

Что такое метод __new__ в Python?

Когда вы создаете экземпляр класса, Python сначала вызывает метод __new__() для создания объекта, а затем вызывает метод __init__() для инициализации атрибутов объекта.

__new__() — это статический метод класса объекта в Python. Он имеет следующий синтаксис:

object.__new__(class, *args, **kwargs)

Первый аргумент метода __new__ — class, это класс нового объекта, который вы хотите создать. Параметры *args и **kwargs должны соответствовать параметрам __init__() класса. Метод __new__() их также использует.

Метод __new__() должен возвращать новый объект класса. Но это не обязательно. Когда вы определяете новый класс, он неявно наследуется от класса объекта. Это означает, что вы можете переопределить статический метод __new__ и сделать что-то до и после создания нового экземпляра класса.

Чтобы создать объект класса, вы вызываете метод super().__new__(). Технически вы можете вызвать метод object.__new__() для создания объекта вручную. Однако после этого вам придется вручную вызвать __init__(). Python не будет автоматически вызывать метод __init__(), если вы явно создаете новый объект с помощью метода object.__new__().

Пример метода __new__() в Python

Ниже определяется класс Person с атрибутом name и создается новый экземпляр класса Person:

class Person:
    def __init__(self, name):
        self.name = name


person = Person('John')

В Python класс можно вызвать. Когда вы вызываете класс для создания нового объекта:

person = Person('John')

Python вызовет методы __new__() и __init__(). Это эквивалентно следующим вызовам методов:

person = object.__new__(Person, 'John')
person.__init__('John')

Ниже показано содержимое __dict__ объекта person после вызова методов __new__() и __init__():

person = object.__new__(Person, 'John')
print(person.__dict__)

person.__init__('John')
print(person.__dict__)

Вывод:

{}
{'name': 'John'}

Как ясно видно из вывода, после вызова метода __new__() объект person.__dict__ становится пустым. И после вызова метода __init__() person.__dict__ содержит атрибут имени со значением «John».

Ниже показана последовательность, в которой Python вызывает методы __new__ и __init__, когда вы создаете новый объект путем вызова класса:

class Person:
    def __new__(cls, name):
        print(f'Creating a new {cls.__name__} object...')
        obj = object.__new__(cls)
        return obj

    def __init__(self, name):
        print(f'Initializing the person object...')
        self.name = name


person = Person('John')

Вывод:

Creating a new Person object...
Initializing the person object...

В этом примере Python вызывает метод __new__() и после этого метод __init__().

При использовании метода __new__()

В следующем примере определяется класс SquareNumber, который наследуется от встроенного типа int:

class SquareNumber(int):
    def __new__(cls, value):
        return super().__new__(cls, value ** 2)


x = SquareNumber(3)
print(x)  # 9

В этом примере метод __new__() класса SquareNumber принимает целое число и возвращает число в квадрате. x — это экземпляр класса SquareNumber, а также экземпляр встроенного типа int:

print(isinstance(x, int)) # True

Обратите внимание, что вы не можете сделать это, используя метод __init__(), поскольку метод __init__() встроенного int не принимает аргументов. Следующий код приведет к ошибке:

class SquareNumber(int):
    def __init__(self, value):
        super().__init__(value ** 2)


x = SquareNumber(3)

Ошибка:

TypeError: object.__init__() takes exactly one argument(the instance to initialize)

На практике вы используете метод __new__(), когда хотите настроить объект в момент его создания.

Например, следующий код определяет класс Person и использует метод __new__() для внедрения атрибута full_name в объект Person:

class Person:
    def __new__(cls, first_name, last_name):
        # create a new object
        obj = super().__new__(cls)

        # initialize attributes
        obj.first_name = first_name
        obj.last_name = last_name

        # inject new attribute
        obj.full_name = f'{first_name} {last_name}'
        return obj


person = Person('John', 'Doe')
print(person.full_name)

print(person.__dict__)

Выход:

John Doe
{'first_name': 'John', 'last_name': 'Doe', 'full_name': 'John Doe'}

Обычно, когда вы переопределяете метод __new__(), вам не нужно определять метод __init__(), поскольку все, что вы можете сделать в методе __init__(), вы можете сделать в методе __new__().

Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *