[Python学习日记-64] 组合
简介
继承与组合
组合的使用
简介
继承其实就是生活当中的归类,就是把对象之间的共同特征再一次提炼,然后形成一个类,但是在实际的开发当中不单单只有归类这一个动作,对象与对象之间都会有一些关系,这个我们在程序当中应该如何构建这一种关系呢?这就是我们下面要介绍的组合了。
继承与组合
在介绍组合之前我们先来回顾一下什么是继承,继承是一种在类之间建立层次关系的方式,其中一个类(子类)从另一个类(父类)继承属性和方法。子类可以扩展父类的功能,也可以重写父类的方法。继承允许子类直接访问父类的属性和方法,并且可以使用父类的实例化对象,即类(子类)与类(父类)之间的关系,用人话说就是“什么是什么的关系”;而组合是其中一个类(容器类)包含另一个类(组件类)的实例作为其属性。组合允许容器类使用组件类的功能,但容器类和组件类之间没有层次关系,即对象与对象之间的关系,用人话说就是“什么有什么的关系”。
他们两者都是实现代码重用的方式,但它们有一些区别。
组合的使用
我们下面来举一个选课的例子,代码如下
class People:
school = '清华大学'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(People):
def __init__(self,name,age,sex,level,salary):
super().__init__(name,age,sex)
self.level = level
self.salary = salary
def teach(self):
print('%s is teachin' % self.name)
class Student(People):
def __init__(self, name, age, sex, class_time):
super().__init__(name, age, sex)
self.class_time = class_time
def learn(self):
print('%s is learning' % self.name)
teacher1 = Teacher('jove',18,'male',10,3000)
student1 = Student('张三',28,'female','8:30:00')
teacher1.teach()
student1.learn()
代码输出如下:
我们运用了前面学习到的继承和派生的知识写下了这一段代码,可以看得出的确是减少了很多的重复代码,但是也存在着一些问题,例如学生和老师都是有课程这一属性吧,我在当初写代码的时候没有考虑那么多,所以没有一起写进去,那现在我们想要增加这一属性该怎么写呢?那第一时间肯定会想到在 __init__ 方法当中加入相应的属性,然后实例化对象时进行输入就可以了,如下
class People:
school = '清华大学'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(People):
def __init__(self,name,age,sex,level,salary,course_name,course_price,course_period):
super().__init__(name,age,sex)
self.level = level
self.salary = salary
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period
def teach(self):
print('%s is teaching' % self.name)
class Student(People):
def __init__(self, name, age, sex, class_time,course_name,course_price,course_period):
super().__init__(name, age, sex)
self.class_time = class_time
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period
def learn(self):
print('%s is learning' % self.name)
teacher1 = Teacher('jove',18,'male',10,3000,'python',3000,'3mons')
student1 = Student('张三',28,'female','8:30:00','python',3000,'3mons')
print(teacher1.course_name)
print(student1.course_name)
代码输出如下:
但是我们发现类与对象实例化时也出现了很多重复性的代码和参数,这是有人提出,之前使用的继承不就能解决这个重复性代码的问题吗,我们在这里也尝试一下如何?于是我们决定把新建的功能都放入一个类里面,想着用于被 Teacher 类和 Student 类来继承,如下
class People:
school = '清华大学'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(People):
def __init__(self,name,age,sex,level,salary):
super().__init__(name,age,sex)
self.level = level
self.salary = salary
def teach(self):
print('%s is teaching' % self.name)
class Student(People):
def __init__(self, name, age, sex, class_time):
super().__init__(name, age, sex)
self.class_time = class_time
def learn(self):
print('%s is learning' % self.name)
class Course:
def __init__(self,course_name,course_price,course_period):
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period
def tell_info(self):
print('课程名<%s> 课程价格<%s> 课程周期<%s>' % (self.course_name,self.course_price,self.course_period))
如果到了这一步还没有发现这个继承关系有问题,就要上去再认真看看继承中父类与子类的关系是什么了。在开头我们说过,继承中的父类和子类是“什么是什么的关系”,而我们现在能不能把老师和学生与课程之间划上什么是什么的关系呢?
这显然是不行的,例如老师是课程?学生是课程?这样一听就知道不合理了,老师和学生与课程之间应该是什么有什么的关系,例如老师有课程,学生有课程,这样一听是不是合理多了。而这个什么有什么是不是似曾相识?没错在之前说类的属性的时候也是这样介绍的,例如老师有名字,学生有年龄;而名字和年龄都是老师和学生的属性,那我们是不是只要把 Course 类进行实例化然后在赋值给 Teacher 类和 Student 类的一个新建的属性就可以了呢?
teacher1 = Teacher('jove',18,'male',10,3000)
student1 = Student('张三',28,'female','8:30:00')
python = Course('python',3000,'3mons')
teacher1.course = python
student1.course = python
teacher1.course.tell_info() # python.tell_info()
student1.course.tell_info()
print(teacher1.course.course_name)
print(student1.course.course_name)
print(teacher1.course)
print(student1.course)
print(python)
代码输出如下:
从输出来看很想然,Course 类所生成的对象与 Teacher 类和 Student 类生成的对象形成了一种绑定关系,而且所指向的都是同一个对象,这种绑定关系就是组合。以 Teacher 类为例子,组合可以把 Teacher 类和 Course 类绑定起来,而且 Teacher 类定义的对象可以使用 Course 类的属性(包括数据属性和函数属性)
在现实当中会存在多个老师教一门课和一个老师教多门课,甚至还有一个学生学多门课的现象,下面我们以学生为例来看看应该如何解决这一问题
# 一个学生学多门课
teacher1 = Teacher('jove',18,'male',10,3000)
student1 = Student('张三',28,'female','8:30:00')
python = Course('python',3000,'3mons')
linux = Course('linux',2000,'4mons')
student1.courses = []
student1.courses.append(python)
student1.courses.append(linux)
for i in student1.courses:
i.tell_info()
代码输出如下:
为了让大家更加深入了解,我们再举一个学生生日的例子来使用组合,代码如下
class Date:
def __init__(self,year,mon,day):
self.year = year
self.mon = mon
self.day = day
def tell_info(self):
print('%d-%d-%d' % (self.year,self.mon,self.day))
student1 = Student('张三',28,'female','8:30:00')
python = Course('python',3000,'3mons')
d = Date(1988,4,20)
student1.birh = d
student1.course = python
student1.birh.tell_info()
student1.course.tell_info()
代码输出如下:
总的来说,继承和组合都是实现代码重用的有效方式,但应根据具体情况选择适合的方法。如果两个类之间存在层次关系,并且子类可以扩展父类的功能,那么可以使用继承。如果两个类之间没有层次关系,但一个类需要使用另一个类的功能,那么可以使用组合。