当继承遇到self和super

    技术2022-07-31  76

    目录

    1.self的含义

    2、super的含义:子类调用父类的函数的一种实现方法

    (1)使用父类名

    (2)使用super函数

    3.继承中,使用super()调用父类后,self到底代表谁?


    1.self的含义

    class X:

        def __init__(self):

            self.x = 10

    def f(self):

            return self.x+1

     x = X()

     x.f()

    类是抽象的模板,实例是根据类创建出的一个个具体对象。

    上述代码中,

    第一句 x = X(),会被python解释器转化为:

    ①x=object.__new__(X) #X类创建一个新的对象x ,object类的new函数负责创建新对象

    ②X.__init__(x) # X将x赋值给self,运行__init__函数,init负责向新对象的属性赋值

    第二句 x.f() 会被python解释器转化为 X.f(x),即对象x被传给了参数 self,用于表明是实例对象x在调用f方法。解释器会把对象x本身传到函数中,即可辨识每一个对象各自的属性了。这种指明被称为将f方法绑定到x对象

    所以类中的函数需要至少拥有一个参数用来保存传进来的对象。在Python中一种约定俗成的做法是在各个方法的第一个参数指定一个特殊的名字:self。带self的类方法被称为类的实例方法(还有类方法@classmethod,静态方法@staticmethod

    这就是为什么类中定义的函数(包括__init__),都要用self作为第一个参数,就是用来接受具体的对象。

    self指的是类实例对象本身(注意:不是类本身),是调用当前方法的实例。

     

    2、super的含义:子类调用父类的函数的一种实现方法

    子类调用父类有两种方式:通过父类名 或 通过super函数。用最常见的是调用父类的 __init__()方法说明。

    (1)使用父类名

    见下例。实际上使用了python解释器的方式调用类的函数,即将父类的 init 函数绑定到b1对象上。注意 A.__init__(self) 这行代码中,self参数实际已经被赋值为 b1 对象。

    class A(object):

        def __init__(self):

             print ('in A init')

    class B(A):

        def __init__(self):

              A.__init__(self) #显式(explicitly)调用父类的函数

    b1 = B() # 解释为B.init(b1)  self赋值b1对象

    (2)使用super函数

    super函数的工作原理如下:

    def super(cls, inst):

        mro = inst.__class__.mro()

        return mro[mro.index(cls) + 1]

     cls代表类,inst代表实例,可以看出上面的代码做了两件事:

    获取inst实例MRO列表。查找cls类MROindex,并返回它的下一个类,即mro[index + 1]

    当使用super(cls, inst).func()时,python会在instMRO列表上搜索下cls的下一个类,并运行该类的function方法。一般在使用时会省略clsinst简写为 super().func(),因此需要留意简写时clsinst指的都是谁:Cls就是这个super函数所在的类(下例的B),inst就是调用super这个函数的实例对象(即可以写成self,下例的b1)。

    class A(object):

        def __init__(self):

             print ('in A init')

    class B(A):

        def __init__(self):

              super().__init__()

    b1 = B() 

    super和父类没有实质性的关联,只是恰巧在MRO列表中子类下一个就是父类。如果是多重继承MRO列表中顺序将更加复杂super(cls, inst)获得的是clsinstMRO列表中下一个类,也就是说super后跟着的不一定非得是父类的init方法,甚至可以是普通的父类方法。

     

    3.继承中,使用super()调用父类后,self到底代表谁?

    self代表类的实例(或者称类的对象),在实例化子类时,并没有实例化父类,所以self只能代表子类的实例!!!

    ①第一个例子

    class father:

        def common(self):

            print("this is a father also common")

            self.childprint() #子类实例化后 这里的self是子类的实例对象

           

    class child(father):

        def common(self):

            super().common()

            print('this is a child common ')

     

        def childprint(self):

            print ("this is a child")

     

    test = child()

    test.common()

    可以看到,“this is a child”也被打印了。很明显子类的一个实例test调用了父类的common方法,而该方法里居然调用了子类的childprint方法。

    事实是这样的,子类实例化后,所有子类继承的父类的方法以及子类自身的方法都归该实例所有,那么此时的common方法自然也是属于子类刚才实例化的对象, 子类对象在super调用父类的common方法时,其self已经代表子类对象(父类并没有实例化),因此可以在common方法中调用子类的childprint方法。表面上看是父类调用子类方法,其实还是子类实例调用自己的方法。self.childprint()中的self指的就是子类实例。

     

    ②再看一个例子

    class parent():     def prepare_env(self):#第2个           print(self) # 这里输出的self是child的实例           self.prepare_data() # 运行这句话时调用了child的实例ads的prepare_data方法,因为此时self是子类的实例     def prepare_data(self): #第4个

              print(self) # 这里输出的self依然是child的实例           self.process_data() #调用child实例ads的processdata   方法 父类并无此方法,就算是父类有此方法,也依然调用子类的方法        class child(parent):     def prepare_env(self): #第1个         super().prepare_env()         # 等价于 super(child,self).prepare_env() super(cls,inst) cls为类 inst为对象 获取self的MRO列表 查找child在MRO的index,并返回它的下一个类     def prepare_data(self):#第3个         # parent.prepare_data(self)         super().prepare_data()          print('child prepare data')     def process_data(self):# 第5个         print('process data')if __name__ ==  '__main__' :     ads = child()     print(child.mro())     ads.prepare_env()

    输出结果:

     

    ③再看一个例子:  

    class X:

        def __init__(self):

            self.x = 10

        def f(self):

            return self.x+1

    class Y(X):

        def __init__(self):

            self.x = 100

        def b(self):

            return super().f()

    x = X()

    x.f() #=>11

    y = Y()

    print(y.b()) # -> 101

    调用y.b()时,super一句调用X类的f()函数,返回 self.x +1 现在问题是搞清楚self是哪个对象。Super().f() 等价于 super(Y,y).f(),Y为class y为instance,因此在调用X类的f()函数时 self指的是y,因此y.x=100,返回值为101.

    super().f()在这里是super(Y, self).f(),self是Y的实例, 即方法调用的是父类的f()方法也就是f(self),但是传入参数self是Y的实例,那么self.x即100

     

    ④再看一个例子:

    由于是对class C()进行实例化,上面的self都是指class C()的实例而不是class A()的或者class B()的。因此self.__class__清一色的显示<class ‘main.C’>而不是<class ‘main.A’>或<class ‘main.B’> 。

     

    最后,还有一个其他的情况(不成熟,待进一步探究):

    Self 不会动态变化,就是指最外层实例化的对象 该对象里面的父类 都不是直接实例化的对象,都不是self

    但如果对象中有属性是其他类的话(如绑定某Engine),那么事情就不同了,在向属性赋值的时候,将Engine类实例化了(self.backtestengine = BacktestingEngine()),那么使用这个实例的方法调用时,self就是指这个实例了。

    Processed: 0.024, SQL: 9