依赖关系:
用实例方法执行某个功能时,如果需要使用另一个类的实例的方法来完成,则称这两个类之间存在关联关系。如:
class Person():
def play(self, tools):
tools.run()
print("很开心,能玩游戏了")
class Computer():
def run(self):
print("电脑开机,可以运行")
class Phone():
def run(self):
print("手机开机,可以运行")
c = Computer()
phone = Phone()
p = Person()
p.play(phone)
p.play(c)
# 输出结果:
#
# 手机开机,可以运行
# 很开心,能玩游戏了
# 电脑开机,可以运行
# 很开心,能玩游戏了
例子定义了Person、Computer、Phone三个类,在Person类的play()方法中,可以使用Computer类或者Phone类的实例作为参数。因为在Person类方法中实现时需要Phone类实例调用方法完成,所以这两个类之间存在依赖关系。
关联关系:
一个类的属性类型是另外一个类的类型,则称这两个类之间存在关联关系。根据属性是单值或多值,关联关系分为一对一关联、一对多关联等。如:
一对一关联:
class Boy:
def __init__(self, name, girlFriend=None):
# 在初始化的时候可以给一个对象的属性设置成另一个类的对象
self.girlFriend=girlFriend # 一个男孩有一个女朋友
self.name=name
def meal(self):
if self.girlFriend:
print(f"{self.name}带着他的女朋友{self.girlFriend.name}去吃饭")
else:
print("我单身,我快乐")
class Girl:
def __init__(self, name):
self.name = name
g = Girl("小红")
b = Boy("小明",g)
b.meal()
# 输出结果:
#
# 小明带着他的女朋友小红去吃饭
例子中表示的是一对一关联,Boy类中的属性girlFriend是Girl类的实例,这两个类之间存在一对一关联关系。
一对多关联:
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("武先生")
t2 = Teacher("太白")
t3 = Teacher ("AAA")
lnh.zhaopin(t1)
lnh.zhaopin(t2)
lnh.zhaopin(t3)
lnh.shangke()
# 输出结果:
#
# 武先生在上课
# 太白在上课
# AAA在上课
例子中表示的是一对多关联,School类中的属性teach_list是Teacher类的实例集合,这两个类之间存在一对多关联关系。
继承关系:
类继承时在已有类基础上构建新类的机制,该新建类也称为子类。子类可以增加新的属性或功能,也可以继承父类的功能。继承所描述的是“is-a”的关系。
如图,猫和狗是继承者,称之为子类或者派生类;动物是被继承者,称之为父类或者超类。而波斯猫和巴厘猫又继承自猫类,对波斯猫来说,它的父类就是猫类。
例:
class People: # 定义一个父类
name = "人"
def Study(self):
print("我爱机器学习")
def __init__(self): # 父类构造函数
print("我是构造函数")
class Japan(People): # 定义一个子类
def __init__(self):
print("你是日本人?")
class China(People): # 再定义一个子类
name = "中国"
def Study(self):
print("我是中国人")
def __init__(self): # 子类构造函数
print("子类构造函数")
People.__init__(self)
class Ren(Japan, China): # 实现多继承
pass
Lisi = Japan()
Zhansan = China()
Liming = Ren()
print(Liming.name)
print(Lisi.name)
print(Zhansan.name)
Lisi.Study()
Zhansan.Study()
print(Ren.name)
# 输出结果:
#
# 你是日本人?
# 子类构造函数
# 我是构造函数
# 你是日本人?
# 中国
# 人
# 中国
# 我爱机器学习
# 我是中国人
# 中国
例子中,首先定义了一个People类,接着从该类派生出两个子类Japan和China,然后同时以这两个类为父类派生出Ren。一般来说,为防止歧义,尽量在类定义时避免多继承。
在Python语言中使用继承机制,有以下几点需要注意:
- 子类拥有父类的属性和方法
- 子类可以创建自己的属性和方法
- 子类可以对父类的方法进行覆盖实现
- 子类可以重新定义父类中的属性
- 一个父类可由多个子类继承,一个子类也可继承多个父类
- 如果父类定义了__init__()方法,子类也定义了自己的__init__()方法并且还要使用父类的__init__()方法,子类需要显示调用父类的__init__()方法。如果子类需要扩展父类的初始化行为,可以添加__init__()方法参数。
- 当继承的多个父类中有相同的属性或方法时,会使用最后一个继承父类的属性或方法。
继承机制的出现,导致父类和子类相同行为出现的可能,以及在实际运行中的动态结果,此为多态。
· 多态(从定义角度出发):同一类事物的多种形态,如猫的多种形态——白猫、黑猫、花猫等。
· 多态性(从使用角度出发):同一种调用方式,不同的执行结果。具有不同功能的函数可以使用相同的函数名,就可以用同一个函数名调用不同内容的函数。
·· 多态性依赖继承机制,并且要定义统一的接口。
在程序语言中,多态实质就是定了一个函数接口,在这个函数中定义了所有类中通用的功能。根据不同对象的相同调用接口,就得到不同的结果。如:
class Animal():
def run(self):
raise AttributeError("子类必须实现这个方法")
class Cat(Animal):
def run(self):
print("猫在跑")
class Pig(Animal):
def run(self):
print("猪在跑")
class Dog(Animal):
def run(self):
print("狗在跑")
a=Cat();b=Pig();c=Dog()
a.run();b.run();c.run()
# 输出结果:
#
# 猫在跑
# 猪在跑
# 够在跑
这种写法最大的问题就是接口不统一,有一种统一的接口写法,如:
class Animal():
def run(self):
raise AttributeError("子类必须实现这个方法")
class Cat(Animal):
def run(self):
print("猫在跑")
class Pig(Animal):
def run(self):
print("猪在跑")
class Dog(Animal):
def run(self):
print("狗在跑")
def func(n):
n.run()
a = Cat()
b = Pig()
c = Dog()
func(a)
func(b)
func(c)
# 输出结果:
#
# 猫在跑
# 猪在跑
# 够在跑
例子中演示了多态性定义和调用的两种方式。也体现了多态依赖的两个步骤:
首先,多态来自同一个父类的继承;
其次,为这些不同的子类定义相同的调用接口。
使用多态性有如下优点:
- 以不变应万变,增加程序的灵活性。不论对象千变万化,调用方式都相同。
- 增加了程序的可扩展性。对于子类来说,使用者无需更改调用代码。