Python中的super
一、前言(所有内容都基于最开始的代码)
class A:
def __init__(self):
print('A')
class B(A):
def __init__(self):
print('B')
if __main__ == '__main__':
b = B()
# B
但在某些情况下我们希望在运行时能够直接调用其父类的方法,怎么做呢?
class A:
def __init__(self):
print('A')
class B(A):
def __init__(self):
super(B,self).__init__()
# super().__init__()
print('B')
if __main__ == '__main__':
b = B()
# B
# A
在python2
中是super(B,self).__init__()
;在python3
中简化为super().__init__()
。
二、既然可以重写对象B的构造函数,那为什么还要去调用super?
拿多线程举例,我们定义一个自己的线程类MyThread
,然后去继承Thread
类。
from threading import Thread
class MyThread(Thread):
def __init(self,name,user):
self.user = user
# self.name = name (不需要)
super().__init__(name=name)
在自定义的MyThread
类中,先定义一个初始化函数,假设给它传递name
(线程名称)和user
(执行的用户)两个参数。此时可以不使用self.name = name
。
因为在源码中的Thread
类中已经实现了__init__
方法,在该方法中有许多的参数,其中就有name
参数,并且在该方法中也实现了许多的逻辑。
实际上让自定义的MyThread
类直接调用父类Thread
即可,等于把构造函数交给了父类去实例化,这样就可以重用父类中的代码(方法,逻辑),所以在某些情况下,super
函数还是需要调用的。
Thread
类源码的__init__
部分 (不用细看,注意结构即可)
class Thread:
"""A class that represents a thread of control.
This class can be safely subclassed in a limited fashion. There are two ways
to specify the activity: by passing a callable object to the constructor, or
by overriding the run() method in a subclass.
"""
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):
"""This constructor should always be called with keyword arguments. Arguments are:
*group* should be None; reserved for future extension when a ThreadGroup
class is implemented.
*target* is the callable object to be invoked by the run()
method. Defaults to None, meaning nothing is called.
*name* is the thread name. By default, a unique name is constructed of
the form "Thread-N" where N is a small decimal number.
*args* is the argument tuple for the target invocation. Defaults to ().
*kwargs* is a dictionary of keyword arguments for the target
invocation. Defaults to {}.
If a subclass overrides the constructor, it must make sure to invoke
the base class constructor (Thread.__init__()) before doing anything
else to the thread.
"""
assert group is None, "group argument must be None for now"
if kwargs is None:
kwargs = {
}
self._target = target
self._name = str(name or _newname())
self._args = args
self._kwargs = kwargs
if daemon is not None:
self._daemonic = daemon
else:
self._daemonic = current_thread().daemon
self._ident = None
self._tstate_lock = None
self._started = Event()
self._is_stopped = False
self._initialized = True
# sys.stderr is not stored in the class like
# sys.exc_info since it can be changed between instances
self._stderr = _sys.stderr
# For debugging and _after_fork()
_dangling.add(self)
三、super函数的执行顺序是什么样的?
super函数是调用父类的构造函数(方法),这句话其实并不是完全正确。(这涉及到MRO
算法)
举个简单的例子,定义4个类,B和C都继承A,而D继承B和C。如果在D类中使用super
函数,而D类的父类又有两个(B和C),因为B在C之前,所以优先调用B中的构造函数。
而B中也有super
函数,那是不是就应该去打印A了? 从运行结果可以看出答案是NO
。
super
函数不仅仅是调用父类函数,而且是按MRO
算法继承链的顺序去进行调用
class A:
def __init__(self):
print('A')
class B(A):
def __init__(self):
print('B')
super().__init__()
class C(A):
def __init__(self):
print('C')
super().__init__()
class D(B,C):
def __init__(self):
print('D')
super(D,self).__init__()
if __main__ == '__main__':
d = D()
# D
# B
# A
# C
使用__mro__
就可以看到继承链中的顺序——首先D然后B,再C,再A。
print(D.__mro__)
# (<class ‘__main__.D’>,<class ‘__main__.B’>,<class ‘__main__.C’>,<class ‘__main__.A’>,<class ‘object’>)