• Python 基础知识----面向对象编程


    一、面向对象编程介绍

    面向过程编程:

    • 核心是过程二字,过程指的是解决问题的步骤,即先做什么再干什么然后干什么。
    • 基于该思想编写程序好比在设计一条流水线,是一种机械式的思维方式。
    • 优点:复杂的问题流程化、进而简单化
    • 缺点:扩展性差

    面向对象编程:

    • 核心二字是对象,对象是数据值和行为特性的融合体。即造出来什么对象,让这个对象具有做某些事的本领。
    • 基于该思想编写程序就好比上帝在创造一个世界,是一种上帝式的思维方式。
    • 优点:扩展性强
    • 缺点:编程的复杂度要高于面向过程编程
    • 应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方

    面向对象编程和面向过程编程是一种编程思想。

    面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。

    二、类与对象

    类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体。

    在现实世界中:先有对象,再有类

    • 对象是具体的存在,而类仅仅只是一个概念,并不真实存在

    在程序中:务必保证先定义类,后产生对象

    • 这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类
    • 不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象

    2.1 创建类

    Python类使用class关键字来创建。简单地类的声明可以是关键字后紧跟类名:

    class ClassName():
        'class documentation string'  # '类文档字符串'
        class_suite  # 类体

    类就像一个Python容器类型。

    2.2 类属性

    • 什么是属性呢?属性就是属于另一个对象的数据或者函数元素,可以通过我们熟悉的句点属性标识法(也就是  . )来访问。一些Python类型比如复数有数据属性(实部和虚部),而另外一些,像列表和字典,拥有方法(函数属性)。
    • 如果你正访问一个属性时,它同时也是一个对象,拥有它自己的属性,可以访问,这导致了一个属性链
    • 类属性仅与其被定义的类相绑定,并且因为实例对象在面向对象编程中用的多。实例数据属性是我们以后将会一直用到的主要数据属性。类数据属性仅当需要有更加‘静态’数据类型时才变得有用,它和任何实例都无关。
    静态表示一个值,不会因为函数调用完毕而消失,它在每两个函数调用的间隙都存在。或者说,一个类中的一些数据对所有的实例来说,都是固定的。
    静态

    2.2.1 类的数据属性

    数据属性仅仅是所定义的类的变量。它们可以向任何其他变量一样在类创建后被使用,并且,要么是由类中的方法来更新,要么是在主程序其他什么地方被更新。

    这种属性即静态变量,或者是静态数据。它们表示这些数据是他们所属的类对象绑定的,不依赖于任何类实例。

    class C():
        foo = 100
    
    print(C.foo)
    
    C.foo = C.foo+1
    print(C.foo)

    2.2.2 Methods

    a.  方法

    方法,比如下面,类Student中的choose_course方法,仅仅是一个作为类定义一部分定义的函数(这使得方法称为类属性)。这表示choose_course仅应用在Student类型的对象(实例)上。

    这里,choose_course是通过句点属性表示法与它的实例绑定的。

    class Student():
        def choose_course(self):
            print('学生正在选课。。。')
    
    zrg = Student()
    zrg.choose_course()

    任何像函数一样对choose_course自身的调用都将失败:

    因为在全局名称空间中没有这样的函数存在。这就告诉你choose_course是一个方法,它表示属于一个类,而不是全局名称空间中的名字。

    b. 绑定(绑定与非绑定方法)

    没有实例,方法是不能被调用的。这种限制即Python所描述的绑定概念,在此,方法必须绑定(到一个实例)才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能确保调用成功。然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的。

    2.2.3 决定类的属性

    要知道一个类有哪些属性,有两种方法。最简单的就是使用dir()内置函数。另外就是通过访问类的字典属性__dict__,这是所有类都具备的特殊属性之一。

    class Student():     #创建类
        name = 'zrg'      #数据属性(静态数据)
        age = 18
        school = '北京大学'
        
        def choose_course(self):      #方法
            print('学生正在选课。。。')
    print(dir(Student))
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'choose_course', 'name', 'school']
    print(Student.__dict__)
    {'__module__': '__main__', 'name': 'zrg', 'age': 18, 'school': '北京大学', 'choose_course': <function Student.choose_course at 0x102b1c620>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

    从上面可以看出,dir()返回的仅是对象的属性的名字列表,而__dict__返回的是一个字典,它的键(key)是属性名,键值(value)是响应的属性对象的数据值。

    2.3 实例

    如果说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量。换言之,实例就是有生命的类。就像设计万一张蓝图后,就是设法让它和成为现实。实例是那些主要用在运行期时的对象,类被实例化得到实例,该实例的类型就是这个被实例化的类。

    2.3.1 初始化(实例化):通过条用类对象来创造实例

    class Student():  # 创建类
        name = 'zrg'  # 数据属性(静态数据)
    
        def choose_course(self):  # 方法
            print('学生正在选课。。。')
    
    
    obj = Student()      #类名加括号,实例化,创建实例,返回值obj就是一个实例,是一个对象

     2.3.2 __init__() ‘构造器’方法

    当类被调用,实例化的第一步是创建实例对象。一旦对象创建了,Python检查是否实现了__init__()方法。默认情况下,如果没有定义(或覆盖)特殊方法__init__(),对实例不会施加任何特别的操作。任何所需的特定操作,都需要程序员实现__init__(),覆盖它的默认行为。如果__init__()没有实现,则返回它的对象,实例化过程完毕。

    然而,如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递进去,像标准方法调用一样。调用类时,传进去的任何参数都交给了__init__()。实际中,你可以想像成这样:把创建实例的调用当成是对构造器的调用。

    2.3.3 __new__()

    2.3.4 __del__()

    2.4 实例属性

    实例仅拥有数据属性(方法严格来说是类属性),或者只是与某个类的实例相关联的数据值,并且可以通过句点属性标识法来访问。这些值独立于其它实例或类。当一个实例被释放后,它的属性同时也被清除了。

    2.4.1 ‘实例化’实例属性()

    设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行。构造器__init__()是设置这些和随行的关键点之一。

    能够在‘运行时’创建实例属性,是Python类的优秀特性之一,Python不仅是动态类型,而且在运行时,允许这些对象属性的动态创建。
    一个缺陷是,属性在条件语句中创建,如果该条件语句块未被执行,属性也就不存在,而你在后面的代码中试着去访问这些属性,就会发生错误。
    核心笔记:实例属性
    1. 在构造器中首先设置实例属性
      1. 构造器是最早可以设置实例属性的地方,因为__init__()是实例创建后第一个被调用的方法。再没有比这更早的可以设置实例属性的机会了。一旦__init__()执行完毕,返回实例对象,即完成了实例化过程。  
    2. 默认参数提供默认的实例安装
      1. 在实际应用中,带默认参数的__init()提供一个有效的方式来初始化实例。在很多情况下,默认值表示设置属性的最常见的情况,如果提供了默认值,我们就没必要显示给构造器传值了。默认参数应当是不变的对象;像列表和字典这样的可变对象可以扮演静态数据,然后在每个方法调用维护它们的内容。  
    3. __init__()应当返回None
      1. 调用类会对象会创建一个实例,也就是说这样一种调用过程返回的对象就是实例。
      2. 如果定义了构造器,它不应当返回任何对象,因为实例对象是自动在实例化调用后返回的。相应的__init__()就不应当返回任何对象(应当为None);否则,就可能出现冲突,因为只能返回实例。  

    2.4.2 查看实例属性

    内置函数dir()可以显示类属性,同样还可以打印所有的实例属性。

    class Student():
        school = '北京大学'
    
        def choose_Course(self):
            print('选课')
    
    student1 = Student()
    student1.name = '张仁国'
    student1.age = 18
    student1.hobby = ['read','swimming','music']
    print(dir(student1))

    结果:

    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'choose_Course', 'hobby', 'name', 'school']

    与类相似,实例也有一个__dict__()特殊属性(可以调用vars()并传入一个实例来获取),它是实例属性构成的一个字典。

    class Student():
        school = '北京大学'
    
        def choose_Course(self):
            print('选课')
    
    student1 = Student()
    
    student1.name = '张仁国'
    student1.age = 18
    student1.hobby = ['read','swimming','music']
    print(student1.__dict__)

    结果:

    __dict__属性由一个字典组成,包含一个实例的所有属性。键是属性名,值是属性对应的数据值。字典中仅有实例属性,没有类属性或特殊属性。

    {'name': '张仁国', 'age': 18, 'hobby': ['read', 'swimming', 'music']}

    2.4.5 实例属性vs类属性

    类属性仅是与类相关的数据值,和实例属性不同。类属性与实例属性无关。这些像静态成员那样被引用,即使在多次实例化中调用类,它们的值都保持不变。不管如何,静态成员不会因为实例而改变它们的值,除非实例中显示改变他们的值(实例属性与类属性的比较,雷诗雨自动变量和静态变量,但这只是笼统的类推。) 

    类和实例都是名称空间。类是类属性的名称空间,实例则是实例属性的名称空间。

    关于类属性和实例属性,可以采用类来访问类属性,如果实例没有同名的属性的话,也可以用实例来访问。

    a、访问类属性

      类属性可以通过类或实例来访问。Python首先会在实例的名称空间中查找,然后是类,再就是继承书中的基类。

    class Student():     #定义类
        school = '北京大学'    #静态成员
    
    zrg = Student()      #实例化
    print(Student.school)    #通过类来访问
    print(zrg.school)        #通过实例来访问
    Student.school = '清华大学'   #通过类(只能这样)来更新
    print(Student.school)        #通过类来访问
    print(zrg.school)            #通过实例来访问,其值已经被改变了
    
    结果:
    北京大学
    北京大学
    清华大学
    清华大学

    注意:

      我们只有当使用类能修改类属性的值!

    b、从实例中访问类属性需谨慎

      与通常Python变量一样,任何对实例属性的赋值都会创建一个实例属性(如果不存在的话)并且对其赋值。如果类属性中存在同名的属性

    c、类属性持久化

      类属性的修改会影响所有的实例。

    2.5 绑定和方法调用 

    Python中绑定(binding)的概念,它主要与方法调用相关联。

      首先,方法仅仅是类内部定义的函数(这意味着方法是类属性而不是实例属性)。

      其次,方法只有在其属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为是绑定到那个实例了。没有实例时方法就是未绑定的。

      最后,任何一个方法定义中的第一个参数都是变量self,它表示调用此方法的实例对象。

    self变量用于在类实例方法中引用方法所绑定的实例。因为方法的实例在任何方法调用中总是作为第一个参数传递的,self被选中用来代表实例。必须在方法声明中放上self,但可以在方法中不适用实例(self)。如果再方法中国没有用到self,建议创建一个常规函数,除非有特别的原因。毕竟方法代码没有使用实例,没有与类关联其功能,这使得它看起来更像一个常规函数。
    self是什么?

    2.5.1 调用绑定方法

      方法,不管绑定与否,都是由相同的代码组成的。唯一的不同在于是否存在一个实例可以调用此方法。在很多情况下,调用的都是一个绑定的方法。

    2.5.2 调用非绑定方法

    2.6 静态方法和类方法

    2.6.1 staticmethod()和classmethod()内奸函数

    2.6.2 使用函数修饰符

    2.7 组合(让一个类的对象具备一个属性,而这个属性的值是另外一个类的对象)

    一个类被定义后,目标就是要把它当成一个模块来使用,并把这些对象嵌入到你的代码中,同其他数据类型及逻辑执行流混合使用。有两种方法可以在你的代码中利用类。第一种是组合(composition)。就是让不同的类混合并加入到其他类中,来增加功能和代码重用性。你可以在一个大点的类中创建你自己的类的实例,实现一些其他属性和方法来增强对原来的类对象。另一种方法是通过派生。

    # 父类
    class People():
        school = '北京大学'
    
        def __init__(self, id, name, age, sex, subject):
            self.id = id
            self.age = age
            self.sex = sex
            self.name = name
            self.subject = subject
    
    
    # 课程类
    class Course():
        def __init__(self, name, price):
            self.name = name
            self.price = price
    
        def course_info(self):
            msg = """
            课程名:%s
            课程价格:%s
            """%(self.name,self.price)
    
            print(msg)
    
    
    # 学生类
    class Student(People):
        def __init__(self, id, name, age, sex, subject):
            People.__init__(self, id, name, age, sex, subject)
    
        def choose_Course(self):
            print('选课')
    
    
    # 老师类
    class Teacher(People):
        def __init__(self, id, name, age, sex, subject, level):
            People.__init__(self, id, name, age, sex, subject)
            self.level = level
    
        def score(self, stu, num):
            stu.score = num
            print('学生%s的成绩是:%s' % (stu.name, num))
    
    
    # 课程实例
    python = Course('Python全栈开发', 20000)
    linux = Course('linux运维', 17000)
    
    # 学生实例
    zrg = Student(1, '张仁国', 18, '', 'Python')
    zrg.choose_Course()
    # 老师实例
    yj = Teacher(1, '杨静', 19, '', '地理', '高级教师')
    
    # 学生、老师与课程组合
    zrg.course = python
    yj.course = linux
    
    
    zrg.course.course_info()
    yj.course.course_info()
    组合使用

    2.8 子类和派生

    面向对象编程的强大功能之一是能够使用一个已经定义好的类,扩展它或者对其进行修改,而不会影响系统中使用现存类的其他代码片段。允许类特征在子孙类或子类中进行继承。这些子类从基类(或成祖先类、超类)继承它们的核心属性。而且,这些派生可能会扩展到多代。在一个层次的派生关系中的相关类(或者是在类树图中垂直相邻)是父类和子类关系。从同一个父类派生出来的这些类(或者是在类树图中水平相邻)是同胞关系。父类和所有高层类都被认为是祖先。

    2.9 继承

    什么是继承?

      继承是一种新建类的方式,新建的类称为子类,被继承的类称为父类

      继承的特性是:子类会继承父类的属性

    为什么用继承?

      继承的好处就是可以减少代码的冗余

    如何用继承?

    class Parent():    #父类
        pass
    
    
    class Children(Parent):   #子类
        pass

    经典类与新式类

    1.只有在python2中才分新式类和经典类,python3中统一都是新式类
    2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
    3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
    3.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

    2.9.1 __bases__类属性

    对任何(子)类,它是一个包含其父类的集合的元组。

    class Parent1():    #父类
        pass
    class Parent2():
        pass
    
    class Children(Parent1,Parent2):   #子类
        pass
    
    print(Parent1.__bases__)
    print(Parent2.__bases__)
    print(Children.__bases__)
    (<class 'object'>,)
    (<class 'object'>,)
    (<class '__main__.Parent1'>, <class '__main__.Parent2'>)

    注意:父类是相对所有基类(它包括了所有祖先类)而言的。那些没有父类的类,它们的__bases__属性为空。

    2.9.2 通过继承覆盖方法

    2.9.3 

    2.9.4 多重继承

    Python允许子类继承多个基类。这种特性就是通常所说的多重继承。概念容易,但最难的工作是,如何正确找到没有在当前(子)类定义的属性。当使用多重继承时,有两个不同的方面要记住。首先,还是要找到合适的属性。另一个就是当你重写方法时,如何调用对应父类方法以发挥他们的作用,同时,在子类中处理好自己的义务。

    1、方法解释顺序

    2、简单属性查找示例

    为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
    而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
    1.子类会先于父类被检查
    2.多个父类会根据它们在列表中的顺序被检查
    3.如果对下一个类存在两个合法的选择,选择第一个父类

    3、菱形效应引起MRO问题

    4、总结

    方式一:知名道姓,直接用父类名.函数名。与继承无关
    class Teacher(People):
        def __init__(self, id, name, age, sex, subject, level):
            People.__init__(self, id, name, age, sex, subject)
            self.level = level
    
    方式二:严格依赖继承属性查找关系
    super()会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按继承的关系来查找属性)
    class Teacher(People):
        def __init__(self, id, name, age, sex, subject, level):
            super().__init__(id, name, age, sex, subject)
            self.level = level
    在子类派生的新方法中重用父类功能的两种方式

    2.10 类、实例和其他对象的内置函数

    多态和多态性 

    什么是多态?

      多态指的是同一种事物的多种形态。 

    为何要用多态?

      多态性:继承同一个类的多个子类中有相同的方法名,那么子类产生的对象就可以不用考虑具体的类型而直接调用功能。

    如何用?

     鸭子类型

    111

  • 相关阅读:
    第一章 zookeeper基础概念
    ntp服务
    nfs与dhcp服务
    samba、ftp和ssh服务
    Linux系统的RAID磁盘阵列
    Linux系统中lvm简介
    Linux系统中的计划任务
    Linux权限管理
    Linux用户及组管理
    简谈OSI七层模型(网络层)
  • 原文地址:https://www.cnblogs.com/zhangrenguo/p/9833985.html
Copyright © 2020-2023  润新知