美文网首页Python全栈工程师
28.3-单继承 和 Super()

28.3-单继承 和 Super()

作者: BeautifulSoulpy | 来源:发表于2019-12-17 22:27 被阅读0次

    在变幻的生命里,岁月是最大的神偷,尽自己所能,不辜负,不遗憾!

    本章总结

    1. 属性查找顺序实例的_dict_ ===》实例的类_dict_ ===如果有继承===》 继承父类_dict_
      如果搜索这些地方后没有找到就会抛异常,先找到就立即返回了;
    2. 方法是 在类的字典上的;实例上是没有的;

    面向对象三要素:封装、继承、多态性;

    1. 继承 Inheritance

    人类和猫类都继承自动物类。个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性。
    在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。子类可以定义自己的属性和方法。

    向上单继承;向下多继承;
    先研究单继承;

    继承: 继承能力(属性) 或 特征(方法);
    继承
    class Cat(Animal) 这种形式就是从父类继承,括号中写上继承的类的列表。
    继承可以让子类从父类获取特征(属性和方法)
    父类
    Animal就是Cat的父类,也称为基类、超类。
    子类
    Cat就是Animal的子类

    类的继承

    # 类各有自己的特点;
    class Animal:
        def __init__(self,name):
            self.name = name
            
        def shout(self):
            print('Animal shuts')
            
    class Cat(Animal): pass # 复用起来;
    #     def shout(self):
    #         print('miao')
    
    class Dog(Animal):pass
            
    a = Animal('Animal')
    a.shout()
    
    c = Cat('garfiled')
    c.shout()
    
    d = Dog('ahuang')
    c.shout()
    
    
    

    类的复用;

    # 类各有自己的特点;
    class Animal:
        def __init__(self,name):
            self.name = name
            
        def shout(self):
            print('{} shouts'.format(type(self).__name__))
            
    class Cat(Animal): pass # 复用起来;
    #     def shout(self):
    #         print('miao')
    class Dog(Animal): pass
            
    a = Animal('Animal')
    a.shout()
    
    c = Cat('garfiled')
    c.shout()
    
    d = Dog('ahuang')
    d.shout()
    
    
    class Animal:
        def __init__(self, name):
            self._name = name
        def shout(self): # 一个通用的吃方法
            print('{} shouts'.format(self.__class__.__name__))
        @property
        def name(self):
            return self._name
    
    class Cat(Animal):
       # 子类拥有父类的所有属性和方法;
        pass
    
    class Dog(Animal):
        pass
    
    a = Animal('monster')
    a.shout()
    
    cat = Cat('garfield')
    cat.shout()
    print(cat.name)
    
    dog = Dog('ahuang')
    dog.shout()
    print(dog.name)
    #-------------------------------------
    Animal shouts
    Cat shouts
    garfield
    Dog shouts
    ahuang
    上例可以看出,通过继承,猫类、狗类不用写代码,直接继承了父类的属性和方法。
    
    
    1.1 继承的定义
    class 子类名(基类1[,基类2,...]):
        pass
    

    如果类定义时,没有基类列表,等同于继承自object。在Python3中,object类是所有对象的根基类
    object 是 祖先类,一切接对象的祖先类;根基类;

    class A:
        pass
    # 等价于
    class A(object):
        pass
    
    **注意**:上例在Python2中,两种写法是不同的。是不同的含义;
    
    class Animal:
        def __init__(self, name):
            self._name = name
        def shout(self): # 一个通用的吃方法
            print('{} shouts'.format(self.__class__.__name__))
        @property
        def name(self):
            return self._name
    
    class Cat(Animal):
        pass
    
    print(Animal.__base__)  # 类的基类(父类)查看
    print(Cat.__base__)
    print(Cat.__base__.__base__)
    print(Cat('tom').__class__.__bases__)   # 返回一个元组; base 非元组;
    print(Cat.__mro__)  # 所有 继承的类;
    print(Cat.mro())   # 继承 类的列表
    
    print(Animal.__subclasses__())
    print(int.__subclasses__())  # 布尔值本质上是 int;
    #------------------------------------------------------------------------------
    <class 'object'>
    <class '__main__.Animal'>
    <class 'object'>
    (<class '__main__.Animal'>,)   # 元组
    [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]    # 列表
    [<class '__main__.Cat'>]
    [<class 'bool'>, <enum 'IntEnum'>, <enum 'IntFlag'>, <class 'sre_constants._NamedIntConstant'>, <class 'subprocess.Handle'>]
    

    Python支持多继承,继承也可以多级。
    查看继承的特殊属性和方法有

    特殊属性和方法 含义 示例
    _base_ 类的基类
    _bases_ 类的基类元组
    _mro_ 显示方法查找顺序,基类的元组
    mro()方法 同上,返回列表 int.mro()
    _subclasses_() 类的子类列表 subclasses()
    1.2 继承中的访问控制

    什么是公有的,保护的,私有的;

    继承时,公有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法中可以访问这个私有变量。

    Python通过自己一套实现,实现和其它语言一样的面向对象的继承机制。

    总结:

    1. 私有属性在哪里定义受谁管;
    2. 从父类继承,自己没有的,就可以到父类中找。
    3. 私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或实例的dict中。知道这个新名称就可以直接找到这个隐藏的变量,这是个黑魔法技巧,慎用。
      类cls的属性 看全局变量; 实例self的属性 看构造函数的设定;
    # 面向对象最复杂的例子;  一切都在字典中,
    class Animal:
        __COUNT = 100 # 私有的属性
        HEIGHT = 0    # 公有属性
        
        def __init__(self, age, weight, height):  # 构造方法;
            self.__COUNT += 1 #动态增加属性 ; 
            self.age = age
            self.__weight = weight
            self.HEIGHT = height
            
        def eat(self):
            print('{} eat '.format(self.__class__.__name__))
            
        def __getweight(self):     # 私有方法;
            print(self.__weight)  # 属于实例的属性;
                  
        @classmethod
        def showcount1(cls):  # 属于Animal 的;
            print(cls)
            print(cls.__dict__) # Cat类里面没有__count, 就往上一级父类里面去找;
            print(cls.__COUNT) # 显示多少?为什么
                  
        @classmethod
        def __showcount2(cls):
            print(cls.__COUNT)
                  
        def showcount3(self):
            print(self.__COUNT) # 是多少?为什么
                  
    class Cat(Animal):
        NAME = 'CAT'   # 自己的类都没有初始化过程,索引不能访问 Cat 类实例化的属性
        __COUNT = 200
        
    # c=Cat() # __init__ 函数参数不够报错;
    c = Cat(3, 5, 15)
    c.eat()
    print(c.HEIGHT)   # Cat没有,Animal 有 height=15
    print(c.__dict__)  
    print(c._Animal__weight)   # 私有的跟他所在的类相关; __weight 属于 Animal类; 私有的加 _Animal__weight  
    print('-'*60)
    print(c._Animal__getweight())
    print('-'*60)
    
    c.showcount1() # 为什么是100? 看传什么类进去,
    print('-'*10)
    c._Animal__showcount2() # 为什么是100? 看传什么类进去,
    c.showcount3()   # 101 ?;  类cls的属性 看全局变量; 实例self的属性 砍构造函数的设定
    #-----------------------------------------------------------------------------------------------------------------------------------------------
    Cat eat 
    15
    {'_Animal__COUNT': 101, 'age': 3, '_Animal__weight': 5, 'HEIGHT': 15}
    5
    ------------------------------------------------------------
    5
    None
    ------------------------------------------------------------
    <class '__main__.Cat'>
    {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
    100
    ----------
    100
    101  #为什么是101????
    
    

    在Python 中,私有的属性可以看到,在其他语言中你看都看不到 私有的属性;都是严格非 访问控制;

    ## 私有是自己的属性; 只能在自己的内部访问;
    class A:
        def __init__(self):
            self.__i = 100
        def geti(self):
            return self.__i
        i = property(geti)
        
    print(A().geti())
    print(A().i)
    #------------------------------------------------------------------------------------------
    100
    100
    

    2. 属性查找顺序

    实例的_dict_ ===》类_dict_ ===如果有继承===》 父类_dict_
    如果搜索这些地方后没有找到就会抛异常,先找到就立即返回了;

    3. 方法重写

    重写必须出现在继承中。它是指当派生类继承了基类的方法之后,如果基类方法的功能不能满足需求,需要对基类的某些地方进行修改,可以在派生类重写基类的方法,也就是重写;

    class Animal:
        def shout(self):
            print('Animal shouts')
            
    class Cat(Animal):
        # 覆盖了父类方法
        def shout(self):
            print('miao')
            
    a = Animal()
    a.shout()
    c = Cat()
    c.shout()
    print(a.__dict__)  #方法在实例的字典中都没有
    print(c.__dict__)
    print('-'*30)
    print(Animal.__dict__)
    print(Cat.__dict__)
    
    #-----------------------------------------------
    Animal shouts
    miao
    {}       # 方法是 在类的字典上的;实例上是没有的;
    {}
    ------------------------------
    {'__module__': '__main__', 'shout': <function Animal.shout at 0x0000022988CD0F28>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
    {'__module__': '__main__', 'shout': <function Cat.shout at 0x0000022988CD08C8>, '__doc__': None}
    
    

    3.1 super()
    super()是Python子类继承父类字段属性或方法使用的关键字,当子类继承父类后,就可以使用父类的方法

    super把self转化为父类Person的一个实例对象,然后再去init()方法;
    super()方法只能在Python3.x中使用,python2.x中必须显式的指明类名;
    super() = super(Cat,self)

    class Animal:
        def shout(self):
            print('Animal shout')
            
    class Cat(Animal):
        # 覆盖了父类方法
        def shout(self):
            print('miao')
        # 覆盖了自身的方法,显式调用了父类的方法
        
        def shout(self):
           # print(super(Cat)) # 不能这么写
            print(super())  # 等价于 = super(Cat, self)
            print(super(Cat, self))
           # super(Cat).shout()
            print('1'*60)
            super().shout()
            super(Cat, self).shout() # 等价于 = super().shout()
            self.__class__.__base__.shout(self) # 不推荐
            Animal.shout(self)
    a = Animal()
    a.shout()
    c = Cat()
    c.shout()
    print(a.__dict__)  #方法在实例的字典中都没有
    print(c.__dict__)
    print('-'*30)
    print(Animal.__dict__)
    print(Cat.__dict__)
    #--------------------------------------------------------------------------------------------------------
    Animal shout
    <super: <class 'Cat'>, <Cat object>>
    <super: <class 'Cat'>, <Cat object>>
    11111111111111111111111111111111111111111111111111111
    Animal shout
    Animal shout
    Animal shout
    Animal shout
    {}
    {}
    ------------------------------
    {'__module__': '__main__', 'shout': <function Animal.shout at 0x0000022988C2E268>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
    {'__module__': '__main__', 'shout': <function Cat.shout at 0x00000229867BDE18>, '__doc__': None}
    
    

    super()可以访问到父类的属性和方法;

    3.2 那对于类方法和静态方法呢?

    1. 通过自己的实例访问,当然优先访问自己的;
    # 子类的方法覆盖 父类的方法;
    class Animal:
        @classmethod
        def class_method(cls):
            print('class_method_animal')
        @staticmethod
        def static_method():
            print('static_method_animal')
            
    class Cat(Animal):
        @classmethod
        def class_method(cls):
            print('class_method_cat')
            
        @staticmethod
        def static_method():
            print('static_method_cat')
            
    c = Cat()  # 通过自己的实例访问,当然优先访问自己的;
    c.class_method()
    c.static_method()
    #--------------------------------------------------------------------------------
    class_method_cat
    static_method_cat
    
    

    这些方法都可以覆盖,原理都一样,属性字典的搜索顺序;

    4. 继承中的初始化

    Python 初始化 方法 必须是显示调用;

    class A:
        def __init__(self, a,d=10):
            self.a = a
            
    class B(A):  # B的父类A
        def __init__(self, b, c):
            super(B,self).__init__(b+c,b-c)  # 实例和类 都往 A上 捆;
          # A.__init__(self, b + c , b - c)   # 等价于 ===
            self.b = b  
            self.c = c
            
        def printv(self):
            print(self.b)
            print(self.a) # 出错吗?B_dict中没有a
        
    f = B(200,300)  # 实例化
    print(f.__dict__)
    print(f.__class__.__bases__)
    #print(f.__base__.__dict__)
    f.printv()
    #--------------------------------------------------------------------------------------------------------------
    {'a': 500, 'b': 200, 'c': 300}
    (<class '__main__.A'>,)
    200
    500
    

    上例代码可知:
    如果类B定义时声明继承自类A,则在类B中base中是可以看到类A;
    但是这和是否调用类A的构造方法 是 两回事 ;
    如果B中调用了A的构造方法,就可以拥有父类的属性了,如何理解这一句话?
    观察B的实例f的_dict中的属性;

    # B 实例化没有,调用A类的属性;
    class A:  # 父类
        def __init__(self):
            self.a1 = 'a1'
            self.__a2 = 'a2'
            print('A init')
    class B(A):  # 
        pass
    #     def __init__(self):
    #         self.b1 = 'b1'
    #         print('B init')
    #         A.__init__(self)
    b = B()
    print(b.__dict__)
    #----------------------------------------
    A init
    {'a1': 'a1', '_A__a2': 'a2'}
    
    
    
    # B类调用 实例化 属性,与A 无关
    class A:  # 父类
        def __init__(self):
            self.a1 = 'a1'
            self.__a2 = 'a2'
            print('A init')
    class B(A):  # 
    
        def __init__(self):
            self.b1 = 'b1'
            print('B init')
            #A.__init__(self)
    b = B() 
    print(b.__dict__)
    #-------------------------------------
    B init
    {'b1': 'b1'}
    
    
    #B实例一旦定义了初始化__init__方法,就不会自动调用父类的初始化__init__方法,需要手动调用。
    class A:  # 父类
        def __init__(self):
            self.a1 = 'a1'
            self.__a2 = 'a2'
            print('A init')
    class B(A):  # 
    
        def __init__(self):
            self.b1 = 'b1'
            print('B init')
            A.__init__(self)  # super()
    b = B()
    print(b.__dict__)
    #-----------------------------------------------------------------------
    B init
    A init
    {'b1': 'b1', 'a1': 'a1', '_A__a2': 'a2'}
    
    
    
    # 如何正确初始化;
    class Animal:
        def __init__(self, age):
            print('Animal init')
            self.age = age
            
        def show(self):
            print(self.age)
            
    class Cat(Animal):
        def __init__(self, age, weight):
            print('Cat init')
            self.age = age + 1
            self.weight = weight
    c = Cat(10, 5)
    c.show() # 能否打印
    #--------------------------------------------------------
    Cat init
    11
    
    
    # 调用父类的方法;
    # 看__dict__,父类Animal 的show方法中age会被截石位Animal.age=10,而不是11;
    class Animal:
        def __init__(self, age):
            print('Animal init')
            self.age = age
            
        def show(self):
            print(self.age)
            
    class Cat(Animal):
        def __init__(self, age, weight):
            super().__init__(age)# 执行父类,10覆盖
            print('Cat init')
            self.age = age + 1  # 先 11
            self.weight = weight
            super().__init__(age)# 执行父类,10覆盖
    c = Cat(10, 5)
    c.show() # 能否打印
    
    print(c.__dict__)
    #----------------------------------------------------------------------------------------------------------
    Animal init
    Cat init
    Animal init
    10
    {'age': 10, 'weight': 5}
    
    

    相关文章

      网友评论

        本文标题:28.3-单继承 和 Super()

        本文链接:https://www.haomeiwen.com/subject/tsosnctx.html