一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Python之路,Day6

 macrui 2017-02-03

本節(jié)內(nèi)容:
面向?qū)ο缶幊探榻B
為什么要用面向?qū)ο筮M(jìn)行開發(fā)?
面向?qū)ο蟮奶匦裕悍庋b、繼承、多態(tài)
類、方法、

引子

你現(xiàn)在是一家游戲公司的開發(fā)人員,現(xiàn)在需要你開發(fā)一款叫做<人狗大戰(zhàn)>的游戲,你就思考呀,人狗作戰(zhàn),那至少需要2個(gè)角色,一個(gè)是人, 一個(gè)是狗,且人和狗都有不同的技能,比如人拿棍打狗, 狗可以咬人,怎么描述這種不同的角色和他們的功能呢?
你搜羅了自己掌握的所有技能,寫出了下面的代碼來描述這兩個(gè)角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def person(name,age,sex,job):
    data = {
        'name':name,
        'age':age,
        'sex':sex,
        'job':job
    }
    return data
def dog(name,dog_type):
    data = {
        'name':name,
        'type':dog_type
    }
    return data

上面兩個(gè)方法相當(dāng)于造了兩個(gè)模子,游戲開始,你得成一個(gè)人和狗的實(shí)際對象吧,怎么生成呢?

1
2
3
4
5
d1 = dog("李闖","京巴")
p1 = person("孫海濤",36,"F","運(yùn)維")
p2 = person("林海峰",27,"F","Teacher")

 

兩個(gè)角色對象生成了,狗和人還有不同的功能呀,狗會咬人,人會打狗,對不對? 怎么實(shí)現(xiàn)呢,。。想到了, 可以每個(gè)功能再寫一個(gè)函數(shù),想執(zhí)行哪個(gè)功能,直接 調(diào)用 就可以了,對不?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def bark(d):
    print("dog %s:wang.wang..wang..."%d['name'])
def walk(p):
    print("person %s is walking..." %p['name'])
d1 = dog("李闖","京巴")
p1 = person("孫海濤",36,"F","運(yùn)維")
p2 = person("林海峰",27,"F","Teacher")
walk(p1)
bark(d1)

上面的功能實(shí)現(xiàn)的簡直是完美!

但是仔細(xì)玩耍一會,你就不小心干了下面這件事

1
2
p1 = person("孫海濤",36,"F","運(yùn)維")
bark(p1) #把人的對象傳給了狗的方法

事實(shí) 上,這并沒出錯(cuò)。很顯然,人是不能調(diào)用狗的功能的,如何在代碼級別實(shí)現(xiàn)這個(gè)限制呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def person(name,age,sex,job):
    def walk(p):
        print("person %s is walking..." % p['name'])
    data = {
        'name':name,
        'age':age,
        'sex':sex,
        'job':job,
        'walk':walk
    }
    return data
def dog(name,dog_type):
    def bark(d):
        print("dog %s:wang.wang..wang..."%d['name'])
    data = {
        'name':name,
        'type':dog_type,
        'bark':bark
    }
    return data
d1 = dog("李闖","京巴")
p1 = person("孫海濤",36,"F","運(yùn)維")
p2 = person("林海峰",27,"F","Teacher")
p1 = person("孫海濤",36,"F","運(yùn)維")
d1['bark'](p1) #把人的對象傳給了狗的方法

你是如此的機(jī)智,這樣就實(shí)現(xiàn)了限制人只能用人自己的功能啦。

但,我的哥,不要高興太早,剛才你只是阻止了兩個(gè)完全 不同的角色 之前的功能混用, 但有沒有可能 ,同一個(gè)種角色,但有些屬性是不同的呢? 比如 ,大家都打過cs吧,cs里有警察和恐怖份子,但因?yàn)槎?是人, 所以你寫一個(gè)角色叫 person(), 警察和恐怖份子都 可以 互相射擊,但警察不可以殺人質(zhì),恐怖分子可以,這怎么實(shí)現(xiàn)呢? 你想了說想,說,簡單,只需要在殺人質(zhì)的功能里加個(gè)判斷,如果是警察,就不讓殺不就ok了么。 沒錯(cuò), 這雖然 解決了殺人質(zhì)的問題,但其實(shí)你會發(fā)現(xiàn),警察和恐怖分子的區(qū)別還有很多,同時(shí)又有很多共性,如果 在每個(gè)區(qū)別處都 單獨(dú)做判斷,那得累死。 

你想了想說, 那就直接寫2個(gè)角色吧, 反正 這么多區(qū)別, 我的哥, 不能寫兩個(gè)角色呀,因?yàn)樗麄冞€有很多共性 , 寫兩個(gè)不同的角色,就代表 相同的功能 也要重寫了,是不是我的哥? 。。。

好了, 話題就給你點(diǎn)到這, 再多說你的智商 也理解不了了!

 

 

  

 

  

