python 面向对象
python 面向对象编程
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
- 面向过程:根据业务逻辑从上到下写垒代码
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装。
ps:Java和C#来说只是支持面向对象编程,而python比较灵活即支持面向对象编程而支持函数式编程
函数式编程与面向对象编程简单对比
def mail(email,message): print('发邮件') return True mail('11111@qq.com','msg')
面向对象编程:类,对象
class Foo: #方法 def mail(self,email,message): print('发邮件') return True
什么时候用面向对象?当某一些函数具有相同参数时,可以使用面向对象的方式,将参数值一次性的封装到对象,以后去对象中取值即可。
面向对象三大特性
封装
构造方法__init__:
class Foo: def __init__(self): print('自动执行__init__') Foo()
执行结果:
自动执行__init__
class SQLHelpe: def fetch(self,sql): print(self.hhost) print(self.uusername) print(self.pwd) print(sql) def create(self,sql): pass def remove(self,id): pass def modify(self,name): pass obj = SQLHelpe() obj.hhost = "c1.jd.com" obj.uusername = "xxx" obj.pwd = "123" obj.fetch("select * from A ")
执行结果:
c1.jd.com xxx 123 select * from A
如果有多个对象该怎么办?
class SQLHelpe: def fetch(self,sql): print(self.hhost) print(self.uusername) print(self.pwd) print(sql) def create(self,sql): pass def remove(self,id): pass def modify(self,name): pass obj = SQLHelpe() obj.hhost = "c1.jd.com" obj.uusername = "xxx" obj.pwd = "123" obj1= SQLHelpe() obj1.hhost = "c2.jd.com" obj1.uusername = "aaa" obj1.pwd = "456" obj.fetch("select * from A ") print("分割线".center(50,'-')) obj1.fetch("obj1....")
执行结果:
c1.jd.com xxx 123 select * from A -----------------------分割线------------------------ c2.jd.com aaa 456 obj1....
这时就要用到类中的构造方法__init__,说__init__之前先要了解一下self是什么鬼?
哪个对象执行方法,self就是谁
封装内容,通过self去调用
class SQLHelpe: def __init__(self,host,name,pwd): self.host = host self.name = name self.pwd =pwd def fetch(self,sql): print(self.host) print(self.name) print(self.pwd) print(sql) def create(self,sql): pass def remove(self,id): pass def modify(self,name): pass obj = SQLHelpe('www.jd.com','xxx',123) obj.fetch('select * from A ') obj1 = SQLHelpe('www.baidu.com','aaa',456) obj1.fetch('bbbbbb')
执行结果:
www.jd.com xxx 123 select * from A www.baidu.com aaa 456 bbbbbb
封装的内容也可以通过对象直接去调用
class SQLHelpe: def __init__(self,host,name,pwd): self.host = host self.name = name self.pwd =pwd def fetch(self,sql): print(self.host) print(self.name) print(self.pwd) print(sql) def create(self,sql): pass def remove(self,id): pass def modify(self,name): pass obj = SQLHelpe('www.jd.com','xxx',123) obj.fetch('select * from A ') print(obj.host,obj.name,obj.pwd)
执行结果:
www.jd.com xxx 123 select * from A www.jd.com xxx 123
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
对象中封装对象
先来简单的铺垫下
class c1: def __init__(self,name,obj): self.name = name self.obj =obj def show(self): print('c1 show') class c2: def __init__(self,name,age): self.name = name self.age = age def show(self): print('c2 show') c2_obj = c2('c2',20) c1_obj = c1('c1',c2_obj) print(c1_obj.name) print(c1_obj.obj.name) c1_obj.obj.show() c1_obj.show()
执行结果:
c1
c2
c2 show
c1 show
此时,将c2_obj作为对象封装到c1里,c1_obj是c1的对象,所以c1_obj.obj = c2_obj
总结:对象里可以封装任意类型的值
稍微复杂点,加点难度:
class c1: def __init__(self,name,obj): self.name = name self.obj =obj def show(self): print('c1 show') class c2: def __init__(self,name,age): self.name = name self.age = age def show(self): print('c2 show') class c3: def __init__(self,a1): self.money = 123 self.aaa = a1 c2_obj =c2('c2',20) c1_obj = c1('c1',c2_obj) c3_obj = c3(c1_obj) print(c3_obj.aaa.name) print(c3_obj.aaa.obj.name) print('----------') c3_obj.aaa.show() c3_obj.aaa.obj.show()
执行结果:
c1 c2 ---------- c1 show c2 show
解析:根据上面的代码,在创建c3类,将c1_obj作为对象封装到c3_obj对象中
通过c3_obj去调c1_obj,c2_obj中的值
此时,
c2_obj是c2类型,”name“=c2,age=20
c1_obj是c1类型,”name“=c1,obj=c2_obj
c3_obj是c3类型,c3_obj.aaa是c1_obj,c1_obj.obj=c2_obj,所以c3_obj.aaa.obj是c2_obj
继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
简单继承:
class F1: #父类,基类 def show(self): print('F1.show') class F2(F1): #子类,派生类 def bar(self): print('bar') f2_obj = F2() f2_obj.bar() f2_obj.show()
执行结果:
bar
F1.show
F2继承F1后,F1就是父类,F2就是子类,F1也可以叫做基类,F2也可以叫做派生类,父类子类是一对,基类和派生类是一对。
继承后,就相当于:F1的show方法在F2里又写了一遍
class F2(F1):
def show(self):
print('F1.show')
def bar(self):
print('bar')
加点难度:
class F1: def show(self): print('F1.show') def foo(self): print(self.name) class F2(F1): def __init__(self,name): self.name = name def bar(self): print('bar') def show(self): print('F2.show') class F3(F2): pass f2_obj = F2('pando') f2_obj.bar() f2_obj.show() f2_obj.foo()
执行结果:
bar
F2.show
pando
解析:F1,F2里同时有show方法,子类自己的show方法优先级高于父类的show方法
F2继承F1后,就把F1里的所有方法全部搬到F2里,所以foo方法会被执行
总结:子类继承父类后,即拥有了父类中的所有方法
在加点难度:
class S1: def f1(self): self.f2() def f2(self): print('s1-f2') class S2(S1): def f3(self): self.f1() def f2(self): print('s2-f2') obj = S2() obj.f3() aa = S1() aa.f1()
执行结果:
s2-f2
s1-f2
解析:
S1里有两个方法,f1,f2,S2里有两个方法,f3,f2,S2继承S1
创建S2对象obj,执行obj.f3方法,去S1里找f1方法,f1方法又去找f2方法
那么问题来了,是执行S1的f2方法还是S2的f2方法??
仔细想下,当然是执行S2的f2方法了,S2继承S1后,相当于把S1里的所有方法搬到S2里,而S2自己本身f2方法优先级高于S1的f2方法,所以执行S2的f2方法
总结:
继承以后,每一次找的时候,一旦涉及到self,都回到起点开始找,子类里没有就去父类里找,父类里找不到再去父父类里找 。
直白的说,就是碰到self.xx,就从起点开始找,找到为止,每次碰到self都在起点开始往上一层一层找。
单继承还不够,多继承来了...
简单的多继承
class C1: def f2(self): print('c1') class C2: def f2(self): print('c2') class C3(C2,C1): def f3(self): self.f2() obj = C3() obj.f3()
执行结果:
c2
C3继承C2,C1,找f2方法时,从左向右依次开始找,所以找到C2的f2
稍微复杂一点
class C0: def f2(self): print('c0') class C1(C0): def f999(self): print('c1') class C2: def f2(self): print('c2') class C3(C1,C2): def f3(self): self.f2() obj = C3() obj.f3()
执行结果:
c0
解析:
如图所示
C3继承C1,C2后,去找f2,从左开始找,所谓“一条道走到黑,不撞南墙不回头,如果撞了,在换一条道”,所以在C0里找到f2,如果C0里没有,在去C2里找
再复杂一点
class C5: def final(self): print('C5') class C2(C5): def c2(self): print('c2') class C4(C5): def c4(self): print('c4') class C1(C2): def c1(self): print('c1') class C3(C4): def final(self): print('c3') class C0(C1,C3): def c0000(self): self.final() obj = C0() obj.c0000()
执行结果:
c3
解析:
如图所示
C0继承C1,C3,
C1继承C2,C2继承C5
C3继承C4,C4继承C5
C0找final方法,C5,C3里都有final方法
C0从做左面的C1开始往上一层一层找,找到C2还没找到final,就不在继续找了,开始从右面的C3开始找
总结:当有共同父类时,左面的找到父类前,还没找到final的话,开始从右面开始找,直到找到为止。
注:只有Python3是这样的,Python2还分经典类和新式类,后续再讲。
继承之解析socket源码
先来点铺垫吧,如图
如图所示,
A类继承B类和C类
C类继承D类
D类继承E类
创建对象
obj=A()
obj.forever()
执行obj.forever()方法,
1.首先A()代表先执行A类的__init__方法,A类里没有,去B类里找,B类里也没有,C类里有,执行C类里的__init__方法。
2.执行完A()后,该执行obj.forever()方法,B类里没有,从C类里往上找,在E类里找到了,然后去找self.run()方法。
3.碰到self从头开始找,在从A类开始找,B类里没有,从C类开始往上找,在D类里找到,run()方法下面执行self.process()。
4.因为有self,所以再从头开始找,去B类里找,B类有process()方法,所以找到了,程序结束。
开始分析socket源码探究类的寻找过程
import socketserver
obj = socketserver.ThreadingTCPServer()
obj.serve_forever()
首先分析ThreadingTCPServer(),因为socketserver是个类,所以ThreadingTCPServer()有可能是一个函数或者是一个类。
如果ThreadingTCPServer()是一个函数则直接执行该函数,如果ThreadingTCPServer()是一个类,则直接执行该类的__init__方法。
查看源码,发现它是一个类,这个类下面没有内容,但是它继承了两个其他的类,一个是ThreadingMixIn,另一个是 TCPServer。
1.ThreadingTCPServer()这个类是一个空类,里面没有__init__方法,所以优先去ThreadingMixIn里找。
2.ThreadingMixIn也没有__init__方法,但是里面有2个方法,一个是process_request_thread,另一个是process_request。所以去下一个类TCPServer里找__init__方法。
3.这个类里有__init__方法,所以先执行__init__方法。__init__执行完毕后,就相当于obj = socketserver.ThreadingTCPServer()这段代码执行完毕,
然后去执行obj.serve_forever()这段代码。
4.执行obj.serve_forever(),还要去ThreadingMixIn和TCPServer这两个里找serve_forever()方法。左面的ThreadingMixIn类里没有这个方法,然后再去右面的TCPServer里找。
5.在TCPServer里也没有serve_forever()方法,在继续去它的父类BaseServer里找。
6.找到serve_forever(),我们看到里面有一个self._handle_request_noblock()方法,下面我们去找这个方法,碰到self,又应该从头开始找。
ThreadingMixIn里没有,我们去TCPServer里找,TCPServer里也没有,然后去它的父类BaseServer里找。
7.找到_handle_request_noblock()方法,发现里面又有一个self.process_request,此时我们发现左边还有一个process_request方法,但是不能执行
这个process_request方法,因为这个process_request方法前还self。我们又要从头开始找。
8.回到步骤1,从头开始找,发现在ThreadingMixIn里有process_request方法,所以最终执行的还是ThreadingMixIn里的process_request方法,
而不是BaseServer里的process_request方法。
9.分析结束。