面向对象编程(oop)

类和面向对象(oop)

概述

  • oop思想
    任意一个任务,首先想到的是任务的构成和实现过程。

  • 类和对象的概念

    • 类:抽象名词,代表一个集合,代表一类事物。
    • 对象:具象的事物,单个个体。
  • 类的内容

    • 属性(变量):表明事物特征
    • 方法(函数):表明事物功能
  • 类的命名

    • 大驼峰比较规范。
    • 必须用class关键字。
    • 由属性和方法组成,其他不许出现。
    • 成员属性定义可直接用变量赋值,如果没有值需使用None。
  • 访问类和对象的成员

    # 对象成员检查
    obj.__dict__
    # 类成员检查
    class_name.__dict__  
    
  • 实例

     # 定义一个简单的类
     class StudentPython():
     name = None
     age = 18
     def Studentplay(self):
         print("learning python")
    
     # David为对象,此时实例化类
     David = StudentPython()
     print(David.name)
     print(David.age)
     David.Studentplay()
    

类和对象的成员分析

  • 类和对象都可以储存成员,成员可以归类所有也可以归对象所有。

  • 创建对象时,类中的成员不会放入对象中,而是得到一个空对象。

  • 通过对象对类中成员重新赋值或者通过对象添加成员时,对应成员会保存在对象中,而不会修改类成员。

  • 实例

    class Teacher():
        name = "aaa"
        age = 28
        def TeacherSay(self):
            self.name = "Mary"
            self.age = 20
            print("My name is {0}".format(self.name))
            print("My age is {0}".format(self.age))
      
    t = Teacher()
    print(t.name)  
    print(t.age)
    t.TeacherSay()
    
    '''
    aaa
    28
    My name is Mary
    My age is 20
    '''
    

关于self

  • self在对象的方法中表示当前对象本身,如果通过对象调用方法,那么该对象会自动传入到当前方法的第一个参数中。

  • self并不是关键字,理论上可以用任何一个普通变量名代替。

  • 方法中有self形参的称为非绑定类的方法,可以通过对象访问,没有self的称为绑定类方法,只能通过类访问。

  • 实例

    class Teacher():
        name = "aaa"
        age = 28
        def TeacherSay(self):
            self.name = "Mary"
            self.age = 20
            print("My name is {0}".format(self.name))
            print("My age is {0}".format(self.age))
        def TeacherSayAgain():
            print(__class__.name)
            print(__class__.age)
            print("learning python")
    
    t = Teacher()
    t.TeacherSay()
    # 调用绑定类方法通过类访问
    Teacher.TeacherSayAgain()
    
    '''
    My name is Mary
    My age is 20
    aaa
    28
    learning python
    '''
    

面向对象的三大特性

封装,继承,多态

  • 封装
    • 三个级别:公开(public),受保护(protected),私有(private)。

    • 判别对象的位置:对象内部,对象外部,子类中。

    • 私有:私有成员是最高级别的封装,只能在当前类或对象中访问,外部不能访问。python的私有不是真私有,是一种为name mangling的改名策略。

      class Person():
          # name为共有成员
          name = "David"
          #__age为私有成员
          __age = 18
      
    • 受保护:将对象成员进行一定级别的封装,在父类和子类中可以访问,外部不能访问。

    • 公开:对对象成员没有任何操作,任何地方都可以访问。

  • 继承
    • 继承就是一个类可以获得另外一个类中的成员属性和方法。

    • 被继承的为父类(基类或超类),用于继承的类为子类(派生类)。

    • 所有的类都继承自object类,即所有的类都是object类的子类。

    • 子类继承父类后并没有将父类成员完全赋值到子类,而是通过引用关系访问调用。

    • 子类一旦继承父类,可以使用父类中除私有成员外的所有成员属性和方法,子类还可以定义独有的成员属性和方法。

    • 子类中定义的成员如果和父类成员相同,则优先使用子类成员。

    • 如果想在子类中扩充父类方法,可以在定义新方法时访问父类成员来进行代码重用,可以父类名.父类方法也可以super().父类方法的格式调用。
      super不是关键字,而是一个类。super的作用是获取MRO(MethodResolutionOrder)列表中的第一个类

      class Person():
          name = "None"
          __age = 18
          _score = 0
          def Study(self):
              print("learning python")
      
      class Student(Person):
          def HaveTest(self):
              print("have to take exam")
          # 扩充父类函数,方法一二
          def Study(self):
              Person.Study(self)# 注意这里有self
              self.HaveTest()
              '''
              super().Study()# 注意这里没有self
              # super(Student, self).Study()也可以
              self.HaveTest()
              '''    
                             
      David = Student()
      print(David.name)
      David.Study()
      
      '''
      None
      learning python
      have to take exam
      '''
      
    • 继承中的查找顺序:子类优先查找自己的变量,没有则查找父类变量;
      子类中如果构造函数没有定义,则按照MRO顺序查找调用父类的构造函数,子类有定义则不继续向上查找。
      构造函数:每次实例化第一个被自动调用的函数

    • 单继承和多继承:单继承每个类只能继承一个类,多继承每个类能继承多个类;单继承逻辑清晰语法简单但功能不能受限,多继承扩展方便但关系混乱且会出现菱形继承(钻石继承)问题。

    • MRO:是在多继承中用于保存继承顺序的一个列表;
      MRO计算原则:子类永远在父类前;如果多个父类则根据括号内类的书写顺序存放;如果多个类继承了同一个父类,孙子类中只会选取括号内第一个父类的父类。

  • 多态
    • 多态就是同一个对象在不同情况下有不同状态出现。

    • Mixin设计模式:主要采用多继承方式对类功能进行扩展。首先必须表示某一单一功能,如果有多个功能则写多个Mixin,当子类没有继承某一Mixin类也能照常工作。

      class Person():
          def __init__(self, name, age):
              self.name = name
              self.age = age
          def Play(self):
              print("have fun")
          def Sleep(self):
              print("sleeping...")
      # 创建一个Mixin
      class TeacherMixin():
          def Work(self):
              print("teaching somebody")
      
      class StudentMixin():
          def Study(self):
              print("learning python")
      
      class Student(Person, StudentMixin):
          pass
      class Teacher(Person):
          def __init__(self):
              self.name = "Alex"
      
      David = Student("David", 18)
      print("my name is {0}, my age is {1}".format(David.name, David.age))
      David.Study()
      David.Play()
      print("*" * 20)
      Alex = Teacher()
      print("my name is {0}".format(Alex.name))
      Alex.Sleep()
      
      '''
      my name is David, my age is 18
      learning python
      have fun
      ********************
      my name is Alex
      sleeping...
      '''
      

