Day06的課程要點(diǎn)記錄
詳細(xì)教程地址:Day6 - 面向?qū)ο髮W(xué)習(xí)
上節(jié)補(bǔ)遺 - Subprocess模塊
os.system # 輸出命令結(jié)果到屏幕,返回命令執(zhí)行狀態(tài)
os.popen("dir").read() # 會(huì)保存命令的執(zhí)行結(jié)果輸出
subprocess 模塊主要用于替換幾個(gè)舊模塊:os.system, os.spawn*
常用subprocess方法示例
subprocess.run() # Python 3.5 后才出現(xiàn)的方法
執(zhí)行命令,返回命令執(zhí)行狀態(tài) , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"]) # 命令用列表單個(gè)傳入,或者完整輸入字符串,用`shell=True`來(lái)執(zhí)行操作系統(tǒng)執(zhí)行。該方法效果類(lèi)似os.system
#執(zhí)行命令,如果命令結(jié)果為0,就正常返回,否則拋異常
>>> subprocess.check_call(["ls", "-l"])
0
#接收字符串格式命令,返回元組形式,第1個(gè)元素是執(zhí)行狀態(tài),第2個(gè)是命令結(jié)果
>>> subprocess.getstatusoutput('ls /bin/ls') # 最常用
(0, '/bin/ls')
#接收字符串格式命令,并返回結(jié)果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'
#執(zhí)行命令,并返回結(jié)果,注意是返回結(jié)果,不是打印,下例結(jié)果返回給res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'
上面那些方法,底層都是封裝的subprocess.Popen
poll() # 檢查是否執(zhí)行完畢,根據(jù)狀態(tài)返回相應(yīng)值
Check if child process has terminated. Returns returncode
wait()
Wait for child process to terminate. Returns returncode attribute.
terminate() 殺掉所啟動(dòng)進(jìn)程
communicate() 等待任務(wù)結(jié)束
stdin 標(biāo)準(zhǔn)輸入
stdout 標(biāo)準(zhǔn)輸出
stderr 標(biāo)準(zhǔn)錯(cuò)誤
pid
The process ID of the child process.
例子:
stdin, stdout等,必須要加參數(shù)subprocess.PIPE,才能read()
類(lèi)似操作系統(tǒng)中,不同進(jìn)程的程序互相傳數(shù)據(jù)需要通過(guò)管道
>>> p = subprocess.Popen("df -h|grep disk",stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=True)
>>> p.stdout.read()
b'/dev/disk1 465Gi 64Gi 400Gi 14% 16901472 104938142 14% /\n'
調(diào)用subprocess.run(...)是推薦的常用方法,在大多數(shù)情況下能滿(mǎn)足需求,但如果你可能需要進(jìn)行一些復(fù)雜的與系統(tǒng)的交互的話(huà),你還可以用subprocess.Popen(),語(yǔ)法如下:
p = subprocess.Popen("find / -size +1000000 -exec ls -shl {} \;",shell=True,stdout=subprocess.PIPE)
print(p.stdout.read())
可用參數(shù)
- args:shell命令,可以是字符串或者序列類(lèi)型(如:list,元組)
- bufsize:指定緩沖。0 無(wú)緩沖,1 行緩沖,其他 緩沖區(qū)大小,負(fù)值 系統(tǒng)緩沖
- stdin, stdout, stderr:分別表示程序的標(biāo)準(zhǔn)輸入、輸出、錯(cuò)誤句柄
- preexec_fn:只在Unix平臺(tái)下有效,用于指定一個(gè)可執(zhí)行對(duì)象(callable object),它將在子進(jìn)程運(yùn)行之前被調(diào)用
- close_sfs:在windows平臺(tái)下,如果close_fds被設(shè)置為T(mén)rue,則新創(chuàng)建的子進(jìn)程將不會(huì)繼承父進(jìn)程的輸入、輸出、錯(cuò)誤管道。
所以不能將close_fds設(shè)置為T(mén)rue同時(shí)重定向子進(jìn)程的標(biāo)準(zhǔn)輸入、輸出與錯(cuò)誤(stdin, stdout, stderr)。 - shell:同上
- cwd:用于設(shè)置子進(jìn)程的當(dāng)前目錄
- env:用于指定子進(jìn)程的環(huán)境變量。如果env = None,子進(jìn)程的環(huán)境變量將從父進(jìn)程中繼承。
- universal_newlines:不同系統(tǒng)的換行符不同,True -> 同意使用 \n
- startupinfo與createionflags只在windows下有效
將被傳遞給底層的CreateProcess()函數(shù),用于設(shè)置子進(jìn)程的一些屬性,如:主窗口的外觀(guān),進(jìn)程的優(yōu)先級(jí)等等
subprocess實(shí)現(xiàn)sudo 自動(dòng)輸入密碼
import subprocess
def mypass():
mypass = '123' #or get the password from anywhere
return mypass
echo = subprocess.Popen(['echo',mypass()],
stdout=subprocess.PIPE,
)
sudo = subprocess.Popen(['sudo','-S','iptables','-L'],
stdin=echo.stdout,
stdout=subprocess.PIPE,
)
end_of_pipe = sudo.stdout
print "Password ok \n Iptables Chains %s" % end_of_pipe.read()
一、面向?qū)ο?/h3>
1.1 引子
開(kāi)發(fā)一款叫做<人狗大戰(zhàn)>的游戲,需要至少2個(gè)角色:人、狗,且人和狗都有不同的技能,如人拿棍打狗, 狗可以咬人,怎么描述這種不同的角色和他們的功能呢?
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è)角色模板,游戲開(kāi)始,需要成實(shí)際對(duì)象,怎么生成呢?
d1 = dog("李闖", "京巴")
p1 = person("孫海濤", 36, "F", "運(yùn)維")
p2 = person("林海峰", 27, "F", "Teacher")
兩個(gè)角色對(duì)象生成了,狗和人還有不同的功能怎么實(shí)現(xiàn)呢?
可以每個(gè)功能再寫(xiě)一個(gè)函數(shù),想執(zhí)行哪個(gè)功能,直接調(diào)用就可以了。
def bark(d):
print("dog %s:wang.wang..wang..." % d['name'])
def walk(p):
print("person %s is walking..." % p['name'])
walk(p1)
bark(d1)
上面的功能實(shí)現(xiàn)的簡(jiǎn)直是完美!
但是仔細(xì)玩耍一會(huì),你就不小心干了下面這件事
p1 = person("孫海濤",36,"F","運(yùn)維")
bark(p1) #把人的對(duì)象傳給了狗的方法
事實(shí)上并沒(méi)出錯(cuò)。但顯然人是不能調(diào)用狗的功能的,如何在代碼級(jí)別實(shí)現(xiàn)這個(gè)限制呢?
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")
d1['bark'](d1)
p1['walk'](p1)
1.2
剛才只是阻止了兩個(gè)完全不同的角色之間的功能混用。 但有可能同一種角色,只有部分屬性是不同的。
比如cs里有警察和恐怖份子,但因?yàn)槎际侨?,所以?xiě)一個(gè)角色叫person(), 警察和恐怖份子都可以互相射擊,但警察不可以殺人質(zhì),恐怖分子可以。
這怎么實(shí)現(xiàn)呢?在殺人質(zhì)的功能里加個(gè)判斷,如果是警察,就不讓殺就ok了。
沒(méi)錯(cuò), 這雖然解決了殺人質(zhì)的問(wèn)題,但其實(shí)警察和恐怖分子的區(qū)別還有很多,同時(shí)又有很多共性,如果在每個(gè)區(qū)別處都單獨(dú)做判斷,那得累死。
但如果就直接寫(xiě)兩個(gè)角色,就代表相同的功能也要重寫(xiě)了。
二、面向過(guò)程 VS 面向?qū)ο?/h3>
2.1 編程范式
編程:程序員用特定的語(yǔ)法+數(shù)據(jù)結(jié)構(gòu)+算法組成的代碼來(lái)告訴計(jì)算機(jī)如何執(zhí)行任務(wù)的過(guò)程。
程序:程序員為了得到一個(gè)任務(wù)結(jié)果而編寫(xiě)的一組指令的集合。
編程范式:實(shí)現(xiàn)一個(gè)任務(wù)的方式有很多種不同的方式, 對(duì)這些不同的編程方式的特點(diǎn)進(jìn)行歸納總結(jié)得出來(lái)的編程方式類(lèi)別,即為編程范式。
不同的編程范式本質(zhì)上代表對(duì)各種類(lèi)型的任務(wù),采取的不同的解決問(wèn)題的思路。
大多數(shù)語(yǔ)言只支持一種編程范式,當(dāng)然也有些語(yǔ)言可以同時(shí)支持多種編程范式。
兩種最重要的編程范式分別是面向過(guò)程編程和面向?qū)ο缶幊?/strong>。
2.2 面向過(guò)程編程(Procedural Programming)
Procedural programming uses a list of instructions to tell the computer what to do step-by-step. *
面向過(guò)程又被稱(chēng)為top-down languages, 就是程序從上到下一步步執(zhí)行,一步步從上到下,從頭到尾的解決問(wèn)題 。
基本設(shè)計(jì)思路就是程序一開(kāi)始是要著手解決一個(gè)大的問(wèn)題,然后把一個(gè)大問(wèn)題分解成很多個(gè)小問(wèn)題或子過(guò)程。
這些子過(guò)程在執(zhí)行的過(guò)程中再繼續(xù)分解,直到小問(wèn)題足夠簡(jiǎn)單,可以在一個(gè)小步驟范圍內(nèi)解決。
舉個(gè)典型的面向過(guò)程的例子數(shù)據(jù)庫(kù)備份*,分三步:
連接數(shù)據(jù)庫(kù),備份數(shù)據(jù)庫(kù),測(cè)試備份文件可用性。
def db_conn():
print("connecting db...")
def db_backup(dbname):
print("導(dǎo)出數(shù)據(jù)庫(kù)...",dbname)
print("將備份文件打包,移至相應(yīng)目錄...")
def db_backup_test():
print("將備份文件導(dǎo)入測(cè)試庫(kù),看導(dǎo)入是否成功")
def main():
db_conn()
db_backup('my_db')
db_backup_test()
if __name__ == '__main__':
main()
這樣做的問(wèn)題也是顯而易見(jiàn)的,就是如果你要對(duì)程序進(jìn)行修改,對(duì)你修改的那部分有依賴(lài)的各個(gè)部分你都也要跟著修改.
舉個(gè)例子:如果程序開(kāi)頭你設(shè)置了一個(gè)變量值為1, 但如果其它子過(guò)程依賴(lài)這個(gè)值為1的變量才能正常運(yùn)行,那如果你改了這個(gè)變量,那這個(gè)子過(guò)程你也要修改。
假如又有一個(gè)其它子程序依賴(lài)這個(gè)子過(guò)程,那就會(huì)發(fā)生一連串的影響,隨著程序越來(lái)越大,這種編程方式的維護(hù)難度會(huì)越來(lái)越高。
所以一般認(rèn)為,如果只是寫(xiě)一些簡(jiǎn)單的腳本,去做一些一次性任務(wù),用面向過(guò)程的方式是極好的。但如果要處理的任務(wù)是復(fù)雜的,且需要不斷迭代和維護(hù) 的, 那還是用面向?qū)ο笞罘奖恪?/p>
2.3 面向?qū)ο缶幊蹋∣bject-Oriented Programming)
面向?qū)ο蟮膸讉€(gè)核心特性如下
2.3.1 Class 類(lèi)
一個(gè)類(lèi)即是對(duì)一類(lèi)擁有相同屬性的對(duì)象的抽象、藍(lán)圖、原型。在類(lèi)中定義了這些對(duì)象的都具備的屬性(variables(data))、共同的方法。
類(lèi)似例子中的模板
2.3.2 Object 對(duì)象
一個(gè)對(duì)象即是一個(gè)類(lèi)實(shí)例化后的實(shí)例。一個(gè)類(lèi)必須經(jīng)過(guò)實(shí)例化后方可在程序中調(diào)用,一個(gè)類(lèi)可以實(shí)例化多個(gè)對(duì)象,每個(gè)對(duì)象亦可以有不同的屬性,就像人類(lèi)是指所有人,每個(gè)人是指具體的對(duì)象,人與人之前有共性,亦有不同。
類(lèi)似例子中的具體的人與狗
2.3.3 Encapsulation 封裝
在類(lèi)中對(duì)數(shù)據(jù)的賦值、內(nèi)部調(diào)用對(duì)外部用戶(hù)是透明的,這使類(lèi)變成了一個(gè)膠囊或容器,里面包含著類(lèi)的數(shù)據(jù)和方法。
- 防止數(shù)據(jù)被隨意修改。
- 使外部程序不需要關(guān)注對(duì)象內(nèi)部的構(gòu)造,只需要通過(guò)此對(duì)象對(duì)外提供的接口進(jìn)行直接訪(fǎng)問(wèn)。
2.3.4 Inheritance 繼承
一個(gè)類(lèi)可以派生出子類(lèi),在這個(gè)父類(lèi)里定義的屬性、方法自動(dòng)被子類(lèi)繼承。
- 通過(guò)父類(lèi) -> 子類(lèi)的方式以最簡(jiǎn)代碼實(shí)現(xiàn)不同角色的共同點(diǎn)和不同點(diǎn)。
2.3.5 Polymorphism 多態(tài)
多態(tài)是面向?qū)ο蟮闹匾匦?簡(jiǎn)單點(diǎn)說(shuō):“一個(gè)接口,多種實(shí)現(xiàn)”,指一個(gè)基類(lèi)中派生出了不同的子類(lèi),且每個(gè)子類(lèi)在繼承了同樣的方法名的同時(shí)又對(duì)父類(lèi)的方法做了不同的實(shí)現(xiàn),這就是同一種事物表現(xiàn)出的多種形態(tài)。
人類(lèi)有多個(gè)人種,不同人種都是用嘴說(shuō)不同語(yǔ)言。
三、面向?qū)ο缶幊?Object-Oriented Programming )介紹
無(wú)論用什么形式來(lái)編程,我們都要明確記住以下原則:
- 寫(xiě)重復(fù)代碼是非常不好的低級(jí)行為
- 你寫(xiě)的代碼需要經(jīng)常變更
函數(shù)編程與OOP的主要區(qū)別就是OOP可以使程序更加容易擴(kuò)展和易更改。
不考慮語(yǔ)法細(xì)節(jié),相比靠函數(shù)拼湊出來(lái)的寫(xiě)法,用面向?qū)ο笾械念?lèi)來(lái)寫(xiě)最直接的改進(jìn)有以下2點(diǎn):
- 代碼量少了近一半
- 角色和它所具有的功能可以一目了然看出來(lái)
3.1 類(lèi)的基本定義
class Role(object): #定義一個(gè)類(lèi), class是定義類(lèi)的語(yǔ)法,Role是類(lèi)名,(object)是新式類(lèi)的寫(xiě)法
def _init__(self,name,role,weapon,life_value=100,money=15000): #初始化函數(shù)
self.name = name
self.role = role
self.weapon = weapon
self.life_value = life_value
self.money = money
上面的__init__()叫做初始化方法(或構(gòu)造方法), 在類(lèi)被調(diào)用時(shí),這個(gè)方法(雖然它是函數(shù)形式,但在類(lèi)中就不叫函數(shù)了,叫方法)會(huì)自動(dòng)執(zhí)行,進(jìn)行一些初始化的動(dòng)作。
所以這里寫(xiě)的__init__(self,name,role,weapon,life_value=100,money=15000)就是要在創(chuàng)建一個(gè)角色時(shí)給它設(shè)置這些屬性,那么這第一個(gè)參數(shù)self是什么呢?
每當(dāng)初始化一個(gè)角色,就需要調(diào)用這個(gè)類(lèi)一次:
r1 = Role('Alex','police','AK47’) # 生成一個(gè)角色 , 會(huì)自動(dòng)把參數(shù)傳給Role下面的__init__(...)方法
r2 = Role('Jack','terrorist','B22’) # 生成一個(gè)角色
上面創(chuàng)建角色時(shí),我們并沒(méi)有給__init__傳值,程序也沒(méi)未報(bào)錯(cuò),是因?yàn)?,?lèi)在調(diào)用它自己的__init__(…)時(shí)自己幫你給self參數(shù)賦值了
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í)干了兩件事:
- 在內(nèi)存中開(kāi)辟一塊空間指向r1這個(gè)變量名
- 調(diào)用Role這個(gè)類(lèi)并執(zhí)行其中的
__init__(…)方法,相當(dāng)于Role.__init__(r1,'Alex','police',’AK47’)。
這么做是為了把'Alex','police',’AK47’這3個(gè)值跟剛開(kāi)辟的r1關(guān)聯(lián)起來(lái),就可以直接r1.name, r1.weapon 來(lái)調(diào)用。
所以,為實(shí)現(xiàn)這種關(guān)聯(lián),在調(diào)用init方法時(shí),就必須把r1這個(gè)變量也傳進(jìn)去,否則init不知道要把那3個(gè)參數(shù)跟誰(shuí)關(guān)聯(lián)呀。 - 所以這個(gè)
__init__(…)方法里的self.name = name, self.role = role等等的意思就是要把這幾個(gè)值 存到r1的內(nèi)存空間里。

