python中super().__init__()

本文详细解析了Python中super()函数与__init__方法的作用及使用场景,特别是super().__init__()在多继承和覆盖父类方法时的正确用法。通过多个实例对比,帮助读者理解Python2与Python3在super()调用上的差异。
该文章已生成可运行项目,

super().__ init__ ()有什么用?

super().__init__()super(B,self).__init__()

python里的super().__init__()有什么作用?很多同学没有弄清楚。

super()用来调用父类(基类)的方法,__init__()是类的构造方法,
super().__init__() 就是调用父类的init方法, 同样可以使用super()去调用父类的其他方法。

1、分别理解super()和 __ init __()

1.1、super()

在这里插入图片描述

需要注意的是python2、3super写法稍有不同。

1.2、__ init __()

__init__() 是python中的构造函数,在创建对象的时"自动调用"。

在这里插入图片描述

定义类时可以不写init方法,系统会默认创建,
你也可以写一个,让你的类在创建时完成一些“动作”。

1.3、super(). __ init __()

如果子类B和父类A,都写了init方法,
那么A的init方法就会被B覆盖。想调用A的init方法需要用super去调用。

在这里插入图片描述
当然,在B内部,除了用super调用父类的方法,也可以用父类名调用,例:

class B(A):
    def __init__(self):
        A.__init__(self)
        print("B init")
1.3.1、关于“覆盖”的疑问

有人可能会误解“覆盖”的意思,认为“覆盖”了就是没有,为什么还能通过super调用?
覆盖了并不是没有了,A的方法终都还在,但需要在B内部用super调用。

例:
A里写了一个方法hi(), B继承自A, B里也写了一个方法hi()。
B的对象在外部调用hi(), 就只能调用B里面写的这个hi()。
想通过B调用A的hi(),只能在B内部用super().hi()调用。
class A:
    def hi(self):
        print("A hi")

class B(A):
    def hello(self):
        print("B hello")
        
b = B()
b.hi()       # B里没有写hi(),这里调用的是继承自A的hi()

------------------------------------------------------------------
class A:
    def hi(self):
        print("A hi")

class B(A):
    def hi(self):
        print("B hi")
        
b = B()
b.hi()    # 这里调用的就是B自己的hi()
------------------------------------------------------------------
class A:
    def hi(self):
        print("A hi")

class B(A):
    def hi(self):
        super().hi()         # 通过super调用父类A的hi()
        print("B hi")
        
b = B()
b.hi()    # 这里调用的就是B里面的hi()

2、super() 在 python2、3中的区别

Python3.x 和 Python2.x 的一个区别: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :

例:
python3 直接写成 : super().__init__()
python2 必须写成 :super(本类名,self).__init__()

Python3.x 实例:

class A:
     def add(self, x):
         y = x+1
         print(y)
class B(A):
    def add(self, x):
        super().add(x)
b = B()
b.add(2)  # 3

Python2.x 实例:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class A(object):   # Python2.x 记得继承 object
    def add(self, x):
         y = x+1
         print(y)
class B(A):
    def add(self, x):
        super(B, self).add(x)
b = B()
b.add(2)  # 3

3、关于继承顺序

最底层:先写一个父类A

class A:
    def __init__(self):
        print('A')

第二层:让 B、C、D 继承自A

class B(A):
    def __init__(self):
        print('B')
        super().__init__()

class C(A):
    def __init__(self):
        print('C')
        super().__init__()

class D(A):
    def __init__(self):
        print('D')
        super().__init__()

第三层: E、F、G 继承

class E(B, C):
    def __init__(self):
        print('E')
        super().__init__()

class F(C, D):
    def __init__(self):
        print('F')
        super().__init__()

class G(E, F):
    def __init__(self):
        print('G')
        super().__init__()

看看G的继承顺序
在这里插入图片描述
在这里插入图片描述
我们发现G继承自E, F是并列的,初始化的时候不会先把E初始化完毕才初始化F。

4、从多个实例中对比super(python3)

下面是三种不同的继承、调用,对比他们的区别,搞清楚super().__init__()的用途。

4.1、实例

在这里插入图片描述

子类名称继承内容
Puple继承所有
Puple_Init继承,但覆盖了init方法
Puple_Super继承,但覆盖了init方法,并在init里面添加了super().__init__()

4.2、运行结果与对比

在这里插入图片描述

4.3、完整代码

在这里插入图片描述

