python笔记10-面向对象的三大特征

面向对象的三大特征

三大特征简介

  1. 封装(隐藏)
    隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用方法”。通过前面学习的“私有属性、私有方法”的方式,实现“封装”。Python追求简洁的语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现。
  2. 继承
    继承可以让子类具有父类的特性,提高了代码的重用性。从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。
  3. 多态
    多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员休息是“敲几行代码”。

1.继承

  1. 简介:
    子类扩展父类已有的类,我们称为“父类或者基类”,新的类,我们称为“子类或者派生类”。
  2. 语法:
    Python支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:
    class 子类类名(父类1[,父类2...]):
    	类体
    
  3. 注意:
    如果在类定义中没有指定父类,则默认父类是 object类 。也就是说, object 是所有类的父类,里面定义了一些所有类共有的默认实现,比如: __new__()
  4. 关于构造函数
    1. 子类不重写 __init__ ,实例化子类时,会自动调用父类定义的 __init__
    2. 子类重写了 __init__ 时,实例化子类,就不会调用父类已经定义的 __init__
    3. 如果重写了 __init__ 时,要使用父类的构造方法,可以使用 super 关键字,也可以使用如下格式调用:父类名.__init__(self, 参数列表)
  5. 继承和方法重写
    1. 成员继承:子类继承了父类除构造方法之外的所有成员。⚠️(私有属性、私有方法也被继承)
    2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”
  6. 代码示例
    class Person:
    
    def __init__(self, name, age):
        print('创建Person类')
        self.name = name
        self.age = age
    
    def say_age(self):
        print('{0}的年龄是{1}'.format(self.name, self.age))
    
    def say_name(self):
        print('我是', self.name)
    
    
    class Student(Person):
    
        def __init__(self, name, age, score):
            print('创建Student类')
            # self.name = name # 由于这两个实例属性和父类的相同,所以可以调用父类的构造方法
            # self.age = age
            # Person.__init__(self,name,age) # 1.这是调用父类构造方法的第一种方式
            super(Student, self).__init__(name, age)  # 2.这是调用父类构造方法的第二种方式
            self.score = score
    
        def day_score(self):
            print('{0}的年龄是{1},分数是{2}'.format(self.name, self.age, self.score))
    
        def say_name(self):  # 重写父类的方法
            print('报告老师,我是', self.name)
    
    
    a = Student('张三', 18, 99)
    a.say_age()
    a.day_score()
    

2.查看类的继承层次结构

  1. 简介:
    通过类的方法 mro() 或者类的属性 __mro__ 可以输出这个类的继承层次结构。
  2. 代码示例:
    class A:pass
    class B(A):pass
    class C(B):pass
    print(C.mro())
    # 运行结果
    [<class '__main__.C'>, <class '__main__.B'>,<class '__main__.A'>, <class 'object'>]
    
  3. 类结构图:
    在这里插入图片描述

3.object根类

  1. 简介:
    object 类是所有类的父类,因此所有的类都有 object 类的属性和方法。
  2. dir() 查看对象属性:
    内置函数 dir() ,他可以让我们方便的看到指定对象所有的属性
  3. 代码示例:
    查看对象所有属性以及和 object 进行比对
    class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say_age(self):
        print(self.name,"的年龄是:",self.age)
    obj = object()
    print(dir(obj))
    s2 = Person("zhangsan",18)
    print(dir(s2))
    # 运行结果
    	['__class__', '__delattr__', '__dir__',
    '__doc__', '__eq__', '__format__', '__ge__',
    '__getattribute__', '__gt__', '__hash__',
    '__init__', '__init_subclass__', '__le__',
    '__lt__', '__ne__', '__new__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__',
    '__sizeof__', '__str__', '__subclasshook__']
    ['__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', 'name', 'say_age']
    
  4. 从上面的代码我们可以发现这样几个要点:
    1. Person 对象增加了六个属性:
      __dict__ __module__ __weakref__ age name say_age
    2. object 的所有属性, Person 类作为 object 的子类,显然包含了所有的属性
    3. 我们打印 agenamesay_age ,发现 say_age 虽然是方法,实际上也是属性。只不过,这个属性的类型是 method 而已。
      age <class 'int'>
      name <class 'str'>
      say_age <class 'method'>
      

4.重写 str() 方法

  1. 简介:
    1. object 有一个 __str__() 方法,用于返回一个对于“对象的描述”。内置函数 str(对象) ,调用的就是__str__()
    2. __str__() 经常用于 print() 方法,帮助我们查看对象的信息。 __str__() 可以重写
  2. 代码示例:
    class Person:
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        '''将一个对象转化成一个字符串,一般用于print方法'''
        print('重写__str__方法')
        return '名字是:{0},年龄是{1}'.format(self.name, self.age)
    
    
    p = Person('张三', 19)  # 实例化对象
    print(p)  # 调用__str__方法一般print输出的就是一个字符串 “重写__str__方法”;输出了__str__方法中的return “名字是:张三,年龄是19”
    s = str(p)  # 又转换str又调用了一次__str__方法又打印了一次 “重写__str__方法“
    
    

5.多重继承

  1. 简介:
    Python支持多重继承,一个子类可以有多个“直接父类”。这样,就
    具备了“多个父类”的特点。但是由于,这样会被“类的整体层次”搞的
    异常复杂,尽量避免使用。
  2. 代码示例:
    class A:
    def aa(self):
        print("aa")
    class B:
        def bb(self):
            print("bb")
    class C(B,A):
        def cc(self):
            print("cc")
    c = C()
    c.cc()
    c.bb()
    c.aa()
    
  3. 类结构图:
    在这里插入图片描述

6.MRO方法解析顺序

  1. 简介:
    Python支持多继承,如果父类中有相同名字的方法,在子类没有指
    定父类名时,解释器将“从左向右”按顺序搜索。MRO(Method Resolution Order):方法解析顺序。 我们可以通过 mro() 方法获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。
  2. 代码示例:
    '''
    MRO(方法解析顺序):通过mro()获得类的层次结构,方法解析顺序也是按这个类的层次结构寻找的
    '''
    
    
    class A:
    
        def aa(self):
            print('aa')
    
        def say(self):
            print('say AAA')
    
    
    class B:
    
        def bb(self):
            print('bb')
    
        def say(self):
            print('say BBB')
    
    
    class C(B, A):
    
        def cc(self):
            print('cc')
    
    
    c = C()
    print(C.mro())  # [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    c.say()  # say BBB
    
    

7.super()获得父类定义

  1. 简介:
    在子类中,如果想要获得父类的方法时,我们可以通过 super() 来做。super() 代表父类的定义,不是父类对象。
    1. ❤️想调用父类的构造方法:
      1. super(子类名称,self).__init__(参数列表)
      2. 父类名.__init__(self,参数列表)
    2. ❤️想调用父类的方法:
      1. super(子类名称,self).父类方法名(参数列表)
      2. 父类名.父类方法名(self,参数列表)
  2. 实例代码:
    '''
    我们知道想要调用父类的构造方法时可以这样做:
        super(子类名,self).__init__(参数列表) 或 父类名.__init__(self,参数列表)
    那么想要调用父类的方法该怎么做呢?如下:
        super().父类方法名 或 父类名.父类方法名(self)
    '''
    
    
    class A:
        def __init__(self):
            print('A的构造方法')
    
        def say(self):
            print('A:', self)  # A: <__main__.B object at 0x0000023B9F987C40> 这里的self就是b = B()的实例化对象
            print('say AAA')
    
    
    class B(A):
        def __init__(self):
            super(B, self).__init__()  # 调用父类的构造方法1
            # A.__init__(self) # 调用父类的构造方法2
            print('B的构造方法')
    
        def say(self):
            super(B, self).say()  # 调用父类的实例方法1
            # A.say(self) # 调用父类的实例方法2,相当于把A类的say方法的方法体(注意是方法体)继承在B类的say方法中了
            print('say BBB')
    
        # 上面的say方法还可以改写成如下形式,两种运行结果是一样的
        # def say(self):
        #     print('A:', self)
        #     print('say AAA')
        #     print('say BBB')
    
    
    b = B()  # 类对象在没有构造方法__init__之前就已经创建好了,只要对象一实例化,就调用了包括他自己B类和父类A类的构造方法(初始化方法)
    b.say()
    print(b)  # <__main__.B object at 0x0000023B9F987C40>
    # 运行结果
    A的构造方法
    B的构造方法
    A: <__main__.B object at 0x00000190E746AD40>
    say AAA
    <__main__.B object at 0x00000190E746AD40>
    say BBB
    <__main__.B object at 0x00000190E746AD40>
    
    

8.多态

  1. 简介:
    在这里插入图片描述
    多态(polymorphism)是指同一个方法调用由于对象不同可能会
    产生不同的行为。
  2. 注意:
    1. 多态是方法的多态,属性没有多态。
    2. 多态的存在有2个必要条件:继承、方法重写
  3. 简单继承代码示例:
    '''
    多态指:对象不同对应的对象行为不同,如中国人和外国人吃饭
    注意:
        1.多态是方法的多态,属性没有多态
        2.多态的存在有2个必要条件:继承,方法重写
    '''
    
    
    class Animal:
        def shout(self):
            print('动物叫了一声!')
    
    
    class Dog(Animal):  # 多态条件之一:继承
        def shout(self):  # 多态条件之二:方法重写
            print('小狗,汪汪汪')
    
    
    class Cat(Animal):  # 多态条件之一:继承
        def shout(self):  # # 多态条件之二:方法重写
            print('小猫,喵喵喵')
    
    
    def animalShout(a):
        a.shout()
    
    
    animalShout(Dog())
    # 运行结果
    小狗,汪汪汪
    

9.特殊方法和运算符重载

  1. 简介:
    Python的运算符实际上是通过调用对象的特殊方法实现的。
  2. 代码示例:
    a = 20
    b = 30
    c = a+b
    d = a.__add__(b)
    print("c=",c)
    print("d=",d)
    # 运行结果
    c= 50
    d= 50
    
  3. 常见的特殊方法总结如下:
    方法说明例子
    __init__构造方法对象创建和初始化:p = Person()
    __del__析构方法对象回收
    __repr__ , __str__打印,转换print(a)
    __call__函数调用a()
    __getattr__点号运算a.xxx
    __setattr__属性赋值a.xxx = value
    __getitem__索引运算a[key]
    __setitem__索引赋值a[key]=value
    __len__长度len(a)
  4. 每个运算符实际上都对应了相应的方法,统计如下:
    运算符特殊方法说明
    +__add__加法
    -__sub__减法
    < <= ==__lt__ __le__ __eq__比较运算符
    > >= !=__gt__ __ge__ __ne__比较运算符
    | ^ &__or__ __xor__ __and__或、异或、与
    << >>__lshift__ __rshift__左移、右移
    * / % //__mul__ __truediv__ __mod__ __floordiv__乘、浮点除、模运算(取余)、整数除
    **__pow__指数运算
  5. 测试代码:
    '''
    像我们常见的+,-,*,/都可以重新修改变为我们想要的运算符
    这就是运算符的重载
    '''
    
    
    class Person:
        def __init__(self,name):
            self.name = name
        
        def __add__(self,other):
            if isinstance(other,Person):
                return '{0} Love {1}'.format(self.name,other.name)
                '''
                这里对这个other.name解释一下,p1+p2其实解释器解释时会解释为:
                p1.__add__(p2),而这个p2刚好是Person类的实例化对象,所以便有了.name的实例化属性
                '''
            else:
                return '不是同类对象不能相加'
    
        def __mul__(self,other):
            if isinstance(other,int):
                return self.name*other
                '''这个解释器会把p1*3解释为p1.__mul__(3)'''
            else:
                return '不是同类不能相乘'
        
    p1 = Person('I')
    p2 = Person('Python')
    x = p1+p2
    print(x) # I Love Python
    print(p1*3) # III
    
  6. 关于5测试代码中isinstance的解释
    1. 简介:
      isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()
    2. 注意:
      isinstance()type() 区别:
      1. type() 不会认为子类是一种父类类型,不考虑继承关系。
      2. isinstance() 会认为子类是一种父类类型,考虑继承关系。
      3. 如果要判断两个类型是否相同推荐使用 isinstance()
      4. intfloatboolcomplexstr(字符串)listdict(字典)settuple要注意的是,classinfo 的字符串是 str 而不是 string,字典也是简写 dict
        实例:
        arg=123
        isinstance(arg, int)    #输出True
        isinstance(arg, str)    #输出False
        isinstance(arg, string) #报错
        
    3. 语法:
      isinstance(object, classinfo)
      # 参数
      object -- 实例对象。
      classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组。
      
    4. 测试代码:
      1. 基本语法测试
        >>>a = 2
        >>> isinstance (a,int)
        True
        >>> isinstance (a,str)
        False
        >>> isinstance (a,(str,int,list))    # 是元组中的一个返回 True
        True	
        
      2. type() 与 isinstance()区别:
        class A:
            pass
         
        class B(A):
            pass
         
        isinstance(A(), A)    # returns True
        type(A()) == A        # returns True
        isinstance(B(), A)    # returns True
        type(B()) == A        # returns False
        

