zoukankan      html  css  js  c++  java
  • 练习40--模块、类和对象——面向对象的三大特性

    面向对象的三大特性是指:封装、继承和多态。

    一 封装

    1 封装的基本概念

    • 不同定义——不管什么形式,封装的都是数据和方法,无论哪种层面的封装,都要对外界提供好访问内部隐藏内容的接口。
      • 代码封装,其实就是隐藏实现功能的具体代码,仅留给用户使用的接口,就好像使用计算机,用户只需要使用键盘、鼠标就可以实现一些功能,而根本不需要知道其内部是如何工作的
      • 类的封装,即在设计类时,刻意地将一些属性和方法隐藏在类的内部,这样在使用此类时,将无法直接以“类对象.属性名”(或者“类对象.方法名(参数)”)的形式调用这些属性(或方法),而只能用未隐藏的类方法间接操作这些隐藏的属性和方法。
      • 模块,可以理解为是对代码更高级的封装,即把能够实现某一特定功能的代码编写在同一个 .py 文件中,并将其作为一个独立的模块,这样既可以方便其它程序或脚本导入并使用,同时还能有效避免函数名和变量名发生冲突。
    • 一般意义上的封装:我们一般讲的封装都是指类的封装
    • 注意:封装绝不是将类中所有的方法都隐藏起来,一定要留一些像键盘、鼠标这样可供外界使用的类方法。
    • 原因:封装不是单纯意义的隐藏
      • 封装数据的主要原因是:保护隐私
      • 封装方法的主要原因是:隔离复杂度
    • 两个层面:
      • 第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装。
        • 注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口
      • 第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

    2 类封装的好处

    • 可维护性:封装机制保证了类内部数据结构的完整性,因为使用类的用户无法直接看到类中的数据结构,只能使用类允许公开的数据,很好地避免了外部对内部数据的影响,提高了程序的可维护性。
    • 复杂度:对一个类实现良好的封装,用户只能借助暴露出来的类方法来访问数据,我们只需要在这些暴露的方法中加入适当的控制逻辑,即可轻松实现用户对类中属性或方法的不合理操作。
    • 代码可移植性:对类进行良好的封装,还可以提高代码的复用性。

    3 具有封装特性的结构:

    • 诸多容器,例如列表、元组、字符串、字典等,它们都是对数据的封装;
    • 函数是对 Python 代码的封装;
    • 类是对方法和属性的封装,也可以说是对函数和数据的封装。
    • 模块是对代码更高级的封装,将代码封装成一个文件,方便其它程序或脚本导入使用。

    4 python类如何进行封装

      • 原理:Python 类中的变量和函数,不是公有的(类似 public 属性),就是私有的(类似 private),因此可通过设置类中变量和函数的该属性来实现类的封装
      • 区别:
        • public:公有属性的类变量和类函数,在类的外部、类内部以及子类(后续讲继承特性时会做详细介绍)中,都可以正常访问;
        • private:私有属性的类变量和类函数,只能在本类内部使用,类的外部以及子类都无法使用。
      • 方法:
        • 名称前没有下划线:默认这些python类中的变量和方法都是公有(public)的,可以通过类对象正常访问;
        • 名称以双下划线“__”开头:表示这些python类中的变量和方法都是私有(private)的,只能在类内部使用,在类外部不能通过类对象进行访问;
          • 类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
            • 类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
            • 这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
            • 在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
            • 注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了。
          • 这种变形需要注意的问题是:
            • 这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
            • 变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
        • 名称以单下划线“_”开头的类属性和类方法:通常将他们看作私有(private)的,但是它们可以通过类对象正常访问;
        • 名称前后分别以双下划线开头和结尾:这些类方法都是python内部定义的,用于python内部调用,我们自己定义类属性或者类方法时,禁止使用这种形式。

    5  调用被封装的内容(第一个层面的封装)

    • 通过对象直接调用
      •  1 class Foo:
         2 
         3     def __init__(self, name, age):
         4         self.name = name
         5         self.age = age
         6 
         7 obj1 = Foo('wupeiqi', 18)
         8 print obj1.name    # 直接调用obj1对象的name属性
         9 print obj1.age     # 直接调用obj1对象的age属性
        10 
        11 obj2 = Foo('alex', 73)
        12 print obj2.name    # 直接调用obj2对象的name属性
        13 print obj2.age     # 直接调用obj2对象的age属性
        对象名直接调用
    • 通过self间接调用
      •  1 class Foo:
         2 
         3     def __init__(self, name, age):
         4         self.name = name
         5         self.age = age
         6 
         7     def detail(self):
         8         print self.name
         9         print self.age
        10 
        11 obj1 = Foo('wupeiqi', 18)
        12 obj1.detail()  # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18
        13 
        14 obj2 = Foo('alex', 73)
        15 obj2.detail()  # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 alex ; self.age 是 78
        self参数间接调用

    二 继承

    1 继承的基本概念

    • 定义:继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。
    • 分类:python中类的继承分为:单继承和多继承
    • 语法:
      1 class 类名(父类1, 父类2, ...):
      2     #类定义部分
    • 特点:
      • 如果该类没有显式指定继承自哪个类,则默认继承 object 类(object 类是 Python 中所有类的父类/基类,即要么是直接父类,要么是间接父类)。
      • Python 的继承是多继承机制(和 C++ 一样),即一个子类可以同时拥有多个直接父类。
    • 查看:
      • 类名.__base__:只查看从左到右继承的第一个子类
      • 类名.__bases__:查看所有继承的父类
    • 经典类与新式类:
      • 只有在python2中才分新式类和经典类,python3中统一都是新式类
      • 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
      • 在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
      • 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

    2 继承与抽象(先抽象再继承)

    • 抽象:
      • 即抽取类似或者说比较像的部分。
      • 抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
      • 抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
    • 继承:
      • 是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
      • 继承描述的是子类与父类之间的关系,是一种什么是什么的关系。要找出这种关系,必须先抽象再继承

    3 继承与重用性

    • 代码重用:通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
    • 开发过程中,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.

    4 派生

    • 定义:它和继承是一个意思,只是观察角度不同而已。换句话说,继承是相对子类来说的,即子类继承自父类;而派生是相对于父类来说的,即父类派生出子类。
    • python父类方法的重写:
      • 适用情况:对于父类中的一些方法,子类对象不能直接使用时,必须将这些方法在子类中重新定义一遍,即重写
      • 定义:重写,有时又称覆盖,是一个意思,指的是对类中已有方法的内部实现进行修改。
      • 举例:
         1 class Bird:
         2     #鸟有翅膀
         3     def isWing(self):
         4         print("鸟有翅膀")
         5     #鸟会飞
         6     def fly(self):
         7         print("鸟会飞")
         8 class Ostrich(Bird):
         9     # 重写Bird类的fly()方法
        10     def fly(self):
        11         print("鸵鸟不会飞")
        12 
        13 # 创建Ostrich对象
        14 ostrich = Ostrich()
        15 #调用 Ostrich 类中重写的 fly() 类方法
        16 ostrich.fly()
        方法重写
      • 调用重写之后的方法:子类对象名.重写后的方法,例如"ostrich.fly()"语句
      • 调用被重写方法:父类名.被重写的方法名(子类对象名)
        • 1 # 创建Ostrich对象
          2 ostrich = Ostrich()
          3 #调用 Bird 类中的 fly() 方法
          4 Bird.fly(ostrich)
          View Code
        • Python 中的类可以看做是一个独立空间,而类方法其实就是出于该空间中的一个函数。
        • 而如果想要全局空间中,调用类空间中的函数,只需要在调用该函数时备注类名即可。
        • 使用类名调用其类方法,Python 不会为该方法的第一个 self 参数自定绑定值,因此采用这种调用方法,需要手动为 self 参数赋值。

    5 关于python的多继承

    • 大部分面向对象的编程语言,都只支持单继承,即子类有且只能有一个父类。而Python 却支持多继承(C++也支持多继承)
    • 和单继承相比,多继承容易让代码逻辑复杂、思路混乱,一般较少使用
    • 使用多继承经常需要面临的问题是,多个父类中包含同名的类方法
      • 对于这种情况,Python 的处置措施是:根据子类继承多个父类时这些父类的前后次序决定,即排在前面父类中的类方法会覆盖排在后面父类中的同名类方法。
      • 代码:
      •  1 class People:
         2     def __init__(self):
         3         self.name = People
         4     def say(self):
         5         print("People类",self.name)
         6 
         7 class Animal:
         8     def __init__(self):
         9         self.name = Animal
        10     def say(self):
        11         print("Animal类",self.name)
        12 #People中的 name 属性和 say() 会遮蔽 Animal 类中的
        13 class Person(People, Animal):
        14     pass
        15 
        16 zhangsan = Person()
        17 zhangsan.name = "张三"
        18 zhangsan.say()
        多个父类中包含同名类方法解决办法
      • 运行结果:
      • 1 People类 张三

    6 继承的实现原理

    • 继承顺序问题(继承了多个类):
      • Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先
    • 经典类和新式类的区别:
      • 语法区分:是否继承了object类
      • 多继承查找方式区分:
        • 当类是经典类时,多继承情况下,会按照深度优先方式查找
          •  1 class D:
             2 
             3     def bar(self):
             4         print 'D.bar'
             5 
             6 
             7 class C(D):
             8 
             9     def bar(self):
            10         print 'C.bar'
            11 
            12 
            13 class B(D):
            14 
            15     def bar(self):
            16         print 'B.bar'
            17 
            18 
            19 class A(B, C):
            20 
            21     def bar(self):
            22         print 'A.bar'
            23 
            24 a = A()
            25 # 执行bar方法时
            26 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
            27 # 所以,查找顺序:A --> B --> D --> C
            28 # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
            29 a.bar()
            30 
            31 经典类多继承
            经典类多继承
          • 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
        • 当类是新式类时,多继承情况下,会按照广度优先方式查找
          •  1 class D(object):
             2 
             3     def bar(self):
             4         print 'D.bar'
             5 
             6 
             7 class C(D):
             8 
             9     def bar(self):
            10         print 'C.bar'
            11 
            12 
            13 class B(D):
            14 
            15     def bar(self):
            16         print 'B.bar'
            17 
            18 
            19 class A(B, C):
            20 
            21     def bar(self):
            22         print 'A.bar'
            23 
            24 a = A()
            25 # 执行bar方法时
            26 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
            27 # 所以,查找顺序:A --> B --> C --> D
            28 # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
            29 a.bar()
            30 
            31 新式类多继承
            新式类多继承
          • 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
    • 继承原理(python如何实现继承)
      • 原理:
        • python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表——F.mro() #等同于F.__mro__
        • 为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
      • MRO列表
        • 构造:通过一个C3线性化算法来实现
        • 实质:它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
          • 子类会先于父类被检查
          • 多个父类会根据它们在列表中的顺序被检查
          • 如果对下一个类存在两个合法的选择,选择第一个父类

    7 子类中调用父类的方法

    • 问题:
      • 当一个子类继承多个类时,在创建子类的对象后,程序运行可能会出错;
      • 因为不同父类的构造方法之间会被覆盖,导致有些父类的构造方法没有得到正确的参数;
      • 为解决该问题,正确的做法是在子类中定义自己的构造方法(等同于重写第一个直接父类的构造方法),并且必须在该方法中调用第一个直接父类的构造方法
    • 调用第一个父类的构造方法:
      • 方法一:父类名.父类构造方法()
      • 方法二:super()
        • Python 2.x 语法:
          1 super(Class, obj).__init__(self,...)
        • python 3.x 语法:
          1 super().__init__(self,...)
    • 上述两种方式也可用于调用父类中的其它方法:
      • 方法一:父类名.父类方法()
        •  1 #_*_coding:utf-8_*_
           2 __author__ = 'Linhaifeng'
           3 
           4 class Vehicle: #定义交通工具类
           5      Country='China'
           6      def __init__(self,name,speed,load,power):
           7          self.name=name
           8          self.speed=speed
           9          self.load=load
          10          self.power=power
          11 
          12      def run(self):
          13          print('开动啦...')
          14 
          15 class Subway(Vehicle): #地铁
          16     def __init__(self,name,speed,load,power,line):
          17         Vehicle.__init__(self,name,speed,load,power)
          18         self.line=line
          19 
          20     def run(self):
          21         print('地铁%s号线欢迎您' %self.line)
          22         Vehicle.run(self)
          23 
          24 line13=Subway('中国地铁','180m/s','1000人/箱','',13)
          25 line13.run()
          非绑定方法
      • 方法二:super()
        •  1 class Vehicle: #定义交通工具类
           2      Country='China'
           3      def __init__(self,name,speed,load,power):
           4          self.name=name
           5          self.speed=speed
           6          self.load=load
           7          self.power=power
           8 
           9      def run(self):
          10          print('开动啦...')
          11 
          12 class Subway(Vehicle): #地铁
          13     def __init__(self,name,speed,load,power,line):
          14         #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
          15         super().__init__(name,speed,load,power)
          16         self.line=line
          17 
          18     def run(self):
          19         print('地铁%s号线欢迎您' %self.line)
          20         super(Subway,self).run()
          21 
          22 class Mobike(Vehicle):#摩拜单车
          23     pass
          24 
          25 line13=Subway('中国地铁','180m/s','1000人/箱','',13)
          26 line13.run()
          super()函数

    三 多态

    1  多态

      • 定义:多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
        • 序列类型有多种形态:字符串,列表,元组
        • 动物有多种形态:人,狗,猪
      • 满足条件:
        • 继承:多态一定是发生在子类和父类之间;
        • 重写:子类重写了父类的方法

     

    • 代码举例:
      •  1 #多态:同一种事物的多种形态,动物分为人类,猪类(在定义角度)
         2 class Animal:
         3     def run(self):
         4         raise AttributeError('子类必须实现这个方法')
         5 
         6 
         7 class People(Animal):
         8     def run(self):
         9         print('人正在走')
        10 
        11 class Pig(Animal):
        12     def run(self):
        13         print('pig is walking')
        14 
        15 
        16 class Dog(Animal):
        17     def run(self):
        18         print('dog is running')
        19 
        20 peo1=People()
        21 pig1=Pig()
        22 d1=Dog()
        23 
        24 peo1.run()
        25 pig1.run()
        26 d1.run()
        多态

    2 多态性

    • 定义:多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
      • 在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。
      • 也就是说,每个对象可以用自己的方式去响应共同的消息。
      • 所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
    • 代码举例:
      •  1 #多态性:一种调用方式,不同的执行效果(多态性)
         2 # 多态性依赖于:继承
         3 ##多态性:定义统一的接口,
         4 def func(obj): #obj这个参数没有类型限制,可以传入不同类型的值
         5     obj.run() #调用的逻辑都一样,执行的结果却不一样
         6 
         7 func(peo1)
         8 func(pig1)
         9 func(d1)
        10 
        11 # peo1.run()
        12 # pig1.run()
        13 # d1.run()
        多态性
      • 综上可以说,多态性是一个接口(函数func),多种实现(如people.talk())
    • 多态性的好处:
      • 增加了程序的灵活性
        • 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
      • 增加了程序的可扩展性
        •  1 >>> class Cat(Animal): #属于动物的另外一种形态:猫
           2 ...     def talk(self):
           3 ...         print('say miao')
           4 ...
           5 >>> def func(animal): #对于使用者来说,自己的代码根本无需改动
           6 ...     animal.talk()
           7 ...
           8 >>> cat1=Cat() #实例出一只猫
           9 >>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
          10 say miao
          11 
          12 '''
          13 这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)
          14 '''
          多态性的好处

          通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

    • 总结:
      • 多态:同一种事物的多种形态,动物分为人类,猪类(在定义角度) 多态性:一种调用方式,不同的执行效果(多态性)

    3 鸭子模型

    • 定义:Python 这种由多态衍生出的更灵活的编程机制,又称为“鸭子模型”或“鸭子类型”。
    • 代码举例:
       1 class WhoSay:
       2     def say(self,who):
       3         who.say()
       4 class CLanguage:
       5     def say(self):
       6         print("调用的是 Clanguage 类的say方法")
       7 
       8 class CPython(CLanguage):
       9     def say(self):
      10         print("调用的是 CPython 类的say方法")
      11 
      12 class CLinux(CLanguage):
      13     def say(self):
      14         print("调用的是 CLinux 类的say方法")
      15 a = WhoSay()
      16 #调用 CLanguage 类的 say() 方法
      17 a.say(CLanguage())
      18 #调用 CPython 类的 say() 方法
      19 a.say(CPython())
      20 #调用 CLinux 类的 say() 方法
      21 a.say(CLinux())
      鸭子类型

      此程序中,通过给 WhoSay 类中的 say() 函数添加一个 who 参数,其内部利用传入的 who 调用 say() 方法。这意味着,当调用 WhoSay 类中的 say() 方法时,我们传给 who 参数的是哪个类的实例对象,它就会调用那个类中的 say() 方法。

    四 参考内容

    教学网站:http://c.biancheng.net/view/2288.html

    博客园:https://www.cnblogs.com/linhaifeng/articles/7340153.html

    博客园:https://www.cnblogs.com/linupython/p/9388610.html

  • 相关阅读:
    PHP中feof()函数的猜测
    PHP curl函数模拟爬虫(操作cookie)
    django发音
    Nginx出现“413 Request Entity Too Large”错误解决方法
    Python开发利器WingIDE破解方法
    开启Apache mod_rewrite
    LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
    python 正则学习
    php定时执行任务
    命令行CURL教程[转载]
  • 原文地址:https://www.cnblogs.com/luoxun/p/13468850.html
Copyright © 2011-2022 走看看