【问题标题】:What is the difference between __init__ and __call__?__init__ 和 __call__ 有什么区别?
【发布时间】:2012-03-28 15:29:34
【问题描述】:

我想知道__init____call__ 方法之间的区别。

例如:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

【问题讨论】:

    标签: python class oop object callable-object


    【解决方案1】:

    第一个用于初始化新创建的对象,并接收用于执行此操作的参数:

    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__
    

    【讨论】:

    • 所以,在调用class时使用__init__方法初始化实例,而在instance时调用__call__方法i> 被称为
    • 这似乎是正确的,显然实例变量可以在实例的生命周期内进行修改,这在某些情况下可能是有益的。
    • init 在实例化类时被调用:myfoo = Foo(1,4,7.8) call 是调用已经实例化的类的模板做某事让我们说 class Foo:\ def __call__(self, zzz) 然后,myfoo(12) 调用该类来执行该类的操作。
    • __call__的实际用法是什么?
    • 下面 Dmitriy Sintsov 的回答提出了一个非常重要的观点,所以我觉得我应该在这里提请注意:__call__ 可以返回 任意值,而 @ 987654327@ 必须返回无.
    【解决方案2】:

    在元类中定义一个自定义的__call__() 方法允许将类的实例作为函数调用,而不是总是修改实例本身。

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

    【讨论】:

    • __call__ 不仅允许将实例用作函数......它定义了当实例用作函数时执行的函数体。
    【解决方案3】:

    在 Python 中,函数是一等对象,这意味着:函数引用可以在输入中传递给其他函数和/或方法,并在它们内部执行。

    类的实例(又名对象),可以被视为函数:将它们传递给其他方法/函数并调用它们。为了实现这一点,__call__ 类函数必须专门化。

    def __call__(self, [args ...]) 它将可变数量的参数作为输入。假设xX 类的实例,x.__call__(1, 2) 类似于调用x(1,2)实例本身作为函数

    在 Python 中,__init__() 被正确定义为类构造函数(__del__() 是类析构函数)。因此,__init__()__call__() 之间有一个净区别:第一个构建 Class up 的实例,第二个使此类实例 callable 作为函数不会影响对象的生命周期本身(即__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
    

    【讨论】:

    • 我理解这个概念,但不了解修改其内部状态的特殊功能。如果我们在上面的代码中将def __call__ 简单地替换为def update,我们会给类一个update 方法来做同样的事情。它现在还可以修改内部状态,如果在下面调用为s.update(7, 8)。那么,__call__ 只是语法糖吗?
    • 是的,差不多。这只是在对象上调用方法而无需指定它的快捷方式。除此之外,它就像任何其他实例方法一样。有趣的是,如果你用@classmethod 装饰它,它既可以用作类方法,也可以使实例可调用。但是由于classmethod不能带self,所以没有状态可以传递,并且尝试调用类作为方法调用__init__,所以幸运的是它并没有破坏类的构造。
    【解决方案4】:

    __call__ 使类的实例可调用。 为什么需要它?

    从技术上讲,__init__ 在创建对象时由__new__ 调用一次,以便可以对其进行初始化。

    但是在很多情况下,您可能想要重新定义您的对象,比如您已经完成了您的对象,并且可能需要一个新对象。使用__call__,您可以重新定义同一个对象,就好像它是新对象一样。

    这只是一种情况,可能还有更多。

    【讨论】:

    • 对于这种特殊情况,我们不应该只是创建一个新实例吗?以某种方式修改和使用同一个实例是否有效。
    【解决方案5】:
    >>> 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 ... 
    >>> 
    

    【讨论】:

    • 我认为这应该是公认的答案。它准确地回答。
    【解决方案6】:

    __init__ 将被视为构造函数,而 __call__ 方法可以使用对象多次调用。 __init____call__ 函数都采用默认参数。

    【讨论】:

    • __init__ 不是构造函数,但 __new__ 是。 __init____new__ 之后被调用
    • 我认为__new__ 创建类实例并接收一个类作为参数,而__init__ 是实例构造函数,这就是它接收self 的原因。一个简单的方法是在调用a = Foo(1,2,3) 中,接收构造函数参数的函数将是__init__
    【解决方案7】:

    我将尝试使用一个示例来解释这一点,假设您想从斐波那契数列中打印固定数量的项。请记住,斐波那契数列的前 2 项是 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__ 仅在第一次实例化该类时被调用一次,那么稍后会调用该对象而无需重新初始化。

    【讨论】:

      【解决方案8】:

      __call__ 允许返回任意值,而 __init__ 作为构造函数隐式返回类的实例。正如其他答案正确指出的那样,__init__ 只被调用一次,而__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__()的意义
      【解决方案9】:

      您也可以使用__call__ 方法来支持实现decorators

      这个例子取自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")
      

      输出

      【讨论】:

      • 能否将输出复制为文本?
      • 这种方法的意义何在?你能用不同的方法来对比它吗?
      【解决方案10】:

      所以,__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
      

      【讨论】:

        【解决方案11】:

        上面已经提供了简短而甜蜜的答案。与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;
                }
            }
        }
        

        【讨论】:

        • 与 Java 比较完全超出了问题的范围。在您的示例中,您看不到任何差异,因为选择不当,数字相同。
        【解决方案12】:

        __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__ 函数。

        【讨论】:

        • 将对象传递给已初始化的类对象。那么一个可调用对象?
        【解决方案13】:

        案例 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
        

        【讨论】:

          【解决方案14】:

          我们可以通过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")  
          

          【讨论】:

            【解决方案15】:

            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 ... "
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-09-28
              • 2012-11-01
              • 2017-09-02
              • 1970-01-01
              • 2018-09-11
              • 2012-12-15
              • 2018-11-05
              • 2022-10-14
              相关资源
              最近更新 更多