10.特殊属性

  1. 简介:
    Python对象中包含了很多双下划线开始和结束的属性,这些是特殊
    属性,有特殊用法。
  2. 总结:
    特殊属性含义
    obj.__dict__对象的属性字典
    obj.__class__对象所属的类
    class.__bases__表示类的父类(多继承时,多个父类放到一个元组中)
    class.__base __类的父类
    class.__mro __类层次结构
    class.__subclasses__()子类列表
  3. 测试代码:
    class A:
    pass
    class B:
        pass
    class C(B,A):
        def __init__(self,nn):
            self.nn = nn
        def cc(self):
            print("cc")
    c = C(3)
    print(c.__dict__)
    print(c.__class__)
    print(C.__bases__)
    print(C.mro())
    print(A.__subclasses__())
    # 运行结果
    {'nn': 3} # c对象的属性字典
    <class '__main__.C'> # c对象所属的类
    (<class '__main__.B'>, <class '__main__.A'>) # C类的父类(多继承时,多个父类放到一个元组中)
    [<class '__main__.C'>, <class '__main__.B'>, <class'__main__.A'>, <class 'object'>] # C类的层次结构 
    [<class '__main__.C'>] # A类的子类列表 
    

11.对象的浅拷贝和深拷贝

  1. 浅拷贝
    Python拷贝一般都是浅拷贝。浅拷贝:拷贝时,拷贝源对象,但对象包含的子对象内容不拷贝。
  2. 深拷贝
    使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象。深拷贝:拷贝时,拷贝源对象,也递归拷贝对象中包含的子对象
  3. 测试代码:
    import copy
    
    class Phone:
        def __init__(self,cpu):
            self.cup = cpu
    
    class CPU:
        pass
    
    c = CPU()
    p = Phone(c) # 通过这里能学到类和函数一样也可以嵌套,也就是组合
    
    print('------浅拷贝------')
    p2 = copy.copy(p)
    print('p:',id(p)) # p: 1292810020576
    print('p2:',id(p2)) # p2: 1292810026240
    print('p的cpu:',id(p.cup)) # p的cpu: 1292810020576
    print('p2的cpu:',id(p2.cup)) # p2的cpu: 1292810018080
    print('------深拷贝------')
    p3 = copy.deepcopy(p)
    print('p:',id(p)) # p: 1292810020576
    print('p3:',id(p3)) # p3: 1292810466832
    print('p的cpu:',id(p.cup)) # p的cpu: 1292810018080
    print('p3的cpu:',id(p3.cup)) # p3的cpu: 1292810467120
    
  4. 深度理解浅拷贝和深拷贝
    在这里插入图片描述

12.组合

  1. 简介:
    除了继承,“组合”也能实现代码的复用!“组合”核心是“将父类对象
    作为子类的属性”。
  2. 继承和组合的使用选择:
    1. is-a 关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。 is-a 关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。
    2. has-a 关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。 has-a 关系指的是这样的关系:手机拥有CPU。 MobilePhone has a CPU。
  3. 测试代码:
    class MobilePhone:
    def __init__(self,cpu,screen):
        self.cpu = cpu
        self.screen = screen
    class CPU:
        def calculate(self):
            print("计算")
    class Screen:
        def show(self):
            print("显示一个好看的画面")
    c = CPU()
    s = Screen()
    m = MobilePhone(c,s)
    m.cpu.calculate()       #通过组合,我们也能调用cpu对象的方法。相当于手机对象间接拥有了“cpu的方法”
    m.screen.show()
    # 运行结果
    计算
    显示一个好看的画面
    

