1.封装
概念:封装主要是指将一些属性和相关方法封装在一个对象中,对外隐藏内部具体实现细节
作用:1)使用起来更加方便,类似于提供了一个工具箱
2)保证数据的安全(设置私有属性)
3)利于代码维护(代码维护时,直接在类内部修改即可,外部无需修改)
2.继承
2.1 继承的基本知识
- 概念:一个类"拥有"另外一个类的"资源"(指"非私有的"属性和方法)的使用权
- 作用:资源重用
- 分类:单继承(只继承了一个父类)、多继承(继承了多个父类)
- 语法: class 子类名(父类名): pass 父类名可以写多个,用逗号“,”隔开,越靠前继承的优先级越高
- 常用内置属性:__bases__ 查看类的所有父类构成元组
- object 和 type 的区别:
type:元类,是用来创建类的类
object:类对象,主要是判别新式类(继承了object类)和经典类(没继承object类), python3版本,在创建类时,默认继承object类,即:默认创建新式类
2.2 继承下的影响
2.2.1 资源的继承
在python中,继承是指对父类资源的使用权 /访问权
除了私有的属性和私有的方法, 其他的基本都能继承(共有、受保护、内置)
在子类中,不能修改父类的属性 / 方法,修改操作相当于在子类中新增一个同名属性
2.2.2 资源的使用
- 继承的几种形态以及遵循的准则
- Python3.x之后新式类的方法解析顺序(MRO):C3算法
形态 | 具体描述 | 资源查找原则 |
---|---|---|
单继承链 | 一个子类只有一个父类 | 遵循"从下到上"的原则 自身 一> 父类 一> 父类的父类 |
无重叠的多继承链 | 继承链无交叉, 无公共父类 | 遵循"单调"原则 顺着一条链找到底 A 一> B 一> D 一> C 一> E |
有重叠的多继承链 | 继承链有交叉, 有公共父类 | 遵循"从下到上"的原则 一级一级查找 A 一> B 一> C 一> D |
- 针对于几种标准原则的发展演变
- 查看某个类的资源查找顺序的语法:
法一(inspect包) | import inspect inspect.getmro(类名) |
法二(mro属性) | 类名.__mro__ |
法三(mro函数) | 类名.mro() |
2.2.3 资源的覆盖
资源覆盖包括属性的覆盖和方法重写。在MRO的资源检索链中,若优先级高的类和优先级低的类都写了一个一样的资源(属性或方法),则在获取资源时,会优先选择优先级高的类中的资源,而摒弃优先级低的资源,就造成了“覆盖”的假象
注意:如果子类方法调用了父类的方法,那么此时参数self和cls是调用这个方法的那个类/属性
2.2.4 资源的累加
概念:在一个类的基础之上, 增加一些额外的资源
- 场景一:子类相比于父类, 多一些自己特有的方法/属性
实现方式:直接在子类中定义就可
- 场景二:在被“覆盖”的方法基础之上,新增新的内容(即:保留旧的某个方法并扩展新内容)
实现方式一:在高优先级类的方法中, 通过"类名"调用低优先级类的方法
这里不能用实例调用,会导致self输入不一致
弊端:对于有重叠的多继承链,会产生重复调用;代码不易维护
class B:def __init__(self):self.weight = 60self.height = 1.75class A(B): # A类继承了B类def __init__(self):B.__init__(self) # 通过类名调用B中的init,保留原来内容# 这里不能通过其他方式调用,会导致self输入不一致self.bmi = 1 # 新增额外内容a = A()
print(a.__dict__)
实现方式二:在低优先级类的方法中, 通过"super"调用高优先级类的方法
super是一个类,它只在新式类中有效,它主要起代理作用,帮助我们沿着MRO链条,找到下一个节点,去调用对应方法
语法:super(). 方法名 在 python3.x 的版本中,不需要填写参数,会自动补全
实现原理:def super(cls参数一, inst参数二):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1] 相当于沿着参数二的mro顺序,找到参数一对应的节点+1,即参数一的下一个节点
# 继承关系 A 一> B,C 一> D
# 在这种情况下,如果使用方法一在B C中调用D,再在A中调用B C,就会重复调用D两次
# 使用super调用,就不会重复出现
class D(object):def __init__(self):print("d")class B(D):def __init__(self):super().__init__()print("b")class C(D):def __init__(self):super().__init__()print("c")class A(B,C):def __init__(self):super().__init__()print("a")A()
3.多态
- 多态大致有两种含义:
1)一个类, 所延伸的多种形态(animal可以延伸出dog cat...); 2)调用时的多种形态(dog和cat中都有同一属性/方法,但调用时输出不同)
- 多态在python中的体现:
鸭子类型(动态类型的一种风格),它的关注点在于对象的"行为和属性"; 而非对象的"类型"
- 回忆:
python是强类型(类型强势,不会轻易自动修改)、动态类型(数据的类型在运行时可以 判定,动态修改) 而多态一般是体现在静态语言中,所以在python中没有真正意义上的多态,也不需要多态
4.补充:抽象类、抽象方法
- 概念
抽象类 是指一个抽象出来的类,并不是某一个具化的类,它是一个不能直接创建实例的类, 主要是用于被其他类继承
抽象方法 是指抽象出来的一个方法,不具备具体实现,不能直接调用 如果一个类中有抽象方法,当他被继承时,继承他的类中必须要具体定义这个方法
- 例子:
animal类就是一个抽象类,他只是存储了所有动物的一些特性,但并不能直接去使用,因 为我们不知道到底是什么动物,同理animal的各种方法,如体型声音等也是如此
- 在python中的实现:
导入模块abc ——> 设置类的元类为abc.ABCMeta ——> 使用装饰器装饰抽象方法 @abc.abstractmethod
5.案例
- 要求:
1. 定义三个类, 小狗, 小猫, 人
2.三个类具体要求如下:
小狗: 姓名, 年龄(默认1岁); 吃饭, 玩, 睡觉, 看家(格式: 名字是xx, 年龄xx岁的小狗在xx)
小猫: 姓名, 年龄(默认1岁); 吃饭, 玩, 睡觉, 捉老鼠(格式: 名字是xx, 年龄xx岁的小猫在xx)
人: 姓名, 年龄(默认1岁), 宠物; 吃饭, 玩, 睡觉(格式: 名字是xx, 年龄xx岁的人在xx)
养宠物(让所有的宠物吃饭, 玩, 睡觉),
宠物工作(让所有的宠物根据自己的职责开始工作)
# 案例
# 我们发现在三个大类中,有很多相同的属性,所以我们可以先定义一个父类,让他们都继承即可class Animal():def __init__(self, name, age=1):self.name = nameself.age = agedef eat(self):print(f"{self}吃饭")def play(self):print(f"{self}玩")def sleep(self):print(f"{self}睡觉")class Person(Animal): #定义人# 在创建小狗实例时,给它设置几个属性,所以这里应该定义实例属性def __init__(self,name,pet,age=1):super(Person,self).__init__(name,age)self.pet = petdef __str__(self): # self本身的一个字符串描述 print(self)直接输出下面字符串return f"名字是{self.name}, 年龄{self.age}岁的人在"def yangPets(self):for pet in self.pet:pet.eat()pet.play()pet.sleep()def petWork(self):for pet in self.pet:pet.work()class Dog(Animal): #定义狗类# 在创建小狗实例时,给它设置几个属性,所以这里应该定义实例属性def __init__(self, name, age=1):super(Dog,self).__init__(name,age)def work(self):print(f"{self}看家")def __str__(self): # self本身的一个字符串描述 print(self)直接输出下面字符串return f"名字是{self.name}, 年龄{self.age}岁的小狗在"class Cat(Animal): #定义猫类# 在创建小猫实例时,给它设置几个属性,所以这里应该定义实例属性def __init__(self, name, age=1):super(Cat,self).__init__(name,age)def work(self):print(f"{self}捉老鼠")def __str__(self): # self本身的一个字符串描述 print(self)直接输出下面字符串return f"名字是{self.name}, 年龄{self.age}岁的小猫在"d = Dog("卡布",5)
c = Cat("喵喵",3)
p = Person("李明",[d,c],25)p.yangPets()
p.petWork()
6.总结:面向对象原则 SOLID(了解)
- S:单一职责原则(一个类只负责一项职责)
- O:开放封闭原则(对扩展开放,对修改关闭)
- L:里氏替换原则(使用父类引用的地方必须能使用子类对象)
- I:接口分离原则(如果类包含了过多的接口方法,而这些方法在使用的过程中并非"不可分 割", 那么应当把他们进行分离)
所谓接口, 在Python中, 可以简单的理解为"抽象方法 - D:依赖倒置原则(高层模块不应该直接依赖低层模,他们应该依赖抽象类或者接口块)