4.类的继承

作者: Stone_説 | 来源:发表于2021-01-02 00:40 被阅读0次

    目录:
    1.基本概念
    2.继承的定义
    3.继承中的访问控制
    4.方法的重写、覆盖override
    5.继承时使用初始化

    1.基本概念:

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

    1.1一个不继承的例子
    >>> class Animal:
    ...     def shout(self):
    ...             print('Animal shouts')
    >>> a = Animal()
    >>> a.shout()
    Animal shouts
    >>> class Cat:
    ...     def shout(self):
    ...             print('Cat shouts')
    >>> c = Cat()
    >>> c.shout()
    Cat shouts
    
    1.2一个继承的例子
    >>> 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
    >>> a = Animal('monster')
    >>> a.shout()
    Animal shouts
    
    >>> class Cat(Animal):
    ...     pass
    >>> cat = Cat('garfield')
    >>> cat.shout()
    Cat shouts
    >>> cat.name
    'garfield'
    >>> class Dog(Animal):
    ...     pass
    >>> dog = Dog('dahuang')
    >>> dog.shout()
    Dog shouts
    >>> dog.name
    'dahuang'
    

    2.继承的定义

    格式:

    class 子类名(基类1[,基类2,...]):
      code_block
    

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

    class A:
      pass
    class A(object):
      pass
    

    查看继承的特殊属性和方法

    特殊属性和方法            含义                             示例
    __base__                类的基类
    __bases__               类的基类元组
    __mro__                 显示方法查找顺序,基类的元组
    mro()方法               同上,返回列表                    int.mro()
    __subclasses__()        类的子类列表                     int.__subclasses__()
    

    3.继承中的访问控制

    >>> 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):
    ...             print(cls)
    ...             print(cls.__dict__)
    ...             print(cls.__COUNT)
    ...     @classmethod
    ...     def __showcount2(cls):
    ...             print(cls.__COUNT)
    ...     def showcount3(self):
    ...             print(self.__COUNT)
    ... 
    >>> class Cat(Animal):
    ...     NAME = 'CAT'
    ...     __COUNT = 200
    
    >>> c = Cat()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: __init__() missing 3 required positional arguments: 'age', 'weight', and 'height'
    >>> c = Cat(3,5,15)
    >>> c.eat()
    Cat eat
    >>> c.HEIGHT
    15
    >>> c.__COUNT
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Cat' object has no attribute '__COUNT'
    >>> c._Cat__COUNT
    200
    >>> c._Animal__COUNT
    101
    >>> c.__getweight
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Cat' object has no attribute '__getweight'
    >>> c._Cat__getweight
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Cat' object has no attribute '_Cat__getweight'
    >>> c._Animal__getweight
    <bound method Animal.__getweight of <__main__.Cat object at 0x7f13b307d6d0>>
    
    >>> c.showcount1()
    <class '__main__.Cat'>
    {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
    100   #这个地方要特别注意
    
    
    >>> c.__showcount2()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Cat' object has no attribute '__showcount2'
    >>> c._Cat__showcount2()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Cat' object has no attribute '_Cat__showcount2'
    >>> c._Animal__showcount2()
    100
    
    >>> c.showcount3()
    101
    
    >>> c.NAME
    'CAT'
    >>> print("{}".format(Animal.__dict__))
    {
    '__module__': '__main__', 
    '_Animal__COUNT': 100,
    'HEIGHT': 0, '__init__': <function Animal.__init__ at 0x7f13ab25cdc0>,
    'eat': <function Animal.eat at 0x7f13ab25cf70>, 
    '_Animal__getweight': <function Animal.__getweight at 0x7f13ab268040>, 
    'showcount1': <classmethod object at 0x7f13b302ce50>, 
    '_Animal__showcount2': <classmethod object at 0x7f13b307d430>, 
    'showcount3': <function Animal.showcount3 at 0x7f13ab2681f0>, 
    '__dict__': <attribute '__dict__' of 'Animal' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Animal' objects>, 
    '__doc__': None
    }
    >>> print("{}".format(Cat.__dict__))
    {
    '__module__': '__main__', 
    'NAME': 'CAT', 
    '_Cat__COUNT': 200, 
    '__doc__': None
    }
    >>> print(c.__dict__)
    {
    '_Animal__COUNT': 101, 
    'age': 3, 
    '_Animal__weight': 5, 
    'HEIGHT': 15
    }
    >>> print(c.__class__.mro())
    [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
    

    从父类继承,自己没有的,就可以到父类中找。私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或实例的dict中。知道这个新名称就可以直接找到这个隐藏的变量,这是个黑魔法,慎用。
    总结:
    继承时,公有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法中可以访问这个私有变量。
    Python通过自己一套实现,实现和其他语言一样的面向对象的继承机制。

    实例属性查找顺序:
    实例的dict===> 类dict ==》如果有继承 ==》父类dict
    如果搜索这些地方后没有找到,就会抛出异常,先找到就立即返回了。

    4.方法的重写、覆盖override

    >>> class Animal:
    ...     def shout(self):
    ...             print('Animal shouts')
    >>> class Cat(Animal):
    ...     def shout(self):
    ...             print('miao')
    >>> a = Animal()
    >>> a.shout()
    Animal shouts
    >>> c = Cat()
    >>> c.shout()
    miao
    >>> a.__dict__
    {}
    >>> c.__dict__
    {}
    >>> print(Animal.__dict__)
    {
    '__module__': '__main__', 
    'shout': <function Animal.shout at 0x7f354da66820>, 
    '__dict__': <attribute '__dict__' of 'Animal' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Animal' objects>, 
    '__doc__': None
    }
    >>> print(Cat.__dict__)
    {
    '__module__': '__main__', 
    'shout': <function Cat.shout at 0x7f354da668b0>, 
    '__doc__': None
    }
    

    Cat是否覆盖自己的方法呢?

    [root@node05 python]# vim test3.py
    class Animal:
        def shout(self):
            print('Animal shout')
    
    class Cat(Animal):
        def shout(self):
            print('miao')
            
        def shout(self):
            print(super())
            print(super(Cat,self))
            print(super(self.__class__,self))
            
            super().shout()
            super(Cat,self).shout()
            self.__class__.__base__.shout(self)
            
    a = Animal()
    a.shout()
    c = Cat()
    c.shout()
    
    print(a.__dict__)
    print(c.__dict__)
    print(Animal.__dict__)
    print(Cat.__dict__)
    
    [root@node05 python]# python3 test3.py 
    Animal shout
    <super: <class 'Cat'>, <Cat object>>
    <super: <class 'Cat'>, <Cat object>>
    <super: <class 'Cat'>, <Cat object>>
    Animal shout
    Animal shout
    Animal shout
    
    {}
    {}
    {
    '__module__': '__main__', 
    'shout': <function Animal.shout at 0x7f49c146a160>, 
    '__dict__': <attribute '__dict__' of 'Animal' objects>,
    '__weakref__': <attribute '__weakref__' of 'Animal' objects>, 
    '__doc__': None
    }
    {
    '__module__': '__main__', 
    'shout': <function Cat.shout at 0x7f49c146a280>, 
    '__doc__': None
    }
    #super()可以访问到父类的类属性
    

    对于类方法和静态方法?

    >>> 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()
    class_method_cat
    >>> c.static_method()
    static_method_cat
    >>> print(Cat.__dict__)
    {
    '__module__': '__main__', 
    'class_method': <classmethod object at 0x7f109de5f730>, 
    'static_method': <staticmethod object at 0x7f109de48190>, 
    '__doc__': None
    }
    >>> print(Animal.__dict__)
    {
    '__module__': '__main__', 
    'class_method': <classmethod object at 0x7f109de0eeb0>, 
    'static_method': <staticmethod object at 0x7f109de5f490>, 
    '__dict__': <attribute '__dict__' of 'Animal' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Animal' objects>, 
    '__doc__': None
    }
    >>> Cat.static_method()
    static_method_cat
    >>> Animal.static_method()
    static_method_animal
    #这些方法都可以覆盖,原理都一样,属性字典的搜索字典的搜索顺序
    

    5.继承时使用初始化

    >>> class A:
    ...     def __init__(self,a):
    ...             self.a = a
    ... 
    >>> class B(A):
    ...     def __init__(self,b,c):
    ...             self.b = b
    ...             self.c = c
    ... 
    >>> class B(A):
    ...     def __init__(self,b,c):
    ...             self.b = b
    ...             self.c = c
    ...     def printv(self):
    ...             print(self.b)
    ...             print(self.a)
    ... 
    >>> f = B(200,300)
    >>> print(f.__dict__)
    {'b': 200, 'c': 300}
    >>> print(f.__class__.__bases__)
    (<class '__main__.A'>,)
    >>> f.printv()
    200
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 7, in printv
    AttributeError: 'B' object has no attribute 'a'
    

    通过以上例子可以看出,如果类B定义时声明继承类A,则在类B中bases中是可以看到类A。但是这和是否调用类A的构造方法是两回事。
    如果类B中调用了父类A的构造方法,就可以拥有父亲的属性了。

    >>> class A:
    ...     def __init__(self,a,d=10):
    ...             self.a = a
    ...             self.__d = d
    >>> class B(A):
    ...     def __init__(self,b,c):
    ...             A.__init__(self,b+c,b-c)
    ...             self.b = b
    ...             self.c = c
    ...     def printv(self):
    ...             print(self.b)
    ...             print(self.a)
    >>> f = B(200,300)
    >>> print(f.__dict__)
    {'a': 500, '_A__d': -100, 'b': 200, 'c': 300}
    >>> print(f.__class__.__bases__)
    (<class '__main__.A'>,)
    >>> f.printv()
    200
    500
    

    子类什么时候调用父类中的init方法呢?
    示例1:

    #B实例的初始化会自动调用基类A的__init__方法
    >>> class A:
    ...     def __init__(self):
    ...             self.a1 = 'a1'
    ...             self.__a2 = 'a2'
    ...             print('init in A')
    ... 
    >>> class B(A):
    ...     pass
    ... 
    >>> b = B()
    init in A
    >>> print(b.__dict__)
    {'a1': 'a1', '_A__a2': 'a2'}
    

    示例2:

    >>> class A:
    ...     def __init__(self):
    ...             self.a1 = 'a1'
    ...             self.__a2 = 'a2'
    ...             print('init in A')
    ... 
    >>> class B(A):
    ...     def __init__(self):
    ...             self.b1 = 'b1'
    ...             print('init in B')
    ... 
    >>> b = B()
    init in B
    >>> print(b.__dict__)
    {'b1': 'b1'}
    

    B实例一旦定义了初始化init方法,就不会自动调用父类的初始化init方法,需要手动调用。
    示例3:

    >>> class A:
    ...     def __init__(self):
    ...             self.a1 = 'a1'
    ...             self.__a2 = 'a2'
    ...             print('init in A')
    ... 
    >>> class B(A):
    ...     def __init__(self):
    ...             self.b1 = 'b1'
    ...             print('init in B')
    ...             A.__init__(self)
    ... 
    >>> b = B()
    init in B
    init in A
    >>> print(b.__dict__)
    {'b1': 'b1', 'a1': 'a1', '_A__a2': 'a2'}  #这个地方有__a2
    

    示例4:正确的初始化

    >>> class Animal:
    ...     def __init__(self,age):
    ...             print('init in Animal')
    ...             self.age = age
    ...     def show(self):
    ...             print(self.age)
    ... 
    >>> class Cat(Animal):
    ...     def __init__(self,age,weight):
    ...             print('init in Cat')
    ...             self.age = age + 1
    ...             self.weight = weight
    ... 
    >>> c = Cat(10,5)
    init in Cat
    >>> c.show()
    11
    

    示例5:在子类的init方法中,应该显式调用父类的init方法

    >>> class Animal:
    ...     def __init__(self,age):
    ...             print('init in Animal')
    ...             self.age = age
    ...     def show(self):
    ...             print(self.age)
    >>> class Cat(Animal):
    ...     def __init__(self,age,weight):
    ...             super().__init__(age)
    ...             print('init in Cat')
    ...             self.age = age + 1
    ...             self.weight = weight 
    >>> c = Cat(10,5)
    init in Animal
    init in Cat
    >>> c.show()
    11
    

    调用父类的init方法,出现在不同的位置,可能导致出现不同结果,如果直接将上例中所有的实例属性改成私有变量呢?
    示例6:

    >>> class Animal:
    ...     def __init__(self,age):
    ...             print('init in Animal')
    ...             self.__age = age
    ...     def show(self):
    ...             print(self.__age)
    ... 
    >>> class Cat(Animal):
    ...     def __init__(self,age,weight):
    ...             super().__init__(age)
    ...             print('init in Cat')
    ...             self.__age = age +1
    ...             self.__weight = weight
    ... 
    >>> c = Cat(10,5)
    init in Animal
    init in Cat
    >>> c.show()
    10
    >>> print(c.__dict__)
    {'_Animal__age': 10, '_Cat__age': 11, '_Cat__weight': 5}
    

    上例中打印的是10,因为父类Animal的show方法中__age会被解释为_Animal__age,因此显示的是10,而不是11。
    解决方法:一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法。

    相关文章

      网友评论

        本文标题:4.类的继承

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