欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

20.Python略有小成(面向对象Ⅱ)

程序员文章站 2022-03-07 09:12:48
Python(面向对象Ⅱ) 一、类的空间问题 1. 何处可以添加对象属性 2. 何处可以添加类的静态属性 3. 对象如何找到类的属性 之前咱们都学习过,实例化一个对象,可以通过点的方式找到类中的属性 那么他为什么可以找到类中的属性呢 , 如下图 : 对象查找属性的顺序:先从对象空间找 类空间找 父类 ......

python(面向对象ⅱ)

一、类的空间问题

  1. 何处可以添加对象属性

    class a:
        def __init__(self,name):
            self.name = name
        def func(self,sex):
            self.sex = sex
    
    # 类外面可以:
    obj = a('barry')
    obj.age = 18
    print(obj.__dict__)  # {'name': 'barry', 'age': 18}
    
    # 类内部也可以:
    obj = a('barry') # __init__方法可以。
    obj.func('男')  # func 方法也可以。
    
    # 总结:对象的属性不仅可以在__init__里面添加,还可以在类的其他方法或者类的外面添加。
  2. 何处可以添加类的静态属性

    class a:
        def __init__(self,name):
            self.name = name
        def func(self,sex):
            self.sex = sex
        def func1(self):
            a.bbb = 'ccc'
    
    # 类的外部可以添加
    a.aaa = 'taibai'
    print(a.__dict__)
    
    # 类的内部也可以添加。
    a.func1(111)
    print(a.__dict__)
    
    # 总结:类的属性不仅可以在类内部添加,还可以在类的外部添加。
  3. 对象如何找到类的属性

    之前咱们都学习过,实例化一个对象,可以通过点的方式找到类中的属性

    那么他为什么可以找到类中的属性呢 , 如下图 :

    20.Python略有小成(面向对象Ⅱ)

    对象查找属性的顺序:先从对象空间找 ------> 类空间找 ------> 父类空间找

    类名查找属性的顺序:先从本类空间找 -------> 父类空间找

    单向不可逆,类名不可能找到对象的属性。

    对象与对象之间原则上互相独立(除去组合这种特殊的关系外)

二、类与类之间的关系