面向過程 VS 面向?qū)ο?nbsp;

編程范式

編程是 程序 員 用特定的語法+數(shù)據(jù)結(jié)構(gòu)+算法組成的代碼來告訴計(jì)算機(jī)如何執(zhí)行任務(wù)的過程 , 一個(gè)程序是程序員為了得到一個(gè)任務(wù)結(jié)果而編寫的一組指令的集合,正所謂條條大路通羅馬,實(shí)現(xiàn)一個(gè)任務(wù)的方式有很多種不同的方式, 對這些不同的編程方式的特點(diǎn)進(jìn)行歸納總結(jié)得出來的編程方式類別,即為編程范式。 不同的編程范式本質(zhì)上代表對各種類型的任務(wù)采取的不同的解決問題的思路, 大多數(shù)語言只支持一種編程范式,當(dāng)然也有些語言可以同時(shí)支持多種編程范式。 兩種最重要的編程范式分別是面向過程編程和面向?qū)ο缶幊獭?/p>

面向過程編程(Procedural Programming)
Procedural programming uses a list of instructions to tell the computer what to do step-by-step.
面向過程編程依賴 - 你猜到了- procedures,一個(gè)procedure包含一組要被進(jìn)行計(jì)算的步驟, 面向過程又被稱為top-down languages, 就是程序從上到下一步步執(zhí)行,一步步從上到下,從頭到尾的解決問題 。基本設(shè)計(jì)思路就是程序一開始是要著手解決一個(gè)大的問題,然后把一個(gè)大問題分解成很多個(gè)小問題或子過程,這些子過程再執(zhí)行的過程再繼續(xù)分解直到小問題足夠簡單到可以在一個(gè)小步驟范圍內(nèi)解決。

舉個(gè)典型的面向過程的例子, 數(shù)據(jù)庫備份, 分三步,連接數(shù)據(jù)庫,備份數(shù)據(jù)庫,測試備份文件可用性。

代碼如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def db_conn():
    print("connecting db...")
def db_backup(dbname):
    print("導(dǎo)出數(shù)據(jù)庫...",dbname)
    print("將備份文件打包,移至相應(yīng)目錄...")
def db_backup_test():
    print("將備份文件導(dǎo)入測試庫,看導(dǎo)入是否成功")
def main():
    db_conn()
    db_backup('my_db')
    db_backup_test()
if __name__ == '__main__':
    main()

  

這樣做的問題也是顯而易見的,就是如果你要對程序進(jìn)行修改,對你修改的那部分有依賴的各個(gè)部分你都也要跟著修改, 舉個(gè)例子,如果程序開頭你設(shè)置了一個(gè)變量值 為1 , 但如果其它子過程依賴這個(gè)值 為1的變量才能正常運(yùn)行,那如果你改了這個(gè)變量,那這個(gè)子過程你也要修改,假如又有一個(gè)其它子程序依賴這個(gè)子過程 , 那就會發(fā)生一連串的影響,隨著程序越來越大, 這種編程方式的維護(hù)難度會越來越高。
所以我們一般認(rèn)為, 如果你只是寫一些簡單的腳本,去做一些一次性任務(wù),用面向過程的方式是極好的,但如果你要處理的任務(wù)是復(fù)雜的,且需要不斷迭代和維護(hù) 的, 那還是用面向?qū)ο笞罘奖懔恕?

面向?qū)ο缶幊?/h4>

OOP編程是利用“類”和“對象”來創(chuàng)建各種模型來實(shí)現(xiàn)對真實(shí)世界的描述,使用面向?qū)ο缶幊痰脑蛞环矫媸且驗(yàn)樗梢允钩绦虻木S護(hù)和擴(kuò)展變得更簡單,并且可以大大提高程序開發(fā)效率 ,另外,基于面向?qū)ο蟮某绦蚩梢允顾烁尤菀桌斫饽愕拇a邏輯,從而使團(tuán)隊(duì)開發(fā)變得更從容。

面向?qū)ο蟮膸讉€(gè)核心特性如下

Class 類
一個(gè)類即是對一類擁有相同屬性的對象的抽象、藍(lán)圖、原型。在類中定義了這些對象的都具備的屬性(variables(data))、共同的方法

Object 對象
一個(gè)對象即是一個(gè)類的實(shí)例化后實(shí)例,一個(gè)類必須經(jīng)過實(shí)例化后方可在程序中調(diào)用,一個(gè)類可以實(shí)例化多個(gè)對象,每個(gè)對象亦可以有不同的屬性,就像人類是指所有人,每個(gè)人是指具體的對象,人與人之前有共性,亦有不同

Encapsulation 封裝
在類中對數(shù)據(jù)的賦值、內(nèi)部調(diào)用對外部用戶是透明的,這使類變成了一個(gè)膠囊或容器,里面包含著類的數(shù)據(jù)和方法

Inheritance 繼承
一個(gè)類可以派生出子類,在這個(gè)父類里定義的屬性、方法自動被子類繼承

Polymorphism 多態(tài)
多態(tài)是面向?qū)ο蟮闹匾匦?簡單點(diǎn)說:“一個(gè)接口,多種實(shí)現(xiàn)”,指一個(gè)基類中派生出了不同的子類,且每個(gè)子類在繼承了同樣的方法名的同時(shí)又對父類的方法做了不同的實(shí)現(xiàn),這就是同一種事物表現(xiàn)出的多種形態(tài)。
編程其實(shí)就是一個(gè)將具體世界進(jìn)行抽象化的過程,多態(tài)就是抽象化的一種體現(xiàn),把一系列具體事物的共同點(diǎn)抽象出來, 再通過這個(gè)抽象的事物, 與不同的具體事物進(jìn)行對話。
對不同類的對象發(fā)出相同的消息將會有不同的行為。比如,你的老板讓所有員工在九點(diǎn)鐘開始工作, 他只要在九點(diǎn)鐘的時(shí)候說:“開始工作”即可,而不需要對銷售人員說:“開始銷售工作”,對技術(shù)人員說:“開始技術(shù)工作”, 因?yàn)椤皢T工”是一個(gè)抽象的事物, 只要是員工就可以開始工作,他知道這一點(diǎn)就行了。至于每個(gè)員工,當(dāng)然會各司其職,做各自的工作。
多態(tài)允許將子類的對象當(dāng)作父類的對象使用,某父類型的引用指向其子類型的對象,調(diào)用的方法是該子類型的方法。這里引用和調(diào)用方法的代碼編譯前就已經(jīng)決定了,而引用所指向的對象可以在運(yùn)行期間動態(tài)綁定

面向?qū)ο缶幊?Object-Oriented Programming )介紹

對于編程語言的初學(xué)者來講,OOP不是一個(gè)很容易理解的編程方式,大家雖然都按老師講的都知道OOP的三大特性是繼承、封裝、多態(tài),并且大家也都知道了如何定義類、方法等面向?qū)ο蟮某S谜Z法,但是一到真正寫程序的時(shí)候,還是很多人喜歡用函數(shù)式編程來寫代碼,特別是初學(xué)者,很容易陷入一個(gè)窘境就是“我知道面向?qū)ο?,我也會寫類,但我依然沒發(fā)現(xiàn)在使用了面向?qū)ο蠛?,對我們的程序開發(fā)效率或其它方面帶來什么好處,因?yàn)槲沂褂煤瘮?shù)編程就可以減少重復(fù)代碼并做到程序可擴(kuò)展了,為啥子還用面向?qū)ο??”?對于此,我個(gè)人覺得原因應(yīng)該還是因?yàn)槟銢]有充分了解到面向?qū)ο竽軒淼暮锰帲裉煳揖蛯懸黄P(guān)于面向?qū)ο蟮娜腴T文章,希望能幫大家更好的理解和使用面向?qū)ο缶幊獭? 
無論用什么形式來編程,我們都要明確記住以下原則:
  1. 寫重復(fù)代碼是非常不好的低級行為
  2. 你寫的代碼需要經(jīng)常變更 
開發(fā)正規(guī)的程序跟那種寫個(gè)運(yùn)行一次就扔了的小腳本一個(gè)很大不同就是,你的代碼總是需要不斷的更改,不是修改bug就是添加新功能等,所以為了日后方便程序的修改及擴(kuò)展,你寫的代碼一定要遵循易讀、易改的原則(專業(yè)數(shù)據(jù)叫可讀性好、易擴(kuò)展)。
如果你把一段同樣的代碼復(fù)制、粘貼到了程序的多個(gè)地方以實(shí)現(xiàn)在程序的各個(gè)地方調(diào)用 這個(gè)功能,那日后你再對這個(gè)功能進(jìn)行修改時(shí),就需要把程序里多個(gè)地方都改一遍,這種寫程序的方式是有問題的,因?yàn)槿绻悴恍⌒穆┑袅艘粋€(gè)地方?jīng)]改,那可能會導(dǎo)致整個(gè)程序的運(yùn)行都 出問題。 因此我們知道 在開發(fā)中一定要努力避免寫重復(fù)的代碼,否則就相當(dāng)于給自己再挖坑。
還好,函數(shù)的出現(xiàn)就能幫我們輕松的解決重復(fù)代碼的問題,對于需要重復(fù)調(diào)用的功能,只需要把它寫成一個(gè)函數(shù),然后在程序的各個(gè)地方直接調(diào)用這個(gè)函數(shù)名就好了,并且當(dāng)需要修改這個(gè)功能時(shí),只需改函數(shù)代碼,然后整個(gè)程序就都更新了。
其實(shí)OOP編程的主要作用也是使你的代碼修改和擴(kuò)展變的更容易,那么小白要問了,既然函數(shù)都能實(shí)現(xiàn)這個(gè)需求了,還要OOP干毛線用呢? 呵呵,說這話就像,古時(shí)候,人們打仗殺人都用刀,后來出來了槍,它的主要功能跟刀一樣,也是殺人,然后小白就問,既然刀能殺人了,那還要槍干毛線,哈哈,顯而易見,因?yàn)闃屇芨酶旄菀椎臍⑷?。函?shù)編程與OOP的主要區(qū)別就是OOP可以使程序更加容易擴(kuò)展和易更改。
小白說,我讀書少,你別騙我,口說無憑,證明一下,好吧,那我們就下面的例子證明給小白看。 
相信大家都打過CS游戲吧,我們就自己開發(fā)一個(gè)簡單版的CS來玩一玩。 
暫不考慮開發(fā)場地等復(fù)雜的東西,我們先從人物角色下手, 角色很簡單,就倆個(gè),恐怖份子、警察,他們除了角色不同,其它基本都 一樣,每個(gè)人都有生命值、武器等。 咱們先用非OOP的方式寫出游戲的基本角色 
1
2
3
4
5
6
7
8
9
10
11
#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100
#rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100

上面定義了一個(gè)恐怖份子Alex和一個(gè)警察Jack,但只2個(gè)人不好玩呀,一干就死了,沒意思,那我們再分別一個(gè)恐怖分子和警察吧,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100
money = 10000
#rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100
money2 = 10000
#role 3
name3 = 'Rain'
role3 = 'terrorist'
weapon3 = 'C33'
life_value3 = 100
money3 = 10000
#rolw 4
name4 = 'Eric'
role4 = 'police'
weapon4 = 'B51'
life_value4 = 100
money4 = 10000

4個(gè)角色雖然創(chuàng)建好了,但是有個(gè)問題就是,每創(chuàng)建一個(gè)角色,我都要單獨(dú)命名,name1,name2,name3,name4…,后面的調(diào)用的時(shí)候這個(gè)變量名你還都得記著,要是再讓多加幾個(gè)角色,估計(jì)調(diào)用時(shí)就很容易弄混啦,所以我們想一想,能否所有的角色的變量名都是一樣的,但調(diào)用的時(shí)候又能區(qū)分開分別是誰? 
當(dāng)然可以,我們只需要把上面的變量改成字典的格式就可以啦。 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
roles = {
    1:{'name':'Alex',
       'role':'terrorist',
       'weapon':'AK47',
       'life_value': 100,
       'money': 15000,
       },
    2:{'name':'Jack',
       'role':'police',
       'weapon':'B22',
       'life_value': 100,
        'money': 15000,
       },
    3:{'name':'Rain',
       'role':'terrorist',
       'weapon':'C33',
       'life_value': 100,
       'money': 15000,
       },
    4:{'name':'Eirc',
       'role':'police',
       'weapon':'B51',
       'life_value': 100,
       'money': 15000,
       },
}
print(roles[1]) #Alex
print(roles[2]) #Jack

很好,這個(gè)以后調(diào)用這些角色時(shí)只需要roles[1],roles[2]就可以啦,角色的基本屬性設(shè)計(jì)完了后,我們接下來為每個(gè)角色開發(fā)以下幾個(gè)功能
  1. 被打中后就會掉血的功能
  2. 開槍功能
  3. 換子彈
  4. 買槍
  5. 跑、走、跳、下蹲等動作
  6. 保護(hù)人質(zhì)(僅適用于警察)
  7. 不能殺同伴
  8. 。。。
我們可以把每個(gè)功能寫成一個(gè)函數(shù),類似如下: 
1
2
3
4
5
6
7
8
9
10
11
def shot(by_who):
    #開了槍后要減子彈數(shù)
    pass
def got_shot(who):
    #中槍后要減血
    who[‘life_value’] -= 10
    pass
def buy_gun(who,gun_name):
    #檢查錢夠不夠,買了槍后要扣錢
    pass
...

so far so good, 繼續(xù)按照這個(gè)思路設(shè)計(jì),再完善一下代碼,游戲的簡單版就出來了,但是在往下走之前,我們來看看上面的這種代碼寫法有沒有問題,至少從上面的代碼設(shè)計(jì)中,我看到以下幾點(diǎn)缺陷:
  1. 每個(gè)角色定義的屬性名稱是一樣的,但這種命名規(guī)則是我們自己約定的,從程序上來講,并沒有進(jìn)行屬性合法性檢測,也就是說role 1定義的代表武器的屬性是weapon, role 2 ,3,4也是一樣的,不過如果我在新增一個(gè)角色時(shí)不小心把weapon 寫成了wepon , 這個(gè)程序本身是檢測 不到的
  2. terrorist 和police這2個(gè)角色有些功能是不同的,比如police是不能殺人質(zhì)的,但是terrorist可能,隨著這個(gè)游戲開發(fā)的更復(fù)雜,我們會發(fā)現(xiàn)這2個(gè)角色后續(xù)有更多的不同之處, 但現(xiàn)在的這種寫法,我們是沒辦法 把這2個(gè)角色適用的功能區(qū)分開來的,也就是說,每個(gè)角色都可以直接調(diào)用任意功能,沒有任何限制。
  3. 我們在上面定義了got_shot()后要減血,也就是說減血這個(gè)動作是應(yīng)該通過被擊中這個(gè)事件來引起的,我們調(diào)用get_shot(),got_shot()這個(gè)函數(shù)再調(diào)用每個(gè)角色里的life_value變量來減血。 但其實(shí)我不通過got_shot(),直接調(diào)用角色roles[role_id][‘life_value’] 減血也可以呀,但是如果這樣調(diào)用的話,那可以就是簡單粗暴啦,因?yàn)闇p血之前其它還應(yīng)該判斷此角色是否穿了防彈衣等,如果穿了的話,傷害值肯定要減少,got_shot()函數(shù)里就做了這樣的檢測,你這里直接繞過的話,程序就亂了。 因此這里應(yīng)該設(shè)計(jì) 成除了通過got_shot(),其它的方式是沒有辦法給角色減血的,不過在上面的程序設(shè)計(jì)里,是沒有辦法實(shí)現(xiàn)的。 
  4. 現(xiàn)在需要給所有角色添加一個(gè)可以穿防彈衣的功能,那很顯然你得在每個(gè)角色里放一個(gè)屬性來存儲此角色是否穿 了防彈衣,那就要更改每個(gè)角色的代碼,給添加一個(gè)新屬性,這樣太low了,不符合代碼可復(fù)用的原則
上面這4點(diǎn)問題如果不解決,以后肯定會引出更大的坑,有同學(xué)說了,解決也不復(fù)雜呀,直接在每個(gè)功能調(diào)用時(shí)做一下角色判斷啥就好了,沒錯(cuò),你要非得這么霸王硬上弓的搞也肯定是可以實(shí)現(xiàn)的,那你自己就開發(fā)相應(yīng)的代碼來對上面提到的問題進(jìn)行處理好啦。 但這些問題其實(shí)能過OOP就可以很簡單的解決。 
之前的代碼改成用OOP中的“類”來實(shí)現(xiàn)的話如下: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Role(object):
    def __init__(self,name,role,weapon,life_value=100,money=15000):
        self.name = name
        self.role = role
        self.weapon = weapon
        self.life_value = life_value
        self.money = money
    def shot(self):
        print("shooting...")
    def got_shot(self):
        print("ah...,I got shot...")
    def buy_gun(self,gun_name):
        print("just bought %s" %gun_name)
