• Python20-Day07


    面向对象之继承与派生

    什么是继承?

      继承是一种创建新类的方式,新建的类可以继承一个或者多个父类,父类又称为基类或者超类,新建的类称为派生类或者子类

      子类会‘遗传’父类的特性,从而解决代码重用问题

    python中分为单继承和多继承

      

    class ParentClass1: #定义父类
        pass
    
    class ParentClass2: #定义父类
        pass
    
    class SubClass1(ParentClass1): #单继承,父类是ParentClass1,子类是SubClass
        pass
    
    class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
        pass

     查看继承

    #print(SubClass1.__bases__)
    (<class '__main__.ParentClass1'>,)
    
    
    #print(SubClass2.__bases__)
    (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

    经典类与新式类

    1、只有python2中才分为经典类和新式类,python3中都是新式类

    2、在python2中没有标明继承object类的类,以及该类的子类,都称为经典类

    3、在python2中标明继承object类的类,以及该类的子类,称为新式类

    4、python3中,无论是否继承object类,默认都继承object类,即python3中都为新式类

    继承与抽象

    继承描述的是子类与父类之间的关系,是什么是什么的关系,要找出这种关系,必须先抽象再继承

    属性查找

    class Foo:
        def f1(self):
            print('Foo.f1')
    
        def f2(self):
            print('Foo.f2')
            self.f1()
    
    class Bar(Foo):
        def f1(self):
            print('Foo.f1')
    
    
    b=Bar()
    b.f2()

    #得到的结果为:

     Foo.f2
     Foo.f1

    派生

    子类也可以添加自己新的属性或者自己重新定义属性。一旦重新定义了自己的属性且与父类重名,调用新增的属性时,就以自己的为准了。

    class ParentClass:
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
        def tell_info(self):
            print('%s is in old_class!' %self.name)
    class SubClass(ParentClass):
        def __init__(self,name,age,sex):
            ParentClass.__init__(self,name,age,sex)
        # def tell_info(self):
        #     print('%s is in subclass!' %self.name)
    
    stu1 = SubClass('zly',25,'female')
    stu1.tell_info()
    
    #此时调用结果为:
    zly is in old_class!
    
    #如果在SubClass子类中也定义tell_info属性,此时调用结果为:
    zly is in subclass!

    组合与重用性

    软件重用的重要方式除了继承之外还有另外一种:组合

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

    继承:

    通过继承建立了派生类与基类之间的关系,是一种什么是什么的关系。

    当类之间有很多相同的功能,提取这些共同的功能做成父类。用继承比较好

    组合:

    用组合的方式建立了类与组合的类之间的关系,是一种有的关系。

    class People:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
    class Course:
        def __init__(self,name,period,price):
            self.name=name
            self.period=period
            self.price=price
        def tell_info(self):
            print('<%s %s %s>' %(self.name,self.period,self.price))
    
    class Teacher(People):
        def __init__(self,name,age,sex,job_title):
            People.__init__(self,name,age,sex)
            self.job_title=job_title
            self.course=[]
            self.students=[]
    
    
    class Student(People):
        def __init__(self,name,age,sex):
            People.__init__(self,name,age,sex)
            self.course=[]
    
    
    egon=Teacher('egon',18,'male','沙河霸道金牌讲师')
    s1=Student('牛榴弹',18,'female')
    
    python=Course('python','3mons',3000.0)
    linux=Course('python','3mons',3000.0)
    
    #为老师egon和学生s1添加课程
    egon.course.append(python)
    egon.course.append(linux)
    s1.course.append(python)
    
    #为老师egon添加学生s1
    egon.students.append(s1)
    
    
    #使用
    for obj in egon.course:
        obj.tell_info()

    抽象类

    抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

    从设计的角度看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象来的。

    抽象类中只有抽象方法,没有实现功能,该类不能被实例化,只能被继承,且子类必须实现抽象方法。

    #_*_coding:utf-8_*_
    __author__ = 'Linhaifeng'
    #一切皆文件
    import abc #利用abc模块实现抽象类
    
    class All_file(metaclass=abc.ABCMeta):
        all_type='file'
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def read(self):
            '子类必须定义读功能'
            pass
    
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def write(self):
            '子类必须定义写功能'
            pass
    
    # class Txt(All_file):
    #     pass
    #
    # t1=Txt() #报错,子类没有定义抽象方法
    
    class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('文本数据的读取方法')
    
        def write(self):
            print('文本数据的读取方法')
    
    class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('硬盘数据的读取方法')
    
        def write(self):
            print('硬盘数据的读取方法')
    
    class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('进程数据的读取方法')
    
        def write(self):
            print('进程数据的读取方法')
    
    wenbenwenjian=Txt()
    
    yingpanwenjian=Sata()
    
    jinchengwenjian=Process()
    
    #这样大家都是被归一化了,也就是一切皆文件的思想
    wenbenwenjian.read()
    yingpanwenjian.write()
    jinchengwenjian.read()
    
    print(wenbenwenjian.all_type)
    print(yingpanwenjian.all_type)
    print(jinchengwenjian.all_type)

    继承实现的原理

    1、继承顺序

      如果继承关系为非菱形结果,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支,

      如果继承关系为菱形结构,属性查找:深度优先和广度优先。

    2、继承原理

      定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。

      所有父类的MRO列表遵循以下3条原则:

      1、子类会先与父类被检查

      2、多个父类会根据他们在列表中的顺序被检查

      3、如果对下一个类存在两个合法的选择,选择第一个父类

    面向对象之封装

    如何隐藏?

      在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

    #其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
    #类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式:
    
    class A:
        __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
        def __init__(self):
            self.__X=10 #变形为self._A__X
        def __foo(self): #变形为_A__foo
            print('from A')
        def bar(self):
            self.__foo() #只有在类内部才可以通过__foo的形式访问到.
    
    #A._A__N是可以访问到的,
    #这种,在外部是无法通过__x这个名字访问到。

    变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形

    在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有。

    #正常情况
    >>> class A:
    ...     def fa(self):
    ...         print('from A')
    ...     def test(self):
    ...         self.fa()
    ... 
    >>> class B(A):
    ...     def fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from B
     
    
    #把fa定义成私有的,即__fa
    >>> class A:
    ...     def __fa(self): #在定义时就变形为_A__fa
    ...         print('from A')
    ...     def test(self):
    ...         self.__fa() #只会与自己所在的类为准,即调用_A__fa
    ... 
    >>> class B(A):
    ...     def __fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from A

    封装数据:

      将数据隐藏起来,然后对外提供操作该数据的接口,然后可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格限制。

    class Teacher:
        def __init__(self,name,age):
            # self.__name=name
            # self.__age=age
            self.set_info(name,age)
    
        def tell_info(self):
            print('姓名:%s,年龄:%s' %(self.__name,self.__age))
        def set_info(self,name,age):
            if not isinstance(name,str):
                raise TypeError('姓名必须是字符串类型')
            if not isinstance(age,int):
                raise TypeError('年龄必须是整型')
            self.__name=name
            self.__age=age
    
    
    t=Teacher('egon',18)
    t.tell_info()
    
    t.set_info('egon',19)
    t.tell_info()

    封装方法:目的是隔离复杂度

    #取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    #隔离了复杂度,同时也提升了安全性
    
    class ATM:
        def __card(self):
            print('插卡')
        def __auth(self):
            print('用户认证')
        def __input(self):
            print('输入取款金额')
        def __print_bill(self):
            print('打印账单')
        def __take_money(self):
            print('取款')
    
        def withdraw(self):
            self.__card()
            self.__auth()
            self.__input()
            self.__print_bill()
            self.__take_money()
    
    a=ATM()
    a.withdraw()

    特性(property)

    什么是特性property?

      property是一种特殊的属性,访问它时会执行一段功能,然后返回值。

    class People:
        def __init__(self,name,weight,height):
            self.name=name
            self.weight=weight
            self.height=height
        @property
        def bmi(self):
            return self.weight / (self.height**2)
    
    p1=People('egon',75,1.85)
    print(p1.bmi)

    为什么要用property?

      将一个类的函数定义成特性后,对象再去使用的时候obj.name,根本无法察觉自己的nameshi执行了一个函数后计算出来的。这种特性的使用方式遵循了统一访问的原则

    面向对象之绑定方法与非绑定方法

    类中定义的函数分成两大类

    1、绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入)

      1、绑定到类的方法:用classmethod装饰器装饰的方法

       为类量身定制

      2、绑定到对象的方法:没有被任何装饰器装饰的方法

       为对象量身定制

               属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值一说。

    2、非绑定方法:用staicmethod装饰器装饰的方法

      1、不与类或对象绑定,类和对象都可以调用。但是没有自动传值一说,就是一个普通工具。

      

    绑定方法:

    绑定给类的方法(classmethod)

      classmethod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数,python内置了函数classmethod来吧类中的函数定义成类方法

    #settings.py
    HOST='127.0.0.1'
    PORT=3306
    DB_PATH=r'C:UsersAdministratorPycharmProjects	est面向对象编程	est1db'
    import settings
    class MySQL:
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
        @classmethod
        def from_conf(cls):
            print(cls)
            return cls(settings.HOST,settings.PORT)
    
    print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
    conn=MySQL.from_conf()
    
    conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类

    非绑定方法

    在类内部用staicmethod装饰的函数即非绑定方法,就是普通函数

    staicmethod不与类或对象绑定,谁都可以调用,没有自动传值效果

    import hashlib
    import time
    class MySQL:
        def __init__(self,host,port):
            self.id=self.create_id()
            self.host=host
            self.port=port
        @staticmethod
        def create_id(): #就是一个普通工具
            m=hashlib.md5(str(time.time()).encode('utf-8'))
            return m.hexdigest()
    
    
    print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
    conn=MySQL('127.0.0.1',3306)
    print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数

    classmethod与staicmethod区别

    import settings
    class MySQL:
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
        @staticmethod
        def from_conf():
            return MySQL(settings.HOST,settings.PORT)
    
        # @classmethod #哪个类来调用,就将哪个类当做第一个参数传入
        # def from_conf(cls):
        #     return cls(settings.HOST,settings.PORT)
    
        def __str__(self):
            return '就不告诉你'
    
    class Mariadb(MySQL):
        def __str__(self):
            return '<%s:%s>' %(self.host,self.port)
    
    
    m=Mariadb.from_conf()
    print(m) #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你:
  • 相关阅读:
    DPDK L2fwd 源码阅读
    集美大学网络1413第六次作业(团队二)
    集美大学网络1413第五次作业(团队一)
    集美大学网络1413第四次作业(结对二)
    集美大学网络1413第三次作业
    集美大学网络1413第二次作业
    集美大学网络1413第一次作业
    集美大学软工-网络1413大家庭
    耿丹16-1上半学期助教总结
    耿丹16-1第十二次作业
  • 原文地址:https://www.cnblogs.com/mrwang1101/p/8349661.html
Copyright © 2020-2023  润新知