我新开了专栏 《恍然大明白》,如果关注 Python 底层实现,请移步

恍然大明白​www.zhihu.com

今天遇到一个有意思的问题,小组一位小哥在开发中,遇到了传说中的 “祖传代码”,有一个数据库的封装类 DBM 没有将数据库配置信息暴露到参数中,而是在 __init__ 方法中硬编码。临时新增的需求需要使用不同的数据库,小兄弟很诚实的将整个类实现 COPY 了一份,然后修改了 _init 方法中的数据库配置。

其实解决这个问题有很多方法,比如:

  • 这个类的初始化参数进行调整,暴露初始化参数,并为止设置默认值;
  • 将一些公用方法抽象到一个更高的父类,DBM 和其他实现继承该父类等等。

当然也可以直接继承祖传的 DBM,重新实现 init 方法,使用新的数据库配置。在子类的 init 方法中主动调用父类的 init 方法,再实现子类的初始化,似乎是一种稳妥的做法,但是调用父类的 init 方法是必须的吗?

一个简单的例子: (所有代码都在 Python3 下运行)

#!/usr/bin/python

Base 中有 1 个属性 name 和 say 方法,假设在子类 Child 中新增属性 color:

class Child(Base):def __init__(self, color):super().__init__()self.color = color# 简单测试父类
base = Base("Lisa")
print(base.name)
base.say()# 子类
child = Child("blue")
try:print(child.name)print(child.color)
except Exception as e:print(e)
child.say()

输出:

Lisa
<class '__main__.Base'> said: Hello!
Lucy
blue
<class '__main__.Child'> said: Hello!

可以看到 Child 继承了所需的 say 方法,相比 Base 新增了 color 属性。但是如果 Child 已经不再需要 name,可以不执行 supper().init() 吗?如果将其注释掉:

# super().__init__()

再尝试运行输出如下:

Lisa
<class '__main__.Base'> said: Hello!
'Child' object has no attribute 'name'
<class '__main__.Child'> said: Hello!

Child 依然继承了 say 方法,但是由于没有调用父类的 init 方法,所以 name 属性就没有被创建,子类中也就不存在这个属性。

所以小哥只需要用一个子类继承 “祖传代码” 中的 DBM 类,并且直接在子类的 init 按照自己的需要初始化即可,无需调用父类的 init 方法。

原因

Python 与 Java、C++ 不同,继承不依赖子类执行父类的 init 方法,init 方法主要完成的是对象的一些初始化工作,真正的对象创建工作是在 new 方法中实现。当子类对象创建完成,Object 在 Python 底层结构都是相同的,在某种程度上它非常像一个字典。只需要对代码稍作修改,在 init 中观察各类实例被 new 之后都包含什么:

#!/usr/bin/python
class Base:def __init__(self, name = "Lucy"):print(f"befor __init__, {self.__class__}:")print(dir(self))self.name = nameprint(f"after __init__, {self.__class__}:")print(dir(self))def say(self):print(f"{self.__class__} said: Hello!")class Child(Base):def __init__(self, color):print(f"befor __init__, {self.__class__}:")print(dir(self))# super().__init__()self.color = colorprint(f"after __init__, {self.__class__}:")print(dir(self))base = Base("Lisa")
print(base.name)
base.say()child = Child("blue")
try:print(child.name)print(child.color)
except Exception as e:print(e)
child.say()

输出太长,我只贴出 Child 的输出:

befor __init__, <class '__main__.Child'>:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'say']
after __init__, <class '__main__.Child'>:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'color', 'say']

显然,在进入 ini 之前,也就是 new 之后,self 中已经包含了大量属性,多数都是内置属性,比较意外的是出现在 __init__ 方法之后的 say 方法反而已经被添加进去了,这是因为 Python 解释类定义的时候,已经知道 Child 继承了 Base 中的 say 方法。另一方面,类的自定义属性都是在稍晚触发 init 才被添加到实例的属性字典中。

如此一来,如果父类 init 方法中的工作不再需要,当然可以不调用。

关注我,了解程序员的烧脑日常,还有开源 Python 教程。