r1 = Role('Alex','police','AK47’) #生成一個(gè)角色
r2 = Role('Jack','terrorist','B22’)  #生成一個(gè)角色

先不考慮語法細(xì)節(jié),相比靠函數(shù)拼湊出來的寫法,上面用面向?qū)ο笾械念悂韺懽钪苯拥母倪M(jìn)有以下2點(diǎn):
  1. 代碼量少了近一半
  2. 角色和它所具有的功能可以一目了然看出來

在真正開始分解上面代碼含義之之前,我們現(xiàn)來了解一些類的基本定義
類的語法
1
2
3
4
5
6
7
8
9
class Dog(object):
    print("hello,I am a dog!")
d = Dog() #實(shí)例化這個(gè)類,
#此時(shí)的d就是類Dog的實(shí)例化對象
#實(shí)例化,其實(shí)就是以Dog類為模版,在內(nèi)存里開辟一塊空間,存上數(shù)據(jù),賦值成一個(gè)變量名

上面的代碼其實(shí)有問題,想給狗起名字傳不進(jìn)去。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog(object):
    def __init__(self,name,dog_type):
        self.name = name
        self.type = dog_type
    def sayhi(self):
        print("hello,I am a dog, my name is ",self.name)
d = Dog('LiChuang',"京巴")
d.sayhi()

為什么有__init__? 為什么有self? 此時(shí)的你一臉蒙逼,相信不畫個(gè)圖,你的智商是理解不了的!  

畫圖之前, 你先注釋掉這兩句

1
2
3
4
# d = Dog('LiChuang', "京巴")
# d.sayhi()
print(Dog)

沒實(shí)例直接打印Dog輸出如下

1
<class '__main__.Dog'>

這代表什么?代表 即使不實(shí)例化,這個(gè)Dog類本身也是已經(jīng)存在內(nèi)存里的對不對, yes, cool,那實(shí)例化時(shí),會產(chǎn)生什么化學(xué)反應(yīng)呢?

 根據(jù)上圖我們得知,其實(shí)self,就是實(shí)例本身!你實(shí)例化時(shí)python會自動把這個(gè)實(shí)例本身通過self參數(shù)傳進(jìn)去。

你說好吧,假裝懂了, 但下面這段代碼你又不明白了, 為何sayhi(self),要寫個(gè)self呢? 

1
2
3
4
5
class Dog(object):
    ...
    def sayhi(self):
        print("hello,I am a dog, my name is ",self.name)

這個(gè)原因,我課上在講。。?!   ?/p>

1
<br><br><br><br>
 

 
好了,明白 了類的基本定義,接下來我們一起分解一下上面的代碼分別 是什么意思 
1
2
3
4
5
6
7
class Role(object): #定義一個(gè)類, class是定義類的語法,Role是類名,(object)是新式類的寫法,必須這樣寫,以后再講為什么
    def __init__(self,name,role,weapon,life_value=100,money=15000): #初始化函數(shù),在生成一個(gè)角色時(shí)要初始化的一些屬性就填寫在這里
        self.name = name #__init__中的第一個(gè)參數(shù)self,和這里的self都 是什么意思? 看下面解釋
        self.role = role
        self.weapon = weapon
        self.life_value = life_value
        self.money = money

上面的這個(gè)__init__()叫做初始化方法(或構(gòu)造方法), 在類被調(diào)用時(shí),這個(gè)方法(雖然它是函數(shù)形式,但在類中就不叫函數(shù)了,叫方法)會自動執(zhí)行,進(jìn)行一些初始化的動作,所以我們這里寫的__init__(self,name,role,weapon,life_value=100,money=15000)就是要在創(chuàng)建一個(gè)角色時(shí)給它設(shè)置這些屬性,那么這第一個(gè)參數(shù)self是干毛用的呢? 
初始化一個(gè)角色,就需要調(diào)用這個(gè)類一次: 
1
2
r1 = Role('Alex','police','AK47’) #生成一個(gè)角色 , 會自動把參數(shù)傳給Role下面的__init__(...)方法
r2 = Role('Jack','terrorist','B22’)  #生成一個(gè)角色

我們看到,上面的創(chuàng)建角色時(shí),我們并沒有給__init__傳值,程序也沒未報(bào)錯(cuò),是因?yàn)椋愒谡{(diào)用它自己的__init__(…)時(shí)自己幫你給self參數(shù)賦值了, 

1
2
r1 = Role('Alex','police','AK47’) #此時(shí)self 相當(dāng)于 r1 ,  Role(r1,'Alex','police','AK47’)
r2 = Role('Jack','terrorist','B22’)#此時(shí)self 相當(dāng)于 r2, Role(r2,'Jack','terrorist','B22’)

為什么這樣子?你拉著我說你有些猶豫,怎么會這樣子?
你執(zhí)行r1 = Role('Alex','police','AK47)時(shí),python的解釋器其實(shí)干了兩件事:
  1. 在內(nèi)存中開辟一塊空間指向r1這個(gè)變量名
  2. 調(diào)用Role這個(gè)類并執(zhí)行其中的__init__(…)方法,相當(dāng)于Role.__init__(r1,'Alex','police',AK47’),這么做是為什么呢? 是為了把'Alex','police',’AK47’這3個(gè)值跟剛開辟的r1關(guān)聯(lián)起來,是為了把'Alex','police',’AK47’這3個(gè)值跟剛開辟的r1關(guān)聯(lián)起來,是為了把'Alex','police',’AK47’這3個(gè)值跟剛開辟的r1關(guān)聯(lián)起來,重要的事情說3次, 因?yàn)殛P(guān)聯(lián)起來后,你就可以直接r1.name, r1.weapon 這樣來調(diào)用啦。所以,為實(shí)現(xiàn)這種關(guān)聯(lián),在調(diào)用__init__方法時(shí),就必須把r1這個(gè)變量也傳進(jìn)去,否則__init__不知道要把那3個(gè)參數(shù)跟誰關(guān)聯(lián)呀。
  3. 明白了么哥?所以這個(gè)__init__(…)方法里的,self.name = name , self.role = role 等等的意思就是要把這幾個(gè)值 存到r1的內(nèi)存空間里。
如果還不明白的話,哥,去測試一下智商吧, 應(yīng)該不會超過70,哈哈。
為了暴露自己的智商,此時(shí)你假裝懂了,但又問, __init__(…)我懂了,但后面的那幾個(gè)函數(shù),噢 不對,后面那幾個(gè)方法 為什么也還需要self參數(shù)么? 不是在初始化角色的時(shí)候 ,就已經(jīng)把角色的屬性跟r1綁定好了么? 
good question, 先來看一下上面類中的一個(gè)buy_gun的方法: 
1
2
def buy_gun(self,gun_name):
    print(“%s has just bought %s” %(self.name,gun_name) )

上面這個(gè)方法通過類調(diào)用的話要寫成如下: 
 
1
2
r1 = Role('Alex','police','AK47')
r1.buy_gun("B21”) #python 會自動幫你轉(zhuǎn)成 Role.buy_gun(r1,”B21")

執(zhí)行結(jié)果
#Alex has just bought B21 
依然沒給self傳值 ,但Python還是會自動的幫你把r1 賦值給self這個(gè)參數(shù), 為什么呢? 因?yàn)椋阍赽uy_gun(..)方法中可能要訪問r1的一些其它屬性呀, 比如這里就訪問 了r1的名字,怎么訪問呢?你得告訴這個(gè)方法呀,于是就把r1傳給了這個(gè)self參數(shù),然后在buy_gun里調(diào)用 self.name 就相當(dāng)于調(diào)用r1.name 啦,如果還想知道r1的生命值 有多少,直接寫成self.life_value就可以了。 說白了就是在調(diào)用類中的一個(gè)方法時(shí),你得告訴人家你是誰。
好啦, 總結(jié)一下2點(diǎn):
  1. 上面的這個(gè)r1 = Role('Alex','police','AK47)動作,叫做類的“實(shí)例化”, 就是把一個(gè)虛擬的抽象的類,通過這個(gè)動作,變成了一個(gè)具體的對象了, 這個(gè)對象就叫做實(shí)例
  2. 剛才定義的這個(gè)類體現(xiàn)了面向?qū)ο蟮牡谝粋€(gè)基本特性,封裝,其實(shí)就是使用構(gòu)造方法將內(nèi)容封裝到某個(gè)具體對象中,然后通過對象直接或者self間接獲取被封裝的內(nèi)容
 

面向?qū)ο蟮奶匦裕?/strong>

封裝

封裝最好理解了。封裝是面向?qū)ο蟮奶卣髦唬菍ο蠛皖惛拍畹闹饕匦浴?/p>

