zoukankan      html  css  js  c++  java
  • Python类的继承

    基本概念

    面向对象三要素之一:继承inheritance。人类和猫类都是继承自动物类。

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

    先看一个不用继承的例子:

    class Animal:
        def shout(self):
            print("Animal shouts")
            
    a = Animal()
    a.shout()
    
    class Cat:
        def shout(self):
            print("cat shouts")
            
    c = Cat()
    c.shout()
    
    结果为:
    Animal shouts
    cat shouts

    上面的两个类虽然有关系,但是定义时并没有建立这种关系,而是各自完成定义。

    动物类和猫类都有吃,但是它们的吃有区别,所以分别定义。

    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()
    
    class Cat(Animal):
        pass
            
    cat = Cat("garfield")
    cat.shout()
    print(cat.name)
    
    
    class Dog(Animal):
        pass
    dog = Dog("ahuang")
    dog.shout()
    print(dog.name)
    
    结果为:
    Animal shouts
    Cat shouts
    garfield
    Dog shouts
    ahuang

    上例可以看出,通过继承,猫类、狗类不用写代码,直接继承了父类的属性和方法。

    继承

    class Cat(Animal)这种形式就是从父类继承,括号中写上继承的类的列表。

    父类

    Animal就是Cat的父类,也称为基类、超类。

    子类

    Cat就是Animal的子类,也称为派生类。

    定义

    格式如下:

    class 子类名(基类1,基类2,……):
        语句块

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

    class A:
        pass
    
    #等价于
    
    class A(object):
        pass

    注意,上例在Python2中,两种写法是不同的。

    Python支持多继承,继承也可以分为多级。

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

    __base__:类的基类

    __bases__:类的基类元组

    __mro__:显示方法查找顺序,基类的元组。

    mro()方法,同__mro__,比如int.mro()

    __subclasses__()类的子类列表,int.__subclasses__()。

    class Animal():
        x = 123
        def __init__(self,name):
            self._name = name
            
        @property
        def name(self):
            return self._name
        def shout(self):
            print("animal shout")
            
    class Cat(Animal):
        x = "cat"
        def shout(self):#override
            print("miao")
    
    class Garfield(Cat):
        pass
    class PersiaCat(Cat):
        pass
    
    class Dog(Animal):
        def run(self):
            print("dog run")
            
    tom = Cat("tom")
    print(tom.name)
    print(tom.shout())
    
    dog = Dog("ahuang")
    print(dog.name)
    print(dog.shout())
    
    gf = Garfield("gf")
    gf.shout()
    print("gf.x",gf.x)
    print("gf",gf.__dict__)
    
    print("gf.mro = {}".format(gf.__class__.__mro__))#祖先列表,继承链,应该注意,类才有这些方法,实例没有。
    print("gf.bases = {}".format(gf.__class__.__bases__))
    
    结果为:
    tom
    miao
    None
    ahuang
    animal shout
    None
    miao
    gf.x cat
    gf {'_name': 'gf'}
    gf.mro = (<class '__main__.Garfield'>, <class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>)
    gf.bases = (<class '__main__.Cat'>,)

    继承中的访问控制

    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.__COUNT)
            
        @classmethod
        def __showcount2(cls):
            print(cls.__COUNT)
            
        def showcount3(self):
            print(self.__COUNT)
            
    class Cat(Animal):
        NAME = "CAT"
        __COUNT = 200
        
        
    #c = Cat()__init__函数参数错误
    c = Cat(3,5,15)
    c.eat()
    print(c.HEIGHT)
    #print(c.__COUNT)#私有的不可访问
    #c.show__weight()#私有的不可访问
    c.showcount1()
    #c.__showcount2()#私有的不可访问
    c.showcount3()
    print(c.NAME)
    
    print("{}".format(Animal.__dict__))
    print("{}".format(Cat.__dict__))
    
    print(c.__dict__)
    print(c.__class__.mro())
            
    
    结果为:
    Cat eat 
    15
    100
    101
    CAT
    {'__module__': '__main__', '_Animal__COUNT': 100, 'HEIGHT': 0, '__init__': <function Animal.__init__ at 0x0000000005A796A8>, 'eat': <function Animal.eat at 0x0000000005A79730>, '_Animal__getweight': <function Animal.__getweight at 0x0000000005A797B8>, 'showcount1': <classmethod object at 0x0000000005A74E10>, '_Animal__showcount2': <classmethod object at 0x0000000005A74748>, 'showcount3': <function Animal.showcount3 at 0x0000000005A79488>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
    {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
    {'_Animal__COUNT': 101, 'age': 3, '_Animal__weight': 5, 'HEIGHT': 15}
    [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]

    从父类继承,自己没有的,就可以到父类中找。

    私有的都是不可访问的,但是本质上依然是改了名称放在这个属性所在类的__dict__中了,知道这个新名称就可以直接找到这个隐藏的变量,但是这是个黑魔法,应该慎用。

    class Animal():
        x = 123
        def __init__(self,name):
            self._name = name
            self.__age = 10
            
        @property
        def name(self):
            return self._name
        def shout(self):
            print("animal shout")
            
    class Cat(Animal):
        x = "cat"
        def shout(self):#override
            print("miao")
    
    class Garfield(Cat):
        pass
    class PersiaCat(Cat):
        pass
    tom = Garfield("tom")
    print(tom.name)
    print(tom.shout())
    print(tom.__dict__)
    
    print(Garfield.__dict__)
    print(Cat.__dict__)  
    print(Animal.__dict__)
    
    结果为:
    tom
    miao
    None
    {'_name': 'tom', '_Animal__age': 10}
    {'__module__': '__main__', '__doc__': None}
    {'__module__': '__main__', 'x': 'cat', 'shout': <function Cat.shout at 0x03FB7F60>, '__doc__': None}
    {'__module__': '__main__', 'x': 123, '__init__': <function Animal.__init__ at 0x03FB7228>, 'name': <property object at 0x03FD3210>, 'shout': <function Animal.shout at 0x03FB7E40>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
    class Animal():
        x = 123
        def __init__(self,name):
            self._name = name
            self.__age = 10
            
        @property
        def name(self):
            return self._name
        def shout(self):
            print("animal shout")
            
    class Cat(Animal):
        x = "cat"
        def __init__(self,name):
            Animal.__init__(self,name)#调整顺序,执行结果不同
            self.catname = name#改成self._name会覆盖前面的_name
        def shout(self):#override
            print("miao")
    
    tom = Cat("tom")
    print(tom.name)
    print(tom.shout())
    print(tom.__dict__)
    
    print(Cat.__dict__)  
    print(Animal.__dict__)
    
    结果为:
    tom
    miao
    None
    {'_name': 'tom', '_Animal__age': 10, 'catname': 'tom'}
    {'__module__': '__main__', 'x': 'cat', '__init__': <function Cat.__init__ at 0x03FDC108>, 'shout': <function Cat.shout at 0x03FDC150>, '__doc__': None}
    {'__module__': '__main__', 'x': 123, '__init__': <function Animal.__init__ at 0x03FDC030>, 'name': <property object at 0x03FB9840>, 'shout': <function Animal.shout at 0x03FDC0C0>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}

    总结:

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

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

    属性查找顺序

    实例的__dict__》类__dict__如果有继承==》父类__dict__

    如果搜索这些地方后没有找到就会抛异常,先找到就立即返回了。

    方法的重写、覆盖override

    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(Animal.__dict__)
    print(Cat.__dict__)
    
    结果为:
    animal shouts
    miao
    {}
    {}
    {'__module__': '__main__', 'shout': <function Animal.shout at 0x0000000005A79D90>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
    {'__module__': '__main__', 'shout': <function Cat.shout at 0x0000000005A79C80>, '__doc__': None}

    cat能否覆盖自己的方法?

    class Animal():
        def shout(self):
            print("animal shouts")
            
    class Cat(Animal):
        #覆盖了父类方法
        def shout(self):
            print("miao")
            
        #覆盖了自身的方法,显式调用了父类的方法
        def shout(self):
            print(super())
            print(super(Cat,self))
            super().shout()
            super(Cat,self).shout()#等价与super()
            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__)
    
    结果为:
    animal shouts
    <super: <class 'Cat'>, <Cat object>>
    <super: <class 'Cat'>, <Cat object>>
    animal shouts
    animal shouts
    animal shouts
    {}
    {}
    {'__module__': '__main__', 'shout': <function Animal.shout at 0x0000000005A79510>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
    {'__module__': '__main__', 'shout': <function Cat.shout at 0x0000000005A69BF8>, '__doc__': None}

    super()可以访问到父类的属性,具体原理后面再说。

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

    class Animal():
        @classmethod
        def class_method(cls):
            print("class_method_animals")
            
        @staticmethod
        def static_method():
            print("static_method_animals")
            
    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

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

    class Animal():
        x = 123
        def __init__(self,name):
            self._name = name
            self.__age = 10
            
        @property
        def name(self):
            return self._name
        def shout(self):
            print("animal shout")
            
        @classmethod
        def clsmtd(cls):
            print(cls,cls.__name__)
            
    class Cat(Animal):
        x = "cat"
        def __init__(self,name):
            Animal.__init__(self,name)#调整顺序,执行结果不同
            self.catname = name#改成self._name会覆盖前面的_name
        def shout(self):#override
            print("miao")
            
        @classmethod
        def clsmtd(cls):
            print(cls,cls.__name__)
            
    class Garfield(Cat):
        pass
    
    
    tom = Garfield("tom")
    print(tom.clsmtd())#注意结果
    print(tom.__dict__)
    
    print(Cat.__dict__)  
    print(Animal.__dict__)
    
    结果为:
    <class '__main__.Garfield'> Garfield
    None
    {'_name': 'tom', '_Animal__age': 10, 'catname': 'tom'}
    {'__module__': '__main__', 'x': 'cat', '__init__': <function Cat.__init__ at 0x03FDC4F8>, 'shout': <function Cat.shout at 0x03FDC4B0>, 'clsmtd': <classmethod object at 0x03FCB990>, '__doc__': None}
    {'__module__': '__main__', 'x': 123, '__init__': <function Animal.__init__ at 0x03FB76F0>, 'name': <property object at 0x03FD7AE0>, 'shout': <function Animal.shout at 0x03FDC588>, 'clsmtd': <classmethod object at 0x03FCB9D0>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}

    继承中的初始化

    先看下面这一段代码,有没有问题:

    class A:
        def __init__(self,a):
            self.a = a
            
    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__)
    print(f.__class__.__bases__)
    f.printv()
    
    结果为:
    {'b': 200, 'c': 300}
    (<class '__main__.A'>,)
    200

    上例代码可知:如果类B定义时声明继承自类A,则在类B中__bases__中可以看到类A。但是这和是否调用类A的构造方法是两回事。

    如果B中调用了A的构造方法,就可以拥有父类的属性了,如何理解这句话?观察B的实例f的__dict__中的属性。

    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__)
    print(f.__class__.__bases__)
    f.printv()
    
    结果为:
    {'a': 500, '_A__d': -100, 'b': 200, 'c': 300}
    (<class '__main__.A'>,)
    200

    作为好的习惯,如果父类定义了__init__方法,你就该在子类的__init__中调用它。那子类什么时候自动调用父类的__init__方法呢?

    示例1

    class A:
        def __init__(self):
            self.a1 = "a1"
            self.__a2 = "a2"
            print("A init")
            
    class B(A):
        pass
    
    b = B()
    print(b.__dict__)
    
    
    结果为:
    A init
    {'a1': 'a1', '_A__a2': 'a2'}

    B实例的初始化会自动调用基类A的__init__方法。

    实例2

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

    上例我们前面都分析过,不会调用父类的__init__方法的,这就会导致没有实现继承效果。所以在子类的__init__方法中,应该显示调用父类的__init__方法。

    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):
            #调用父类的__init__方法的顺序决定着show方法的结果
            super().__init__(age)
            print("cat init")
            self.age = age+1
            self.weight = weight
            #super().__init__(age)
            
    c = Cat(10,5)
    c.show()
    
    结果为:
    Animal init
    cat init
    11

    注意,调用父类的__init__方法,出现在不同的位置,可能导致出现不同的结果。那么直接将上例中所有的实例属性改成私有变量呢?

    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):
            #调用父类的__init__方法的顺序决定着show方法的结果
            super().__init__(age)
            print("cat init")
            self.__age = age+1
            self.__weight = weight
            #super().__init__(age)
            
    c = Cat(10,5)
    c.show()
    
    print(c.__dict__)
    
    结果为:
    Animal init
    cat init
    10
    {'_Animal__age': 10, '_Cat__age': 11, '_Cat__weight': 5}

    上例中打印10,原因看__dict__就知道了。因为父类Animal的show方法中__age会被解释为_Animal__age,因此显示的是10,而不是11。

    这样的设计好不好,cat的实例c应该显示自己的属性值更好。

    解决的办法:一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即便是父类或者派生类的方法。

  • 相关阅读:
    Dot Net WinForm 控件开发 (七) 为属性提下拉式属性编辑器
    WinForm 程序的界面多语言切换
    c#遍历HashTable
    Dot Net WinForm 控件开发 (三) 自定义类型的属性需要自定义类型转换器
    Dot Net WinForm 控件开发 (六) 为属性提供弹出式编辑对话框
    Dot Net WinForm 控件开发 (一) 写一个最简单的控件
    Dot Net WinForm 控件开发 (四) 设置属性的默认值
    Dot Net WinForm 控件开发 (二) 给控件来点描述信息
    Dot Net WinForm 控件开发 (八) 调试控件的设计时行为
    Dot Net WinForm 控件开发 (五) 复杂属性的子属性
  • 原文地址:https://www.cnblogs.com/xpc51/p/11760352.html
Copyright © 2011-2022 走看看