【python】面向对象程序设计
一 概述
编程三大范式:面向过程,函数式,面向对象
面向过程:过程指的是解决问题的步骤,即先干什么再干什么,有点像pipeline
- 优点:复杂度的问题流程化,进而简单化(一个复杂的问题,分成一个个小的步骤去实现,实现小的步骤将会非常简单)
- 缺点:一套流水线或者流程就是用来解决一个问题
- 应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等
函数式编程:
(缺少)
面向对象:对象是特征和技能的结合体
- 优点:解决了程序的扩展性,对某一个对象单独修改,会立刻反映到整个体系中
- 缺点:编程复杂度远高于面向过程
- 无法像面向过程设计一样去精准预测问题的处理过程和结果,只能由对象之间交互解决问题
- 应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等
note: 面向对象设计主要用来解决扩展性的问题
二 类与对象
类:把一类事物的相同特征和动作整合到一起就是类,类是一个抽象的概念
对象:就是基于类而创建的一个具体的事物(具体存在的),也是特征和动作整合到一起
类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类 类名.__bases__# 类所有父类构成的元组 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
__dict__ :对应数据属性和函数属性并以字典的方式返回,函数属性的话对应的是地址
其中,在python3中都是新式类,但是也可以定义经典类
1 #-------------经典类------------ 2 class module_A(object): #经典类在定义时基类中添加"object" 3 def __init__(self,name) 4 print('----->', self.name) 5 6 class module_B(module_A): 7 pass 8 9 10 #-------------新式类------------ 11 class module_A: #经典类在定义时基类中什么都没有 12 def __init__(self,name) 13 print('----->', self.name) 14 15 class module_B(module_A): 16 pass
ps. 从代码级别看面向对象
1 #1、在没有学习类这个概念时,数据与功能是分离的 2 def exc1(host,port,db,charset): 3 conn=connect(host,port,db,charset) 4 conn.execute(sql) 5 return xxx 6 7 8 def exc2(host,port,db,charset,proc_name) 9 conn=connect(host,port,db,charset) 10 conn.call_proc(sql) 11 return xxx 12 13 #每次调用都需要重复传入一堆参数 14 exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;') 15 exc2('127.0.0.1',3306,'db1','utf8','存储过程的名字') 16 17 18 19 20 #2、我们能想到的解决方法是,把这些变量都定义成全局变量 21 HOST=‘127.0.0.1’ 22 PORT=3306 23 DB=‘db1’ 24 CHARSET=‘utf8’ 25 26 def exc1(host,port,db,charset): 27 conn=connect(host,port,db,charset) 28 conn.execute(sql) 29 return xxx 30 31 32 def exc2(host,port,db,charset,proc_name) 33 conn=connect(host,port,db,charset) 34 conn.call_proc(sql) 35 return xxx 36 37 exc1(HOST,PORT,DB,CHARSET,'select * from tb1;') 38 exc2(HOST,PORT,DB,CHARSET,'存储过程的名字') 39 40 41 #3、但是2的解决方法也是有问题的,按照2的思路,我们将会定义一大堆全局变量,这些全局变量并没有做任何区分,即能够被所有功能使用,然而事实上只有HOST,PORT,DB,CHARSET是给exc1和exc2这两个功能用的。言外之意:我们必须找出一种能够将数据与操作数据的方法组合到一起的解决方法,这就是我们说的类了 42 43 class MySQLHandler: 44 def __init__(self,host,port,db,charset='utf8'): 45 self.host=host 46 self.port=port 47 self.db=db 48 self.charset=charset 49 def exc1(self,sql): 50 conn=connect(self.host,self.port,self.db,self.charset) 51 res=conn.execute(sql) 52 return res 53 54 55 def exc2(self,sql): 56 conn=connect(self.host,self.port,self.db,self.charset) 57 res=conn.call_proc(sql) 58 return res 59 60 61 obj=MySQLHandler('127.0.0.1',3306,'db1') 62 obj.exc1('select * from tb1;') 63 obj.exc2('存储过程的名字') 64 65 66 #改进 67 class MySQLHandler: 68 def __init__(self,host,port,db,charset='utf8'): 69 self.host=host 70 self.port=port 71 self.db=db 72 self.charset=charset 73 self.conn=connect(self.host,self.port,self.db,self.charset) 74 def exc1(self,sql): 75 return self.conn.execute(sql) 76 77 def exc2(self,sql): 78 return self.conn.call_proc(sql) 79 80 81 obj=MySQLHandler('127.0.0.1',3306,'db1') 82 obj.exc1('select * from tb1;') 83 obj.exc2('存储过程的名字') 84 85 数据与专门操作该数据的功能组合到一起
Note:面向对象设计并不是用class写的就是,用def写的就是函数式编程
1 学校类: 2 特征:name, addr, type 3 动作:考试,招生,开除 4 5 object-oriented design detail expression: 6 对特征而言,可以创建一个字典: 7 school_1 = {"name":"明珠", "addr":"浦东", "type":"公办院校" } 8 对动作而言,可以创建一个函数: 9 def test(school): 10 print("%s 学校正在考试" %school["name"]) 11 def enroll(school): 12 print("%s %s学校正在考试" %(school["addr"],school["name"])) 13 14 上述虽然可以创建对于学校特征和动作的描述但是,特征和动作是分离的,也就是说除了学校可以调用这些动作以外,别的事物也可以进行调用;另一方面,学校是写死的,只有school_1这么一个学校,为此对上述code进行改写: 15 def school(name, addr, type): 16 school_1 = {"name": name, 17 "addr": addr, 18 "type":type, 19 "test":test, 20 "enroll":enroll, 21 } 22 def test(school): 23 print("%s 学校正在考试" %school["name"]) 24 def enroll(school): 25 print("%s %s学校正在考试" %(school["addr"],school["name"])) 26 27 此时将学校类的动作和特征关联在一起,但引入新的问题: 28 1. test和enroll执行报错,原因是在执行school_1字典的时候先执行了test和enroll,这时没有声明 29 2. 执行该函数后没有任何打印结果,对于动作而言,只是返回了地址信息,而school_1也只是加载在内存中 30 为此进行进一步改写: 31 32 def school(name, addr, type): 33 def init(name, addr, type): 34 sch = {"name": name, 35 "addr": addr, 36 "type":type, 37 "test":test, 38 "enroll":enroll, 39 } 40 return sch 41 def test(school): 42 print("%s 学校正在考试" %school["name"]) 43 def enroll(school): 44 print("%s %s学校正在考试" %(school["addr"],school["name"])) 45 46 return init(name,addr,type) #i.e. return sch 47 48 if __name__ == "__main__": 49 s1 = school("明珠", "浦东", "公办院校") 50 print(s1["name"]) #打印特征 51 s1["test"](s1) #调用s1拥有的动作 52 此时即返回了init中sch字典的结果,这时候不会报错sch的原因,是因为只是将函数加载到内存中并没有执行,直到执行到返回值的时候才调用init函数,进而调用test和enroll函数,而这个时候,已经声明过了,所以不会报错。 53 可以看出,面向对象设计并不是用class写的就是面向对象,用def写的就是函数式编程
三 类的属性
类有两类属性:数据属性和函数属性(方法)
- 类的数据属性是所有对象共享的
- 类的函数属性是绑定给对象用的
四 实例化
类的实例化就是由类生产对象的过程
与函数不同的地方在于函数的运行代表运行函数中的很多逻辑,而类的运行就是实例化
多态:不同的类具有相同的方法, 并且由这些不同的类实例化出的实例去调用相同的方法称为多态
多态的基础是继承,多态是继承的体现方式
多态实际上是继承的实现细节
属性是"_"开头就是隐藏属性,不应该在外部调用,但是强制性调用还是可以调用的
p1.__star() ==> p1._People__star() #本质上是重命名
类的继承有两层意义:1.改变 2.扩展
而多态就是类的这两层意义的一个具体的实现机制
即,调用不同的类实例化得对象下的相同的方法,实现过程不一样
python中的标准类型就是多态概念的一个很好的示范
第一个层面的封装:类的本身就是一个封装,wrapper
第二个层面的封装:定义成私有的属性,单下划线或双下划线开头,只有类的内部使用,外部无法访问(但不是不能访问)
第三个层面的封装:明确区分内外,内部的实行逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问借口给外部使用
浙公网安备 33010602011771号