封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對象操作,對不可信的進(jìn)行信息隱藏。

繼承

面向?qū)ο缶幊?(OOP) 語言的一個(gè)主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現(xiàn)有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進(jìn)行擴(kuò)展。

通過繼承創(chuàng)建的新類稱為“子類”或“派生類”。

被繼承的類稱為“基類”、“父類”或“超類”。

繼承的過程,就是從一般到特殊的過程。

要實(shí)現(xiàn)繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實(shí)現(xiàn)。

在某些 OOP 語言中,一個(gè)子類可以繼承多個(gè)基類。但是一般情況下,一個(gè)子類只能有一個(gè)基類,要實(shí)現(xiàn)多重繼承,可以通過多級繼承來實(shí)現(xiàn)。

繼承概念的實(shí)現(xiàn)方式主要有2類:實(shí)現(xiàn)繼承、接口繼承。

         實(shí)現(xiàn)繼承是指使用基類的屬性和方法而無需額外編碼的能力;
         接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實(shí)現(xiàn)的能力(子類重構(gòu)爹類方法);
在考慮使用繼承時(shí),有一點(diǎn)需要注意,那就是兩個(gè)類之間的關(guān)系應(yīng)該是“屬于”關(guān)系。例如,Employee 是一個(gè)人,Manager 也是一個(gè)人,因此這兩個(gè)類都可以繼承 Person 類。但是 Leg 類卻不能繼承 Person 類,因?yàn)橥炔⒉皇且粋€(gè)人。
抽象類僅定義將由子類創(chuàng)建的一般屬性和方法。

OO開發(fā)范式大致為:劃分對象→抽象類→將類組織成為層次化結(jié)構(gòu)(繼承和合成) →用類與實(shí)例進(jìn)行設(shè)計(jì)和實(shí)現(xiàn)幾個(gè)階段。

繼承示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!_*_coding:utf-8_*_
#__author__:"Alex Li"
class SchoolMember(object):
    members = 0 #初始學(xué)校人數(shù)為0
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def  tell(self):
        pass
    def enroll(self):
        '''注冊'''
        SchoolMember.members +=1
        print("\033[32;1mnew member [%s] is enrolled,now there are [%s] members.\033[0m " %(self.name,SchoolMember.members))
     
    def __del__(self):
        '''析構(gòu)方法'''
        print("\033[31;1mmember [%s] is dead!\033[0m" %self.name)
class Teacher(SchoolMember):
    def __init__(self,name,age,course,salary):
        super(Teacher,self).__init__(name,age)
        self.course = course
        self.salary = salary
        self.enroll()
    def teaching(self):
        '''講課方法'''
        print("Teacher [%s] is teaching [%s] for class [%s]" %(self.name,self.course,'s12'))
    def tell(self):
        '''自我介紹方法'''
        msg = '''Hi, my name is [%s], works for [%s] as a [%s] teacher !''' %(self.name,'Oldboy', self.course)
        print(msg)
class Student(SchoolMember):
    def __init__(self, name,age,grade,sid):
        super(Student,self).__init__(name,age)
        self.grade = grade
        self.sid = sid
        self.enroll()
    def tell(self):
        '''自我介紹方法'''
        msg = '''Hi, my name is [%s], I'm studying [%s] in [%s]!''' %(self.name, self.grade,'Oldboy')
        print(msg)
