• 类与类之间的关系:依赖关系和关联关系及继承关系中self是什么? 类里面的特殊成员


    类与类之间的关系


      ⼤千世界, 万物之间皆有规则和规律. 我们的类和对象是对⼤千世界中的所有事物进行归
    类. 那事物之间存在着相对应的关系. 类与类之间也同样如此. 在⾯向对象的世界中. 类与类
    中存在以下关系:
      1. 依赖关系
      2. 关联关系
      3. 组合关系
      4. 聚合关系
      5. 继承关系
      6. 实现关系
      由于python是⼀门弱类型编程语言. 并且所有的对象之间其实都是多态的关系. 也就是说,
    所有的东⻄都可以当做对象来使用. 所以我们在写代码的时候很容易形成以上关系. ⾸先. 我
    们先看第一种, 也是这些关系中紧密程度最低的一个, 依赖关系.

    # 写一个植物大战僵尸
    # 1. 植物
    #       打僵尸. 僵尸掉血
    # 2. 僵尸
    #       吃植物. 植物掉血
    
    class Plant:
        def __init__(self, name, hp, ad): # 200
            self.name = name
            self.hp = hp
            self.ad = ad
    
        def attack(self, js):
            print("植物攻击僵尸")
            js.hp -= self.ad
            print(f"僵尸掉血{self.ad}, 还剩下{js.hp}")
    
    class JiangShi:
        def __init__(self, name, hp, ad): # 1000  800
            self.name = name
            self.hp = hp
            self.ad = ad
    
        def attack(self, zw):
            print("僵尸咬植物")
            zw.hp -= self.ad
            print(f"植物掉血{self.ad}, 还剩{zw.hp}")
    
    
    # 植物
    wd = Plant("歪脖子豌豆", 10, 20)
    # 僵尸
    js = JiangShi("铁桶僵尸", 200, 1)
    wd.attack(js)
    wd.attack(js)
    wd.attack(js)
    wd.attack(js)
    wd.attack(js)
    
    js.attack(wd)
    js.attack(wd)
    js.attack(wd)
    js.attack(wd)
    # 攻击僵尸
    # 僵尸掉血20, 还剩下180
    # 植物攻击僵尸
    # 僵尸掉血20, 还剩下160
    # 植物攻击僵尸
    # 僵尸掉血20, 还剩下140
    # 植物攻击僵尸
    # 僵尸掉血20, 还剩下120
    # 植物攻击僵尸
    # 僵尸掉血20, 还剩下100
    # 僵尸咬植物
    # 植物掉血1, 还剩9
    # 僵尸咬植物
    # 植物掉血1, 还剩8
    # 僵尸咬植物
    # 植物掉血1, 还剩7
    # 僵尸咬植物
    # 植物掉血1, 还剩6

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

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

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

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

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

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


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

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

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

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

      首先我们看关联关系:

    这个最简单. 也是最常用的⼀种关系. 比如. 大家都有男女朋友. 男人

    关联着女朋友. 女人关联着男朋友. 这种关系可以是互相的, 也可以是单⽅的.

    class Boy:
    
        def __init__(self, name,  girlFriend=None):
            # 在初始化的时候可以给一个对象的属性设置成另一个类的对象
            self.girlFriend = girlFriend  # 一个男孩有一个女朋友
    
        def chi(self):
            if self.girlFriend:
                print(f"带着他的女朋友{self.girlFriend.name}去吃饭")
            else:
                print("单身狗, 吃什么吃? 滚去学习.")
    
        def movie(self):
            if self.girlFriend:
                print(f"带着他的女朋友{self.girlFriend.name}去看电影")
            else:
                print("单身狗, 看什么看? 滚去学习.")
    class Girl:
        def __init__(self, name):
            self.name = name
    
    b = Boy("宝浪")
    g = Girl("孙艺珍")
    b.chi()
    
    # alex给包浪介绍了一个女朋. 孙艺珍
    b.girlFriend = g
    b.chi()
    
    g2 = Girl("梁咏琪")
    b.girlFriend = g2 # 换了个女朋友
    b.chi()
    # 单身狗, 吃什么吃? 滚去学习.
    # 带着他的女朋友孙艺珍去吃饭
    # 带着他的女朋友梁咏琪去吃饭

      注意. 此时Boy和Girl两个类之间就是关联关系. 两个类的对象紧密练习着. 其中一个没有
    了. 另一个就孤单的不得了. 关联关系, 其实就是 我需要你. 你也属于我. 这就是关联关系. 像
    这样的关系有很多很多. 比如. 学校和老师之间的关系.
    School --- 学校
    Teacher--- 老师
    老师必然属于⼀个学校. 换句话说. 每个老师肯定有⼀个指定的工作机构. 就是学校. 那老师
    的属性中必然关联着学校.

    class School:
    
        def __init__(self, name):
            self.teach_list = [] # 这里要装多个老师
            self.name = name
    
        def zhaopin(self, teach):
            self.teach_list.append(teach)
    
        def shangke(self):
            for t in self.teach_list:
                t.work()
    
    class Teacher:
        def __init__(self, name):
            self.name = name
        def work(self):
            print(f"{self.name}在上课")
    
    lnh = School("老男孩")
    t1 = Teacher("武sir")
    t2 = Teacher("太白")
    t3 = Teacher("哪吒")
    t4 = Teacher("女神")
    t5 = Teacher("日天")
    t6 = Teacher("宝浪")
    
    lnh.zhaopin(t1)
    lnh.zhaopin(t2)
    lnh.zhaopin(t3)
    lnh.zhaopin(t4)
    lnh.zhaopin(t5)
    lnh.zhaopin(t6)
    
    lnh.shangke()
    # 武sir在上课
    # 太白在上课
    # 哪吒在上课
    # 女神在上课
    # 日天在上课
    # 宝浪在上课

      好了. 这就是关联关系. 当我们在逻辑上出现了. 我需要你. 你还得属于我. 这种逻辑 就是关
    联关系. 那注意. 这种关系的紧密程度比上面的依赖关系要紧密的多. 为什么呢? 想想吧
    至于组合关系和聚合关系. 其实代码上的差别不⼤大. 都是把另一个类的对象作为这个类的
    属性来传递和保存. 只是在含义上会有些许的不同而已.

    继承关系


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

    class Foo:
        pass
    
    
    print(hash(Foo)) # 可哈希
    ##-9223371886657599443
    
    print(hash(Foo()))
    ##150199209592
    # 我们写好的类和创建的对象默认都是可哈希的
    
    
    # 去掉可哈希
    class Foo:
        __hash__ = None # 当前类的对象不可哈希
    
    
    print(hash(Foo)) # 可哈希
    # -9223371886657599384
    
    # print(hash(Foo())) # TypeError: unhashable type: 'Foo'不可哈希
    
    
    class Foo:
        def chi(self, food):
            print("我爱吃鱼和", food)
    
    class Bar:
        def chi(self, food):
            print("我爱吃肉和", food)
    
    dic = {Foo: "鸡蛋", Bar: "香肠"}
    
    for k, v in dic.items():
        k().chi(v)
    # 我爱吃鱼和 鸡蛋
    # 我爱吃肉和 香肠
    类名和对象默认是可以作为字典的key的

      接下来. 我们来继续研究继承上的相关内容. 在本节中主要研究一下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
    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
    # 
    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

    总结. 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()
    
    # 1
    # 111 1
    # 2
    # 111 2
    # 3
    # 222 3

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

     类中的关系: 依赖关系是最轻的. 最重的是继承关系. 关联关系是比较微妙的.

    self到底是谁?
    self:谁调用的就是谁. 类型是根据调用方的对象来进行变换的
    super:表示的是父类

    类中的特殊成员


    什么是特殊成员呢? __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 对象就不可哈希了了.

     ...

    ......

    .........

     

    特殊成员:
    __init__() # 创建对象的时候初始化操作
    __call__() # 对象()
    __getitem__() # 对象[哈哈]
    __setitem__() # 对象[哈哈] = 值
    __new__() # 创建对象的时候.开辟内存
    __enter__() # with 对象
    __exit__() #结束with的时候
    __hash__() # 可哈希 hash()

    创建对象的真正步骤:
      首先, 在执行类名()的时候. 系统会自动先执行__new__()来开辟内存. 此时新开辟出来的内
    存区域是空的. 紧随其后, 系统⾃动调⽤__init__()来完成对象的初始化工作. 按照时间轴来算.
      1. 加载类
      2. 开辟内存(__new__)
      3. 初始化(__init__)
      4. 使⽤用对象⼲干xxxxxxxxx
    类似的操作还有很多很多. 我们不需要完全刻意的去把所有的特殊成员全都记住. 实战中也
    用不到那么多. 用到了查就是了.

    面向对象编程的执行流程 ->
      1. 加载类 -> 给类创建一个名称空间 -> 主要存放类变量.
      2. 创建对象 -> 先找类. -> 根据类来开辟内存 ->

      执行类中的__new__() -> 执行__init__() -> 返回对象

    class Foo:
        def __init__(self): # 初始化操作
            print("我是init,  我是老二")
            print("初始化操作. 在创建对象的时候自动调用这个方法")
    
        def __new__(cls, *args, **kwargs): # 创建, 它是真正的构造方法,  可以开辟内存
            print("我是new. 我是老大")
            return object.__new__(cls)
    
    
        # 为了 对象()
        def __call__(self, *args, **kwargs):
            print("我是对象()")
    
        # 对象[]
        def __getitem__(self, item):
            print("item=",item)
            print("你执行了__getitem__")
            return "哈哈"
    
        # 对象[key] = value
        def __setitem__(self, key, value):
            print("key, ", key)
            print("value, ", value)
    
        # del lst[1]
        def __delitem__(self, key):
            print("key=", key)
    
        # with 对象:
        def __enter__(self):
            print("我是enter")
    
        # with 对象: 代码执行完毕. 最后执行这里
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("我叫exit")
    
        def __len__(self):
            print("我的天哪")
            return 3
    
    
    f = Foo()    # 自动执行__init__()
    f()     # 调用-> __call__()
    print(callable(f)) # 对象()
    
    print(f["李嘉诚"]) # 自动调用__getitem__()
    f['jay'] = "林俊杰"
    
    del f['哈哈']
    
    with f:
        print("我是哈哈哈哈")
    
    with open() :
    
    
    lst = ["孙艺珍", "李金珠", "井柏然"]
    
    lst[2] # =>自动的调用__getitem__()
    
    
    def func():
        pass
    func = 3
    print(callable(func)) # 判断xxx是否是可调用的
    
    
    f.__init__()  # 第一次这么写. 以后别这么写
    lst = [1,2,3,4]
    it = iter(lst)
    
    print(it.__next__())
    
    print(next(it)) # __next__()
    
    面试之前翻一番
    写出15个特殊成员, 并给出具体作用
  • 相关阅读:
    1414 冰雕
    1475 建设国家(优先队列)
    Digit Division
    Sleep Buddies
    Reverse and Compare(DP)
    1536 不一样的猜数游戏
    Genealogical tree
    网站日志实时分析工具GoAccess使用
    CentOS下一键安装Openstack
    权重轮询调度算法(WeightedRound-RobinScheduling)-Java实现2
  • 原文地址:https://www.cnblogs.com/H1050676808/p/10145672.html
Copyright © 2020-2023  润新知