def __init__(self)是什么意思_子类必须调用 super().__init__() 吗?相关推荐

  1. qt 子类调用父类的函数_子类中调用父类的方法

    父类名 . __init__(self, ...) 可以将父类中的init中的属性重复调用,减少代码的重复 class Vehicle: def __init__(self, name, speed, ...

  2. python父类方法的装饰器_Python使用装饰器自动调用父类__init__

    众所周知,Python中class的构造函数实际是__new__(),但是如果我们执行p1=Point()的时候,不仅会调用Point的__new__方法,而且会调用Point的__init__方法. ...

  3. Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)

    Python 在子类中调用父类方法详解(单继承.多层继承.多重继承)   by:授客 QQ:1033553122   测试环境: win7 64位 Python版本:Python 3.3.5 代码实践 ...

  4. 什么是python中子类父类_零基础入门:python中子类继承父类的__init__方法实例

    前言: 今天为大家带来的内容是零基础入门:python中子类继承父类的__init__方法实例!具有不错的参考意义,希望在此能够帮助到各位!(喜欢的话记得点赞转发关注不迷路哦) 使用Python写过面 ...

  5. python中的继承的初始化_python中子类继承父类的__init__方法实例

    前言 使用Python写过面向对象的代码的同学,可能对__init__方法已经非常熟悉了,__init__方法在类的一个对象被建立时,马上运行.这个方法可以用来对你的对象做一些你希望的 初始化 . 注 ...

  6. qt 子类调用父类的函数_子类调用父类方法

    当有相同的属性或者方法是this子类表示调用自己的属性或者方法, super调用父类的属性或者方法. 当子类调用的属性方法在子类中不存在时,那么this和super都是调用父类的属性或者方法 1.方式 ...

  7. 根据父类id查询所有的父级_父类子类抽象类,super final 重写方法,搞懂继承中复杂的知识点...

    继承 继承(Inheritance)可以实现类之间共享属性和方法,是面向对象编程的另一个特性 使用继承可以最大限度地实现代码复用. 定义:继承就是在已有类的基础上构建新的类,一个类继承已有类后,可以对 ...

  8. idea实现抽象类的所有抽象方法_父类子类抽象类,super final 重写方法,搞懂继承中复杂的知识点

    继承 继承(Inheritance)可以实现类之间共享属性和方法,是面向对象编程的另一个特性 使用继承可以最大限度地实现代码复用. 定义:继承就是在已有类的基础上构建新的类,一个类继承已有类后,可以对 ...

  9. java子类可以修改父类成员吗_子类重写父类成员方法

    最近在学习到Java的继承和多态时遇到了这样的一个问题:关于继承链中子类对父类的私有方法是否可以覆盖的问题,在此记录一下自己对这个问题解决以后的一些心得. 首先要明确:子类是不能够覆盖(重写)父类的私 ...

最新文章

  1. 使用 trait 时报PHP Parse error: syntax error, unexpected 'use' (T_USE) 这个错误
  2. ef mysql自动更新_EF Core中怎么实现自动更新实体的属性值到数据库
  3. HDU 1430 关系映射 + 打表 .
  4. deep learning:RBM公式推导+源码 ----- C++
  5. psp能装安卓软件吗_客户crm 软件能定制吗
  6. python安装pyqt5第三方_搭建pyqt5开发环境(python3+pycharm2019+pyqt5)
  7. linux文件读保护,Linux Rootkit实现文件保护
  8. LG P4899 [IOI2018] werewolf 狼人(kruskal重构树,二维数点)
  9. Sencha touch API
  10. CaseStudy-数据缓存出错
  11. 20145302张薇 《信息安全系统设计基础》第14周学习总结
  12. grub配置文件丢失的解决方法
  13. OpenCV-图像处理(30、轮廓周围绘制矩形框和圆形框)
  14. 二级mysql刷题_计算机二级通手机版(计算机二级刷题软件)V1.1 简化版
  15. MongoDB——聚合管道之$group操作
  16. 高云FPGA系列教程(2):FPGA点灯工程创建、程序下载和固化
  17. Google设置应用专用密码
  18. 微信小程序点赞成功,取消点赞、评论。
  19. MySQL中查看数据库
  20. html复习第七天 京东首页布局完成

热门文章

  1. 在ubuntu 14.04 64bit下配置安装PyQt4(python2.7和python3.4)
  2. 虚幻引擎的数学知识学习教程 Math for Unreal Engine (Early Preview)
  3. 游戏风格化角色创建入门指南视频教程
  4. MMSE(Minimum Mean Square Error)
  5. s-seq 生成序列化数字
  6. Qt 编译一直死循环问题
  7. Educational Codeforces Round 45 (Rated for Div. 2) D Graph And Its Complement(图的构造)
  8. Thrift源码解析--TBinaryProtocol
  9. 《学习OpenCV》第三章习题 第3题
  10. iPhone App开发实战手册学习笔记(5)之IOS常用机制