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 2 3
1 merge 不为空, 取出第一个列表列表1的表头 E, 进行判断
各个列表的表尾分别是[O], [E,F,O],E 在这些表尾的集合中, 因而跳过当前当前列表
2 取出列表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
Python 入门 之 类的三大关系(依赖 / 组合 / 继承关系)
来源: http://www.bubuko.com/infodetail-3210730.html