• 类与类之间的关系


    本节主要内容:

      1. 依赖关系

      2. 关联关系, 组合关系, 聚合关系

      3. 继承关系, self到底是什什么⿁鬼?

      4. 类中的特殊成员

    ⼀. 类与类之间的依赖关系   

      ⼤大千世界, 万物之间皆有规则和规律律. 我们的类和对象是对⼤大千世界中的所有事物进⾏行行归 类. 那事物之间存在着相对应的关系. 类与类之间也同样如此. 在⾯面向对象的世界中. 类与类 中存在以下关系:

      1. 依赖关系

      2. 关联关系

      3. 组合关系

      4. 聚合关系

      5. 继承关系

      6. 实现关系

      ⾸首先. 我 们先看第⼀一种, 也是这些关系中紧密程度最低的⼀一个, 依赖关系. 

      ⾸首先, 我们设计⼀一个场景.  还是最初的那个例例⼦子. 要把⼤大象装冰箱. 注意. 在这个场景中,  其 实是存在了了两种事物的. ⼀一个是⼤大象, ⼤大象负责整个事件的掌控者, 还有⼀一个是冰箱, 冰箱负 责被⼤大象操纵. ⾸首先, 写出两个类, ⼀一个是⼤大象类, ⼀一个是冰箱类. 

    class Elphant:   
         def __init__(self, name):       
             self.name = name    
        def open(self):       
             '''       
             开⻔       
            :return :return:       
             '''        
            pass    
        def close(self):
            '''        
            关⻔        
            :return :return:        
            '''        
            pass
    class Refrigerator:       
        def open_door(self):        
            print("冰箱⻔门被打开了了")       
        def close_door(self):        
            print("冰箱⻔门被关上了了")
    

      冰箱的功能非常简单, 只要会开⻔门, 关⻔门就⾏行行了了.  但是⼤大象就没那么简单了了. 想想. ⼤大象开 ⻔门和关⻔门的时候是不是要先找个冰箱啊. 然后呢? 打开冰箱⻔门. 是不是打开刚才找到的那个冰 箱⻔门.  然后装⾃自⼰己. 最后呢? 关冰箱⻔门, 注意, 关的是刚才那个冰箱吧. 也就是说. 开⻔门和关⻔门 ⽤用的是同⼀一个冰箱. 并且. ⼤大象有更更换冰箱的权利利, 想进那个冰箱就进那个冰箱. 这时, ⼤大象类 和冰箱类的关系并没有那么的紧密. 因为⼤大象可以指定任何一个冰箱.  接下来. 我们把代码完 善一下. 

    class Elphant:    
        def __init__(self, name):        
            self.name = name    
        def open(self, ref):        
            print("⼤大象要开⻔门了了. 默念三声. 开!")        
    # 由外界传递进来⼀一个冰箱, 让冰箱开⻔门. 这时. ⼤大象不不⽤用背着冰箱到处跑.        
    # 类与类之间的关系也就不那么的紧密了了. 换句话说. 只要是有open_door()⽅方法的对象. 都可以接收运⾏    
            ref.open_door()           
        def close(self, ref):        
            print("⼤大象要关⻔门了了. 默念三声. 关!")        
            pass    
        def take(self):        
            print("钻进去")
    class Refrigerator:    
        def open_door(self):        
            print("冰箱⻔门被打开了了")    
        def close_door(self):        
            print("冰箱⻔门被关上了了")
    # 造冰箱 
    r = Refrigerator() 
    # 造⼤大象 
    el = Elphant("神奇的⼤大象") 
    el.open(r)  # 注意. 此时是把⼀一个冰箱作为参数传递进去了了. 也就是说. ⼤大象可以指定任何⼀一个 冰箱. 
    el.take() 
    el.close(r)
    

      此时, 我们说, ⼤大象和冰箱之间就是依赖关系. 我⽤用着你. 但是你不属于我. 这种关系是最弱的. 比如. 公司和雇员之间. 对于正式员⼯工, 肯定要签订劳动合同. 还得⼩小⼼心伺候着. 但是如果是兼 职. 那⽆无所谓. 需要了了你就来. 不需要你就可以拜拜了了. 这⾥里里的兼职(临时⼯工) 就属于依赖关系. 我⽤用你. 但是你不属于我.

    二.  关联关系.组合关系, 聚合关系   

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

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

      2. 聚合关系. 属于关联关系中的⼀一种特例例. 侧重点是xxx和xxx聚合成xxx.  各⾃自有各⾃自的 声明周期. 比如电脑. 电脑⾥里里有CPU, 硬盘, 内存等等. 电脑挂了了. CPU还是好的. 还是 完整的个体

      3. 组合关系. 属于关联关系中的⼀一种特例例. 写法上差不多. 组合关系比聚合还要紧密. 比 如⼈人的⼤大脑, ⼼心脏, 各个器官. 这些器官组合成⼀一个⼈人. 这时. ⼈人如果挂了了. 其他的东⻄西 也跟着挂了了.  

      ⾸首先我们看关联关系: 这个最简单. 也是最常⽤用的⼀一种关系. 比如. ⼤大家都有男女朋友.  男⼈人 关联着女朋友. 女⼈人关联着男朋友. 这种关系可以是互相的, 也可以是单⽅方⾯面的. 

    class Boy:    
        def __init__(self, name, girlFriend=None):        
            self.name = name        
            self.girlFriend = girlFriend    
    def have_a_dinner(self):        
        if self.girlFriend:            
            print("%s 和 %s⼀一起去吃晚餐" % (self.name, self.girlFriend.name))        
        else:            
            print("单身狗. 吃什什么饭")
    class Girl:
        def __init__(self, name):        
            self.name = name
    
    b = Boy("alex") 
    b.have_a_dinner() 
    
    # 突然⽜牛B了了. 找到女朋友了了 
    g = Girl("如花") b.girlFriend = g # 有女朋友了了. 6666 
    b.have_a_dinner()
    
    gg = Girl("李李⼩小花") 
    bb = Boy("wusir", gg) # 娃娃亲. 出⽣生就有女朋友. 服不服 
    bb.have_a_dinner()  # 多么幸福的一家 
    
    # 突然.bb失恋了了. 娃娃亲不不跟他好了了 
    
    bb.girlFriend = None bb.have_a_dinner() # ⼜单身了.
    

       注意. 此时Boy和Girl两个类之间就是关联关系. 两个类的对象紧密练习着. 其中⼀一个没有 了了. 另一个就孤单的不得了了.  关联关系, 其实就是  我需要你. 你也属于我. 这就是关联关系. 像 这样的关系有很多很多. 比如. 学校和老师之间的关系.     

      School --- 学校   

      Teacher--- 老师

    老师必然属于一个学校. 换句句话说. 每个老师肯定有一个指定的工作机构. 就是学校. 那老师 的属性中必然关联着学校. 

    class School:    
      def __init__(self, name, address):        
        self.name = name        
        self.address = address
    class Teacher:   def __init__(self, name, school=None):     self.name = name     self.school = school
    s1 = School("北京高中", "美丽的首都") s2 = School("上海高中", "美丽的上海") s3 = School("深圳高中", "发达的深圳") t1 = Teacher("李老师", s1) t2 = Teacher("王老师", s1) t3 = Teacher("张老师", s2) t4 = Teacher("⾼老师", s3) # 找到⾼老师所在的校区地址 print(t4.school.address)

       想想, 这样的关系如果反过来. ⼀一个老师可以选⼀一个学校任职, 那反过来. ⼀一个学校有多少 老师呢? ⼀一堆吧? 这样的关系如何来描述呢? 

    class School:    
      def __init__(self, name, address):        
        self.name = name        
        self.address = address        
        self.t_list = [] # 每个学校都应该有⼀一个装⼀一堆⽼老老师的列列表    
      def add_teacher(self, teacher):        
        self.t_list.append(teacher) class Teacher:    
      def __init__(self, name, school=None):        
        self.name = name        
        self.school = school
    s1 = School("北京高中", "美丽的首都") s2 = School("上海高中", "美丽的上海") s3 = School("深圳高中", "发达的深圳")
    t1 = Teacher("李老师", s1) t2 = Teacher("王老师", s1) t3 = Teacher("张老师", s2) t4 = Teacher("高老师", s3)
    s1.add_teacher(t1) s1.add_teacher(t2) s1.add_teacher(t3) # 查看北京高中有哪些⽼老老师 for t in s1.t_list:
      print(t.name)

       好了了. 这就是关联关系. 当我们在逻辑上出现了了. 我需要你. 你还得属于我. 这种逻辑 就是关 联关系.   那注意. 这种关系的紧密程度比上⾯面的依赖关系要紧密的多. 为什什么呢? 想想吧

       至于组合关系和聚合关系. 其实代码上的差别不大. 都是把另一个类的对象作为这个类的属性来传递和保存. 只是在含义上会有些许的不同⽽而已. 

    三. 继承关系. 

      在⾯面向对象的世界中存在着继承关系. 我们现实中也存在着这样的关系. 我们说过. x是一 种y, 那x就可以继承y. 这时理解层⾯面上的. 如果上升到代码层面. 我们可以这样认为. 子类在不 影响父类的程序运⾏的基础上对父类进⾏的扩充和扩展. 这里.我们可以把⽗类被称为超类或 者基类. 子类被称为派生类.  

      首先, 类名和对象默认是可以作为字典的key的 

    class Foo:    
        def __init__(self):        
            pass    
        def method(self):        
            pass
       
    # __hash__ = None 
    
    print(hash(Foo)) 
    print(hash(Foo()))    
    

       既然可以hash. 那就是说字典的key可以是对象或者类 

    dic = {} 
    dic[Foo] = 123 
    dic[Foo()] = 456 
    print(dic) # {<class '__main__.Foo'>: 123, <__main__.Foo object at 0x103491550>: 456}
    

       虽然显⽰示的有点⼉儿诡异. 但是是可以⽤用的. 

      接下来. 我们来继续研究继承上的相关内容. 在本节中主要研究⼀一下self.  记住. 不管⽅方法之 间如何进⾏行行调⽤用. 类与类之间是何关系. 默认的self都是访问这个⽅方法的对象. 

    class Base:    
        def __init__(self, num):        
            self.num = num    
        def func1(self):        
            print(self.num)
    
    class Foo(Base):    
        pass 
    
    obj = Foo(123) 
    obj.func1() # 123  运⾏行行的是Base中的func1
    

       继续: 

    class Base:    
        def __init__(self, num):        
            self.num = num    
        def func1(self):        
            print(self.num)
    
    class Foo(Base):    
        def func1(self):        
            print("Foo. func1", self.num) 
    
    obj = Foo(123) 
    obj.func1() # Foo. func1 123  运⾏行行的是Foo中的func1
    

      再来:

    class Base:    
        def __init__(self, num):        
            self.num = num    
    
        def func1(self):        
            print(self.num)        
            self.func2()    
    
        def func2(self):        
            print("Base.func2")
    
    class Foo(Base):    
        def func2(self):        
            print("Foo.func2") 
    
    obj = Foo(123) 
    obj.func1() # 123  Foo.func2  func1是Base中的  func2是⼦子类中的
    

       总结. self在访问⽅方法的顺序:  永远先找⾃自⼰己的. ⾃自⼰己的找不到再找⽗父类的. 

       接下来. 来难的: 

    class Base:    
        def __init__(self, num):        
            self.num = num
        def func1(self):        
            print(self.num)        
            self.func2()    
        def func2(self):        
            print(111, self.num)
    
    class Foo(Base):    
        def func2(self):        
            print(222, self.num)
    
    lst = [Base(1), Base(2), Foo(3)] 
    for obj in lst:    
        obj.func2() # 111 1  |   111 2   | 222 3
    

      这个看起来还不够绕,再来

    class Base:    
        def __init__(self, num):        
            self.num = num    
        def func1(self):        
            print(self.num)        
            self.func2()    
        def func2(self):        
            print(111, self.num)
    class Foo(Base):    
        def func2(self):        
            print(222, self.num)
    lst = [Base(1), Base(2), Foo(3)] 
    for obj in lst:    
        obj.func1() # 那笔来吧. 好好算.
    

      结论: self就是你访问方法的那个对象.  先找自⼰, 然后在找父类的.

    四. 类中的特殊成员   

      什么是特殊成员呢? __init_()就是一个特殊的成员. 说白了了. 带双下划线的那一坨.  这些方 法在特殊的场景的时候会被自动的执行. 比如, 

      1. 类名() 会⾃自动执⾏行行__init__()

      2. 对象() 会⾃自动执⾏行行__call__()

      3. 对象[key] 会⾃自动执⾏行行__getitem__()

      4. 对象[key] = value 会⾃自动执⾏行行__setitem__()

      5. del 对象[key] 会⾃自动执⾏行行 __delitem__()

      6. 对象+对象 会⾃自动执⾏行行 __add__()

      7. with 对象 as 变量量 会⾃自动执⾏行行__enter__ 和__exit__

      8. 打印对象的时候 会⾃自动执⾏行行 __str__

      9. ⼲干掉可哈希  __hash__ == None  对象就不可哈希了了. 

    创建对象的真正步骤: 

      ⾸首先, 在执⾏行行类名()的时候. 系统会⾃自动先执⾏行行__new__()来开辟内存. 此时新开辟出来的内 存区域是空的. 紧随其后, 系统⾃自动调⽤用__init__()来完成对象的初始化⼯工作. 按照时间轴来算. 

      1. 加载类

      2. 开辟内存(__new__)

      3. 初始化(__init__)

      4. 使⽤用对象⼲干xxxxxxxxx

    类似的操作还有很多很多.  我们不需要完全刻意的去把所有的特殊成员全都记住. 实战中也 ⽤用不到那么多. ⽤用到了了查就是了了. 

  • 相关阅读:
    mac与phy怎样实现网络自适应
    POJ 3304 Segments(计算几何:直线与线段相交)
    iOS类目
    MFC中改变控件的大小和位置(zz)
    Delphi中根据分类数据生成树形结构的最优方法
    Delphi下EasyGrid使用体会
    Delphi中Messagedlg用法
    Delphi获取其它进程窗口句柄的3种方法
    delphi获取一个窗口的所有子窗口(包括嵌套)
    关于获取其它程序窗口编辑框内容解决思路
  • 原文地址:https://www.cnblogs.com/guchenxu/p/10146061.html
Copyright © 2020-2023  润新知