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应该显示自己的属性值更好。

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

  • 相关阅读:
    jquery.js 一个非常不错的本脚库。
    AjaxPro 框架学习 (支持vs2003/vs2005)
    利用HttpResponse来导出excel文件.
    一个比较好的WEB时间控件
    CCR 编写一个不用创建线程,不用考虑资源互斥的多线程程序
    页面优化 .net 版
    [转]window.opener用法
    导出excel文件 解决科学计数法问题
    抽象类和抽象方法的特征和用途
    超强悍的右键菜单
  • 原文地址:https://www.cnblogs.com/xpc51/p/11760352.html
Copyright © 2011-2022 走看看