7

В чем разница между __init__ и __call__?

1

Заголовок: В чем разница между методами __init__ и __call__ в Python?

Текст вопроса:

Я хочу понять различия между методами __init__ и __call__ в Python.

Например, у меня есть следующий код:

class test:
  
  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

Каковы основные отличия в функциональности и использовании этих методов? Как они работают в классе и какие практические сценарии их применения?

5 ответ(ов)

10

Первый метод используется для инициализации только что созданного объекта и принимает аргументы, которые используются для этого:

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__ позволяет экземпляру класса вести себя как функция.

3

Определение пользовательского метода __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__().

1

В 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__. Мы можем менять внутренние значения атрибутов объекта, вызывая экземпляр как функцию.

0

В данной части кода мы видим два класса: 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__.

0

Метод __call__ позволяет экземплярам класса быть вызываемыми, как функции. В каких случаях это может понадобиться?

Технически, метод __init__ вызывается один раз в процессе создания объекта через метод __new__, чтобы произвести его инициализацию.

Однако существует множество сценариев, когда вы можете захотеть переопределить ваше состояние объекта. Например, вы завершили работу с текущим объектом и хотите создать новый объект, используя тот же экземпляр. С помощью метода __call__ вы можете переопределить поведение объекта, как будто он был создан заново.

Это лишь один из возможных случаев; на самом деле их может быть значительно больше.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь