В чем разница между __init__ и __call__?
Заголовок: В чем разница между методами __init__ и __call__ в Python?
Текст вопроса:
Я хочу понять различия между методами __init__ и __call__ в Python.
Например, у меня есть следующий код:
class test:
def __init__(self):
self.a = 10
def __call__(self):
b = 20
Каковы основные отличия в функциональности и использовании этих методов? Как они работают в классе и какие практические сценарии их применения?
5 ответ(ов)
Первый метод используется для инициализации только что созданного объекта и принимает аргументы, которые используются для этого:
class Foo:
def __init__(self, a, b, c):
# ...
x = Foo(1, 2, 3) # вызов __init__
Второй метод реализует оператор вызова функции.
class Foo:
def __call__(self, a, b, c):
# ...
x = Foo()
x(1, 2, 3) # вызов __call__
Таким образом, __init__ отвечает за процесс создания объекта, а __call__ позволяет экземпляру класса вести себя как функция.
Определение пользовательского метода __call__() позволяет экземпляру класса вызываться как функция, не всегда изменяя сам экземпляр.
In [1]: class A:
...: def __init__(self):
...: print("init")
...:
...: def __call__(self):
...: print("call")
...:
In [2]: a = A()
init
In [3]: a()
call
В данном примере, когда мы создаем экземпляр класса A, вызывается метод __init__(), который выводит "init". После этого, когда мы вызываем экземпляр a как функцию, срабатывает метод __call__(), который выводит "call". Это демонстрирует, что экземпляр класса может вести себя как функция, благодаря реализации метода __call__().
В Python функции являются объектами первого класса, что означает, что ссылки на функции могут передаваться в качестве аргументов другим функциям и/или методам и могут вызываться внутри них.
Экземпляры классов (или объекты) могут обрабатываться так, как если бы они были функциями: их можно передавать другим методам/функциям и вызывать. Для этого необходимо специальным образом определить метод класса __call__.
def __call__(self, [args ...])
Этот метод принимает переменное количество аргументов. Допустим, x — экземпляр класса X, тогда x.__call__(1, 2) аналогичен вызову x(1, 2), или вызову экземпляра как функции.
В Python метод __init__() является конструктором класса (а __del__() — деструктором класса). Таким образом, существует четкое различие между __init__() и __call__(): первый создает экземпляр класса, а второй делает экземпляр вызываемым как функция, не влияя на жизненный цикл самого объекта (то есть __call__ не влияет на процесс создания/разрушения), хотя и может изменять его внутреннее состояние (как показано ниже).
Пример:
class Stuff(object):
def __init__(self, x, y, range):
super(Stuff, self).__init__()
self.x = x
self.y = y
self.range = range
def __call__(self, x, y):
self.x = x
self.y = y
print('__call__ with (%d,%d)' % (self.x, self.y))
def __del__(self):
del self.x
del self.y
del self.range
>>> s = Stuff(1, 2, 3)
>>> s.x
1
>>> s(7, 8)
__call__ with (7,8)
>>> s.x
7
В этом примере мы создаем класс Stuff, который имеет конструктор и метод __call__. Мы можем менять внутренние значения атрибутов объекта, вызывая экземпляр как функцию.
В данной части кода мы видим два класса: A и B.
Для класса A, конструктор (__init__) просто печатает "From init ... " при создании экземпляра. Однако, когда мы пытаемся вызвать экземпляр класса A (например, a()), возникает ошибка AttributeError, поскольку класс A не определяет метод __call__. Это означает, что объекты класса A не могут быть вызваны как функции.
class A:
def __init__(self):
print("From init ...")
a = A() # Вывод: From init ...
a() # Ошибка: AttributeError: A instance has no __call__ method
В отличие от этого, класс B также определяет конструктор, который печатает "From init ... ", но добавляет метод __call__, который печатает "From call ... ". Это позволяет нам вызывать экземпляры класса B как функции.
class B:
def __init__(self):
print("From init ... ")
def __call__(self):
print("From call ... ")
b = B() # Вывод: From init ...
b() # Вывод: From call ...
Таким образом, для того чтобы экземпляр класса можно было вызывать как функцию, необходимо определить метод __call__.
Метод __call__ позволяет экземплярам класса быть вызываемыми, как функции. В каких случаях это может понадобиться?
Технически, метод __init__ вызывается один раз в процессе создания объекта через метод __new__, чтобы произвести его инициализацию.
Однако существует множество сценариев, когда вы можете захотеть переопределить ваше состояние объекта. Например, вы завершили работу с текущим объектом и хотите создать новый объект, используя тот же экземпляр. С помощью метода __call__ вы можете переопределить поведение объекта, как будто он был создан заново.
Это лишь один из возможных случаев; на самом деле их может быть значительно больше.
Почему классы в Python наследуют от object?
Понимание Python super() с методами __init__()
Разница между старыми и новыми классами в Python?
Можно ли создать экземпляр абстрактного класса?
Имеет ли Python "приватные" переменные в классах?