Python 入门 之 类的三大关系(依赖 / 组合/ 继承关系)
在面向对象的中,类与类之间存在三种关系:依赖关系、组合关系、继承关系。
1、依赖关系:将一个类的类名或对象当做参数传递给另一个函数被使用的关系就是依赖关系
class People:
def __init__(self,name):
self.name = name
def open(self,bx):
bx.open_door(self)
def close(self,bx):
bx.close_door(self)
class Refrigerator:
def __init__(self,name):
self.name = name
def open_door(self,p):
print(f"{p.name} 打开冰箱")
def close_door(self,p):
print(f"{p.name} 关闭冰箱")
r = People("大魔") # People类实例化一个对象r
aux = Refrigerator("奥克斯") # Refrigerator类实例化一个对象aux
r.open(aux) # 将aux对象当做参数传递给r对象的open方法使用
r.close(aux) # 将aux对象当做参数传递给r对象的close方法使用
2、组合关系:将一个类的对象封装到另一个类的对象的属性中,就叫组合
class Boy:
def __init__(self,name,g):
self.name = name # self = b
self.g = g # g就是girl类实例化的一个对象内存地址
def eat(self):
print(f"{self.name}和{self.g.age}岁,且{self.g.weight}公斤的{self.g.name}py朋友.一起吃了个烛光晚餐!")
def make_keep(self):
self.g.live(f"{self.g.weight}公斤的{self.g.name}给{self.name}踩背")
class Girl:
def __init__(self,name,age,sex,weight,*args):
self.name = name
self.age = age
self.sex = sex
self.weight = weight
self.args = args
def live(self,argv):
print(f"直播内容:{argv}")
g = Girl("乔毕得",54,"女",220)
b = Boy("太博",g) # 将对象g当做属性封装到b对象的属性中
b.make_keep()
3、继承关系
(1)什么是面向对象的继承
继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。
一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。
(2)程序中 A(B)
<1> A -- 子类,派生类
<2> B -- 父类,基类,超类
当我们写多个类的时候会发现许多问题如:
class Human:
def __init__(self,name,age,sex):
self.name = name
self.sex = sex
self.age = age
def eat(self):
print("吃")
class Dog:
def __init__(self, name, age, sex):
self.name = name
self.sex = sex
self.age = age
def eat(self):
print("吃")
class Cat:
def __init__(self, name, age, sex):
self.name = name
self.sex = sex
self.age = age
def eat(self):
print("吃")
class Pig:
def __init__(self, name, age, sex):
self.name = name
self.sex = sex
self.age = age
def eat(self):
print("吃")
上述代码重复,这时我们可以简化相关代码如:
class Animal: # 父类
"""
动物类
"""
live = "活的"
def __init__(self, name, age, sex):
print("is __init__")
self.name = name
self.sex = sex
self.age = age
def eat(self): # self 是函数的位置参数
print("吃")
class Human(Animal): # 子类
pass
class Dog(Animal): # 子类
pass
class Cat(Animal): # 子类
pass
class Pig(Animal): # 子类
pass
(3)继承的优点
<1> 减少重复代码
<2> 结构清晰,规范
<3> 增加耦合性(耦合性不宜多,在精)
(4)继承的分类
<1> 单继承
<2> 多继承
Python2: python2.2 之前都是经典类,python2.2之后出现了新式类,继承object就是新式类
Python3: 只有新式类,不管你继不继承object都是新式类
(5)单继承
<1> 通过子类的类名使用父类的属性和方法
class Animal: # 父类
live = "活的"
def __init__(self, name, age, sex):
print("is __init__")
self.name = name
self.sex = sex
self.age = age
def eat(self): # self 是函数的位置参数
print("吃")
class Human(Animal): # 子类
pass
class Dog(Animal): # 子类
pass
Human.eat(12)
Human.__init__(Human,"大魔",18,"男")
print(Human.live)
print(Human.__dict__)
<2> 通过子类的对象使用父类的属性和方法
class Animal: # 父类
live = "活的"
def __init__(self, name, age, sex):
print("is __init__")
self.name = name
self.sex = sex
self.age = age
def eat(self): # self 是函数的位置参数
print("吃")
class Human(Animal): # 子类
pass
class Dog(Animal): # 子类
pass
p = Human("大魔",18,"男")
d = Dog("remmom",1,'母')
print(d.__dict__)
print(p.__dict__)
p = Human("大魔",18,"男")
print(p.live)
(6)查找顺序
<1> 不可逆(就近原则)
<2> 通过子类,类名使用父类的属性或方法(查找顺序):当前类,当前类的父类,当前类的父类的父类---->
<3> 通过子类对象使用父类的属性或者方法(查找顺序):先找对象,实例化这个对象的类,当前类的父类--->
(7)同时使用子类和父类方法或属性
<1> 方法一:不依赖(不需要)继承
class Animal: # 父类
live = "活的"
def __init__(self, name, age, sex):
# self = p的内存地址
self.name = name
self.sex = sex
self.age = age
def eat(self): # self 是函数的位置参数
print("吃")
class Human: # 子类
def __init__(self, name, age, sex, hobby):
# print(Animal.live)
# self = p的内存地址
Animal.__init__(self,name,age,sex) # 直接使用Animal类调用Animal类中的方法
self.hobby = hobby
class Dog:
def __init__(self, name, age, sex, attitude):
# self = p的内存地址
self.name = name
self.sex = sex
self.age = age
self.attitude = attitude # 与Human类进行比较
p = Human("大魔",18,"男","健身")
print(p.__dict__)
<2> 方法二:依赖(需要)继承
class Animal: # 父类
live = "活的"
def __init__(self, name, age, sex):
# self = p的内存地址
self.name = name
self.sex = sex
self.age = age
def eat(self): # self 是函数的位置参数
print("吃")
class Dog(Animal):
def __init__(self, name, age, sex, attitude):
# self = p的内存地址
# super(Dog,self).__init__(name,age,sex) # 完整写法
super().__init__(name,age,sex) # 正常写法 # 通过super方法使用父类中的方法
self.attitude = attitude
d = Dog("大魔",18,"男","忠诚")
print(d.__dict__)
练习:
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()
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()
(8)多继承
多继承是继承多个父类
多继承中, 存在着这样⼀个问题. 当两个⽗类中出现了重名⽅法的时候. 就会涉及到如何查找⽗类⽅法的这么⼀个问题.即MRO(method resolution order) 问题. 在python中这是⼀个很复杂的问题. 因为在不同的python版本中使⽤的是不同的算法来完成MRO的.
(1)经典类:多继承时从左向右执行
class A:
name = "小宝"
class B(A):
name = "太博"
class C(A):
name = "marry"
class D(B, C):
name = "魔22"
class E:
name = "魔11"
class F(E):
name = "魔"
class G(F, D):
name = "bb"
class H:
name = "aaa"
class Foo(H, G):
pass
f = Foo()
print(f.name)
# 结果为aaa
总结:
经典类:(深度优先)左侧优先,一条路走到头,找不到会回到起点向右查询
(2)新式类:采用c3算法 (也有说用广度优先的 -- 不精确)
# 下述例子在python2.7中运行
class O(object):
name = "小宝"
class D(O):
name = "天魔"
class E(O):
name = "太博"
class F(O):
name = "marry"
class B(D,E):
pass
class C(E,F):
name = "金刚"
class A(B,C):
pass
a = A()
print a.name
# 结果为 天魔
(3)c3 算法的核心 mro
<1> mro() -- python提供的可以查看多继承时的执行顺序的一种方法
<2> MRO是一个有序列表L,在类被创建时就计算出来。通用计算公式为:
mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
(其中Child继承自Base1, Base2)
如果继承至一个基类:class B(A) 这时B的mro序列为
mro( B ) = mro( B(A) )
= [B] + merge( mro(A) + [A] )
= [B] + merge( [A] + [A] )
= [B,A]
如果继承至多个基类:class B(A1, A2, A3 …) 这时B的mro序列
mro(B) = mro( B(A1, A2, A3 …) )
= [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] )
= ...
计算结果为列表,列表中至少有一个元素即类自己,如上述示例[A1,A2,A3]。merge操作是C3算法的核心。
<3> 表头和表尾
表头: 列表的第一个元素
表尾: 列表中表头以外的元素集合(可以为空)
示例 列表:[A, B, C] 表头是A,表尾是B和C
<4> 列表之间的+操作
+操作:
[A] + [B] = [A, B] (以下的计算中默认省略) ---------------------
merge操作示例:
如计算merge( [E,O], [C,E,F,O], [C] )
有三个列表 : ① ② ③
1 merge不为空,取出第一个列表列表①的表头E,进行判断
各个列表的表尾分别是[O], [E,F,O],E在这些表尾的集合中,因而跳过当前当前列表
2 取出列表②的表头C,进行判断
C不在各个列表的集合中,因而将C拿出到merge外,并从所有表头删除
merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] )
3 进行下一次新的merge操作 ......
---------------------
<5> 经典类不能使用mro , 新式类才能使用mro
天之涯,海之角,吾与money天荒地老。