本文章已经生成可运行项目
<think>我们正在讨论Python中调用父类初始化方法的两种方式:`super().__init__()`和显式调用基类的`__init__`方法(如`BaseClass.__init__(self)`)。以下将详细解释两者的区别和最佳实践。 ###一、两种调用方式的区别####1. `super().__init__()` -**动态查找父类**:`super()`根据当前类的MRO(方法解析顺序)动态确定下一个要调用的类。这在单继承和多继承中都能正确工作。- **避免硬编码**:不直接指定父类名称,因此当类继承关系改变时,无需修改代码。 -**多继承友好**:在多重继承中,`super()`能确保所有父类的初始化方法按MRO顺序被调用一次,避免重复调用或遗漏。 示例: ```pythonclass Base:def __init__(self):print("Baseinit")class Child(Base):def__init__(self):super().__init__()#调用Base.__init__print("Childinit")c =Child()#输出:# Baseinit# Childinit``` ####2.显式调用基类方法(如`BaseClass.__init__(self)`) -**静态指定父类**:直接通过父类名调用其`__init__`方法。- **硬编码依赖**:若继承关系变化(如中间插入新的父类),则需要修改所有显式调用的地方。- **多继承问题**:在多重继承中,如果多个父类有共同的祖先,可能导致共同祖先的初始化方法被多次调用(钻石继承问题)。错误示例(钻石继承问题):```pythonclassA:def__init__(self):print("A init") classB(A):def __init__(self):print("Binit")A.__init__(self)#显式调用AclassC(A):def __init__(self):print("Cinit")A.__init__(self)#显式调用AclassD(B, C):def __init__(self):print("Dinit")B.__init__(self)C.__init__(self)#导致A被初始化两次!d =D()#输出:# Dinit# Binit# Ainit# Cinit# Ainit<-A被初始化两次!``` ###二、为什么推荐使用`super().__init__()`?1.**解决多继承问题**使用`super()`时,Python按MRO顺序自动安排初始化调用链,确保每个父类只初始化一次:```pythonclassA:def__init__(self):print("A init")class B(A):def__init__(self):print("B init")super().__init__()#按MRO调用下一个类(可能是C或A)classC(A):def __init__(self):print("Cinit")super().__init__()#调用AclassD(B, C):def __init__(self):print("Dinit")super().__init__()#按MRO调用Bd =D()#输出:# Dinit#B init# Cinit#A init<-A只初始化一次```MRO顺序:`D-> B-> C-> A-> object`(可通过`print(D.__mro__)`查看)。2.**代码可维护性**当调整类的继承结构时(如在`B`和`A`之间新增一个父类),使用`super()`的代码无需修改。###三、使用`super().__init__()`的注意事项1.**参数传递**必须确保参数与父类`__init__`匹配:```pythonclass Parent:def __init__(self,x):self.x =xclassChild(Parent):def__init__(self, x, y):super().__init__(x)#传递x给Parentself.y =y```2.**所有父类必须协作使用`super()`**在多继承中,若某个父类没有调用`super().__init__()`,则初始化链可能中断。因此,设计可继承的类时,应在`__init__`中调用`super().__init__()`(除非明确知道自己在做什么)。 3. **调用位置**通常放在子类初始化代码之前:```pythonclass Child(Parent):def __init__(self,...):super().__init__(...)#先初始化父类#再初始化子类属性```###四、何时使用显式调用? 以下情况可能需要显式调用:-**需要跳过MRO中的某些类**(罕见)。- **父类未使用`super()`**(需手动管理初始化链)。 -**特定性能优化场景**(通常不推荐)。 ###五、总结对比|**特性**|`super().__init__()`|`BaseClass.__init__(self)`| |------------------------|---------------------------------------|----------------------------------||继承结构变化| ✅无需修改代码|❌需修改所有显式调用| |多继承支持|✅自动按MRO顺序调用|❌易导致重复初始化或遗漏| |参数传递|需匹配父类签名|需匹配父类签名| |代码可读性|更高(避免硬编码)|较低(父类名重复出现)| >**最佳实践**:在Python3中,**优先使用`super().__init__()`**,除非有明确理由使用显式调用[^1][^4]。--- ###常见问题解答**Q1:如果父类的`__init__`方法有返回值,怎么办?**A: Python要求`__init__`必须返回`None`,否则会引发`TypeError`。因此无论用哪种方式调用,都不应返回非`None`值。**Q2:在单继承中两种方式有区别吗?**A:在单继承中,功能上无区别。但`super()`的代码更灵活,未来改为多继承时无需重构。 **Q3:为什么有时用`super(CurrentClass,self).__init__()`?**A:这是Python2的语法,Python3中可简化为`super().__init__()`。--- §§相关问题 §§1.如何查看一个类的MRO(方法解析顺序)?2.在多重继承中,如果某个父类没有调用`super().__init__()`会怎样?3. Python2和Python3中`super()`的用法有什么区别?4.如何设计一个可被安全继承的基类(确保`__init__`正确处理`super()`)?5.在元类中如何使用`super()`?</think>### Python 中 `super().__init__()` 与显式调用基类 `__init__` 的区别详解 在 Python 继承体系中,调用父类初始化方法有两种主要方式,它们的关键区别在于**多继承场景下的行为机制**: #### 一、核心区别对比表 | **特性** | `super().__init__()` | `BaseClass.__init__(self)` | |------------------------|---------------------------------------|----------------------------------| | **调用机制** | 动态按 MRO 顺序调用下一个类 | 静态指定特定父类 | | **多继承支持** | ✅ 自动处理钻石继承问题 | ❌ 可能导致重复初始化 | | **参数传递** | 需匹配父类签名 | 需匹配父类签名 | | **代码维护性** | 继承结构变化时无需修改 | 父类名变更需更新所有调用点 | | **Python 版本兼容** | Python 3 简洁语法 | 所有版本通用 | #### 二、工作机制深度解析 1. **`super().__init__()` 的动态解析** ```python class A: def __init__(self): print("A initialized") class B(A): def __init__(self): super().__init__() # 自动指向 C (按 MRO) print("B initialized") class C(A): def __init__(self): super().__init__() # 自动指向 A print("C initialized") class D(B, C): def __init__(self): super().__init__() # 启动 MRO 链 print("D initialized") d = D() # 输出顺序: # A initialized → C initialized → B initialized → D initialized ``` - ✅ 通过 `D.__mro__` 确定的顺序 `(D, B, C, A, object)` 自动传递调用 - ✅ 每个父类只初始化一次(解决钻石问题) 2. **显式调用的静态行为** ```python class D(B, C): def __init__(self): B.__init__(self) # 手动调用 B C.__init__(self) # 手动调用 C print("D initialized") d = D() # 输出顺序: # A initialized (来自 B) → B initialized # A initialized (来自 C) → C initialized # A 被重复初始化! # D initialized ``` - ❌ 导致公共祖先类 `A` 被初始化两次 - ❌ 破坏初始化顺序的一致性 #### 三、关键使用场景建议 1. **优先使用 `super()` 的场景** - 多继承体系(尤其是钻石继承结构) - 需要长期维护的代码库 - 类继承结构可能变化的情况 ```python class ModernView(tk.Frame): def __init__(self, master): super().__init__(master) # ✅ 动态查找父类 self.create_widgets() ``` 2. **显式调用的适用场景** - 明确规避某些父类的初始化 - 需要精确控制特定父类的初始化顺序 - 兼容不支持 `super()` 的旧式类(Python 2) ```python class LegacyView(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) # 显式指定 self.create_widgets() ``` #### 四、参数传递的注意事项 两种方式都需正确传递参数,但 `super()` 在多重继承中要求协作设计: ```python class Person: def __init__(self, name): self.name = name class Employee(Person): def __init__(self, name, id): super().__init__(name) # ✅ 正确传递 self.id = id # 错误示例 class Employee(Person): def __init__(self, name, id): Person.__init__(self, name) # 显式调用 OK self.id = id ``` #### 五、常见错误及解决方案 1. **错误:父类初始化被跳过** ```python class Child(Parent): def __init__(self): self.child_var = 42 # 先初始化子类属性 super().__init__() # 父类可能覆盖 child_var! ``` **修复**:始终将 `super().__init__()` 放在子类初始化开头 2. **错误:多继承中的参数不匹配** ```python class A: def __init__(self, x): self.x = x class B: def __init__(self, y): self.y = y class C(A, B): def __init__(self): super().__init__(x=1) # ❌ 缺少 y 参数 ``` **修复**:使用关键字参数明确传递 ```python class C(A, B): def __init__(self): super().__init__(x=1, y=2) # ✅ 协作式设计 ``` #### 六、最佳实践总结 | **实践要点** | `super().__init__()` | 显式调用 | |-----------------------|----------------------|------------------| | **初始化位置** | 子类构造器首行 | 任意位置 | | **多继承** | **强制使用** | 避免使用 | | **维护性** | 高 (解耦父类名) | 低 (硬编码依赖) | | **协作要求** | 所有父类需使用 super | 无特殊要求 | > **终极建议**:在新项目中**始终使用 `super().__init__()`**,仅在处理遗留代码或特殊需求时使用显式调用[^1][^4]。 ---
评论 104
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值