if __name__ == '__main__':
    t1 = Teacher("Alex",22,'Python',20000)
    t2 = Teacher("TengLan",29,'Linux',3000)
    s1 = Student("Qinghua", 24,"Python S12",1483)
    s2 = Student("SanJiang", 26,"Python S12",1484)
    t1.teaching()
    t2.teaching()
    t1.tell()

  

多態(tài)

 
多態(tài)性(polymorphisn)是允許你將父對象設(shè)置成為和一個(gè)或更多的他的子對象相等的技術(shù),賦值之后,父對象就可以根據(jù)當(dāng)前賦值給它的子對象的特性以不同的方式運(yùn)作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
那么,多態(tài)的作用是什么呢?我們知道,封裝可以隱藏實(shí)現(xiàn)細(xì)節(jié),使得代碼模塊化;繼承可以擴(kuò)展已存在的代碼模塊(類);它們的目的都是為了——代碼重用。而多態(tài)則是為了實(shí)現(xiàn)另一個(gè)目的——接口重用!多態(tài)的作用,就是為了類在繼承和派生的時(shí)候,保證使用“家譜”中任一類的實(shí)例的某一屬性時(shí)的正確調(diào)用。
 
Pyhon不直接支持多態(tài),但可以間接實(shí)現(xiàn)
通過Python模擬的多態(tài)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Animal:
    def __init__(self, name):    # Constructor of the class
        self.name = name
    def talk(self):              # Abstract method, defined by convention only
        raise NotImplementedError("Subclass must implement abstract method")
class Cat(Animal):
    def talk(self):
        return 'Meow!'
class Dog(Animal):
    def talk(self):
        return 'Woof! Woof!'
animals = [Cat('Missy'),
           Dog('Lassie')]
for animal in animals:
    print animal.name + ': ' + animal.talk()

  

 

本節(jié)作業(yè): 選課系統(tǒng)

角色:學(xué)校、學(xué)員、課程、講師
要求:
1. 創(chuàng)建北京、上海 2 所學(xué)校
2. 創(chuàng)建linux , python , go 3個(gè)課程 , linux\py 在北京開, go 在上海開
3. 課程包含,周期,價(jià)格,通過學(xué)校創(chuàng)建課程
4. 通過學(xué)校創(chuàng)建班級, 班級關(guān)聯(lián)課程、講師
5. 創(chuàng)建學(xué)員時(shí),選擇學(xué)校,關(guān)聯(lián)班級
5. 創(chuàng)建講師角色時(shí)要關(guān)聯(lián)學(xué)校,
6. 提供兩個(gè)角色接口
6.1 學(xué)員視圖, 可以注冊, 交學(xué)費(fèi), 選擇班級,
6.2 講師視圖, 講師可管理自己的班級, 上課時(shí)選擇班級, 查看班級學(xué)員列表 , 修改所管理的學(xué)員的成績
6.3 管理視圖,創(chuàng)建講師, 創(chuàng)建班級,創(chuàng)建課程

7. 上面的操作產(chǎn)生的數(shù)據(jù)都通過pickle序列化保存到文件里

  

  

  

  

  

  

  

  

  

 

 

        

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧美午夜一级特黄大片| 中文字幕无线码一区欧美| 麻豆精品在线一区二区三区| 欧美日韩亚洲国产av| 国产精品美女午夜视频| 国产黄色高清内射熟女视频| 青青操精品视频在线观看| 国产在线成人免费高清观看av| 国产欧洲亚洲日产一区二区| 亚洲综合色在线视频香蕉视频| 太香蕉久久国产精品视频| 日韩国产欧美中文字幕| 免费一级欧美大片免费看| 日韩在线视频精品视频| 不卡一区二区在线视频| 国产精品久久香蕉国产线| 日韩高清中文字幕亚洲| 精品日韩av一区二区三区| 国产熟女一区二区不卡| 精品欧美一区二区三久久| 亚洲一二三四区免费视频| 男人和女人干逼的视频| 少妇肥臀一区二区三区| 精品人妻一区二区三区四在线| 久久精品亚洲欧美日韩| 国产一区二区久久综合| 高潮少妇高潮久久精品99| 国产精品成人又粗又长又爽| 日韩欧美一区二区亚洲| 色综合伊人天天综合网中文| 我想看亚洲一级黄色录像| 日本精品中文字幕人妻| 五月婷婷六月丁香在线观看| 日韩熟妇人妻一区二区三区| 国产成人国产精品国产三级| 亚洲欧美国产中文色妇| 精品国产亚洲av成人一区| 欧美日韩无卡一区二区| 精品久久综合日本欧美| 国产传媒中文字幕东京热| 欧美日韩国内一区二区|