类与类中存在以下关系:

  1. 依赖关系(主从关系)

    将一个类的对象或者类名传到另一个类的方法中

    class elephant:
        def __init__(self, name):
            self.name = name
        def e_open(self,ref1):
            print(f"{self.name}要开门了,默念三声,开")
            ref1.open_door()
        def e_close(self,ref2):
            print(f"{self.name}要关门了,默念三声,关")
            ref2.close_door()
    class refrigerator:
        def __init__(self, name):
            self.name = name
        def open_door(self):
            print(f"{self.name}冰箱门被打开了")
        def close_door(self):
            print(f"{self.name}冰箱门被关上了")
    elephant = elephant('大象')
    haier = refrigerator('美丽')
    elephant.e_open(haier)
    elephant.e_close(haier)
  2. 组合关系(关联,聚合)

    这三个在代码写法是一样的,但含义上不一样.

    • 关联关系 : 两种事物必须是互相关联的,但是在某些特殊情况下是可以更改和更换的

      class boy:
          def __init__(self,name):
              self.name = name
          def meet(self,girl_friend=none):
              self.girl_friend = girl_friend
          def have_a_diner(self):
              if self.girl_friend:
                  print('%s和%s一起晚饭'%(self.name,self.girl_friend.name))
                  self.girl_friend.shopping(self)
              else:
                  print('一个人吃饭')
      class girl:
          def __init__(self,name,age):
              self.name = name
              self.age = age
          def shopping(self,boy_friend):
              print(f"{self.name},{boy_friend.name}一起去购物")
      
      wu=boy("老道")
      flower=girl("尼姑",48)
      wu.meet(flower)
      wu.have_a_diner()
      
      # 注意:此时boy和girl两个类之间就是关联关系,两个类的对象紧密联系,其中一个如果没有了,另一个就孤单存在,关联关系其实就是当我需要你,你也属于我,这就是关联关系.
    • 聚合关系 : 属于关联关系中的一种特例,侧重点是xxx和xxx聚合成xxx,各有各的声明周期,互相独立.

    • 组合关系 : 属于关联关系中的一种特例,写法上差不多,组合关系比聚合还要紧密,互相关联,将一个类的对象封装到另一个类的对象的属性中,就叫组合

      组合关系和聚合关系,其实代码上差别不大,以组合关系举例:

      # 设计一个游戏人物类,让实例化几个对象让这几个游戏人物实现互殴的效果,同时增加装备系统.
      class gamerole:
          def __init__(self,name,ad,hp):
              self.name = name
              self.ad = ad
              self.hp = hp  
          def equip_weapon(self,wea):
              self.wea = wea  # 组合:给一个对象封装一个属性改属性是另一个类的对象
      class weapon:
          def __init__(self,name,ad):
              self.name = name
              self.ad = ad
          def weapon_attack(self,p1,p2):
              p2.hp = p2.hp - self.ad - p1.ad
              print('%s 利用 %s 攻击了%s,%s还剩%s血'
                    %(p1.name,self.name,p2.name,p2.name,p2.hp))
      
      # 实例化三个人物对象:
      barry = gamerole('师太',10,100)
      panky = gamerole('师爷',20,250)
      pillow = weapon('拖鞋',10)
      
      # 给人物装备武器对象。
      barry.equip_weapon(pillow)
      
      # 开始攻击
      barry.wea.weapon_attack(barry,panky)
      
      # 注意:上面就是组合,只要是人物.equip_weapon这个方法,那么人物就封装了一个武器对象,再利用武器对象调用其类中的weapon_attack方法。
  3. 继承关系

    官方说法 : 继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别a“继承自”另一个类别b,就把这个a称为“b的子类别”,而把b称为“a的父类别”也可以称“b是a的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。

    1. 面向对象的三大特性 : 封装,继承,多态

    2. 什么是继承

      b继承a类,b就叫做a的子类,a叫做b的父类,基类,超类,b类以及b类的对象使用a类的所有的属性以及方法

      举例如下 :

      # 正常类的用法
      class person:
          def __init__(self,name,sex,age):
              self.name = name
              self.age = age
              self.sex = sex
      class cat:
          def __init__(self,name,sex,age):
              self.name = name
              self.age = age
              self.sex = sex
      class dog:
          def __init__(self,name,sex,age):
              self.name = name
              self.age = age
              self.sex = sex
      
      # 继承的用法:
      class aniaml(object): # 在这里 aminal 叫做父类,基类,超类。
          def __init__(self,name,sex,age):
                  self.name = name
                  self.age = age
                  self.sex = sex
      class person(aniaml): # person: 子类,派生类。
          pass
      class cat(aniaml): # cat: 子类,派生类。
          pass
      class dog(aniaml): # dog: 子类,派生类。
          pass
    3. 继承的优点

      1. 减少代码重复
      2. 增加类的耦合性
      3. 代码规范化合理化
    4. 继承分类

      1. 单继承

        • 类名对象执行父类方法,子类以及子类对象只能调用父类的属性以及方法,不能操作(增删改)

          class aniaml(object):
              type_name = '动物类'
              def __init__(self,name,sex,age):
                      self.name = name
                      self.age = age
                      self.sex = sex
              def eat(self):
                  print(self)
                  print('吃东西')
          class person(aniaml):
              pass
          class cat(aniaml):
              pass
          class dog(aniaml):
              pass
          
          # 类名:
          print(person.type_name)  # 可以调用父类的属性,方法。
          person.eat(111)
          print(person.type_name)
          
          # 对象:
          # 实例化对象
          p1 = person('师太','女',20)
          print(p1.__dict__)
          
          # 对象执行类的父类的属性,方法。
          print(p1.type_name)
          p1.type_name = '666'
          print(p1)
          p1.eat()
        • 执行顺序

          class aniaml(object):
              type_name = '动物类'
              def __init__(self,name,sex,age):
                      self.name = name
                      self.age = age
                      self.sex = sex
              def eat(self):
                  print(self)
                  print('吃东西')
          class person(aniaml):
              def eat(self):
                  print('%s 吃饭'%self.name)
          class cat(aniaml):
              pass
          class dog(aniaml):
              pass
          p1 = person('lala','女',20)
          # 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。
          p1.eat()
          # 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。
        • 同时执行类以及父类方法

          方法一

          子类的方法中写上:父类.func(对象,其他参数)

          class aniaml(object):
              type_name = '动物类'
              def __init__(self,name,sex,age):
                      self.name = name
                      self.age = age
                      self.sex = sex
              def eat(self):
                  print('吃东西')
          class person(aniaml):
              def __init__(self,name,sex,age,mind):
                  aniaml.__init__(self,name,sex,age)  # 方法一
                  self.mind = mind
              def eat(self):
                  super().eat()
                  print('%s 吃饭'%self.name)
          class cat(aniaml):
              pass
          class dog(aniaml):
              pass
          
          # 方法一: aniaml.__init__(self,name,sex,age)
          p1 = person('师太','女',20,'武艺高强')
          print(p1.__dict__)

          方法二

          利用super,super().func(参数)

          class aniaml(object):
              type_name = '动物类'
              def __init__(self,name,sex,age):
                      self.name = name
                      self.age = age
                      self.sex = sex
              def eat(self):
                  print('吃东西')
          class person(aniaml):
              def __init__(self,name,sex,age,mind):
                  # super(person,self).__init__(name,sex,age)  
                  # super()括号内可不写person,self 在python自动执行此操作
                  super().__init__(name,sex,age)  # 方法二
                  self.mind = mind
              def eat(self):
                  super().eat()
                  print('%s 吃饭'%self.name)
          class cat(aniaml):
              pass
          class dog(aniaml):
              pass
          p1 = person('师太','女',20,'武艺高强')
          print(p1.__dict__)
      2. 多继承

        class shenxian:
            def fei(self):
                print("飞")
        class monkey:
            def pi(self):
                print("皮")
        class sunwukong(shenxian, monkey): # 孙悟空是神仙,同时也是也只猴
            pass
        sxz = sunwukong() # 孙悟空
        sxz.pi() # 此时猴子皮
        sxz.fei() # 此时会飞

        当两个超类中出现了重名方法的时候,这时就涉及到如何查找超类方法的这个问题.即mro(method resolution order) 问题. 在python中这是个很复杂的问题. 因为在不同的python版本中使用的是不同的算法来完成mro的.

    5. python中类的种类(继承需要) :

      class a:
          pass
      class b(a):
          pass
      class c(a):
          pass
      class d(b, c):
          pass
      class e:
          pass
      class f(d, e):
          pass
      class g(f, d):
          pass
      class h:
          pass
      class foo(h, g):
          pass
      1. python2.2之前 : 都是经典类,

      2. python2.2-python2.7存在两种类型 :经典类,新式类

      3. python3x只有新式类

        • 经典类 : 基类不继承object,查询规则依靠深度优先原则(画图)

          从头开始,从左往右.,一条路跑到头,然后回头,继续一条路跑到头,就是经典类的mro算法

          foo-> h -> g -> f -> e -> d -> b -> a -> c

          20.Python略有小成(面向对象Ⅱ)

        • 新式类 : 基类必须继承object,查询规则依靠mro算法,mro是一个有序列表l,在类被创建时就计算出来。
          通用计算公式为:

          mro(child(base1,base2))=[child]+merge( mro(base1),mro(base2),[base1,base2])
          (其中child继承自base1, base2)

          merge操作示例:

          如计算merge( [e,o], [c,e,f,o], [c] )
          有三个列表 :  ①      ②          ③
          1 merge不为空,取出第一个列表列表①的表头e,进行判断                              
             各个列表的表尾分别是[o], [e,f,o],e在这些表尾的集合中,因而跳过当前当前列表
          2 取出列表②的表头c,进行判断
             c不在各个列表的集合中,因而将c拿出到merge外,并从所有表头删除
             merge( [e,o], [c,e,f,o], [c]) = [c] + merge( [e,o], [e,f,o] )
          3 进行下一次新的merge操作 ......
          --------------------- 

          计算mro(a)方式:

          # mro(a)内部运行情况如下
          mro(a) = mro( a(b,c) )
          原式= [a] + merge( mro(b),mro(c),[b,c] )
            mro(b) = mro( b(d,e) )
                   = [b] + merge( mro(d), mro(e), [d,e] )  # 多继承
                   = [b] + merge( [d,o] , [e,o] , [d,e] )  # 单继承mro(d(o))=[d,o]
                   = [b,d] + merge( [o] , [e,o]  ,  [e] )  # 拿出并删除d
                   = [b,d,e] + merge([o] ,  [o])
                   = [b,d,e,o]
            mro(c) = mro( c(e,f) )
                   = [c] + merge( mro(e), mro(f), [e,f] )
                   = [c] + merge( [e,o] , [f,o] , [e,f] )
                   = [c,e] + merge( [o] , [f,o]  ,  [f] )  # 跳过o,拿出并删除
                   = [c,e,f] + merge([o] ,  [o])
                   = [c,e,f,o]
          原式= [a] + merge( [b,d,e,o], [c,e,f,o], [b,c])
              = [a,b] + merge( [d,e,o], [c,e,f,o],   [c])
              = [a,b,d] + merge( [e,o], [c,e,f,o],   [c])  # 跳过e
              = [a,b,d,c] + merge([e,o],  [e,f,o])
              = [a,b,d,c,e] + merge([o],    [f,o])  # 跳过o
              = [a,b,d,c,e,f] + merge([o],    [o])
              = [a,b,d,c,e,f,o]
          --------------------- 
          # python提供了简单的方法查询
          python(mro(a)) # 此时会打印出a的超级之间的排序关系