面向对象编程设计与开发
代码优化和注重编程范式,两种最重要的编程范式分别是面向过程编程和面向对象编程。
什么是面向对象
emmmm……大概就是把很多东西都变成对象,然后去调用它实现功能,不用去纠结实现的过程。每个人理解不同,-。-就这吧。
面向对象有什么
类
一个类即是对一类拥有相同属性的对象的抽象化。在类中定义了这些对象共同具备的属性、共同的方法。
类的格式:
class + 类名
类的加载顺序
- 类内部一个缩进的所有代码都是在py文件从上到下解释的时候就已经被执行了。
- 包括类在内,代码永远都是从上到下依次执行。
类和对象的命名空间
类和对象存储在两块命名空间内的:
- 只要是对一个对象名字直接赋值,那么就是在这个对象的空间内创建了新的属性。
- 只要是对一个可变的数据类型内部的变化,那么仍然是所有的对象和类共享这个改变的成果。
- 所有的静态变量都是用类名来操作,这样修改就能被所有的对象感知到
- 如果是对于可变数据类型的静态变量,操作的是这个数据内部的内容,也可以使用对象来调用。
类的组合使用
一个类的对象是另一个类对象的属性,如:圆形类的对象,是圆环类对象的属性,计算圆形相关数据的公式只和圆形类在一起,其余的用到公式的地方都是通过圆形类来使用的,公式与其他类之间的关系是一个“松耦合”的关系。
紧偶合就是模块或者系统之间关系太紧密,存在相互调用。
松耦合系统通常是基于消息的系统,此时客户端和远程服务并不知道对方是如何实现的。
类的内置方法
__init__ : 构造函数,在生成对象时调用
__del__ : 析构函数,释放对象时使用
__repr__ : 打印,转换
__setitem__ : 按照索引赋值
__getitem__: 按照索引获取值
__len__: 获得长度
__cmp__: 比较运算
__call__: 函数调用
__add__: 加运算
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方
属性
即字面意思,属性,特性,特征。如所有人都是人这个类,有性别、年龄、身高这些共同具有的属性。
实例化后的实例
经过详细的属性值描述而成的事物;也为具有这些特征的事物,经过类的实例化而编程的实例。
方法
即这类事物可以具备的功能。
面向对象的三大特性
封装(Encapsulation)
封装本质就是私有化,即限制外部得到,变为私有化的。
如何私有化:
python中用双下划线开头的方式将对象变为私有。
变为私有化的特点:
- 类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
- 这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
- 在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
注意:
1. 这种私有化并非真正的限制我们从外部直接访问属性,如果知道类名,也是可以__类名__属性,然后就可以继续调用了。
2. 在继承冲,父类如果不想让子类覆盖自己的方法,可以将方法私有化。
class A: def fa(self): print('from A') def test(self): self.fa() class B(A): def fa(self): print('from B') b = B() b.test()
结果:
from B
将fa定义为私有之后的情况,即__fa:
class A: def __fa(self): print('from A') def test(self): self.__fa() class B(A): def __fa(self): print('from B') b = B() b.test()
结果:
from A
三个比较重要的装饰器:
@property:
作用:把装饰的一个方法伪装成一个属性,只让看不让改,调用时直接按属性调用方法调用。
@classmethod:
作用:把一个方法从对象方法,变成一个类方法。常用在修改类中的变量时,防止因类名改变而导致出错。
例如:修改类中的__discount值。
class Fruits: __discount = 0.8 def __init__(self, name, price): print('init', self) self.name = name self.__price = price @classmethod # 把一个方法从对象方法,变成一个类方法 def change_discount(cls, value): cls.__discount = value # cls代表当前类,当修改类名时不会对cls造成影响,代码不会出错 @classmethod def get_discount(cls): return cls.__discount print(Fruits.get_discount()) Fruits.change_discount(1) print(Fruits.get_discount())
@staticmethod:
声明这个方法只是一个普通的不会使用任何和这个类中的变量相关的方法,即静态方法。
封装的好处:
- 数据封装:将数据隐藏起来,然后对外提供操作数据的接口,并且可以在接口上附加对数据操作的限制,以此来完成对数据属性操作的严格控制。
- 隔离复杂度,同时提升了安全性。
例如ATM:
class ATM: def __card(self): print('插卡') def __auth(self): print('用户认证') def __input(self): print('输入取款金额') def __print_bill(self): print('打印账单') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a = ATM() a.withdraw()
继承(Inheritance)
概念:继承指的是类与类之间的关系,分为单继承和多继承。继承是一种创建新类的方式,新建的类称为派生类或子类。
继承分为经典类和新式类
- 只有在python2中才分新式类和经典类,python3中统一都是新式类。
- 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类。
- 在python2中,显式地声明继承object的类,以及该类的子类,都是新式类。
- 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类。
- python3中,如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
继承与重用性、派生
开发过程中,我们已经定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A相同时,只有一部分不同,这时我们选择继承A中的东西,实现代码重用,只有部分不同的地方进行派生。
代码:
class A(): @staticmethod def fight(): print('原攻击') @staticmethod def buy(): print('原购买') class B(A): @staticmethod def fight(): print('现攻击') a = B() a.fight() a.buy()
结果:
现攻击
原购买
继承的实现
python会在mro列表上查找基类,直到找到第一个匹配这个属性的类为止。这个mro列表的构造是通过一个C3线性化算法来实现的。它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
- 子类会先于父类被检查。
- 多个父类会根据它们在列表中的顺序被检查。
- 如果对下一个类存在两个合法的选择,选择第一个父类。
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 #新式类继承顺序:F->D->B->E->C->A #经典类继承顺序:F->D->B->A->E->C #python3中统一都是新式类 #pyhon2中才分新式类与经典类
抽象类
概念:抽象类是特殊的类,只能被继承,不能被实例化。
抽象类的意义:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,看以下示例。
#一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #这样大家都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
多态(Polymorphism)
在python中,处处是多态,一切皆对象,多态指的是一类事物有多种形态。
鸭子类型
python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象,也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。
接口与归一化设计
接口
概念
给使用者来调用自己功能的方式、方法或入口。
好处
- 归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
- 归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。
- 抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
- 抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
抽象类与接口(归一化设计)
type和class、元类
type
type一个对象的时候,结果总是这个对象所属的类
所有的类的类型都是type,python中任何class定义的类型其实都是type类型实例化的对象。
元类
对象是被创造出来的、被实例化出来的
类也是被创造出来的,特殊的方式来创造
常规创造的类,总有几个特性:
- 能够实例化
- 能有属性
- 能有方法
元类、能够帮助创造不同寻常的类
- 特殊的需求一:不能实例化
- 特殊的需求二:只能有一个实例
面向对象的软件开发
- 面向对象分析(object oriented analysis ,OOA)
- 面向对象设计(object oriented design,OOD)
- 面向对象编程(object oriented programming,OOP)
- 面向对象测试(object oriented test,OOT)
- 面向对象维护(object oriendted soft maintenance,OOSM)