zoukankan      html  css  js  c++  java
  • 第十章 面向对象之继承与派生

    继承与派生

    继承

      当我们创建一个类时,新建的类可以继承一个或多个父类(python支持多继承),父类又可以称为基类或超类,新建的类称为派生类或子类,子类会继承父类的属性,可以减少代码冗余

    #单继承和多继承
    class ParentClass1:
        pass
    
    class ParentClass2:
        pass
    
    class subClass1(ParentClass1):  #单继承
        pass
    
    class subClass2(ParentClass1,ParentClass2): #多继承,用逗号分隔开多个继承的类
        pass
    
    #查看继承的父类
    print(subClass1.__bases__)
    print(subClass2.__base__)   #只查看从左到右继承的第一个子类
    print(subClass2.__bases__)  #查看所有继承的父类
    #经典类和新式类
    print(ParentClass1.__bases__)
    #object类:所有python类的基类,提供了一些常见方法的实现(如__str__)
    #经典类:没有继承object的类,以及该类的子类
    #新式类:无论是否继承object,都默认继承object的类,以及该类的子类
    #在python2中,类分为经典类和新式类;在python3中,所有类均为新式类
    经典类和新式类

      继承描述的是子类与父类之间的关系,是一种“什么是什么”(例如:人是动物)的关系。要找出这种关系,必须先抽象再继承。

      抽象只是分析和设计过程中,一个动作或者一种技巧,通过抽象可以得到类

    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
    class Student(People):
        def learn(self):
            print('%s is learning'%self.name)
    
    class Teacher(People):
        def teach(self):
            print('%s is teaching'%self.name)
    
    stu1=Student('lary',18)
    teacher1=Teacher('egon',20)
    stu1.learn()
    teacher1.teach()

      属性查找顺序:对象->类->父类->父类的父类。。。

    class Foo:
        def f1(self):
            print('foo f1...')
    
        def f2(self):
            print('foo f2...')
            self.f1()
    
    
    class Bar(Foo):
        def f1(self):
            print('bar f1...')
    
    b=Bar()
    b.f2()
    View Code

    派生

       派生:子类可以添加自己的新属性,或者重新定义已经在父类中定义过的属性,一旦定义了与父类重名的属性,那么调用该属性时,以子类自己的属性为准

    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def tell_info(self):
            print('name:%s--age:%s'%(self.name,self.age))
    
    class Student(People):
        def learn(self):
            print('%s is learning'%self.name)
    
        def tell_info(self):
            print('student:',end='')
            print('name:%s--age:%s' % (self.name, self.age))
    
    stu1=Student('lary',18)
    stu1.tell_info()

      在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即使是self参数也要为其传值

    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
    class Student(People):
        def __init__(self,name,age,grade):
            People.__init__(self,name,age) #调用父类功能
            self.grade=grade                #新属性

    组合

      组合:指的是在一个类中以另外一个类的对象作为数据属性

      组合与继承:通过继承建立了派生类与基类之间的关系,它是一种“是”的关系,当类之间有很多相同的功能,提取这些共同的功能做成基类;组合的方式建立了类与组合的类之间的关系,它是一种“有”的关系,当类之间有明显的不同,并且较小的类是较大的类所需要的组件时,用组合比较好

    class People:
        def __init__(self,name,age,date_obj):
            self.name=name
            self.age=age
            self.birth=date_obj
    
        def tell_info(self):
            print('name:%s--age:%s'%(self.name,self.age))
    
    class Student(People):
        def learn(self):
            print('%s is learning'%self.name)
    
        def tell_info(self):
            print('student:',end='')
            People.tell_info(self)
    
    class Date:
        def __init__(self,year,mon,date):
            self.year=year
            self.mon=mon
            self.date=date
    
        def tell_birth(self):
            print('birth day is <%s-%s-%s>'%(self.year,self.mon,self.date))
    
    
    day1=Date(1990,12,12)
    #day1.tell_birth()
    stu1=Student('lary',18,day1)
    stu1.birth.tell_birth()

      抽象类:抽象类是一个特殊的类,特殊之处在于只能被继承,不能被实例化。抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法

    #抽象类
    import abc
    
    class Animal(metaclass=abc.ABCMeta):
        @abc.abstractmethod     #定义抽象方法,无需实现功能
        def eat(self):
            pass
    
    class People(Animal):       #子类继承抽象类,但是必须定义抽象类中定义的方法(如eat方法)
        def eat(self):
            print('people is eating')
    
    peo1=People()
    peo1.eat()
    抽象类

    继承的实现原理

      继承顺序:如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性,如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先

    class A(object):
        def test(self):
            print('from A')
    
    class B(A):
        def test(self):
            print('from B')
    
    class C(A):
        def test(self):
            print('from C')
    
    class D(B):
        def test(self):
            print('from D')
    
    class E(C):
        def test(self):
            print('from E')
    
    class F(D,E):
        # def test(self):
        #     print('from F')
        pass
    f1=F()
    f1.test()
    print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
    
    #新式类继承顺序:F->D->B->E->C->A
    #经典类继承顺序:F->D->B->A->E->C
    #python3中统一都是新式类
    #pyhon2中才分新式类与经典类
    继承顺序

      继承原理:对于定义的每一个类,python会计算出一个方法解析顺序列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配的属性为止

      子类中调用父类的方法

    # 子类重用父类的两种方法
    # 父类名.父类方法()
    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def tell_info(self):
            print('name:%s---age:%s'%(self.name,self.age))
    
    class Student(People):
        def __init__(self,name,age,sex):
            People.__init__(self,name,age)
            self.sex=sex
    
    stu1=Student('lary',18,'female')
    stu1.tell_info()
    
    #super()
    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def tell_info(self):
            print('name:%s---age:%s'%(self.name,self.age))
    
    class Student(People):
        def __init__(self,name,age,sex):
            super().__init__(name,age)  #参照MRO列表检查父类,获取属性
            self.sex=sex
    
    stu1=Student('lary',18,'female')
    stu1.tell_info()
    重用父类方法的两种方式

    多态与多态性

      多态指的是同一类事物有多种形态

    #多态
    class Animal:       #同一类事物:Animal
        pass
    
    class Pig(Animal):  #Animal的形态之一:Pig
        def eat(self):
            print('Pig is eating')
    
    class Dog(Animal):  #Animal的形态之二:Dog
        def eat(self):
            print('Dog is eating')
    
    #多态性:在不考虑对象具体类型的情况下,直接使用对象
    pig1=Pig()
    dog1=Dog()
    pig1.eat()
    dog1.eat()
    #多态性的好处:增加了程序的灵活性和扩展性
    多态性

      鸭子类型:如果看起来像、叫声像而且走起路来像鸭子那么它就是鸭子

    class Pig:
        def eat(self):
            print('Pig is eating')
    
    class Dog:
        def eat(self):
            print('Dog is eating')
    
    class Radio:
        def eat(self):
            print('Radio is eating')
    View Code

      多态性的好处

    s=str('hello')
    l=list([1,2,3])
    t=tuple((4,5,6))
    
    print(len(s))
    print(len(l))
    print(len(t))
    View Code

    封装

      属性隐藏

    #在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
    #__开头的属性只是语法意义的变形,这种变形只在定义时发生一次,这种隐藏对外不对内
    class Foo:
        __N=1            #将类的数据属性设置成私有的__N,在定义阶段会自动变形为_Foo__N
    
        def __init__(self,x,y):
            self.x=x
            self.__y=y
    
        def f1(self):
            print('f1')
    
        def f2(self):     #在内部可以访问私有的数据
            print("N:%s---Y:%s"%(self.__N,self.__y))
    
    print(Foo.__dict__)
    obj=Foo(1,2)
    print(obj.f2())
    print(obj.__dict__)
    obj.__z=5
    print(obj.__dict__)
    print(obj.__z)      #变形的过程只在类的定义阶段发生,在定义后的赋值操作不会变形
    
    #在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有
    class Parent:
        def __f1(self):
            print('P f1')
    
        def f2(self):
            print('p f2')
            self.__f1()  #只会与自己所在的类为准,即调用_Parent__f2
    
    class sub(Parent):
        def __f1(self):
            print('s f1')
    
    s=sub()
    s.f2()
    View Code

      封装的意义:

      封装数据属性的意义:将数据隐藏起来然后对外提供操作该数据的接口,在该接口上对该数据操作进行限制,以完成对数据属性操作的严格控制

    #封装数据属性的意义
    class People:
        def __init__(self,name,age):
            self.set_info(name,age)
    
        def tell_info(self):
            print("name:%s---age:%s"%(self.__name,self.__age))
    
        def set_info(self,name,age):
            if type(name) is not str:
                raise TypeError('name must be str')
            self.__name=name
            self.__age=age
    
    p=People('lary',18)
    p.tell_info()
    View Code

      封装方法的意义:隔离复杂度(对于使用者来说只需要知道接口的功能,其余功能可以隐藏起来,这样既隔离了复杂度,也提升了安全性)

      特性(property):property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值,这种特性的使用方式遵循了统一访问的原则

    #特性(property)
    class People:
        def __init__(self,name,age,height,weight):
            self.name=name
            self.age=age
            self.height=height
            self.weight=weight
    
        @property
        def bmi(self):
            return self.weight/(self.height ** 2)
    
    egon=People('egon',18,1.80,75)
    print(egon.bmi)
    class People:
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
    
        @property
        def name(self):
            return self.__name
    
        @name.setter
        def name(self,obj):
            self.__name=obj
    
        @name.deleter
        def name(self):
            #del self.__name
            raise PermissionError ('no del')
    
    egon=People('egon',28)
    print(egon.name)
    egon.name='EGON'
    print(egon.name)
    del egon.name
    View Code
  • 相关阅读:
    Linux的用户和组的属性
    Linux文件/目录 的新建、复制、移动(更名)、删除、查看、压缩命令
    排序算法和实现
    面试-三三原则
    phpstudy 最新版linux 面板 web防火墙后门防护功能教程
    小白必看的Python爬虫流程
    Python3迭代器与生成器
    python的异常处理机制
    python变量的作用域
    值得收藏的Python第三方库
  • 原文地址:https://www.cnblogs.com/iamluoli/p/8350092.html
Copyright © 2011-2022 走看看