super函数的使用方法

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’>)

猜你喜欢

转载自blog.csdn.net/weixin_43901214/article/details/106908672