Метод __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__().