簡體   English   中英

__init__ 和 __call__ 有什么區別?

[英]What is the difference between __init__ and __call__?

我想知道__init____call__方法之間的區別。

例如:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

第一個用於初始化新創建的對象,並接收用於執行此操作的參數:

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__

在元類中定義自定義__call__()方法允許將類的實例作為函數調用,而不是總是修改實例本身。

In [1]: class A:
   ...:     def __init__(self):
   ...:         print "init"
   ...:         
   ...:     def __call__(self):
   ...:         print "call"
   ...:         
   ...:         

In [2]: a = A()
init

In [3]: a()
call

在 Python 中,函數是一等對象,這意味着:函數引用可以作為輸入傳遞給其他函數和/或方法,並從它們內部執行。

(又名對象)的實例可以被視為函數:將它們傳遞給其他方法/函數並調用它們。 為了實現這一點, __call__類函數必須專門化。

def __call__(self, [args ...])它將可變數量的參數作為輸入。 假設xX類的實例, x.__call__(1, 2)類似於將x(1,2)實例本身作為函數調用

在 Python 中, __init__() __del__()被正確定義為類構造函數(而__del__()是類析構函數)。 因此, __init__()__call__()之間存在凈區別:第一個構建 Class 的實例,第二個使此類實例可調用為函數,而不會影響對象本身的生命周期(即__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

__call__使類的實例可調用。 為什么需要它?

從技術上講, __init__在創建對象時由__new__調用一次,以便對其進行初始化。

但是在很多情況下,您可能想要重新定義對象,比如您已經完成了對象,並且可能需要一個新對象。 使用__call__您可以重新定義同一個對象,就好像它是新對象一樣。

這只是一種情況,可能還有更多。

>>> class A:
...     def __init__(self):
...         print "From init ... "
... 
>>> a = A()
From init ... 
>>> a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no __call__ method
>>> 
>>> class B:
...     def __init__(self):
...         print "From init ... "
...     def __call__(self):
...         print "From call ... "
... 
>>> b = B()
From init ... 
>>> b()
From call ... 
>>> 

__init__將被視為構造函數,而__call__方法可以用對象調用任意次數。 __init____call__函數都采用默認參數。

我將嘗試使用一個示例來解釋這一點,假設您想從斐波那契數列中打印固定數量的項。 請記住,斐波那契數列的前兩項是 1。 例如:1, 1, 2, 3, 5, 8, 13....

您希望包含斐波那契數列的列表只初始化一次,之后應該更新。 現在我們可以使用__call__功能。 閱讀@mudit verma 的回答。 就像您希望對象可以作為函數調用,但每次調用時都不會重新初始化。

例如:

class Recorder:
    def __init__(self):
        self._weights = []
        for i in range(0, 2):
            self._weights.append(1)
        print self._weights[-1]
        print self._weights[-2]
        print "no. above is from __init__"

    def __call__(self, t):
        self._weights = [self._weights[-1], self._weights[-1] + self._weights[-2]]
        print self._weights[-1]
        print "no. above is from __call__"

weight_recorder = Recorder()
for i in range(0, 10):
    weight_recorder(i)

輸出是:

1
1
no. above is from __init__
2
no. above is from __call__
3
no. above is from __call__
5
no. above is from __call__
8
no. above is from __call__
13
no. above is from __call__
21
no. above is from __call__
34
no. above is from __call__
55
no. above is from __call__
89
no. above is from __call__
144
no. above is from __call__

如果您觀察到輸出__init__僅在第一次實例化該類時被調用一次,則稍后該對象被調用而沒有重新初始化。

__call__允許返回任意值,而__init__作為構造函數隱式返回類的實例。 正如其他答案正確指出的那樣, __init__被調用一次,而__call__可以多次__call__ ,以防初始化的實例分配給中間變量。

>>> class Test:
...     def __init__(self):
...         return 'Hello'
... 
>>> Test()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: __init__() should return None, not 'str'
>>> class Test2:
...     def __call__(self):
...         return 'Hello'
... 
>>> Test2()()
'Hello'
>>> 
>>> Test2()()
'Hello'
>>> 

您還可以使用__call__方法來實現裝飾器

這個例子取自Python 3 Patterns, Recipes and Idioms

class decorator_without_arguments(object):
    def __init__(self, f):
        """
        If there are no decorator arguments, the function
        to be decorated is passed to the constructor.
        """
        print("Inside __init__()")
        self.f = f

    def __call__(self, *args):
        """
        The __call__ method is not called until the
        decorated function is called.
        """
        print("Inside __call__()")
        self.f(*args)
        print("After self.f( * args)")


@decorator_without_arguments
def sayHello(a1, a2, a3, a4):
    print('sayHello arguments:', a1, a2, a3, a4)


print("After decoration")
print("Preparing to call sayHello()")
sayHello("say", "hello", "argument", "list")
print("After first sayHello() call")
sayHello("a", "different", "set of", "arguments")
print("After second sayHello() call")

輸出

在此處輸入圖片說明

因此,當您創建任何類的實例並初始化實例變量時,會調用__init__

例子:

class User:

    def __init__(self,first_n,last_n,age):
        self.first_n = first_n
        self.last_n = last_n
        self.age = age

user1 = User("Jhone","Wrick","40")

當您像調用任何其他函數一樣調用對象時,就會調用__call__

例子:

class USER:
    def __call__(self,arg):
        "todo here"
         print(f"I am in __call__ with arg : {arg} ")


user1=USER()
user1("One") #calling the object user1 and that's gonna call __call__ dunder functions

上面已經提供了簡短而甜蜜的答案。 與 Java 相比,我想提供一些實際的實現。

 class test(object):
        def __init__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c
        def __call__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c


    instance1 = test(1, 2, 3)
    print(instance1.a) #prints 1

    #scenario 1
    #creating new instance instance1
    #instance1 = test(13, 3, 4)
    #print(instance1.a) #prints 13


    #scenario 2
    #modifying the already created instance **instance1**
    instance1(13,3,4)
    print(instance1.a)#prints 13

注意:場景 1 和場景 2 在結果輸出方面似乎相同。 但是在場景 1 中,我們再次創建了另一個新實例instance1 在場景 2 中,我們只需修改已經創建的instance1 __call__在這里很有用,因為系統不需要創建新實例。

在 Java 中等效

public class Test {

    public static void main(String[] args) {
        Test.TestInnerClass testInnerClass = new Test(). new TestInnerClass(1, 2, 3);
        System.out.println(testInnerClass.a);

        //creating new instance **testInnerClass**
        testInnerClass = new Test().new TestInnerClass(13, 3, 4);
        System.out.println(testInnerClass.a);

        //modifying already created instance **testInnerClass**
        testInnerClass.a = 5;
        testInnerClass.b = 14;
        testInnerClass.c = 23;

        //in python, above three lines is done by testInnerClass(5, 14, 23). For this, we must define __call__ method

    }

    class TestInnerClass /* non-static inner class */{

        private int a, b,c;

        TestInnerClass(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }
}

__init__是 Python 類中的一個特殊方法,它是一個類的構造方法。 每當構造類的對象時都會調用它,或者我們可以說它初始化了一個新對象。 例子:

    In [4]: class A:
   ...:     def __init__(self, a):
   ...:         print(a)
   ...:
   ...: a = A(10) # An argument is necessary
10

如果我們使用 A(),它會給出錯誤TypeError: __init__() missing 1 required positional argument: 'a'因為它需要 1 個參數a因為__init__

…………

__call__在 Class 中實現時幫助我們調用 Class 實例作為函數調用。

例子:

In [6]: class B:
   ...:     def __call__(self,b):
   ...:         print(b)
   ...:
   ...: b = B() # Note we didn't pass any arguments here
   ...: b(20)   # Argument passed when the object is called
   ...:
20

這里如果我們使用 B(),它運行得很好,因為這里沒有__init__函數。

情況1:

class Example:
    def __init__(self, a, b, c):
        self.a=a
        self.b=b
        self.c=c
        print("init", self.a, self.b, self.c)

跑步:

Example(1,2,3)(7,8,9)

結果:

- init 1 2 3
- TypeError: 'Example' object is not callable

案例2:

class Example:
    def __init__(self, a, b, c):
        self.a=a
        self.b=b
        self.c=c
        print("init", self.a, self.b, self.c)
    def __call__(self, x, y, z):
        self.x=x
        self.y=y
        self.z=z
        print("call", self.x, self.y, self.z)

跑步:

Example(1,2,3)(7,8,9)

結果:

- init 1 2 3
- call 7 8 9

我們可以使用call方法將其他類方法用作靜態方法。

class _Callable:
    def __init__(self, anycallable):
        self.__call__ = anycallable

class Model:

    def get_instance(conn, table_name):

        """ do something"""

    get_instance = _Callable(get_instance)

provs_fac = Model.get_instance(connection, "users")  

call方法用於使對象像函數一樣運行。

>>> class A:
...     def __init__(self):
...         print "From init ... "
... 
>>> a = A()
From init ... 
>>> a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no __call__ method

<*There is no __call__ method so it doesn't act like function and throws error.*>

>>> 
>>> class B:
...     def __init__(self):
...         print "From init ... "
...     def __call__(self):
...         print "From call it is a function ... "
... 
>>> b = B()
From init ... 
>>> b()
From call it is a function... 
>>> 

<* __call__ method made object "b" to act like function *>

我們也可以將它傳遞給一個類變量。

class B:
    a = A()
    def __init__(self):
       print "From init ... "

__init__()可以:

  • 初始化 class 的實例。
  • 多次被調用。
  • 只返回None

__call__()可以像實例方法一樣自由使用。

例如, Person class 有__init__()__call__()如下所示:

class Person:
    def __init__(self, f_name, l_name):
        self.f_name = f_name
        self.l_name = l_name
        print('"__init__()" is called.')
        
    def __call__(self, arg):
        return arg + self.f_name + " " + self.l_name

現在,我們創建並初始化Person class 的實例,如下所示:

    # Here
obj = Person("John", "Smith")

然后,調用__init__()如下所示:

"__init__()" is called.

接下來,我們以兩種方式調用__call__() ,如下所示:

obj = Person("John", "Smith")
print(obj("Hello, ")) # Here
print(obj.__call__("Hello, ")) # Here

然后,調用__call__()如下所示:

"__init__()" is called.
Hello, John Smith # Here
Hello, John Smith # Here

並且, __init__()可以被多次調用,如下所示:

obj = Person("John", "Smith")
print(obj.__init__("Tom", "Brown")) # Here
print(obj("Hello, "))
print(obj.__call__("Hello, "))

然后,調用__init__()並重新初始化Person class 的實例,並從__init__()返回None ,如下所示:

"__init__()" is called.
"__init__()" is called. # Here
None # Here
Hello, Tom Brown
Hello, Tom Brown

並且,如果__init__()不返回None並且我們調用__init__()如下所示:

class Person:
    def __init__(self, f_name, l_name):
        self.f_name = f_name
        self.l_name = l_name
        print('"__init__()" is called.')
        return "Hello" # Here
        
    # ...

obj = Person("John", "Smith") # Here

出現以下錯誤:

TypeError: __init__() 應該返回 None,而不是 'str'

並且,如果__call__未在Person class 中定義:

class Person:
    def __init__(self, f_name, l_name):
        self.f_name = f_name
        self.l_name = l_name
        print('"__init__()" is called.')
        
    # def __call__(self, arg):
    #     return arg + self.f_name + " " + self.l_name

然后,我們調用obj("Hello, ")如下所示:

obj = Person("John", "Smith")
obj("Hello, ") # Here

出現以下錯誤:

TypeError: 'Person' object 不可調用

再一次,我們調用obj.__call__("Hello, ")如下所示:

obj = Person("John", "Smith")
obj.__call__("Hello, ") # Here

出現以下錯誤:

AttributeError: 'Person' object 沒有屬性 '__call__'

我想提出一些捷徑和語法糖,以及一些可以使用的技術,但我在當前的答案中沒有看到它們。

實例化 class 並立即調用它

In many cases, for example when need to make a APi request, and the logic is encapsulated inside a class and what we really need is just give the data to that class and run it immediatelly as a separate entity, the instantiate class may not been需要。 那就是

instance = MyClass() # instanciation
instance() # run the instance.__call__()
# now instance is not needed 

相反,我們可以做類似的事情。

class HTTPApi:

    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2

    def __call__(self, *args, **kwargs):
        return self.run(args, kwargs)

    def run(self, *args, **kwargs):
        print("hello", self.val1, self.val2, args, kwargs)
        
if __name__ == '__main__':
    # Create a class, and call it
    (HTTPApi("Value1", "Value2"))("world", 12, 213, 324, k1="one", k2="two")


Give 調用另一個現有方法

我們也可以為__call__聲明一個方法,而無需創建實際的__call__方法。

class MyClass:

    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2

    def run(self, *args, **kwargs):
        print("hello", self.val1, self.val2, args, kwargs)

    __call__ = run

if __name__ == '__main__':
    (MyClass("Value1", "Value"))("world", 12, 213, 324, k1="one", k2="two")

這允許聲明另一個全局 function 而不是方法,無論出於何種原因(可能有一些原因,例如您不能修改該方法,但您需要由類調用它)。

def run(self, *args, **kwargs):
    print("hello",self.val1, self.val2,  args, kwargs)

class MyClass:

    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2

    __call__ = run

if __name__ == '__main__':
    (MyClass("Value1", "Value2"))("world", 12, 213, 324, k1="one", k2="two")

__ init__方法在創建對象時自動運行。 它用於初始化實例變量。

class my_class():
    def __init__(self,a,b):
        self.a = a
        self.b = b
        print("Object was created, instance variables were initialized")

obj = my_class(1,2)  
print(obj.a)        #prints 1
print(obj.b)        #prints 2

__ call__方法可用於重新定義/重新初始化相同的對象。 它還通過將參數傳遞給對象,便於將類的實例/對象用作函數。

class my_class():
    def __init__(self, a,b):
        self.a=a
        self.b=b

    def __call__(self,a,b):
        Sum = a+b   
        return Sum 

obj = my_class(1,2)   #  a=1, b=2
Sum = obj(4,5)        #  a=4, b=5 instance variables are re-initialized 
print(Sum)            #  4 + 5= 9

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM