• Python 基础 面向对象之二 三大特性


    Python 基础 面向对象之二 三大特性

        上一篇主要介绍了Python中,面向对象的类和对象的定义及实例的简单应用,本篇继续接着上篇来谈,在这一篇中我们重点要谈及的内容有:Python 类的成员、成员修饰符 面向对象的三大特性:继承、多态和封装,貌似今天内容挺多的,没有关系,慢慢来!

    一、类中的基本知识:

        一、类的成员、成员修饰符

           

           

            一、字段

                      字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同

    •           普通字段属于对象
    •           静态字段属于类   
     1 class Room:
     2     tag=1
     3     def __init__(self,name,owner,width,length,heigh):
     4         self.name=name
     5         self.owner=owner
     6         self.width=width
     7         self.length=length
     8         self.heigh=heigh
     9 
    10 
    11 
    12 r1=Room('厕所','sb',100,100,100000)   
    13 print(r1.name)  # 访问的是普通字段
    14 print(Room.tag) # #直接访问静态字段

        so,通过上述例子可以看出:

    •     静态字段在内存中只保存一份
    •     普通字段在每个对象中都要保存一份(相当于绑定在每个实例化对象的身上,然后本身有一个类“指针”的东西指向类)

        应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

        二、方法

            方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同

    •     普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
    •     类方法:由调用; 至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls
    •     静态方法:由调用;无默认参数;
    class Foo:
    
        def __init__(self, name):
            self.name = name
    
        def ord_func(self):
            """ 定义普通方法,至少有一个self参数 """
    
            # print self.name
            print ('普通方法')
    
        @classmethod
        def class_func(cls):
            """ 定义类方法,至少有一个cls参数 """
    
            print ('类方法')
    
        @staticmethod
        def static_func():
            """ 定义静态方法 ,无默认参数"""
    
            print ('静态方法')
    
    
    # 调用普通方法
    f = Foo('sb')
    f.ord_func()
    
    # # 调用类方法
    Foo.class_func()
    f.class_func()
    #
    # # 调用静态方法
    Foo.static_func()
    f.static_func()

        类方法调用和静态方法调用的时候既可以使用使用类名调用,也可以使用对象来调用,区别在哪里呢?注意:最大的区别是用类名调用的时候是不需要实例化的,而使用对象来访问时是先要进行实例化,之后再进行调用。

        三、属性  

          如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。

          对于属性,有以下三个知识点:

    •     属性的基本使用
    •     属性的两种定义方式
     1 class Goods:
     2     @property
     3     def pricr(self):
     4         #print('nijiushidahbi')
     5         return "haishininiubi"
     6 
     7 obj = Goods()
     8 result = obj.pricr
     9 print(result)
    10 
    11 
    12 显示结果:haishininiubi

        由属性的定义和调用要注意一下几点:

    •     定义时,在普通方法的基础上添加 @property 装饰器;
    •     定义时,属性仅有一个self参数
    •     调用时,无需括号
                 方法:foo_obj.func()
                 属性:foo_obj.prop

        注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象

                 属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

                Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回

    二、面向对象编程的三大特性介绍

           1.继承与派生

                 继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类称为基类或超类,新建的类称为派生类或子类。

                 python中类的继承分为:单继承和多继承

    1 class Grandfather:
    2     pass
    3 
    4 class Father(Grandfather): #单继承 Father是派生类,grandfather 是基类
    5     pass
    6 
    7 class son(Father,Grandfather): #多继承
    8     pass

                继承使用产生的场景:如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时我们不可能从头开始写一个类B,这就用到了类的继承的概念。通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用。

                除了继承与派生的概念还有一个概念是需要提出的那就是:组合。组合就是指在一个类中以另外一个类的对象作为数据属性,成为类的组合。

                组合与继承两者到底什么的关系?1.继承的方式:通过继承建立了派生类与基类的关系,两者之间是“”的关系。当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师    2.组合的方式:用组合的方式建立了类与组合的类之间的关系,它是一种‘’的关系,比如教授有生日,教授教python课程(当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

    class School:
        def __init__(self,name,addr):
            self.name=name
            self.addr=addr
    
        def zhao_sheng(self):
            print('%s 正在招生' %self.name)
    
    class Course:
        def __init__(self,name,price,period,school):
            self.name=name
            self.price=price
            self.period=period
            self.school=school
    
    s1=School('北大','北京') 
    
    c1=Course('linux',10,'1h',s1)
    print(c1.name) #linux
    print(c1.school) # <__main__.School object at 0x02182330>
    print(c1.school.name) # 北大
    print(c1.school.addr) # 北京
    View Code

             在这个例子中呢,创建了两个类,一个是学校类一个是课程类。需要将课程类与学校类进行相关联的时候,继承在这里是不合适的,那我们采用组合的方式来对这两个类进行关联,这样就在实例化一门课程的时候把学校的信息进行关联。

              接口的概念:

                    继承有两种用途:

                        一:继承基类的方法,并且做出自己的改变或者扩展(解决代码重用,但是子类与基类出现强耦合

                        二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能。(推举使用)

           归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

           接口继承:就是定义一个父类,其主要目的就是所有的子类,必须去实现父类的方法(与是否省代码,么有半毛钱关系),父类可以不去具体实现,具体的实现由子类自己去实现。

          举个例子:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样。

      抽象类: 从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法(这种解释有点像接口了)

    #一切皆文件
    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)
    View Code

        继承实现的原理:(多继承,很多语言只支持单继承)

            Python继承中有两种方式:一种是深度优先;另一种是广度优先;

            当类是经典类时,多继承会按照深度优先方式查找;当类是新式类时,多继承会按照广度优先方式查找。注:当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

          现在举个例子进行说明:

    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中才分新式类与经典类
    View Code

            在查找的过长中一旦找到查找过程就会立即中断,不会继续查找。

        多继承的原理:(Python实现多继承的原理)

            python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如:

    F.mro() #等同于F.__mro__
    [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'objec
    t
    '>]

        子类调用父类方法的实现过程:子类继承了父类的方法,然后想进行修改注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法。

        方法一:父类名.父类方法()

    class Vehicle: #定义交通工具类
         Country='China'
         def __init__(self,name,speed,load,power):
             self.name=name
             self.speed=speed
             self.load=load
             self.power=power
    
         def run(self):
             print('开动啦...')
    
    class Subway(Vehicle): #地铁
        def __init__(self,name,speed,load,power,line):
            Vehicle.__init__(self,name,speed,load,power) #子类继承父类
            self.line=line
    
        def run(self):
            print('地铁%s号线欢迎您' %self.line)
            Vehicle.run(self)
    
    line13=Subway('中国地铁','180m/s','1000人/箱','',13)
    line13.run()
    
    
    # 地铁13号线欢迎您
    开动啦...
    View Code

        方法二:super()(推举使用此种方式)

    class Vehicle: #定义交通工具类
         Country='China'
         def __init__(self,name,speed,load,power):
             self.name=name
             self.speed=speed
             self.load=load
             self.power=power
    
         def run(self):
             print('开动啦...')
    
    class Subway(Vehicle): #地铁
        def __init__(self,name,speed,load,power,line):
            #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
            super().__init__(name,speed,load,power)
            self.line=line
    
        def run(self):
            print('地铁%s号线欢迎您' %self.line)
            super(Subway,self).run()
    
    class Mobike(Vehicle):#摩拜单车
        pass
    
    line13=Subway('中国地铁','180m/s','1000人/箱','',13)
    line13.run()
    View Code

        注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表。

        2.多态(性)

            多态是指一个事物有多中形态(一个抽象类有多个子类,so 多态的概念依赖于继承),而多态性是指:指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。

            在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

            多态是反映在执行时的状态,在面向对象中,多态是怎么来得呢?首先,先定义一个基类,再定义一堆子类。其次,才能展现不同的类实例化出的实例去调用同一个方法。

     1 class H2O:
     2     def __init__(self,name,temperature):
     3         self.name=name
     4         self.temperature=temperature
     5     def turn_ice(self):
     6         if self.temperature < 0:
     7             print('[%s]温度太低结冰了' %self.name)
     8         elif self.temperature > 0 and self.temperature < 100:
     9             print('[%s]液化成水' %self.name)
    10         elif self.temperature > 100:
    11             print('[%s]温度太高变成了水蒸气' %self.name)
    12     def aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(self):
    13         pass
    14 
    15 class Water(H2O):
    16     pass
    17 class Ice(H2O):
    18     pass
    19 class Steam(H2O):
    20     pass
    21 
    22 w1=Water('',25)
    23 i1=Ice('',-20)
    24 s1=Steam('蒸汽',3000)
    25 
    26 w1.turn_ice()
    27 i1.turn_ice()
    28 s1.turn_ice()

       3.封装

        装就是把东西包起来,而封的意思就是把口子封起来。为何要在编程的过程中使用这一特性呢?封装数据的作用就是为了保护隐私,而封装方法的作用就是为了隔离复杂程度。
        封装其实是有两个层面的意思:

        其一:第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装。(注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口

        其二:第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

        在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的形式访问到.
    
    f1 = A()
    # f1.__foo() # 'A' object has no attribute '__foo'
    f1.bar() # 通过在对象A中创建一个接口,在内部调用私有方法

    好了,今天就搞到这里,明天继续!!!

           

           

              

           

       

       

  • 相关阅读:
    python常见的错误异常
    vim命令c编程
    递归函数初步理解---python实现(汉诺塔问题)
    python报错:AttributeError: module 'pdb' has no attribute 'set_trace'
    python程序控制结构
    python函数
    结构体的四种定义方法
    基于一维数组的桶排序入门
    双链表与单链表的比较
    在用free()函数释放指针内存时为何要将其指针置空
  • 原文地址:https://www.cnblogs.com/haishiniu123/p/6790068.html
Copyright © 2020-2023  润新知