根據(jù)上圖得知,其實(shí)
self就是實(shí)例本身!你實(shí)例化時(shí)python會(huì)自動(dòng)把這個(gè)實(shí)例本身通過(guò)self參數(shù)傳進(jìn)去。
總結(jié):
- 上面的這個(gè)
r1 = Role('Alex','police','AK47’)動(dòng)作,叫做類(lèi)的“實(shí)例化”, 就是把一個(gè)虛擬的抽象的類(lèi),通過(guò)這個(gè)動(dòng)作,變成了一個(gè)具體的對(duì)象了, 這個(gè)對(duì)象就叫做實(shí)例 - 剛才定義的這個(gè)類(lèi)體現(xiàn)了面向?qū)ο蟮牡谝粋€(gè)基本特性封裝,其實(shí)就是使用構(gòu)造方法將內(nèi)容封裝到某個(gè)具體對(duì)象中,然后通過(guò)對(duì)象直接或者self間接獲取被封裝的內(nèi)容。
3.2
四、面向?qū)ο蟮奶匦?/h3>
4.1 封裝
封裝是面向?qū)ο蟮奶卣髦?,是?duì)象和類(lèi)概念的主要特性。
封裝,也就是把客觀(guān)事物封裝成抽象的類(lèi),并且類(lèi)可以把自己的數(shù)據(jù)和方法只讓可信的類(lèi)或者對(duì)象操作,對(duì)不可信的進(jìn)行信息隱藏。
4.1.1 類(lèi) --> 實(shí)例化 --> 實(shí)例對(duì)象
__init__ # 構(gòu)造函數(shù)
self.name = name # 屬性、成員變量、字段
def sayhi() # 方法、動(dòng)態(tài)屬性
4.1.2 私有屬性:
__private_attr_name = value (只能內(nèi)部訪(fǎng)問(wèn))
def get_heart(self): # 對(duì)外部提供只讀訪(fǎng)問(wèn)接口
return self.__heart
r1._Role__heart # 強(qiáng)制訪(fǎng)問(wèn)私有屬性
4.1.3 公有屬性
在類(lèi)里定義的屬性,為公有屬性
例子中的nationality
實(shí)例指向公有屬性
4.1.4 析構(gòu)方法
def del(self):
print("del......run......")
4.2 繼承
面向?qū)ο缶幊?(OOP) 語(yǔ)言的一個(gè)主要功能就是“繼承”。
繼承是指:它可以使用現(xiàn)有類(lèi)的所有功能,并在無(wú)需重新編寫(xiě)原來(lái)的類(lèi)的情況下對(duì)這些功能進(jìn)行擴(kuò)展。
通過(guò)繼承創(chuàng)建的新類(lèi)稱(chēng)為“子類(lèi)”或“派生類(lèi)”。
被繼承的類(lèi)稱(chēng)為“基類(lèi)”、“父類(lèi)”或“超類(lèi)”。
繼承的過(guò)程,就是從一般到特殊的過(guò)程。
要實(shí)現(xiàn)繼承,可以通過(guò)“繼承”(Inheritance)和“組合”(Composition)來(lái)實(shí)現(xiàn)。
在某些 OOP 語(yǔ)言中,一個(gè)子類(lèi)可以繼承多個(gè)基類(lèi)。但是一般情況下,一個(gè)子類(lèi)只能有一個(gè)基類(lèi),要實(shí)現(xiàn)多重繼承,可以通過(guò)多級(jí)繼承來(lái)實(shí)現(xiàn)。
繼承概念的實(shí)現(xiàn)方式主要有2類(lèi):實(shí)現(xiàn)繼承、接口繼承。
? 實(shí)現(xiàn)繼承是指使用基類(lèi)的屬性和方法而無(wú)需額外編碼的能力;
? 接口繼承是指僅使用屬性和方法的名稱(chēng)、但是子類(lèi)必須提供實(shí)現(xiàn)的能力(子類(lèi)重構(gòu)爹類(lèi)方法);
在考慮使用繼承時(shí),有一點(diǎn)需要注意,那就是兩個(gè)類(lèi)之間的關(guān)系應(yīng)該是“屬于”關(guān)系。例如,Employee 是一個(gè)人,Manager 也是一個(gè)人,因此這兩個(gè)類(lèi)都可以繼承 Person 類(lèi)。但是 Leg 類(lèi)卻不能繼承 Person 類(lèi),因?yàn)橥炔⒉皇且粋€(gè)人。
抽象類(lèi)僅定義將由子類(lèi)創(chuàng)建的一般屬性和方法。
OO開(kāi)發(fā)范式大致為:劃分對(duì)象→抽象類(lèi)→將類(lèi)組織成為層次化結(jié)構(gòu)(繼承和合成) →用類(lèi)與實(shí)例進(jìn)行設(shè)計(jì)和實(shí)現(xiàn)幾個(gè)階段。
4.2.1 繼承示例
class SchoolMember(object):
"""學(xué)校成員基類(lèi)"""
member = 0
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
self.enroll()
def enroll(self):
"""注冊(cè)"""
print("A new school member [%s] has been enrolled" % self.name)
SchoolMember.member += 1
def tell(self):
print("-----%s info-----" % self.name)
for k, v in self.__dict__.items():
print('\t', k, ':', v)
def __del__(self):
print("%s has been kicked off" % self.name)
SchoolMember.member -= 1
class School(object):
def branch(self, address):
self.address = address
print("Openning a new branch school in %s" % self.address)
class Teacher(SchoolMember, School): # 多繼承
"""講師類(lèi)"""
def __init__(self, name, age, sex, salary, course):
# SchoolMember.__init__(self, name, age, sex) # 經(jīng)典類(lèi)寫(xiě)法
super(Teacher, self).__init__(name, age, sex) # 新式類(lèi)寫(xiě)法
self.salary = salary
self.course = course
def teach(self):
print("Teacher [%s] is teaching [%s]" % (self.name, self.course))
class Student(SchoolMember):
def __init__(self, name, age, sex, tuition, course):
SchoolMember.__init__(self, name, age, sex)
self.tuition = tuition
self.course = course
self.amount = 0
def pay_tuition(self, amount):
print("student [%s] has paid [%s]" % (self.name, amount))
self.amount += amount
t1 = Teacher('Alex', 32, 'Male', 1000000, 'Python')
s1 = Student('Will', 32, 'Male', 7000, 'PY_Net')
s2 = Student('Simth', 22, 'Male', 11000, 'PY_S14')
print(SchoolMember.member)
del s2
print(SchoolMember.member)
t1.tell()
s1.tell()
t1.branch("Shanghai")
4.2.2 經(jīng)典類(lèi) vs 新式類(lèi)
- 寫(xiě)法
class Person(object): # new style
super(Person, self).()
class Person: # classical style
Person.init() - 多繼承時(shí),繼承順序的區(qū)別
廣度查詢(xún)、深度查詢(xún)
Python2 中經(jīng)典類(lèi)為深度查詢(xún),新式類(lèi)為廣度查詢(xún)
Python3 中經(jīng)典類(lèi)和新式類(lèi)均為廣度查詢(xún)
4.3 多態(tài)
多態(tài)是為了實(shí)現(xiàn)接口重用。
多態(tài)的作用,就是為了類(lèi)在繼承和派生的時(shí)候,保證使用“家譜”中任一類(lèi)的實(shí)例的某一屬性時(shí)的正確調(diào)用。
Pyhon不直接支持多態(tài),但可以間接實(shí)現(xiàn)。
通過(guò)Python模擬的多態(tài)
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())
五、補(bǔ)充
5.1 什么是面向?qū)ο缶幊?/h4>
- 之前使用的是函數(shù)式編程
- 現(xiàn)在是類(lèi) + 對(duì)象
5.2 什么是類(lèi),什么是對(duì)象?又有什么關(guān)系?
class 類(lèi):
def 函數(shù)1():
pass
def 函數(shù)2():
pass
# obj是對(duì)象,實(shí)例化的過(guò)程
obj = 類(lèi)()
obj.函數(shù)1()
class 類(lèi):
def 函數(shù)1():
pass
def 函數(shù)2():
pass
# obj是對(duì)象,實(shí)例化的過(guò)程
obj = 類(lèi)()
obj.函數(shù)1()
乍看之下,面向?qū)ο蟛缓?,函?shù)編程好
有時(shí)候,函數(shù)編程能實(shí)現(xiàn)功能,但是麻煩;而面向?qū)ο蠓浅:?jiǎn)單的就能實(shí)現(xiàn)。
5.3 什么時(shí)候適用面向?qū)ο螅?/h4>
- 根據(jù)一個(gè)模板創(chuàng)建某些東西時(shí)
- 如果多個(gè)函數(shù)需要傳入多個(gè)共同參數(shù)時(shí)
- 應(yīng)用場(chǎng)景
class SSH:
def __init__(self, host, port, username, passwd):
self.host = host
...
def connection(self):
# 去創(chuàng)建連接
self.conn = 和服務(wù)器創(chuàng)建的連接對(duì)象()
def close(self):
# 關(guān)閉
self.conn.close
def upload(self):
self.conn 使用連接上傳文件
def cmd(self):
self.conn 使用連接執(zhí)行命令
obj = SHH(......)
obj.connection()
obj.upload()
obj.close()
obj = SHH(......)
obj.connection()
obj.cmd()
obj.upload()
obj.cmd()
obj.close()
5.4 self就是調(diào)用當(dāng)前方法的對(duì)象
class Foo:
# 靜態(tài)字段 使用場(chǎng)景為:每個(gè)對(duì)象中保存相同的屬性時(shí)。
# 公有屬性
country = '中國(guó)'
def __init__(self, name, amount):
# 普通字段
# 普通屬性
self.NAME = name
self.AMOUNT = amout
def bar(self):
pass
obj1 = Foo('Alex', 100000)
obj1.bar()
obj2 = Foo('Eric', 10000)
obj2.bar()
5.5
class SSH:
def __init__(self, host, port, username, passwd):
self.host = host
...
def connection(self):
# 去創(chuàng)建連接
self.conn = 和服務(wù)器創(chuàng)建的連接對(duì)象()
def close(self):
# 關(guān)閉
self.conn.close
def upload(self):
self.conn 使用連接上傳文件
def cmd(self):
self.conn 使用連接執(zhí)行命令
obj = SHH(......)
obj.connection()
obj.upload()
obj.close()
obj = SHH(......)
obj.connection()
obj.cmd()
obj.upload()
obj.cmd()
obj.close()
class Foo:
# 靜態(tài)字段 使用場(chǎng)景為:每個(gè)對(duì)象中保存相同的屬性時(shí)。
# 公有屬性
country = '中國(guó)'
def __init__(self, name, amount):
# 普通字段
# 普通屬性
self.NAME = name
self.AMOUNT = amout
def bar(self):
pass
obj1 = Foo('Alex', 100000)
obj1.bar()
obj2 = Foo('Eric', 10000)
obj2.bar()
封裝:
類(lèi)中封裝了字段、方法
對(duì)象中封裝了普通字段的值
class F1:
def __init__(self, n):
self.N = n
print('F1')
class F2:
def __init__(self, arg1):
self.a =arg1
print('F2')
class F3:
def __init__(self, arg2):
self.b =arg2
print('F3')
o1 = F1('Alex')
o2 = F2(o1)
o3 = F3(o2)
###### Print Alex ######
# o3 = F3(o2)
o3.b => o2
# o2 = F2(o1)
o3.b.a => o1
# o2 = F1('Alex')
o3.b.a.N
繼承:
class F1(object):
def __init__(self):
print('F1')
def a1(self):
print("F1a1")
def a2(self):
print("F1a2")
class F2(F1):
def __init__(self):
print('F2')
def a1(self):
self.a2()
print("F2a1")
def a2(self):
print("F2a2")
class F3(F2):
def __init__(self):
print('F3')
def a2(self):
print("F3a2")
obj1 = F3()
obj1.a1()
# F3a2
# F2a1
5.6
字段:
- 普通字段(保存在對(duì)象中)
- 靜態(tài)字段(保存在類(lèi)中)
方法:
- 普通方法(保存在類(lèi)中,調(diào)用者為對(duì)象,至少有一個(gè)
self參數(shù))
class F1:
def __init__(self, name,......)
self.name = name
...
def a1(self):
print(self,name,......)
obj = F1('Alex')
obj,a1()
class F1:
def a1(self):
print('Alex')
obj = F1()
obj.a1()
- 靜態(tài)方法(保存在類(lèi)中,調(diào)用者為類(lèi),無(wú)需創(chuàng)建對(duì)象,可以有任意個(gè)參數(shù))
class F1:
@staticmethod
def a1(self):
print('Alex')
F1.a1()
六、作業(yè):選課系統(tǒng)
角色:學(xué)校、學(xué)員、課程、講師
要求:
- 創(chuàng)建北京、上海 2 所學(xué)校
- 創(chuàng)建linux , python , go 3個(gè)課程 , linux\py 在北京開(kāi), go 在上海開(kāi)
- 課程包含,周期,價(jià)格,通過(guò)學(xué)校創(chuàng)建課程
- 通過(guò)學(xué)校創(chuàng)建班級(jí), 班級(jí)關(guān)聯(lián)課程、講師
- 創(chuàng)建學(xué)員時(shí),選擇學(xué)校,關(guān)聯(lián)班級(jí)
- 創(chuàng)建講師角色時(shí)要關(guān)聯(lián)學(xué)校,
- 提供兩個(gè)角色接口
6.1 學(xué)員視圖, 可以注冊(cè), 交學(xué)費(fèi), 選擇班級(jí),
6.2 講師視圖, 講師可管理自己的班級(jí), 上課時(shí)選擇班級(jí), 查看班級(jí)學(xué)員列表 , 修改所管理的學(xué)員的成績(jī)
6.3 管理視圖,創(chuàng)建講師, 創(chuàng)建班級(jí),創(chuàng)建課程 - 上面的操作產(chǎn)生的數(shù)據(jù)都通過(guò)pickle序列化保存到文件里