类相关函数

  • issubclass:检测一个类是否是另一个类的子类。
  • isinstance:检测一个对象是否是一个类的实例。
  • hasattr:检测一个对象是否有成员xxx。
  • dir:获取对象的成员列表

类的成员描述符(属性)

  • 类的成员描述符是为了在类中对类的成员属性进行相关操作

  • 三种方式:使用类实现描述器;使用属性修饰符;使用property函数property(fget, fset, fdel, doc)

    class Person():
        '''
        property函数实例
        定义一个Person类,具有name,age属性,
        对于任意输入的name都希望以大写字母保存
        '''
        def fget(self):
            # 返回操作后的属性
            return self._name
        def fset(self, name):
            # 传入需要操作的属性
            self._name = name.upper()
        def fdel(self):
            self._name = "NoName"
        name = property(fget, fset, fdel, "None")
        age = 18
      
    David = Person()
    David.name = "David"
    print("my name is {0}".format(David.name))
    '''
    my name is DAVID
    '''
    

类的内置属性

__dict__:以字典的方式显示类成员的组成
__doc__:获取类的文档信息
__name__:获取类的名称,如果在模块中使用则获取模块名称
__bases__:以元组方式显示类的所有父类

类的常用魔术方法

  • 魔术方法就是不需要认为调用,在特定的情况下自动触发的方法,统一特征是方法名被前后两个下划线包裹。
    __init__:构造函数
    __new__:对象实例化方法
    __call__:对象当函数时使用
    __str__:对象被当作字符串时使用
    __repr__:返回字符串
    __getattr__:访问一个不存在的属性时触发,此时不会报错
    __gt__:进行大于判断的时候触发
    __setattr__:对成员属性设置的时候触发

    # __setattr__示例
    class A():
        def __setattr__(self, name, value):
            print("设置属性{0}为{1}".format(name, value))
            '''
            这样会导致死循环
            self.name = value
            '''
            # 为了避免这种情况,使用父类魔法函数
            super().__setattr__(name, value)
    a = A()
    a.age = 18       
    

类和对象的三种方法

  • 实例方法:必须要创建实例对象才能调用,第一个参数必须是实例对象,该参数名一般约定为self,如果方法里面有初始化函数也必须对初始化函数进行传参。且只能由实例对象调用

  • 类方法:使用装饰器@classmethod,第一个参数必须是当前类对象,该参数名一般约定为cls,可以由实例对象和类对象调用。

  • 静态方法:使用装饰器@staticmethod,没有self和cls参数,可以由实例对象和类对象调用。
    参考博客Python 实例方法、类方法、静态方法的区别与作用

    class Person():
        def Work(self):
            print("have to work")
        @classmethod
        def Study(cls):
            print("have to study")
        @staticmethod
        def Sleep():
            print("sleeping...")
    # 实例方法
    p = Person()
    p.Work()
    #Person.Work()会报错
    # 类方法
    p.Study()
    Person.Study()
    # 静态方法
    p.Sleep()
    Person.Sleep()
    
posted @ 2020-02-22 17:29  搁浅Lee  阅读(146)  评论(0编辑  收藏  举报