Python super()使用注意事项
Python 中,由于基类不会在 __init__() 中被隐式地调用,需要程序员显式调用它们。这种情况下,当程序中包含多重继承的类层次结构时,使用 super 是非常危险的,往往会在类的初始化过程中出现问题。
运行结果为:
但是,有时这种层次结构的一部分位于第三方代码中,我们无法确定外部包的这些代码中是否使用 super(),因此,当需要对某个第三方类进行子类化时,最好查看其内部代码以及 MRO 中其他类的内部代码。
运行结果为:
运行结果为:
混用super与显式类调用
分析如下程序,C 类使用了 __init__() 方法调用它的基类,会造成 B 类被调用了 2 次:
- class A:
- def __init__(self):
- print("A",end=" ")
- super().__init__()
- class B:
- def __init__(self):
- print("B",end=" ")
- super().__init__()
- class C(A,B):
- def __init__(self):
- print("C",end=" ")
- A.__init__(self)
- B.__init__(self)
- print("MRO:",[x.__name__ for x in C.__mro__])
- C()
MRO: ['C', 'A', 'B', 'object']
C A B B
但是,有时这种层次结构的一部分位于第三方代码中,我们无法确定外部包的这些代码中是否使用 super(),因此,当需要对某个第三方类进行子类化时,最好查看其内部代码以及 MRO 中其他类的内部代码。
不同种类的参数
使用 super 的另一个问题是初始化过程中的参数传递。如果没有相同的签名,一个类怎么能调用其基类的 __init__() 代码呢?这会导致下列问题:
- class commonBase:
- def __init__(self):
- print("commonBase")
- super().__init__()
- class base1(commonBase):
- def __init__(self):
- print("base1")
- super().__init__()
- class base2(commonBase):
- def __init__(self):
- print("base2")
- super().__init__()
- class myClass(base1,base2):
- def __init__(self,arg):
- print("my base")
- super().__init__(arg)
- myClass(10)
my base
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\demo.py", line 20, in <module>
myClass(10)
File "C:\Users\mengma\Desktop\demo.py", line 19, in __init__
super().__init__(arg)
TypeError: __init__() takes 1 positional argument but 2 were given
- class commonBase:
- def __init__(self,*args,**kwargs):
- print("commonBase")
- super().__init__()
- class base1(commonBase):
- def __init__(self,*args,**kwargs):
- print("base1")
- super().__init__(*args,**kwargs)
- class base2(commonBase):
- def __init__(self,*args,**kwargs):
- print("base2")
- super().__init__(*args,**kwargs)
- class myClass(base1,base2):
- def __init__(self,arg):
- print("my base")
- super().__init__(arg)
- myClass(10)
my base
base1
base2
commonBase
总结
如果想要避免程序中出现以上的这些问题,这里给出几点建议:- 尽可能避免使用多继承,可以使用一些设计模式来替代它;
- super 的使用必须一致,即在类的层次结构中,要么全部使用 super,要么全不用。混用 super 和传统调用是一种混乱的写法;
- 如果代码需要兼容 Python 2.x,在 Python 3.x 中应该显式地继承自 object。在 Python 2.x 中,没有指定任何祖先地类都被认定为旧式类。
- 调用父类时应提前查看类的层次结构,也就是使用类的 __mro__ 属性或者 mro() 方法查看有关类的 MRO。
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算