13.设计模式_工厂模式的实现

  1. 工厂模式
    1. 简介:
      工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。
    2. 代码示例:
      '''
      1.工厂模式:顾名思义,工厂可以批量化生产
      '''
      
      
      class Benz:
          def say_price(self, p):
              print(p)
      
      
      class BMW:
          def say_price(self, p):
              print(p)
      
      
      class BYD:
          def say_price(self, p):
              print(p)
      
      
      class CarFactory:
      
          def createCar(self, brand):
              if brand == '奔驰':
                  return Benz()
              elif brand == '宝马':
                  return BMW()
              elif brand == '比亚迪':
                  return BYD()
              else:
                  return '未知品牌'
      
      
      factory = CarFactory()
      c1 = factory.createCar('奔驰')
      c1.say_price(1000000)
      # 运行结果
      1000000
      
  2. 单例模式
    1. 简介
      单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销。
    2. 代码示例:
      ⚠️单例模式有多种实现的方式,我们这里推荐重写 __new__() 的方法。
      # 单例模式
      class MySingleton:
      	__obj_flag = None
      	__init_flag = True
      
      	def __new__(cls, *arg, **kwargs):
      		if __obj_flag is None:
      		cls.__obj_flag = object.__new__(cls)
      		return cls.__obj_flag
      
      	def __init__(self, name):
      		if MySingleton.__init_flag:
                  print('初始化第一个对象......')
                  self.name = name
                  MySingleton.__init_flag = False
      a = MySingleton('a')
      print(a)  # <__main__.MySingleton object at 0x0000021F087740A0>
      b = MySingleton('b')
      print(b)  # None 单例模式只能创建一个对象
      c = MySingleton('c')
      print(c)  # None 单例模式只能创建一个对象
      
  3. 工厂和单例模式结合
    1. 简介:
      设计模式称之为“模式”,就是一些固定的套路。我们很容易用到其他场景上,比如前面讲的工厂模式,我们需要将工厂类定义成“单例”,只需要简单的套用即可实现
    2. 测试代码:
      
      class Benz:
          def say_price(self, p):
              print(p)
      
      
      class BMW:
          def say_price(self, p):
              print(p)
      
      
      class BYD:
          def say_price(self, p):
              print(p)
      
      
      class CarFactory:
      	__obj_flag = None
      	__init_flag = True
      
      	def __new__(cls, *arg, **kwargs):
      			if cls.__obj_flag is None:
      				cls.__obj_flag = object.__new__(cls)
      				return cls.__obj_flag
      
      	def __init__(self):
      		if CarFactory.__init_flag:
                  print('init CarFactory ...)
                  CarFactory.__init_flag = False
      
      	def createCar(self, brand):
              if brand == '奔驰':
                  return Benz()  # 这里为什么要加括号(),因为要实例化对象例如 c = Benz(),没初始化方法没实例属性的实例对象。以下同理。
              elif brand == '宝马':
                  return BMW()
              elif brand == '比亚迪':
                  return BYD()
              else:
                  return '未知品牌'
      factory = CarFactory()
      car1 = factory.createCar("奔驰")
      car2 = factory.createCar("比亚迪")
      print(car1)  # 两个不同的汽车实例对象
      print(car2)  # 两个不同的汽车实例对象
      # 在继续创建汽车工厂类CarFactory,测试单例模式
      factory2 = CarFactory()
      print(factory)
      print(factory2)
      # 运行结果
      init CarFactory ...
      <__main__.Benz object at 0x000001B17E9D74C0>
      <__main__.BYD object at 0x000001B17E9D7490>
      <__main__.CarFactory object at 0x000001B17E9D7520>
      <__main__.CarFactory object at 0x000001B17E9D7520>
      

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值