Python面向对象中__init__的实际作用是什么?

例1: 使用__init__求和

class Person():
    
    def __init__(self, x, y): 
        self.x = x   
        self.y = y
        # return self.x + self.y   # __init__函数没有返回值
    
    def math(self):
        return self.x+self.y
   
P1 = Person(5, 6)
print(P1.math())  

例2: 不使用__init__

class Person():
  
    def math(self, x, y):
        return x+y
   
P1 = Person()
print(P1.math(5, 6))  

在知乎上找了几个回答, 其中一个高赞的回答逻辑很奇怪, 他创建一个空的class, 然后说需要在类的外部对数据进行计算, 这种方式有很多缺点. 如下例子. 无法理解这是想表达什么.......

class Person():
    pass
P1 = Person()
P1.x=5
P1.y=6
math=P1.x+P1.y
print(math)

例1和例2有什么区别吗? 没有体会到__init__的优点啊? 或者是我举的例子不好? __init__在实际开发中有什么作用呢? 多谢了.

阅读 784
评论 2月17日提问
    3 个回答
    zonxin
    • 12k

    给个知乎的连接吧,知乎的例子说的应该是:

    设计模式六大原则之一:迪米特法则,一个对象应该对其他对象保持最少的了解。如果类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
    # 任务1
    class Person():
       def __init__(self, x, y):
         self._x = x
         self._y = y
        def math(self): return self._x + self._y
    # 任务2
    P1 = Person(5, 6)
    print(P1.math())
    # 分割线 ===================================================
    # 任务1
    class Person():
        def math(self): return self._x + self._y
    # 任务2
    P1 = Person()
    P1._x = 5
    P2._y = 6
    print(P1.math())

    两个人合作,每人完成一个任务。前者的完成任务2的人,不需要知道Person的内部属性,但是后者需要两人事先约定Person的内部属性。所有代码相关代码写到一块,由一个人维护,要比两个人共同维护好。对于第二种写法,如果有一天完成任务1的人把math改成了def math(self): self._z + self._y,如果他没有通知完成任务2的人,那么整个程序将会挂掉。而如果写到了一起,那么由一个人维护,他就会把两处都改了,另一个人不需要做任何额外的修改。所以如果要写成后者,我们更愿意写成,完成任务1的人生成一个会计算加法的Person,而完成任务2的人使用Person计算了5+6

    # 任务1
    class Person():
        def math(self, x, y):
            return x+y
    # 任务2
    P1 = Person()
    print(P1.math(5, 6))  

    所以,你给的两种写法都可以,只是他们留了不同的接口。至于那种更好么,应用场景决定(如何选择就是能不能写出优秀代码的难点)。

    除了编译器提供的内置类型,我们还可以自定义类型。为了构造自己的类型,我们需要自己给出自定义类型的定义以及如何创建和初始化这个类型。“构造函数”就给出了如何创建以及初始化这个类型。Python是弱类型,所有对象构造方法一样,只是初始化不一样,所以这个函数叫做init。这个__init__就是一个“构造函数”!这个函数在创建对象的时候由编译器自动调用,并且编译器会忽略他的返回值。其实真正的构造函数是__new__,由__new__创建对象,然后把这个对象当做self传递给__init__,所以__init__不需要返回值。

    从另一个角度来说,构造函数的作用是:类型转换, 类型转换, 类型转换。第一个Person可以把tuple(x,y)转换成了一个只会计算x+yPerson。而第二种写法把()转换成了一个会计算加法的Person--因为遵循了迪米特法则,一句话搞定而不是解释半天。

    在其他一些语言里,构造函数是与类名相同的一个函数。这些语言里更容易理解P1 = Person(5,6)里的Person更多的是一个函数,而不是一个类名。只是这个函数名与类名重名而已。Python里的__init__就是构造函数完成初始化对象的那一部分,它的参数就是所有构造函数的参数+已经构造出的对象。

    评论 赞赏

      __init__是初始化函数,P1 = Person()时会自动调用。
      你的例子太过简单,这俩的区别就在于,是否要把xy这俩参数挂载到P1这个实例上,相当于P1的属性,以便其它函数复用。
      假如还有个函数sum,也用到xy

       def sum(self, a):
              return self.x + self.a

      肯定用1合适了。

      评论 赞赏

        说到类的 '__init__' 方法,就不得不说类的实例化,而类的实例化是通过 '__new__' 方法为对象分配内存空间,构建一个“空”对象;然后,'__init__' 方法被调用来初始化它。
        以下是类的实例化的步骤:

        1. Foo(*args, *kwargs) 等价于 Foo.__call__(*args, *kwargs)。
        2. 既然 Foo 是一个 type 的实例,Foo.__call__(*args, *kwargs) 实际调用的是 type.__call__(Foo, *args, *kwargs)。
        3. type.__call__(Foo, *args, *kwargs) 调用 type.__new__(Foo, *args, *kwargs),然后返回一个对象。
        4. obj 随后通过 obj.__init__(*args, *kwargs) 被初始化。
        5. obj 被返回。

        现在我们回过头来看你的两个例子,例1 是一个 __init__ 方法的经典类,由于存在 __init__ 方法,所以类在实例化的过程中,直接会对数据进行初始化;而例2 是一个没有 __init__ 方法的经典类,所以实例化的步骤缺少了第 4 步,需要在外部对数据进行初始化。


        对于例2 的情况,在使用 sqlalchemy 对数据库操作中,是不建议使用 __init__ 方法的,具体的内容,你可以看看我的这个实战例子--https://github.com/eastossifr...

        如果喜欢看视频,建议看看我的 B 站主页 -- https://space.bilibili.com/19...

        评论 赞赏
          撰写回答

          登录后参与交流、获取后续更新提醒