上篇我們寫到第九條內容,接下來我們繼續(xù)撮奏。
十.抽象類
什么是抽象類:
與 java 一樣,Python 也有抽象類的概念但是同樣需要借助模塊實現(xiàn)当宴,抽象類是一個特殊的類畜吊,它的特殊之處在于只能被繼承,不能被實例化户矢。
為什么要有抽象類:
如果說類是從一堆對象中抽取相同的內容而來的玲献,那么抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。 比如我們有香蕉的類捌年,有蘋果的類瓢娜,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類礼预,你吃水果時眠砾,要么是吃一個具體的香蕉,要么是吃一個具體的桃子托酸。褒颈。。励堡。谷丸。。你永遠無法吃到一個叫做水果的東西应结。從設計角度去看刨疼,如果類是從現(xiàn)實對象抽象而來的,那么抽象類就是基于類抽象而來的鹅龄。從實現(xiàn)角度來看揩慕,抽象類與普通類的不同之處在于:抽象類中只能有抽象方法(沒有實現(xiàn)功能),該類不能被實例化砾层,只能被繼承漩绵,且子類必須實現(xiàn)抽象方法贱案。這一點與接口有點類似肛炮,但其實是不同的,即將揭曉答案
其實抽象類說直白了就是讓你們的程序更加的規(guī)范化:
1 #宝踪!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #一切皆文件
7 import abc #利用abc模塊實現(xiàn)抽象類
8
9 class All_file(metaclass=abc.ABCMeta):
10 all_type='file'
11 @abc.abstractmethod #定義抽象方法侨糟,無需實現(xiàn)功能,只要這個類中的方法中含有該裝飾器“abc.abstractmethod”,所以繼承該類的人都要重寫該方法!
12 def read(self):
13 '子類必須定義讀功能'
14 pass
15 @abc.abstractmethod #定義抽象方法诸典,無需實現(xiàn)功能
16 def write(self):
17 '子類必須定義寫功能'
18 pass
19 # class Txt(All_file):
20 # pass
21 #
22 # t1=Txt() #報錯,子類沒有定義抽象方法
23
24 class Txt(All_file): #子類繼承抽象類沫换,但是必須定義read和write方法
25 def read(self):
26 print('文本數據的讀取方法')
27
28 def write(self):
29 print('文本數據的讀取方法')
30
31 class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法
32 def read(self):
33 print('硬盤數據的讀取方法')
34
35 def write(self):
36 print('硬盤數據的讀取方法')
37
38 class Process(All_file): #子類繼承抽象類谐区,但是必須定義read和write方法
39 def read(self):
40 print('進程數據的讀取方法')
41
42 def write(self):
43 print('進程數據的讀取方法')
44
45 wenbenwenjian=Txt()
46
47 yingpanwenjian=Sata()
48
49 jinchengwenjian=Process()
50
51 #這樣大家都是被歸一化了,也就是一切皆文件的思想
52 wenbenwenjian.read()
53 yingpanwenjian.write()
54 jinchengwenjian.read()
55
56 print(wenbenwenjian.all_type)
57 print(yingpanwenjian.all_type)
58 print(jinchengwenjian.all_type)
59
60
61 #以上代碼執(zhí)行結果如下:
62 文本數據的讀取方法
63 硬盤數據的讀取方法
64 進程數據的讀取方法
65 file
66 file
67 file
- 抽象類與接口
抽象類的本質還是類,指的是一組類的相似性,包括數據屬性(如 all_type )和函數屬性(如 read凳兵、write ),而接口只強調函數屬性的相似性企软。抽象類是一個介于類和接口直接的一個概念庐扫,同時具備類和接口的部分特性,可以用來實現(xiàn)歸一化設計
十一.多態(tài)
多態(tài)性( polymorphisn )是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之后形庭,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作铅辞。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針萨醒。
那么斟珊,多態(tài)的作用是什么呢?我們知道富纸,封裝可以隱藏實現(xiàn)細節(jié)倍宾,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類)胜嗓;它們的目的都是為了——代碼重用高职。而多態(tài)則是為了實現(xiàn)另一個目的——接口重用!多態(tài)的作用辞州,就是為了類在繼承和派生的時候怔锌,保證使用“家譜”中任一類的實例的某一屬性時的正確調用。
多態(tài):我們可以這么理解变过,就是一種事物的不同形態(tài)如序列類型包含:字符串埃元,列表,元組媚狰;
多態(tài)性:一個接口函數有多種實現(xiàn)方式岛杀。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #序列內形的不同形態(tài)如下:分別有字符串崭孤,元組和列表类嗤,這就體現(xiàn)了同一種食物的不同形態(tài),簡稱多態(tài)辨宠。
7 name = "尹正杰"
8 tuple_test = ("yinzhengjie","123")
9 list_test = ["尹正杰","25","175","北京","朝陽區(qū)"]
10
11
12 #多態(tài)性:一個入口函數有多種實現(xiàn)方式
13 def fun(obj): #"obj"這個參數就體現(xiàn)多態(tài)性遗锣。原理就是傳遞進來的對象都有相同(同名)的“__len__()”方法,只不過同一個方法會執(zhí)行不同的功能而已嗤形。
14 print(obj.__len__()) #相當于len(obj)
15
16 fun(name)
17 fun(tuple_test)
18 fun(list_test)
19
20
21 #以上代碼的執(zhí)行結果如下:
22 3
23 2
24 5
1 #精偿!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #多態(tài)如下:
7 class Animal:
8 def talk(self):
9 pass
10 class People(Animal):
11 def talk(self):
12 print("say hello")
13
14 class Pig(Animal):
15 def talk(self):
16 print("say heng····heng")
17
18 class Dog(Animal):
19 def talk(self):
20 print("say wang···wang")
21
22 class Cat(Animal):
23 def talk(self):
24 print("miao~miao~miao~")
25
26 p1 = People() #這就是一個實例,其本質上就是將“p1 = People()”傳遞給“self”
27 pig1 = Pig()
28 D1 = Dog()
29 c = Cat()
30
31 #多態(tài)性:
32 def fun(obj):
33 obj.talk()
34 fun(p1)
35 fun(pig1)
36 fun(D1)
37 fun(c)
38
39 #以上代碼執(zhí)行結果如下:
40 say hello
41 say heng····heng
42 say wang···wang
43 miao~miao~miao~
1 #赋兵!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #定義多態(tài)如下:
7 class Animal(object):
8 def __init__(self, name): # Constructor of the class
9 self.name = name
10
11 def talk(self): # Abstract method, defined by convention only
12 raise NotImplementedError("Subclass must implement abstract method")
13 class Cat(Animal):
14 def talk(self):
15 print('【%s】: 喵喵喵!' % self.name)
16 class Dog(Animal):
17 def talk(self):
18 print('【%s】: 汪笔咽!汪!汪霹期!' % self.name)
19 #定義多態(tài)性:
20 def func(obj): # 一個接口叶组,多種形態(tài)
21 obj.talk()
22 c1 = Cat('hello kitty')
23 d1 = Dog('犬夜叉')
24
25 func(c1)
26 func(d1)
27
28 #以上代碼執(zhí)行結果如下:
29 【hello kitty】: 喵喵喵!
30 【犬夜叉】: 汪!汪经伙!汪扶叉!
Pyhon 很多語法都是支持多態(tài)的勿锅,比如 len(),sorted(), 你給 len 傳字符串就返回字符串的長度,傳列表就返回列表長度枣氧。
1 #溢十!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 '''
7 繼承的作用:
8 1>.直接調用父類方法;
9 2>.繼承父類方法并重構父類方法达吞,先重構张弛,在重構的方法里手動調用父類方法
10 3>.可以定義子類自己的方法;
11 4>.可以析構方法"__del__"
12 '''
13 class SchoolMember(object):
14 members = 0
15 test = 111111
16 print("in SchoolMember ")
17 def __init__(self,name,age,sex):
18 self.name = name
19 self.age = age
20 self.sex = sex
21 SchoolMember.members += 1 #注意著是調用的類變量,我們不能用"self.members"去自加喲酪劫!
22 print("初始化了一個新學校成員",self.name)
23 class Course(object): #定義一個類其實也可以不用謝初始化函數用吞鸭,只寫類變量也可以
24 test = 666666
25 print("in Course ")
26 Course_name = "python自動化"
27 period = "9m"
28 outtline = "test"
29 class Student(SchoolMember,Course): #繼承了2個父類,繼承順序優(yōu)先級由左往右依次遞減
30 def __init__(self, name, age, sex, salary): # 初始化函數
31 SchoolMember.__init__(self, name, age, sex)
32 def pay_tuition(self, amount):
33 self.paid_tuition = amount #存在實例里覆糟,方便其他函數(方法)調用刻剥。
34 print("student %s has paid tution amoint %s" % (self.name, amount))
35 s = Student("yinzhengjie","25","M","pys11000")
36 print(s.Course_name,Course.outtline)
37 print(s.test)
38
39
40 #以上代碼執(zhí)行結果如下:
41 in SchoolMember
42 in Course
43 初始化了一個新學校成員 yinzhengjie
44 python自動化 test
45 111111
十二.靜態(tài)方法和類方法
通常情況下,在類中定義的所有函數(注意了滩字,這里說的就是所有造虏,跟 self 啥的沒關系,self 也只是一個再普通不過的參數而已)都是對象的綁定方法麦箍,對象在調用綁定方法時會自動將自己作為參數傳遞給方法的第一個參數漓藕。除此之外還有兩種常見的方法:靜態(tài)方法和類方法,二者是為類量身定制的挟裂,但是實例非要使用享钞,也不會報錯,后續(xù)將介紹诀蓉。
1.靜態(tài)方法
是一種普通函數栗竖,位于類定義的命名空間中,不會對任何實例類型進行操作交排,Python 為我們內置了函數 staticmethod 來把類中的函數定義成靜態(tài)方法划滋。靜態(tài)方法既不屬于類也不屬于實例。與類只是屬于名義上的歸屬關系
class Foo:
def spam(x,y,z): #類中的一個函數埃篓,千萬不要懵逼,self和x啥的沒有不同都是參數名
print(x,y,z)
spam=staticmethod(spam) #把spam函數做成靜態(tài)方法
基于之前所學裝飾器的知識根资,@staticmethod 等同于spam=staticmethod(spam),于是
class Foo:
@staticmethod #裝飾器
def spam(x,y,z):
print(x,y,z)
使用演示
1 #架专!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 class Foo:
8 @staticmethod #裝飾器
9 def spam(x,y,z):
10 print(x,y,z)
11
12
13 print(type(Foo.spam)) #類型本質就是函數
14 Foo.spam(100,200,300) #調用函數應該有幾個參數就傳幾個參數
15
16 f1=Foo()
17 f1.spam(6,6,6) #實例也可以使用,但通常靜態(tài)方法都是給類用的,實例在使用時喪失了自動傳值的機制
18
19
20
21 #以上代碼執(zhí)行結果如下:
22 <class 'function'>
23 100 200 300
24 6 6 6
** 使用展示二:**
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 class food(object):
8 def __init__(self,name):
9 self.name = name
10 print("good boy!")
11 @staticmethod
12 def eat():
13 print("你吃了嗎玄帕?")
14
15 test = food("rice")
16 test.eat()
17 food.eat() #不用實例化就可以直接調用靜態(tài)方法部脚,所以說靜態(tài)方法既不屬于類也不屬于實例。與類只是屬于名義上的歸屬關系裤纹,兩者的唯一聯(lián)系就是在調用靜態(tài)方法(eat)的時候必須先調用該類(food).
18
19 #以上代碼執(zhí)行結果如下:
20 good boy!
21 你吃了嗎委刘?
22 你吃了嗎丧没?
應用場景:編寫類時需要采用很多不同的方式來創(chuàng)建實例,而我們只有一個init函數锡移,此時靜態(tài)方法就派上用場了
#呕童!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com
import time
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@staticmethod
def now(): #用Date.now()的形式去產生實例,該實例用的是當前時間
t=time.localtime() #獲取結構化的時間格式
return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建實例并且返回
@staticmethod
def tomorrow():#用Date.tomorrow()的形式去產生實例,該實例用的是明天的時間
t=time.localtime(time.time()+86400)
return Date(t.tm_year,t.tm_mon,t.tm_mday)
a=Date('2014',7,18) #自己定義時間
b=Date.now() #采用當前時間
c=Date.tomorrow() #采用明天的時間
print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)
#以上代碼執(zhí)行結果如下:
2014 7 18
2017 5 9
2017 5 10
2.類方法
類方法是給類用的,類在使用時會將類本身當做參數傳給類方法的第一個參數淆珊,Python 為我們內置了函數 classmethod 來把類中的函數定義成類方法夺饲,類方法屬于類不屬于實例。因為類方法能訪問類變量施符,但是無法訪問實例變量往声。
使用展示一:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7
8 class A:
9 x=1
10 @classmethod
11 def test(cls):
12 print(cls,cls.x)
13
14 class B(A):
15 x=100
16
17 B.test()
18 print(A)
19
20
21 #以上代碼執(zhí)行結果如下:
22 <class '__main__.B'> 100
23 <class '__main__.A'>
使用展示二:
1 #戳吝!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 class Man(object):
8 name = "尹正杰" #定義一個類變量
9 def __init__(self,name):
10 self.name = name #實例變量
11 print("good boy!")
12 @classmethod #類方法
13 def talk(self):
14 print("%s is talking"% self.name) #類方法只能調用class中的方法或變量浩销,但是無法調用某個方法下的實例變量。
15
16 @staticmethod #靜態(tài)方法
17 def eat():
18 print("你吃了嗎听哭?")
19
20 a = Man("yinzhengjie")
21 a.talk()
22
23
24
25
26 #以上代碼執(zhí)行結果如下:
27 good boy!
28 尹正杰 is talking
應用場景:
1 #撼嗓!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 import time
8 class Date:
9 def __init__(self,year,month,day):
10 self.year=year
11 self.month=month
12 self.day=day
13 @staticmethod
14 def now():
15 t=time.localtime()
16 return Date(t.tm_year,t.tm_mon,t.tm_mday)
17
18 class EuroDate(Date):
19 def __str__(self):
20 return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
21
22 e=EuroDate.now()
23 print(e) #我們的意圖是想觸發(fā)EuroDate.__str__,但是結果為“<__main__.Date object at 0x029F38D0>”
24
25
26
27 #以上代碼執(zhí)行結果如下:
28 <__main__.Date object at 0x029838D0>
原因:
因為 e 就是用 Date 類產生的,所以根本不會觸發(fā) EuroDate.str, 解決方法就是用 classmethod
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 import time
8 class Date:
9 def __init__(self,year,month,day):
10 self.year=year
11 self.month=month
12 self.day=day
13 # @staticmethod
14 # def now():
15 # t=time.localtime()
16 # return Date(t.tm_year,t.tm_mon,t.tm_mday)
17
18 @classmethod #改成類方法
19 def now(cls):
20 t=time.localtime()
21 return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪個類來調用,即用哪個類cls來實例化
22
23 class EuroDate(Date):
24 def __str__(self):
25 return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
26
27 e=EuroDate.now()
28 print(e) #我們的意圖是想觸發(fā)EuroDate.__str__,此時e就是由EuroDate產生的,所以會如我們所愿
29
30 '''
31 注意:
32 靜態(tài)方法和類方法雖然是給類準備的欢唾,但是如果實例去用且警,也是可以用的,只不過實例去調用的時候容易讓人混淆礁遣,不知道你要干啥
33 '''
34 x=e.now() #通過實例e去調用類方法也一樣可以使用,靜態(tài)方法也一樣
35 print(x)
36
37
38 #以上代碼執(zhí)行結果如下:
39 year:2017 month:5 day:9
40 year:2017 month:5 day:9
注意:
以上所述都是 Python 都是 class 語音上的一些特性斑芜,你可以去用它,但是不一樣要一定去用它祟霍!那么如何面向對象開發(fā)程序呢杏头?所以下面基本上是寫一個程序你需要考慮的因素,弄明白了需求才去用以上的方法去解決問題沸呐,所以下面的才是干貨醇王!必看喲~
十三.面向對象的軟件開發(fā)
很多人在學完了 Python 的 class 機制之后,遇到一個生產中的問題崭添,還是會懵逼寓娩,這其實太正常了,因為任何程序的開發(fā)都是先設計后編程呼渣,Python 的 class 機制只不過是一種編程方式棘伴,如果你硬要拿著 class 去和你的問題死磕,變得更加懵逼都是分分鐘的事屁置,在以前焊夸,軟件的開發(fā)相對簡單,從任務的分析到編寫程序蓝角,再到程序的調試阱穗,可以由一個人或一個小組去完成饭冬。但是隨著軟件規(guī)模的迅速增大,軟件任意面臨的問題十分復雜揪阶,需要考慮的因素太多昌抠,在一個軟件中所產生的錯誤和隱藏的錯誤、未知的錯誤可能達到驚人的程度遣钳,這也不是在設計階段就完全解決的扰魂。
所以軟件的開發(fā)其實一整套規(guī)范,我們所學的只是其中的一小部分蕴茴,一個完整的開發(fā)過程劝评,需要明確每個階段的任務,在保證一個階段正確的前提下再進行下一個階段的工作倦淀,稱之為軟件工程
面向對象的軟件工程包括下面幾個部:
1.面向對象分析(object oriented analysis 蒋畜,OOA)
軟件工程中的系統(tǒng)分析階段,要求分析員和用戶結合在一起撞叽,對用戶的需求做出精確的分析和明確的表述姻成,從大的方面解析軟件系統(tǒng)應該做什么,而不是怎么去做愿棋。面向對象的分析要按照面向對象的概念和方法科展,在對任務的分析中,從客觀存在的事物和事物之間的關系糠雨,貴南出有關的對象(對象的‘特征’和‘技能’)以及對象之間的聯(lián)系才睹,并將具有相同屬性和行為的對象用一個類class來標識。
建立一個能反映這是工作情況的需求模型甘邀,此時的模型是粗略的琅攘。
2.面向對象設計(object oriented design,OOD)
根據面向對象分析階段形成的需求模型松邪,對每一部分分別進行具體的設計坞琴。
首先是類的設計,類的設計可能包含多個層次(利用繼承與派生機制)逗抑。然后以這些類為基礎提出程序設計的思路和方法剧辐,包括對算法的設計。
在設計階段并不牽涉任何一門具體的計算機語言锋八,而是用一種更通用的描述工具(如偽代碼或流程圖)來描述
3.面向對象編程(object oriented programming浙于,OOP)
根據面向對象設計的結果,選擇一種計算機語言把它寫成程序挟纱,可以是 Python
4.面向對象測試(object oriented test,OOT)
在寫好程序后交給用戶使用前腐宋,必須對程序進行嚴格的測試紊服,測試的目的是發(fā)現(xiàn)程序中的錯誤并修正它檀轨。
面向對的測試是用面向對象的方法進行測試,以類作為測試的基本單元欺嗤。
5.面向對象維護(object oriendted soft maintenance参萄,OOSM)
正如對任何產品都需要進行售后服務和維護一樣,軟件在使用時也會出現(xiàn)一些問題煎饼,或者軟件商想改進軟件的性能讹挎,這就需要修改程序。
由于使用了面向對象的方法開發(fā)程序吆玖,使用程序的維護比較容易筒溃。
因為對象的封裝性,修改一個對象對其他的對象影響很小沾乘,利用面向對象的方法維護程序怜奖,大大提高了軟件維護的效率,可擴展性高翅阵。
在面向對象方法中歪玲,最早發(fā)展的肯定是面向對象編程( OOP ),那時 OOA 和OOD 都還沒有發(fā)展起來,因此程序設計者為了寫出面向對象的程序掷匠,還必須深入到分析和設計領域滥崩,尤其是設計領域,那時的 OOP 實際上包含了現(xiàn)在的 OOD 和 OOP 兩個階段讹语,這對程序設計者要求比較高钙皮,許多人感到很難掌握。
現(xiàn)在設計一個大的軟件募强,是嚴格按照面向對象軟件工程的5個階段進行的株灸,這個5個階段的工作不是由一個人從頭到尾完成的,而是由不同的人分別完成擎值,這樣OOP 階段的任務就比較簡單了慌烧。程序編寫者只需要根據 OOD 提出的思路,用面向對象語言編寫出程序既可鸠儿。
在一個大型軟件開發(fā)過程中屹蚊,OOP 只是很小的一個部分。
對于全棧開發(fā)的你來說进每,這五個階段都有了汹粤,對于簡單的問題,不必嚴格按照這個5個階段進行田晚,往往由程序設計者按照面向對象的方法進行程序設計嘱兼,包括類的設計和程序的設計
十四.小白容易犯的錯誤
1. 面向對象的程序設計看起來高大上,所以我在編程時就應該保證通篇class贤徒,這樣寫出的程序一定是好的程序(面向對象只適合那些可擴展性要求比較高的場景)
2. 很多人喜歡說面向對象三大特性(這是從哪傳出來的芹壕,封裝汇四,多態(tài),繼承踢涌?漏洞太多太多通孽,好吧暫且稱為三大特性),那么我在基于面向對象編程時睁壁,我一定要讓我定義的類中完整的包含這三種特性背苦,這樣寫肯定是好的程序
好家伙,我說降龍十八掌有十八掌潘明,那么你每次跟人干仗都要從第一掌打到第18掌這才顯得你會了是么行剂,我來一萬個人你需要打10000*18掌對么,傻叉
3. 類有類屬性钉疫,實例有實例屬性硼讽,所以我們在定義 class 時一定要定義出那么幾個類屬性,想不到怎么辦牲阁,那就使勁的想固阁,定義的越多越牛逼
這就犯了一個嚴重的錯誤,程序越早面向對象城菊,死的越早备燃,為啥面向對象,因為我們要將數據與功能結合到一起凌唬,程序整體的結構都沒有出來并齐,或者說需要考慮的問題你都沒有搞清楚個八九不離十,你就開始面向對象了客税,這就導致了况褪,你在那里干想,自以為想通了更耻,定義了一堆屬性测垛,結果后來又都用不到,或者想不通到底應該定義啥秧均,那就一直想吧食侮,想著想著就瘋了。
你見過哪家公司要開發(fā)一個軟件目胡,上來就開始寫锯七,肯定是頻繁的開會討論計劃,請看第八節(jié)
4. 既然這么麻煩誉己,那么我徹底解脫了眉尸,我們不要用面向對象編程了,你啊,你有大才效五,你能成事啊地消,傻叉炉峰。
十五.python中關于OOP的常用術語
1.抽象/實現(xiàn)
抽象指對現(xiàn)實世界問題和實體的本質表現(xiàn),行為和特征建模,建立一個相關的子集,可以用于 繪程序結構,從而實現(xiàn)這種模型畏妖。抽象不僅包括這種模型的數據屬性,還定義了這些數據的接口。
對某種抽象的實現(xiàn)就是對此數據及與之相關接口的現(xiàn)實化(realization)√劾現(xiàn)實化這個過程對于客戶 程序應當是透明而且無關的戒劫。
2.封裝/接口
封裝描述了對數據/信息進行隱藏的觀念,它對數據屬性提供接口和訪問函數。通過任何客戶端直接對數據的訪問,無視接口,與封裝性都是背道而馳的,除非程序員允許這些操作婆廊。作為實現(xiàn)的 一部分,客戶端根本就不需要知道在封裝之后,數據屬性是如何組織的迅细。在Python中,所有的類屬性都是公開的,但名字可能被“混淆”了,以阻止未經授權的訪問,但僅此而已,再沒有其他預防措施了。這就需要在設計時,對數據提供相應的接口,以免客戶程序通過不規(guī)范的操作來存取封裝的數據屬性淘邻。
注意:
封裝絕不是等于“把不想讓別人看到茵典、以后可能修改的東西用private隱藏起來”真正的封裝是,經過深入的思考宾舅,做出良好的抽象统阿,給出“完整且最小”的接口,并使得內部細節(jié)可以對外透明(注意:對外透明的意思是筹我,外部調用者可以順利的得到自己想要的任何功能扶平,完全意識不到內部細節(jié)的存在)
3.合成
合成擴充了對類的 述,使得多個不同的類合成為一個大的類,來解決現(xiàn)實問題。合成 述了 一個異常復雜的系統(tǒng),比如一個類由其它類組成,更小的組件也可能是其它的類,數據屬性及行為, 所有這些合在一起,彼此是“有一個”的關系蔬蕊。
4.派生/繼承/繼承結構
派生描述了子類衍生出新的特性,新類保留已存類類型中所有需要的數據和行為,但允許修改或者其它的自定義操作,都不會修改原類的定義结澄。
繼承描述了子類屬性從祖先類繼承這樣一種方式
繼承結構表示多“代”派生,可以述成一個“族譜”,連續(xù)的子類,與祖先類都有關系。
5.泛化/特化
基于繼承
泛化表示所有子類與其父類及祖先類有一樣的特點岸夯。
特化描述所有子類的自定義,也就是,什么屬性讓它與其祖先類不同麻献。
6.多態(tài)與多態(tài)性
多態(tài)指的是同一種事物的多種狀態(tài):水這種事物有多種不同的狀態(tài):冰,水蒸氣
多態(tài)性的概念指出了對象如何通過他們共同的屬性和動作來操作及訪問,而不需考慮他們具體的類猜扮。
冰勉吻,水蒸氣,都繼承于水破镰,它們都有一個同名的方法就是變成云餐曼,但是冰.變云(),與水蒸氣.變云()是截然不同的過程,雖然調用的方法都一樣
7.自省/反射
自省也稱作反射鲜漩,這個性質展示了某對象是如何在運行期取得自身信息的源譬。如果傳一個對象給你,你可以查出它有什么能力,這是一項強大的特性。如果 Python 不支持某種形式的自省功能,dir 和 type 內建函數,將很難正常工作孕似。還有那些特殊屬性,像 dict, name 及 doc
8.類的特殊成員方法
1>. "doc"表示類的描述信息
1 #踩娘!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7
8 class Foo:
9 """ 未成年勿進,老司機要開車了"""
10 def func(self):
11 pass
12
13 print(Foo.__doc__)
14
15
16
17 #以上代碼執(zhí)行結果如下:
18 未成年勿進,老司機要開車了
2>. module 和 class
假設存在以下目錄結構:
1 #养渴!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Name:
7 def __init__(self):
8 self.name = 'yinzhengjie'
1 #雷绢!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7
8 from lib.test import Name
9 '''
10 __module__ 表示當前操作的對象在那個模塊.
11
12 __class__ 表示當前操作的對象的類是什么.
13 '''
14
15 obj = Name()
16 print(obj.__module__) # 輸出模塊
17 print(obj.__class__) # 輸出類
18
19
20 #以上代碼執(zhí)行結果如下:
21 lib.test
22 <class 'lib.test.Name'>
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 import importlib
8 '''
9 導入模塊理卑,我們可以花式玩翘紊,請往下看。
10 '''
11
12
13 #導入方法一:這是puthon解釋器自己內部用的藐唠。
14 __import__('lib.test')
15
16 #導入方法二:與上面這局效果一樣帆疟,官方建議用這個。
17 importlib.import_module('lib.test')
3>. init 構造方法宇立,通過類創(chuàng)建對象時踪宠,自動觸發(fā)執(zhí)行。
4>. del析構方法妈嘹,當對象在內存中被釋放時柳琢,自動觸發(fā)執(zhí)行。
注:此方法一般無須定義润脸,因為 Python 是一門高級語言柬脸,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給 Python 解釋器來執(zhí)行津函,所以肖粮,析構函數的調用是由解釋器在進行垃圾回收時自動觸發(fā)執(zhí)行的
5>. call 對象后面加括號,觸發(fā)執(zhí)行尔苦。
注:構造方法的執(zhí)行是由創(chuàng)建對象觸發(fā)的涩馆,即:對象 = 類名() ;而對于 call 方法的執(zhí)行是由對象后加括號觸發(fā)的允坚,即:對象() 或者 類()()
1 #魂那!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo:
7 def __init__(self):
8 pass
9
10 def __call__(self, *args, **kwargs):
11 print("My name is yinzhengjie")
12
13 a = Foo() #對這個類進行實例化。
14 a() #調用該實例稠项。
15
16
17
18 #以上代碼執(zhí)行結果如下:
19 My name is yinzhengjie
6>. dict 查看類或對象中的所有成員
1 #涯雅!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Province:
7 country = 'China'
8
9 def __init__(self, name, count):
10 self.name = name
11 self.count = count
12
13 def func(self, *args, **kwargs):
14 print('func')
15
16
17 # 獲取類的成員,即:靜態(tài)字段展运、方法活逆、
18 print(Province.__dict__)
19
20 obj1 = Province('陜西', "1924km")
21 print(obj1.__dict__) # 獲取 對象obj1 的成員
22
23 obj2 = Province('河北', "365km")
24 print(obj2.__dict__) # 獲取 對象obj2 的成員
25
26
27
28 #以上代碼執(zhí)行結果如下:
7>. str 如果一個類中定義了str方法,那么在打印 對象 時拗胜,默認輸出該方法的返回值蔗候。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo:
7 def __str__(self):
8 return 'yinzhengjie is good boy !'
9
10
11 obj = Foo()
12 print(obj)
13
14
15
16 #以上代碼執(zhí)行結果如下:
17 yinzhengjie is good boy !
8>. getitem埂软、setitem锈遥、delitem
用于索引操作,如字典。以上分別表示獲取所灸、設置丽惶、刪除數據
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo(object):
7 def __getitem__(self, key):
8 print('__getitem__', key)
9
10 def __setitem__(self, key, value):
11 print('__setitem__', key, value)
12
13 def __delitem__(self, key):
14 print('__delitem__', key)
15
16
17 obj = Foo()
18
19 result = obj['Boy'] # 自動觸發(fā)執(zhí)行 __getitem__
20 obj['k2'] = 'Yinzhengjie' # 自動觸發(fā)執(zhí)行 __setitem__
21 del obj['123']
22
23
24 #以上代碼執(zhí)行結果如下:
25 __getitem__ Boy
26 __setitem__ k2 Yinzhengjie
27 __delitem__ 123
9>. new 和 metaclass
1 #爬立!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo(object):
7 def __init__(self, name):
8 self.name = name
9 f = Foo("yinzhengjie")
10 '''
11 注意:
12 上述代碼中钾唬,obj 是通過 Foo 類實例化的對象,其實懦尝,不僅 obj 是一個對象知纷,F(xiàn)oo類本身也是一個對象,因為在Python
13 中一切事物都是對象陵霉。如果按照一切事物都是對象的理論:obj對象是通過執(zhí)行Foo類的構造方法創(chuàng)建,那么Foo類對象應該也是
14 通過執(zhí)行某個類的 構造方法 創(chuàng)建伍绳。
15 '''
16 print(type(f))
17 print(type(Foo))
18
19
20 以上代碼執(zhí)行結果如下:
21 <class '__main__.Foo'> #輸出:<class '__main__.Foo'> 表示踊挠,obj 對象由Foo類創(chuàng)建。
22 <class 'type'> # 輸出:<type 'type'> 表示冲杀,F(xiàn)oo類對象由 type 類創(chuàng)建效床。
所以,f 對象是 Foo 類的一個實例权谁,F(xiàn)oo 類對象是 type 類的一個實例剩檀,即:Foo 類對象 是通過 type 類的構造方法創(chuàng)建。
1 #旺芽!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 #那么沪猴,創(chuàng)建類就可以有兩種方式:
8 #a).普通方式
9 class Foo(object):
10 def func(self):
11 print('hello Yinzhengjie')
12 print(Foo)
13 print(type(Foo))
14 print("*"*50,"我是分割線","*"*50)
15 #b).特殊方式
16 def func(self,name):
17 print('hello',name)
18
19 f = type('Test', (object,), {'func': func})
20 # type第一個參數:類名
21 # type第二個參數:當前類的基類,也就是第一個參數的類名的父類。
22 # type第三個參數:類的成員(將其他函數封裝進去)
23 print(f)
24 print(type(f))
25 print(dir(f)) #查看“f”類有哪些可用的方法
26 f_obj = f()
27 f_obj.func("尹正杰")
28
29
30
31
32 #以上代碼執(zhí)行結果如下:
33 <class '__main__.Foo'>
34 <class 'type'>
35 ************************************************** 我是分割線 **************************************************
36 <class '__main__.Test'>
37 <class 'type'>
38 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'func']
39 hello 尹正杰
1 #采章!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 def func(self):
8 print("hello %s"%self.name)
9
10 def __init__(self,name,age):
11 self.name = name
12 self.age = age
13 Foo = type('Foo',(object,),{'func':func,'__init__':__init__})
14
15 f = Foo("Yinzhengjie",25)
16 f.func()
17
18
19 #以上代碼執(zhí)行結果如下:
20 hello Yinzhengjie
So 运嗜,要記住類 是由 type 類實例化產生
那么問題來了,類默認是由 type 類實例化產生悯舟,type類中如何實現(xiàn)的創(chuàng)建類担租?類又是如何創(chuàng)建對象?
答:類中有一個屬性 metaclass抵怎,其用來表示該類由 誰 來實例化創(chuàng)建奋救,所以,我們可以為 metaclass 設置一個type類的派生類反惕,從而查看 類 創(chuàng)建的過程尝艘。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class MyType(type):
7 def __init__(self,*args,**kwargs):
8
9 print("Mytype __init__",*args,**kwargs)
10
11 def __call__(self, *args, **kwargs):
12 print("Mytype __call__", *args, **kwargs)
13 obj = self.__new__(self)
14 print("obj ",obj,*args, **kwargs)
15 print(self)
16 self.__init__(obj,*args, **kwargs)
17 return obj
18
19 def __new__(cls, *args, **kwargs):
20 print("Mytype __new__",*args,**kwargs)
21 return type.__new__(cls, *args, **kwargs)
22
23 print("*"*50,"我是分割線","*"*50)
24 class Foo(object,metaclass=MyType):
25
26
27 def __init__(self,name):
28 self.name = name
29
30 print("Foo __init__")
31
32 def __new__(cls, *args, **kwargs):
33 print("Foo __new__",cls, *args, **kwargs)
34 return object.__new__(cls)
35
36 f = Foo("Yinzhengjie")
37 print("對象是:",f)
38 print("尹正杰",f.name)
39
40
41 以上代碼執(zhí)行結果如下:
42 ************************************************** 我是分割線 **************************************************
43 Mytype __new__ Foo (<class 'object'>,) {'__init__': <function Foo.__init__ at 0x006C0078>, '__new__': <function Foo.__new__ at 0x006C00C0>, '__qualname__': 'Foo', '__module__': '__main__'}
44 Mytype __init__ Foo (<class 'object'>,) {'__init__': <function Foo.__init__ at 0x006C0078>, '__new__': <function Foo.__new__ at 0x006C00C0>, '__qualname__': 'Foo', '__module__': '__main__'}
45 Mytype __call__ Yinzhengjie
46 Foo __new__ <class '__main__.Foo'>
47 obj <__main__.Foo object at 0x006BBC50> Yinzhengjie
48 <class '__main__.Foo'>
49 Foo __init__
50 對象是: <__main__.Foo object at 0x006BBC50>
51 尹正杰 Yinzhengjie
類的生成 調用 順序依次是 new --> init --> call
metaclass 詳解文章:
http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
得票最高那個答案寫的非常好