一.类与类之间的依赖关系python
大千世界, 万物之间皆有规则和规律,咱们的类和对象是对大千世界中的全部事物进行归类,那事物之间存在着相对应的关系,类与类之间也一样如此,在面向对象的世界中. 类与类中存在如下关系:编程
1. 依赖关系app
2. 关联关系编程语言
3. 组合关系学习
4. 聚合关系设计
5. 继承关系code
6. 实现关系对象
因为python是一门弱类型编程语言,而且全部的对象之间其实都是多态的关系,也就是说,全部的东西均可以当作对象来使用, 因此咱们在写代码的时候很容易造成以上关系.首先,咱们先看第一种, 也是这些关系中紧密程度最低的一个, 依赖关系.继承
首先, 咱们设计一个场景,仍是最初的那个例子,要把大象装冰箱,注意,在这个场景中, 实际上是存在了两种事物的, 一个是大象, 大象负责整个事件的掌控者, 还有一个是冰箱, 冰箱负责被大象操纵.事件
首先, 写出两个类, 一个是大象类, 一个是冰箱类
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)
此时咱们说, 大象和冰箱之间就是依赖关系,我用着你,可是你不属于我, 这种关系是最弱的.好比,公司和雇员之间,对于正式员工, 确定要签定劳动合同, 还得当心伺候着,可是若是是兼职,那无所谓,须要了你就来,不须要你就能够拜拜了. 这里的兼职(临时工) 就属于依赖关系,我用你可是你不属于我.
二.关联关系.组合关系.聚合关系
其实这三个在代码上写法是同样的. 可是, 从含义上是不同的.
关联关系: 两种事物必须是互相关联的,可是在某些特殊状况下是能够更改和更换的
聚合关系: 属于关联关系中的一种特例,侧重点是xxx和xxx聚合成xxx. 各自有各自的声明周期, 好比电脑,电脑里有CPU, 硬盘, 内存等等,电脑挂了, CPU仍是好的,仍是完整的个体
组合关系: 属于关联关系中的一种特例, 写法上差很少,组合关系比聚合还要紧密,好比人的大脑, 心脏, 各个器官. 这些器官组合成一我的. 这时人若是挂了,其余的东西也跟着挂了.
首先咱们看关联关系: 这个最简单,也是最经常使用的一种关系. 好比,你们都有男女友,男人关联着女友,女人关联着男友. 这种关系能够是互相的, 也能够是单方面的.
#Python学习交流群:778463939 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 at0x103491550>: 456}
虽然显示的有点儿诡异,可是是能够用的.
接下来,咱们来继续研究继承上的相关内容. 在本节中主要研究一下self,记住,无论方法之间如何进行调用, 类与类之间是何关系, 默认的self都是访问这个方法的对象.
#Python学习交流群:778463939 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
再来,还不够绕.
#Python学习交流群:778463939 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_()
就是一 个特殊的成员,说白了,带双下划线的那一坨,这些方法在特殊的场景的时候会被自动的执行. 好比,
类名() 会自动执行__init__()
对象() 会自动执行__call__()
对象[key] 会自动执行__getitem__()
对象[key] = value 会自动执行__setitem__()
del 对象[key] 会自动执行 __delitem__()
对象+对象 会自动执行 __add__()
with 对象 as 变量 会自动执行__enter__
和__exit__
打印对象的时候 会自动执行 __str__
干掉可哈希 __hash__ == None
对象就不可哈希了.
abs(对象) 会自动执行__abs__()
bool(对象)会自动执行__bool__()
bytes(对象)会自动执行__bytes__()
float(对象)会自动执行__float__()
int(对象)会自动执行__int__()
对象.index()会自动执行__index__()
len(对象)会自动执行__len__()
next() 会自动执行__next__()
repr()会自动执行__repr__()
round(对象)会自动执行__round__()
copy.对象会自动执行__copy__()
建立对象的真正步骤:
首先, 在执行类名()的时候,系统会自动先执行__new__()
来开辟内存. 此时新开辟出来的内存区域是空的, 紧随其后, 系统自动调用__init__()
来完成对象的初始化工做,按照时间轴来算.
加载类
开辟内存(__new__
)
初始化(__init__)
使用对象干xxxxxxxxx
相似的操做还有不少不少,咱们不须要彻底刻意的去把全部的特殊成员全都记住,实战中也用不到那么多, 用到了查就是了.