• chapter8.2、面向对象三要素--继承


    继承Inheritance

    单继承

    个体继承自父母,继承父母的属性,但也会有自己的属性

    面向对象里,属性和方法可以从从父类继承,这样能减少代码,能复用就复用,子类复用的属性不能表达他自己的属性时,再为子类增添属性。

    父类father,也称为   基类base超类super,它们意思相同

    子类son派生类sub

    class Creatures:
        def __init__(self,name):
            self._name = name
        def shout(self):
            print('{} shout'.format(self.__class__.__name__))
        @property
        def name(self):
            return self._name
    
    a = Creatures('monster')
    a.shout()
    
    class Cat(Creatures):
        pass
    
    cat = Cat("garfield")
    cat.shout()
    print(cat.name)
    
    class Pig(Creatures):
        pass
    
    pig = Pig('peiqi')
    pig.shout()
    print(pig.name)

    表达方法 :

    class 子类名(基类1[,基类2,.....]):
      语句块

    class Person:
        pass
    class Person(object)
        pass

    以上两种等价

    object 祖先,根基类,唯一没有父类的,所有的类都继承自object,python3中全部类最终继承自object,如果不写父类,父类就是object,Python3之前不是

    python 支持多继承,查看继承的特殊属性和方法有

    __base__   类的基类,只往上找一层,返回类对象

    __bases__  类的基类元祖,不会找到object,但在中间的类都会包装成元组返回

    __mro__   返回继承路线,返回所有元祖,包括object,也可以mro() 

    mor()  方法,返回的是列表

    __subclasses__()    类的子类列表

    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,'--------1')
            
        @classmethod
        def __showcount2(cls):
            print(cls.COUNT,'---------2')
            
        def showcount3(self):
            print(self.__COUNT,'-------3')
            
    class Pig(Animal):
        NAME = 'PAGE'
        __COUNT = 300
    
    #p = Pig()###创建实例访问的属性为Pig的类,Pig没有,就去父类Animal里找,要求有参数
    p = Pig(5,50,30)#创建实例
    p.eat()##实例所属的类Pig没有该方法,但是Animal有,调用
    print(p.HEIGHT)##实例自身的属性
    #print(p.__COUNT)##对象的属性是储存在自己字典里的,只不过换了名字
    #p.__getweight()##该方法属于它的类,但是类隐藏了该属性,所以调不到
    p.showcount1()##该方法属于类的父类,且未隐藏,可以调,返回100,该方法是Animal类调用的__count,不会去实例里边找,所以返回类里的隐藏变量100
    #p.showcount2()##该方法属于它的类,但是类隐藏了该属性,所以调不到
    p.showcount3()##返回101,对象调用类的方法,首先找的是对象的字典,谁调用,去谁那找
    
    ##信息都藏在字典里!
    print('{}'.format(Animal.__dict__))
    print('{}'.format(Pig.__dict__))
    print(p.__dict__)
    print(p.__class__.mro())

    继承中的访问控制

    只要是继承,自己没有的,就可以到父类中找,私有的和类相关,私有属性的更改就要在类下更改

    私有的都是不可访问的,但本质时放在了属性或类的字典__dict__中,可以找到,黑魔法,慎用,

    谁调用谁的self和cls

    继承时,公有的,子类和实例都可以随意访问;隐藏的,子类和实例都不可直接访问,私有不会和子类父类分享,只有自己用,自己类下的方法都可以访问这个私有变量

    子类实例都不可访问私有属性,私有一旦出了类,就不能访问了

    其他语言有的严格限制访问

    方法的重写,覆盖override

    继承修改了属性,就是重写,自己类下的方法也可以覆盖

    class Creatures:
        def shout(self):
            print('Animal shout')
    
    class Pig(Creatures):
        def shout(self):
            print('hengheng')
        
        def shout(self):
            print(super())
            print(super(Pig,self))##和上一种相同
            super().shout()##子类调父类的方法,以下两种与之相同
            super(Pig,self).shout()
            self.__class__.__base__.shout(self)##不推荐使用此方法
            
    c = Creatures()
    c.shout()
    pig = Pig()
    pig.shout()
    
    print(c.__dict__)
    print(pig.__dict__)
    print(Creatures.__dict__)
    print(Pig.__dict__)

    子类可以使用super()访问父类的属性,super()  等价于 super(父类名,self)

    静态方法和类方法都与之相同,都可以覆盖

    class Creatures:
        @classmethod
        def class_method(cls):
            print('class method creatures')
    
        @staticmethod
        def static_method():
            print('static method creatures')
    
    
    class Pig(Creatures):
        @classmethod
        def class_method(cls):
            print('class method pig')
    
        @staticmethod
        def static_method():
            print('static method pig')
    
    
    p = Pig()
    p.class_method()
    p.static_method()

    继承中的初始化

    本身有初始化方法,初始化不会自动调父类的初始化方法。

    如果没有,就会调父类的初始化方法 ,父类有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):
            super().__init__(age)##放在此处,age返回11,先执行Animal的初始化,在执行Cat的age,所以c的age是11
            print('cat init')
            self.age  = age +1##此处执行时age为10加11
            self.weight = weight
            #super().__init__(age)
         ##如果放在此处,返回的为10,首次执行Cat的初始化,将c的age设置为了11,再执行Animal的初始化,传入的是参数age,不是c的age,所以又覆盖了c的属性,输出10
            
    c = Cat(10,5)
    c.show()

    调用父类的初始化方法的顺序会影响实例的属性,应当注意

    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)
            print('cat init')
            self.__age  = age +1
            self.__weight = weight
            #super().__init__(age)
            
    c = Cat(10,5)
    c.show()
    print(c.__dict__)

    实例调用类的私有方法调用时产生的属性放在实例中,但是名字与类有关,最好不要用。

    父类的初始化方法调用后,使用的实例是当前类的实例,父类不产生实例,子类调用他父类的的方法

    主要原则:自己的私有属性,就用自己的方法读取修改,不要使用父类和派生类或者其他类的方法

    python的不同版本的类

    python2.2之前,没有类的共同祖先,之后,引入了object 所有类的共同祖先,

    python2.2之后,分为古典类和新式类。

    python3中都是新式类,都继承自object

    dir 所有属性的列表

    多继承

    OCP原则,多‘继承’,少修改

    在子类上对基类的增强,实现多态,

    多态:在面向对象中,父类,子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同表现,就是多态

    一个类继承自多个类就是多继承,它将具有多个类的特征

    多继承弊端

    复杂性,只要不是单一继承,就会面对复杂性这一问题

    二义性,如果继承自两个类,两个类里都有相同的方法,子类该继承那个方法,就产生了二义性

    C++支持多继承,Java舍弃了多继承

    解决途径

    排除二义性,要遍历,方法是深度优先,或广度优先

    菱形选择问题,类D继承自B和C,B和C又继承自A,当然,类的继承顺序可以比这更复杂

    2.2之前,最早是深度优先,先根再左在右

    2.2古典类继承路线中重复的以最后为准,但不能解决问题

    python2.3 之后,C3算法,可以解决二义性问题,还可以抛异常,解决了继承的单调性,出现二义性就阻止它,求得的MRO本质就是为了线性化,且确定顺序,

    多继承使用时要注意

    python语法是可以多继承,但是它是解释执行,只有执行到时才会发现错误。

    尽量避免多继承,不论语言是否支持

    团队协作开发,引入多继承,代码可能会不可控

    python开发要求较高,只有运行时才能提示错误,所以在编写时要团队遵守相同的规矩

    Mixin

    抽象方法可以不实现,继承实现,父类不具体实现,子类来覆盖它

    基类中只定义,不实现,称为‘抽象方法’,在Python中,如果采用这种定义方式,子类也可以不实现,直到子类使用该方法时才报错。

    import math
    import json
    import msgpack
    
    class Shape:
        @property
        def area(self):
            raise NotImplemented('weishixian')
    
    class Triangle(Shape):
        def __init__(self, a, b, c):
            self.__a = a
            self.__b = b
            self.__c = c
    
        @property
        def area(self):
            p = (self.__a + self.__b + self.__c) / 2
            ret = math.sqrt(p*(p - self.__a)*(p - self.__b)*(p - self.__c))
            return ret
    
    class Square(Shape):
        def __init__(self,a,b):
            self.__a = a
            self.__b = b
    
        @property
        def area(self):
            return self.__a*self.__b
    
    class Circle(Shape):
        def __init__(self,r):
            self.__r = r
    
        @property
        def area(self):
            return math.pi*(self.__r**2)
    
    t = Triangle(3,4,5)
    print(t.area)
    s = Square(2,2)
    print(s.area)
    r = Circle(1)
    print(r.area)

    临时注入功能,可以用装饰器注入,装饰器为类灵活的增添功能;也可以Mixin,用Mixin时,混入谁,把谁放在前边,Mixin覆盖其他的属性,缺少啥补啥,一般不会太复杂

    class SerializableMixin:##Mixin类
        def dump(self,t = 'json'):##增添的功能,可序列化
            if t == 'json':
                return json.dumps(self.__dict__)
            if t == 'msgpack':
                return msgpack.dumps(self.__dict__)
            else :
                return "not realize serialize"
    
    class SerializableCircleMixin(SerializableMixin,Circle): pass#多继承,放在第一位
    
    scm = SerializableCircleMixin(4)
    print(scm.area)
    s = scm.dump('msgpack')
    print(s)
    j = scm.dump('json')
    print(j)

    子类指向父类,画图的规则

    继承的层次多了,会笨拙,

    Mixin,混入其他功能,Mixin带来了其他属性和方法,Mixin本质是多继承实现的

    Mixin体现的是组合的·设计思想,模式,

    在面向对象的设计中,复杂功能可以抽取出来,为其他类提供功能,可以装饰或者Mixin,

    从设计角度来说,多组合,少继承,为了改变mro列表

    使用原则

    Mixin类中不能显式的出现初始化方法__init__

    Mixin通常不能单独工作,因为它是为混入其他类增加功能的

    Mixin的祖先只能是Mixin类,只是为了增添功能,不是强制,但这是原则,一般都这样做

    注意Mixin类通常在继承列表的第一个位置

    两种方式都可以,如果要继承就使用Mixin,

    PEP是python的增强提案,Python Enhancement Proposals

    新特性,PEP 572,2018年7月,Guido van Rossum,python之父卸任了BDFL(benevolent dictator for life),没有指定继任者,

    PEP 0 文档索引
    PEP 1 协议指南
    PEP 8 代码规范!!!

    Python社区,新的规范的原始文档都在这里,可以访问

    https://www.python.org/dev/peps/pep-0008/

  • 相关阅读:
    pku2351 Colored Sticks
    JSOI2010 满汉全席
    享元模式
    适配器模式
    合成模式
    原型模式
    创建型设计模式
    建造者模式
    装饰模式
    单例模式
  • 原文地址:https://www.cnblogs.com/rprp789/p/9651981.html
Copyright © 2020-2023  润新知