. 类与类之间的依赖关系

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

                 1. 依赖关系

                 2. 关联关系

                 3. 组合关系

                 4. 聚合关系

                 5. 继承关系

                 6. 实现关系

   先来看紧密程度最低的⼀个----依赖关系。假设要玩游戏,那么使用什么呢?现在的方式挺多的,手机电脑,PS等,都可以,但是如果没有这些工具,就玩不了这样的游戏,所以玩游戏又得依赖这些工具,但是我可以选择使用哪种工具,一种玩够了,可以换别的工具继续玩。看代码:

 

class Phone:
    def __init__(self):
        self.name = '手机'
class Computer:
    def __init__(self):
        self.name = '电脑'
class PS:
    def __init__(self):
        self.name = 'PS'
class Person:
    def play(self, type):
        print(f'使用{type.name}打游戏')

phone = Phone()
computer = Computer()
ps = PS()
p = Person()
p.play(phone)
p.play(computer)
p.play(ps)

 

      可以看到,人玩游戏时,是可以随便更换工具的,所以这种依赖关系很不稳定,也就是紧密程度很低。

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

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

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

     2. 聚合关系:属于关联关系中的⼀种特例侧重点是xxxxxx聚合成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("jerry")
b.have_a_dinner()

# 突然⽜B了. 找到⼥朋友了
g = Girl("如花")
b.girlFriend = g # 有⼥朋友了
b.have_a_dinner()

gg = Girl("李⼩花")
bb = Boy("tom", gg) # 娃娃亲. 出⽣就有⼥朋友. 服不服
bb.have_a_dinner() # 多么幸福的⼀家
# 突然.bb失恋了. 娃娃亲不跟他好了
bb.girlFriend = None
bb.have_a_dinner() # ⼜单身了

 

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

  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("北京大学", "地址111111")
s2 = School("清华大学", "地址222222")
s3 = School("哈佛大学", "地址333333")
t1 = Teacher("Tom", s1)
t2 = Teacher("Jerry", s1)
t3 = Teacher("Tony", s2)
t4 = Teacher("Peter", s3)

# 找到Peter所在的校区地址
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("北京大学", "地址111111")
s2 = School("清华大学", "地址222222")
s3 = School("哈佛大学", "地址333333")
t1 = Teacher("Tom", s1)
t2 = Teacher("Jerry", s1)
t3 = Teacher("Tony", s2)
t4 = Teacher("Peter", s3)
s1.add_teacher(t1)
s1.add_teacher(t2)
s1.add_teacher(t3)
# 查看北京大学有哪些⽼师
for t in s1.t_list:
 	print(t.name)

 

   好了这就是关联关系当我们在逻辑上出现了我需要你你还得属于我这种逻辑就是关联关系那注意这种关系的紧密程度比上⾯的依赖关系要紧密的多为什么呢? 老师没有学校,就不能教书了,学校没有了老师,就没有老师教学生了。两者是互相需要的,学校需要老师,而且这个老师也是属于这个学校的。开始的依赖关系中,我玩游戏,不用手机,还可以使用其他的电脑或者PS等。

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

. 继承关系.

      在⾯向对象的世界中存在着继承关系我们现实中也存在着这样的关系我们说过。 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都是访问这个⽅法的对象

                                                                                      

 

   分析一下上面的代码:1号线表示初始化时,把值=123给了num2号线表示obj调用func1(),但是在类Foo中没有这个方法,所以程序会去Foo的父类Base中查找,OK,找到了,同时把obj传递给了self。在func1()里打印完num的值后,又调用了func2(),谁调用的这个func2()呢?当然是self,这里的self,又是func1()被调用时,传递进来的obj,也就是Foo的对象,所以,要找func2(),也得去Foo中找,这是就近原则,也就是3号线的方向,虽然这样看起来是跑远了,但是Python就是这样规定的。很完美找到了,所以直接打印就好了。看看下面代码,自己玩玩吧。

 

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() # 那笔来吧. 算好了,可以跑一遍,看看结果。

  

. 类中的特殊成员

     什么是特殊成员呢? __init_()就是⼀个特殊的成员说⽩了带双下划线的那些, 这些⽅法在特殊的场景的时候会被⾃动的执⾏比如

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

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

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

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

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

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

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

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

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

 创建对象的真正步骤:

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

   1. 加载类

   2. 开辟内存(__new__)

   3. 初始化(__init__)

   4. 使⽤对象调用变量或者方法