對(duì)Python Pexpect 模塊的使用說(shuō)明詳解
背景介紹
Expect 程序主要用于人機(jī)對(duì)話(huà)的模擬,就是那種系統(tǒng)提問(wèn),人來(lái)回答 yes/no ,或者賬號(hào)登錄輸入用戶(hù)名和密碼等等的情況。因?yàn)檫@種情況特別多而且繁瑣,所以很多語(yǔ)言都有各種自己的實(shí)現(xiàn)。最初的第一個(gè) Expect 是由 TCL 語(yǔ)言實(shí)現(xiàn)的,所以后來(lái)的 Expect 都大致參考了最初的用法和流程,整體來(lái)說(shuō)大致的流程包括:
運(yùn)行程序
程序要求人的判斷和輸入
Expect 通過(guò)關(guān)鍵字匹配
根據(jù)關(guān)鍵字向程序發(fā)送符合的字符串
TCL 語(yǔ)言實(shí)現(xiàn)的 Expect 功能非常強(qiáng)大,我曾經(jīng)用它實(shí)現(xiàn)了防火墻設(shè)備的完整測(cè)試平臺(tái)。也因?yàn)樗褂梅奖恪⒎秶鷱V,幾乎所有腳本語(yǔ)言都實(shí)現(xiàn)了各種各樣的類(lèi)似與Expect的功能,它們叫法雖然不同,但原理都相差不大
pexpect 是 Python 語(yǔ)言的類(lèi) Expect 實(shí)現(xiàn)。從我的角度來(lái)看,它在功能上與 TCL 語(yǔ)言的實(shí)現(xiàn)還是有一些差距,比如沒(méi)有buffer_full 事件、比如沒(méi)有 expect before/after 事件等,但用來(lái)做一般的應(yīng)用還是足夠了。
基本使用流程
pexpect 的使用說(shuō)來(lái)說(shuō)去,就是圍繞3個(gè)關(guān)鍵命令做操作:
首先用 spawn 來(lái)執(zhí)行一個(gè)程序
然后用 expect 來(lái)等待指定的關(guān)鍵字,這個(gè)關(guān)鍵字是被執(zhí)行的程序打印到標(biāo)準(zhǔn)輸出上面的
最后當(dāng)發(fā)現(xiàn)這個(gè)關(guān)鍵字以后,根據(jù)關(guān)鍵字用 send 方法來(lái)發(fā)送字符串給這個(gè)程序
第一步只需要做一次,但在程序中會(huì)不停的循環(huán)第二、三步來(lái)一步一步的完成整個(gè)工作。掌握這個(gè)概念之后 pexpect 的使用就很容易了。當(dāng)然 pexpect 不會(huì)只有這 3 個(gè)方法,實(shí)際上還有很多外圍的其他方法,我們一個(gè)一個(gè)來(lái)說(shuō)明
API
spawn() - 執(zhí)行程序
spawn() 方法用來(lái)執(zhí)行一個(gè)程序,它返回這個(gè)程序的操作句柄,以后可以通過(guò)操作這個(gè)句柄來(lái)對(duì)這個(gè)程序進(jìn)行操作,比如:
process = pexpect.spawn('ftp sw-tftp')
上面 spawn() 中的字符串就是要執(zhí)行的程序,這里我們打開(kāi)一個(gè)到 sw-tftp 服務(wù)器的 ftp 連接。 spawn() 中的第一個(gè)元素就是要執(zhí)行的命令,除此之外還可以指定一些其他參數(shù),比如: pexpect.spawn('ftp sw-tftp', timeout=60) 就指定了超時(shí)時(shí)間,這些具體的會(huì)在后面講解。
process 就是 spawn() 的程序操作句柄了,之后對(duì)這個(gè)程序的所有操作都是基于這個(gè)句柄的,所以它可以說(shuō)是最重要的部分。盡量給它起個(gè)簡(jiǎn)短點(diǎn)的名字,不然后面的程序要多打不少字的。-
注意: spawn() ,或者說(shuō) pexpect 并不會(huì)轉(zhuǎn)譯任何特殊字符 比如 | * 字符在Linux的shell中有特殊含義,但是在 pexpect 中不會(huì)轉(zhuǎn)譯它們,如果在 linux 系統(tǒng)中想使用這些符號(hào)的正確含義就必須加上 shell 來(lái)運(yùn)行,這是很容易犯的一個(gè)錯(cuò)誤。
正確的方式:
process = pexpect.spawn('/bin/bash –c "ls –l | grep LOG > log_list.txt"') process.expect(pexpect.EOF)
spawn() 還有一種調(diào)用方式就是第一個(gè)參數(shù)是主程序,而下一個(gè)參數(shù)是主程序的參數(shù),理解起來(lái)很麻煩?看看實(shí)際代碼吧:
cmd = "ls –l | grep LOG > log_list.txt" process = pexpect.spawn("/bin/bash", ["-c", cmd]) process.expect(pexpect.EOF)
這些代碼和上面一個(gè)例子是相同的,是不是更清晰一些?
spawn 的選項(xiàng)包括下面這些:
timeout - 超時(shí)時(shí)間
默認(rèn)值: 30 (單位:秒)
指定程序的默認(rèn)超時(shí)時(shí)間。程序被啟動(dòng)之后會(huì)有輸出,我們也會(huì)在腳本中檢查輸出中的關(guān)鍵字是否是已知并處理的,如果指定時(shí)間內(nèi)沒(méi)找到程序就會(huì)出錯(cuò)返回。
maxread - 緩存設(shè)置
默認(rèn)值: 2000 (單位:字符)
指定一次性試著從命令輸出中讀多少數(shù)據(jù)。如果設(shè)置的數(shù)字比較大,那么從 TTY 中讀取數(shù)據(jù)的次數(shù)就會(huì)少一些。
設(shè)置為 1 表示關(guān)閉讀緩存。
設(shè)置更大的數(shù)值會(huì)提高讀取大量數(shù)據(jù)的性能,但會(huì)浪費(fèi)更多的內(nèi)存。這個(gè)值的設(shè)置與 searchwindowsize 合作會(huì)提供更多功能。
緩存的大小并不會(huì)影響獲取的內(nèi)容,也就是說(shuō)如果一個(gè)命令輸出超過(guò)2000個(gè)字符以后,先前緩存的字符不會(huì)丟失掉,而是放到其他地方去,當(dāng)你用 self.before (這里 self 代表 spawn 的實(shí)例)還是可以取到完整的輸出的。
searchwindowsize - 模式匹配閥值
默認(rèn)值: None
searchwindowsize 參數(shù)是與 maxread 參數(shù)一起合作使用的,它的功能比較微妙,但可以顯著減少緩存中有很多字符時(shí)的匹配時(shí)間。
默認(rèn)情況下, expect() 匹配指定的關(guān)鍵字都是這樣:每次緩存中取得一個(gè)字符時(shí)就會(huì)對(duì)整個(gè)緩存中的所有內(nèi)容匹配一次正則表達(dá)式,你可以想像如果程序的返回特別多的時(shí)候,性能會(huì)多么的低。
設(shè)置 searchwindowsize 的值表示一次性收到多少個(gè)字符之后才匹配一次表達(dá)式,比如現(xiàn)在有一條命令會(huì)出現(xiàn)大量的輸出,但匹配關(guān)鍵字是標(biāo)準(zhǔn)的 FTP 提示符 ftp> ,顯然要匹配的字符只有 5 個(gè)(包括空格),但是默認(rèn)情況下每當(dāng) expect 獲得一個(gè)新字符就從頭匹配一次這幾個(gè)字符,如果緩存中已經(jīng)有了 1W 個(gè)字符,一次一次的從里面匹配是非常消耗資源的,這個(gè)時(shí)候就可以設(shè)置 searchwindowsize=10, 這樣 expect 就只會(huì)從最新的(最后獲取的) 10 個(gè)字符中匹配關(guān)鍵字了,如果設(shè)置的值比較合適的話(huà)會(huì)顯著提升性能。不用擔(dān)心緩存中的字符是否會(huì)被丟棄,不管有多少輸出,只要不超時(shí)就總會(huì)得到所有字符的,這個(gè)參數(shù)的設(shè)置僅僅影響匹配的行為。
這個(gè)參數(shù)一般在 expect() 命令中設(shè)置, pexpect 2.x 版本似乎有一個(gè) bug ,在 spawn 中設(shè)置是不生效的。
logfile - 運(yùn)行輸出控制
默認(rèn)值: None
當(dāng)給 logfile 參數(shù)指定了一個(gè)文件句柄時(shí),所有從標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出獲得的內(nèi)容都會(huì)寫(xiě)入這個(gè)文件中(注意這個(gè)寫(xiě)入是 copy 方式的),如果指定了文件句柄,那么每次向程序發(fā)送指令(process.send)都會(huì)刷新這個(gè)文件(flush)。
這里有一個(gè)很重要的技巧:如果你想看到spawn過(guò)程中的輸出,那么可以將這些輸出寫(xiě)入到 sys.stdout 里去,比如:
process = pexpect.spawn("ftp sw-tftp", logfile=sys.stdout)
用這樣的方式可以看到整個(gè)程序執(zhí)行期間的輸入和輸出,很適合調(diào)試。
還有一個(gè)例子:
process = pexpect.spawn("ftp sw-tftp") logFileId = open("logfile.txt", 'w') process.logfile = logFileId
注意: logfile.txt 文件里,既包含了程序運(yùn)行時(shí)的輸出,也包含了 spawn 向程序發(fā)送的內(nèi)容,有的時(shí)候你也許不希望這樣,因?yàn)槟承﹥?nèi)容出現(xiàn)了2次,那么還有 2 個(gè)很重要的 logfile 關(guān)聯(lián)參數(shù):
logfile_read - 獲取標(biāo)準(zhǔn)輸出的內(nèi)容
默認(rèn)值: None
記錄執(zhí)行程序中返回的所有內(nèi)容,也就是去掉你發(fā)出去的命令,而僅僅只包括命令結(jié)果的部分:
process.logfile_read = sys.stdout
上面的語(yǔ)句會(huì)在屏幕上打印程序執(zhí)行過(guò)程中的所有輸出,但是一般不包含你向程序發(fā)送的命令,不過(guò)大部分程序都有回顯機(jī)制,比如發(fā)命令的時(shí)候設(shè)備不光接收到命令字符串,還會(huì)反向在你的終端上把字符串顯示出來(lái)讓你明白哪些字符被輸入了,這種時(shí)候也是會(huì)被這個(gè)方法讀到的。只有那些不會(huì)回顯的情況 logfile_read 才會(huì)拿不到,比如輸入密碼的時(shí)候。
logfile_send - 獲取發(fā)送的內(nèi)容
默認(rèn)值: None
記錄向執(zhí)行程序發(fā)送的所有內(nèi)容
process.logfile_send = sys.stdout
上面的語(yǔ)句僅僅在屏幕上打印向程序發(fā)送的內(nèi)容。
cwd - 指定命令執(zhí)行的目錄
默認(rèn)值: None 或者說(shuō) ./
cwd 用來(lái)指定命令發(fā)送的命令在哪個(gè)路徑下執(zhí)行,它一般是用在 send() 系列命令中,比如在 Linux 中,你想在 /etc 目錄下執(zhí)行 ls –l 命令,那么完全不需要用 sendline("cd /etc && ls -l") 這樣的方式,而是用 sendline("ls –l", cwd="/etc") 就可以了。
env - 指定環(huán)境變量
默認(rèn)值: None
指定環(huán)境變量的值,這個(gè)值是一個(gè)字典,如果你發(fā)送的命令要使用一些環(huán)境變量,那么可以在這里提供
ignore_sighup - 是否過(guò)濾 SIGHUP 信號(hào)
默認(rèn)值: True
這個(gè)參數(shù)是 pexpect 3.1 開(kāi)始引入的,在 3.1 之前(比如 pexpect 2.3),spawn 的子程序會(huì)過(guò)濾 SIGHUP 信號(hào),也就是用 Ctrl+C 是不能終止子程序的,3.1的默認(rèn)值也繼承了這個(gè)行為,但是如果設(shè)置 ignore_sighup = False 就可以改變這個(gè)行為。
delaybeforesend - 字符發(fā)送延時(shí)
默認(rèn)值: 0.05
這是一個(gè)隱藏參數(shù)用來(lái)設(shè)置發(fā)送字符串之前的延時(shí)。增加這個(gè)參數(shù)的最大理由是因?yàn)楹芏嗳伺鲆?jiàn)這樣一個(gè)問(wèn)題:
在 FTP 程序中登錄時(shí)如果用腳本輸入密碼時(shí)會(huì)直接顯示出來(lái)。這是基于一個(gè)一般人不可思議的事實(shí):當(dāng) FTP 登錄時(shí),實(shí)際上服務(wù)器會(huì)先打印要求你輸入密碼的提示符,然后再發(fā)一個(gè)信號(hào)把回顯功能取消,當(dāng)人使用鍵盤(pán)輸入的時(shí)候因?yàn)檫@個(gè)動(dòng)作延時(shí)比較高所以不可能看到回顯的密碼,但腳本會(huì)在發(fā)現(xiàn)輸入密碼的提示符時(shí)立即發(fā)送,于是密碼就會(huì)在關(guān)閉回顯之前出現(xiàn)了。 Pexpect 為了解決這個(gè)問(wèn)題在每次發(fā)送字符前默認(rèn)等待 50 毫秒,如果你認(rèn)為不必要的話(huà)就可以自己設(shè)置為 0 來(lái)取消這個(gè)行為。
expect() - 關(guān)鍵字匹配
當(dāng) spawn() 啟動(dòng)了一個(gè)程序并返回程序控制句柄后,就可以用 expect() 方法來(lái)等待指定的關(guān)鍵字了。它最后會(huì)返回 0 表示匹配到了所需的關(guān)鍵字,如果后面的匹配關(guān)鍵字是一個(gè)列表的話(huà),就會(huì)返回一個(gè)數(shù)字表示匹配到了列表中第幾個(gè)關(guān)鍵字,從 0 開(kāi)始計(jì)算。
expect() 利用正則表達(dá)式來(lái)匹配所需的關(guān)鍵字。(正則表達(dá)式使用范圍非常廣,幾乎所有語(yǔ)言都對(duì)它提供支持,如果不知道如何使用的話(huà),可以參考我的另一份文檔《正則表達(dá)式參考》)。
它的使用方式:
# pattern_list 正則表達(dá)式列表,表示要匹配這些內(nèi)容 # timeout 不設(shè)置或者設(shè)置為-1的話(huà),超時(shí)時(shí)間就采用self.timeout的值,默認(rèn)是30秒。也可以自己設(shè)置。 # searchwindowsize 功能和 spawn 上的一樣,但是!請(qǐng)注意這個(gè)但是!下面會(huì)實(shí)際說(shuō)明 process.expect(pattern_list, timeout=-1, searchwindowsize=None)
在這里的 searchwindowsize 是在 expect() 方法中真正生效的,默認(rèn)情況下是 None,也就是每從子進(jìn)程中獲取一個(gè)字符就做一次完整匹配,如果子進(jìn)程的輸出很多的話(huà)……性能會(huì)非常低。如果設(shè)置為其他的值,表示從子進(jìn)程中讀取到多少個(gè)字符才做一次匹配,這樣會(huì)顯著減少匹配的次數(shù),增加性能。
經(jīng)過(guò)測(cè)試,對(duì)于一個(gè)有 48100000 個(gè)字符的子進(jìn)程,將 searchwindowsize 設(shè)置為 2000 時(shí),完全處理完成需要 73.2730 秒;同樣的子進(jìn)程將這個(gè)參數(shù)設(shè)置為 None 則需要 1949.6259 秒,Oh, my Lady GAGA…… 完全是指數(shù)上升啊。
最簡(jiǎn)單的匹配方式
process.expect('[Nn]ame')
上面的代碼表示:匹配 process 這個(gè)句柄(代表 spawn 方法的例子中我們啟動(dòng)的 ftp 連接)中的 name 關(guān)鍵字,其中 n 不分大小寫(xiě)。
上面的關(guān)鍵字一旦匹配,就會(huì)返回0表示匹配成功,但是如果一直匹配不到呢?默認(rèn)是會(huì)一直等下去,但是如果設(shè)置了 timeout 的話(huà)就會(huì)超時(shí)。
匹配一系列輸出
實(shí)際上, expect() 可以匹配一系列輸出,通過(guò)檢查匹配到的輸出,我們可以做不同的事情。比如之前 spawn 的 ftp 連接,如果我們輸入用戶(hù)名之后有不同的情況,就可以通過(guò)監(jiān)控這些不同情況來(lái)做不同的動(dòng)作,比如:
index = process.expect([ 'Permission Denied', 'Terminal type', 'ftp>', ]) if index == 0: print "Permission denied at host, can't login." process.kill(0) elif index == 1: print "Login ok, set up terminal type…" process.sendline('vty100') process.expect("ftp>") elif index == 2: print "Login Ok, please send your command" process.interact()
上面的代碼中,expect 方法中的是一個(gè)列表,列表中的每個(gè)元素都是一個(gè)關(guān)鍵字的正則表達(dá)式,也就是說(shuō)我們期待這 3 種情況之一,而 expect 返回一個(gè)順序值來(lái)代表我匹配到了哪一個(gè)元素(也就是發(fā)生了哪種情況了),這個(gè)順序值是從 0 開(kāi)始計(jì)算的。
當(dāng)expect之后,下面的 if 語(yǔ)句就開(kāi)始處理這 3 種情況了:
權(quán)限不足,這可能是 ftp 服務(wù)器出現(xiàn)問(wèn)題,或者沒(méi)有這個(gè)帳號(hào),或者其他什么情況,反正只要發(fā)現(xiàn)這種情況的話(huà),我們就給用戶(hù)提示一下,然后殺掉這個(gè)進(jìn)程
登陸成功,但還要用戶(hù)指定終端模式才能真正使用,所以我們?cè)诖a中指定了 vty100 這種模式,然后看是不是能真正使用了
還是登陸成功了,而且還可以直接輸入命令操作 ftp 服務(wù)器了,于是我們提示用戶(hù),然后把操作權(quán)限交給用戶(hù)
另外有一種特殊情況,如果同時(shí)有2個(gè)被匹配到,那么怎么辦?簡(jiǎn)單來(lái)說(shuō)就是這樣:
原始流中,第一個(gè)被關(guān)鍵字匹配到的內(nèi)容會(huì)被使用
匹配關(guān)鍵字列表中,最左邊的會(huì)被使用
給個(gè)例子:
# 如果流里面的內(nèi)容是 "hello world" index = process.expect(["hi", "hello", "hello world"])
返回的值是 1,也就是 'hello' 被匹配到了,哪怕真正最好的匹配是 "hello world" 但因?yàn)榉旁诤竺嫠匀匀粺o(wú)效。
使用技巧
如果要檢查或者匹配 expect.EOF 和 expect.TIMEOUT 這兩種情形,那么必須將它們放進(jìn)匹配列表里面去,這樣可以通過(guò)檢查返回的數(shù)字來(lái)處理它們。如果沒(méi)放進(jìn)列表的話(huà),就會(huì)發(fā)生 EOF 或者 TIMEOUT 錯(cuò)誤,程序就會(huì)中途停止了
匹配規(guī)則中有些特殊語(yǔ)法,比如下面的規(guī)則中前 2 個(gè)匹配都是大小寫(xiě)無(wú)關(guān)的,關(guān)鍵就是這個(gè) (?i) 匹配規(guī)則,它相當(dāng)于 re.IGNORE 或者 re.I 這個(gè)關(guān)鍵字,因?yàn)楫吘共皇钦嬲恼齽t表達(dá)式引擎,所以 pexpect 使用這樣特殊語(yǔ)法:
child.expect(['(?i)etc', '(?i)readme', pexpect.EOF, pexpect.TIMEOUT])
expect_exact() - 精確匹配
它的使用和 expect() 是一樣的,唯一不同的就是它的匹配列表中不再使用正則表達(dá)式。
從性能上來(lái)說(shuō) expect_exact() 要更好一些,因?yàn)榧词鼓銢](méi)有使用正則表達(dá)式而只是簡(jiǎn)單的用了幾個(gè)字符 expect() 也會(huì)先將它們轉(zhuǎn)換成正則表達(dá)式模式然后再搜索,但 expect_exact() 不會(huì),而且也不會(huì)把一些特殊符號(hào)轉(zhuǎn)換掉。
expect_list() - 預(yù)轉(zhuǎn)換匹配
使用方式和 expect() 一樣,唯一不同的就是它里面接受的正則表達(dá)式列表只會(huì)轉(zhuǎn)換一次。
expect() 稍微有點(diǎn)笨,每調(diào)用一次它都會(huì)將內(nèi)部的正則表達(dá)式轉(zhuǎn)換一次(當(dāng)然也有其他辦法避免),如果你是在以后循環(huán)中調(diào)用 expect() 的話(huà),多余的轉(zhuǎn)換動(dòng)作就會(huì)降低性能,在這種情況下建議用 expect_list() 來(lái)代替。
使用方法:
# timeout 為 -1 的話(huà)使用 self.timeout 的值 # searchwindowsize 為 -1 的話(huà),也使用系統(tǒng)默認(rèn)的值 process.expect_list(pattern_list, timeout=-1, searchwindowsize=-1)
expect_loop()
用于從標(biāo)準(zhǔn)輸入中獲取內(nèi)容,loop這個(gè)詞代表它會(huì)進(jìn)入一個(gè)循環(huán),必須要從標(biāo)準(zhǔn)輸入中獲取到關(guān)鍵字才會(huì)往下繼續(xù)執(zhí)行。
使用方法:
expect_loop(self, searcher, timeout=-1, searchwindowsize=-1)
send() - 發(fā)送關(guān)鍵字
send() 作為3個(gè)關(guān)鍵操作之一,用來(lái)向程序發(fā)送指定的字符串,它的使用沒(méi)什么特殊的地方,比如:
process.expect("ftp>") process.send("by\n")
這個(gè)方法會(huì)返回發(fā)送字符的數(shù)量。
sendline() - 發(fā)送帶回車(chē)符的字符串
sendline() 和 send() 唯一的區(qū)別就是在發(fā)送的字符串后面加上了回車(chē)換行符,這也使它們用在了不同的地方:
只需要發(fā)送字符就可以的話(huà)用send()
如果發(fā)送字符后還要回車(chē)的話(huà),就用 sendline()
它也會(huì)返回發(fā)送的字符數(shù)量
sendcontrol() - 發(fā)送控制信號(hào)
sendcontrol() 向子程序發(fā)送控制字符,比如 <kbd>ctrl+C</kbd> 或者 <kbd>ctrl+D</kbd> 之類(lèi)的,比如你要向子程序發(fā)送 <kbd>ctrl+G</kbd>,那么就這樣寫(xiě):
process.sendcontrol('g')
sendeof() - 發(fā)送 EOF 信號(hào)
向子程序發(fā)送 End Of File 信號(hào)。
sendintr() - 發(fā)送終止信號(hào)
向子程序發(fā)送 SIGINT 信號(hào),相當(dāng)于 Linux 中的 kill 2 ,它會(huì)直接終止掉子進(jìn)程。
interact() - 將控制權(quán)交給用戶(hù)
interact() 表示將控制權(quán)限交給用戶(hù)(或者說(shuō)標(biāo)準(zhǔn)輸入)。一般情況下 pexpect 會(huì)接管所有的輸入和輸出,但有的時(shí)候還是希望用戶(hù)介入,或者僅僅是為了完成一部分工作的時(shí)候, interact() 就很有用了。
比如:
登陸 ftp 服務(wù)器的時(shí)候,在輸入用戶(hù)密碼階段希望用戶(hù)手工輸入密碼,然后腳本完成剩余工作時(shí)(將用戶(hù)密碼寫(xiě)在腳本中可不安全)
只希望完成登陸工作,比如要 ssh 連接到一臺(tái)遠(yuǎn)方的服務(wù)器,但中間要經(jīng)過(guò)好幾跳,用手工輸入實(shí)在太麻煩,所以就用腳本先跳到目的服務(wù)器上,然后再把控制權(quán)限還給用戶(hù)做操作。
使用方法:
# escape_character 就是當(dāng)用戶(hù)輸出這里指定的字符以后表示自己的操作完成了,將控制權(quán)重新交給 pexpect process.interact(escape_character='\x1d', input_filter=None, output_filter= None)
詳細(xì)來(lái)說(shuō),這個(gè)方法將控制權(quán)交給用戶(hù)(或者說(shuō)用戶(hù)操作的鍵盤(pán)),然后簡(jiǎn)單的將標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯(cuò)誤輸出和標(biāo)準(zhǔn)輸入綁定到系統(tǒng)上來(lái)。
通過(guò)設(shè)置 escape_character 的值,可以定義返回碼,默認(rèn)是 <kbd>ctrl+]</kbd> 或者說(shuō) <kbd>^]</kbd>,當(dāng)輸入了返回碼以后,腳本會(huì)將控制權(quán)從用戶(hù)那里重新拿回來(lái),然后繼續(xù)向下執(zhí)行。
close() - 停止應(yīng)用程序
如果想中途關(guān)閉子程序,那么可以用 close 來(lái)完成,調(diào)用這個(gè)方法后會(huì)返回這個(gè)程序的返回值。
如果設(shè)置 force=True 會(huì)強(qiáng)行關(guān)閉這個(gè)程序,大概的過(guò)程就是先發(fā)送 SIGHUP 和 SIGINT 信號(hào),如果都無(wú)效的話(huà)就發(fā) SIGKILL 信號(hào),反正不管怎么樣都會(huì)保證這個(gè)程序被關(guān)閉掉。
多次調(diào)用這個(gè)方法是允許的,但是不保證每次都能返回正確的返回值。盡量不要這么做,如果想保證程序被關(guān)閉的話(huà)只要設(shè)置force的值就可以了。
下面是實(shí)例:
process.close(force=True)
terminate() - 停止應(yīng)用程序
可以看作是上面 close() 的別名,因?yàn)椴还苁枪δ苓€是使用方法都是一樣的。
Kill() - 發(fā)送 SIGKILL 信號(hào)
向子程序發(fā)送 SIGKILL 的信號(hào)。
flush()
什么都不干,只是為了與文件方法兼容而已。
isalive() - 檢查子程序運(yùn)行狀態(tài)
檢查被調(diào)用的子程序是否正在運(yùn)行,這個(gè)方法是運(yùn)行在非阻斷模式下面的。
如果獲得的返回是 True 表示子程序正在運(yùn)行;返回 False 則表示程序運(yùn)行終止。
isatty() - 檢查是否運(yùn)行在 TTY 設(shè)備上
返回 True 表示打開(kāi)和連接到了一個(gè) tty 類(lèi)型的設(shè)備,或者返回 False 表示未連接。
next() - 返回下一行內(nèi)容
和操作文件一樣,這個(gè)方法也是返回緩存中下一行的內(nèi)容。
read() - 返回剩下的所有內(nèi)容
獲取子程序返回的所有內(nèi)容,一般情況下我們可以用 expect 來(lái)期待某些內(nèi)容,然后通過(guò) process.before 這樣的方式來(lái)獲取,但這種方式有一個(gè)前提:那就是必須先 expect 某些字符,然后才能用 process.before 來(lái)獲取緩存中剩下的內(nèi)容。
read() 的使用很不同,它期待一個(gè) EOF 信號(hào),然后將直到這個(gè)信號(hào)之前的所有輸出全部返回,就像讀一個(gè)文件那樣。
一般情況下,交互式程序只有關(guān)閉的時(shí)候才會(huì)返回 EOF ,比如用 by 命令關(guān)閉 ftp 服務(wù)器,或者用 exit 命令關(guān)閉一個(gè) ssh 連接。
這個(gè)方法使用范圍比較狹窄,因?yàn)橥耆梢杂?expect.EOF 方式來(lái)代替。當(dāng)然如果是本機(jī)命令,每執(zhí)行完一次之后都會(huì)返回 EOF ,這種情況下倒是很有用:
process = pexpect.spawn('ls –l') output = process.read() print output
看起來(lái)這么做有點(diǎn)無(wú)聊?但我想一定有什么理由支持這個(gè)方法。
可以用指定 read(size=-1) 的方式來(lái)設(shè)置返回的字符數(shù),如果沒(méi)有設(shè)置或者設(shè)置為負(fù)數(shù)則返回所有內(nèi)容,正數(shù)則返回指定數(shù)量的內(nèi)容,返回的內(nèi)容是字符串形式。
readline() - 返回一行輸出
返回一行輸出,返回的內(nèi)容包括最后的\r\n字符。
也可以設(shè)置 readline(size=-1) 來(lái)指定返回的字符數(shù),默認(rèn)是負(fù)數(shù)表示返回所有的。
readlines() - 返回列表模式的所有輸出
返回一個(gè)列表,列表中的每個(gè)元素都是一行(包括\r\n字符)。
setecho() - 子程序響應(yīng)模式
設(shè)置子程序運(yùn)行時(shí)的響應(yīng)方式,一般情況下向子程序發(fā)送字符的時(shí)候,這些字符都會(huì)在標(biāo)準(zhǔn)輸出上顯示出來(lái),這樣你可以看到你發(fā)送出去的內(nèi)容,但是有的時(shí)候,我們不需要顯示,那么就可以用這個(gè)方法來(lái)設(shè)置了。
注意,必須在發(fā)送字符之前設(shè)置,設(shè)置之后在之后的代碼中都一直有效。比如:
process = pexpect.spawn('cat') # 默認(rèn)情況下,下面的1234這個(gè)字符串會(huì)顯示2次,一次是pexpect返回的,一次是cat命令返回的 process.sendline("1234") # 現(xiàn)在我們關(guān)閉pexpect()的echo功能 process.setecho(False) # 下面的字符只會(huì)顯示一次了,這是由cat返回的 process.sendline("abcd") # 現(xiàn)在重新開(kāi)啟echo功能,就可以再次看到我們發(fā)送的字符了 process.setecho(True)
setwinsize() - 控制臺(tái)窗口大小
如果子程序是一個(gè)控制臺(tái)(TTY),比如 SSH 連接、 Telnet 連接這種通過(guò)網(wǎng)絡(luò)登陸到系統(tǒng)并發(fā)送命令的都算控制臺(tái),那么可以用這個(gè)方法來(lái)設(shè)置這個(gè)控制太的大?。ɑ蛘哒f(shuō)長(zhǎng)寬)。
它的調(diào)用方式是 process.setwinsize(r, c)
默認(rèn)值是 setwinsize(24, 80),其中 24 是高度,單位是行; 80 是寬度,單位是字符。
為什么要用它?想像下面的場(chǎng)景:
有的時(shí)候你通過(guò)pexpect登陸到某個(gè)ssh控制臺(tái)之后,又用 interact() 來(lái)將控制權(quán)交給用戶(hù),然后用戶(hù)到控制臺(tái)里面寫(xiě)自己的命令,如果命令比較長(zhǎng),就會(huì)發(fā)現(xiàn)當(dāng)命令到屏幕邊緣之后不會(huì)自動(dòng)換行,而是又返回到這一行的最前面重新覆蓋前面的字符;這不會(huì)影響命令的實(shí)際效果,但是很惱人。
這種情況用 setwinsize() 就可以解決,找到自己終端支持的長(zhǎng)度,重新設(shè)置一下,比如 setwinsize(25, 96 ),如果設(shè)置的正確的話(huà)就可以解決了。
wait() - 執(zhí)行等待
直到被調(diào)用的子程序執(zhí)行完畢之前,程序都停止(或者說(shuō)等待)執(zhí)行。它不會(huì)從被調(diào)用的子程序中讀取任何內(nèi)容。
waitnoecho()
它使用的地方比較特殊,唯一匹配的地方就是:當(dāng)子程序的 echo 功能被設(shè)置為 Fals 時(shí)。
看起來(lái)很奇怪?其實(shí)這個(gè)功能是基于一個(gè)很讓人難以置信但的確是真實(shí)的情況:
在命令行模式下,很多要求輸入密碼的地方,比如 FTP/SSH 等,密碼實(shí)際上都會(huì)在你輸入之后又重新返回并打印出來(lái)的,但是為什么我們看不到我們自己輸入的密碼呢?這就是因?yàn)槊艽a在要打印出來(lái)之前被程序?qū)?echo 功能設(shè)置為 False 了。
現(xiàn)在知道為什么有這么一個(gè)方法了吧?比如要進(jìn)行一個(gè) ssh 連接時(shí),如何檢查是否要輸入密碼?用關(guān)鍵字 password 是一個(gè)方法,但還有一個(gè)方法就是這樣:
# 啟動(dòng)ssh連接 process = pexpect.spawn("ssh user@example.com") # 等待echo被設(shè)置為False,這就意味著本地不會(huì)有回顯 process.waitnoecho() process.sendline('mypassword')
可以設(shè)置超時(shí)時(shí)間,默認(rèn)是:waitnoecho(timeout=-1),表示和系統(tǒng)設(shè)置的超時(shí)時(shí)間相同,也可以設(shè)置為 None 表示永遠(yuǎn)等待,直到回顯被設(shè)置為 False ,當(dāng)然還可以設(shè)置其他的數(shù)字來(lái)表示超時(shí)時(shí)間。
write() - 發(fā)送字符串
類(lèi)似于send()命令,只不過(guò)不會(huì)返回發(fā)送的字符數(shù)。
writelines() - 發(fā)送包含字符串的列表
類(lèi)似于 write() 命令,只不過(guò)接受的是一個(gè)字符串列表, writelines() 會(huì)向子程序一條一條的發(fā)送列表中的元素,但是不會(huì)自動(dòng)在每個(gè)元素的最后加上回車(chē)換行符。
與 write() 相似的是,這個(gè)方法也不會(huì)返回發(fā)送的字符數(shù)量。
特殊變量
pexpect.EOF - 匹配終止信號(hào)
EOF 變量使用范圍很廣泛,比如檢查 ssh/ftp/telnet 連接是否終止啊,文件是否已經(jīng)到達(dá)末尾啊。 pexpect 大部分腳本的最后都會(huì)檢查 EOF 變量來(lái)判斷是不是正常終止和退出,比如下面的代碼:
process.expect("ftp>") process.sendline("by") process.expect(pexpect.EOF) print "ftp connect terminated."
pexpect.TIMEOUT - 匹配超時(shí)信號(hào)
TIMEOUT 變量用來(lái)匹配超時(shí)的情況,默認(rèn)情況下 expect 的超時(shí)時(shí)間是 60 秒,如果超過(guò) 60 秒還沒(méi)有發(fā)現(xiàn)期待的關(guān)鍵字,就會(huì)觸發(fā)這個(gè)行為,比如:
# 匹配pexpect.TIMEOUT的動(dòng)作,只有超時(shí)事件發(fā)生的時(shí)候才會(huì)有效 index = process.expect(['ftp>', pexpect.TIMEOUT],) if index == 1: process.interactive() ; # 將控制權(quán)交給用戶(hù) elif index == 2: print "Time is out." process.kill(0) ; # 殺掉進(jìn)程 # 那么怎么改變超時(shí)時(shí)間呢?其實(shí)可以修改spawn對(duì)象里的timeout參數(shù): # 下面的例子僅僅加了一行,這樣就改變了超時(shí)的時(shí)間了 process.timeout = 300 ; # 注意這一行 index = process.expect(['ftp>', pexpect.TIMEOUT],) if index == 1: process.interactive() ; # 將控制權(quán)交給用戶(hù) elif index == 2: print "Time is out." process.kill(0) ; # 殺掉進(jìn)程
process.before/after/match - 獲取程序運(yùn)行輸出
當(dāng) expect() 過(guò)程匹配到關(guān)鍵字(或者說(shuō)正則表達(dá)式)之后,系統(tǒng)會(huì)自動(dòng)給3個(gè)變量賦值,分別是 before, after 和 match
process.before - 保存了到匹配到關(guān)鍵字為止,緩存里面已有的所有數(shù)據(jù)。也就是說(shuō)如果緩存里緩存了 100 個(gè)字符的時(shí)候終于匹配到了關(guān)鍵字,那么 before 就是除了匹配到的關(guān)鍵字之外的所有字符
process.after - 保存匹配到的關(guān)鍵字,比如你在 expect 里面使用了正則表達(dá)式,那么表達(dá)式匹配到的所有字符都在 after 里面
process.match - 保存的是匹配到的正則表達(dá)式的實(shí)例,和上面的 after 相比一個(gè)是匹配到的字符串,一個(gè)是匹配到的正則表達(dá)式實(shí)例
如果 expect() 過(guò)程中發(fā)生錯(cuò)誤,那么 before 保存到目前位置緩存里的所有數(shù)據(jù), after 和 match 都是 None
self.exitstatus | self.signalstatus
上面的2個(gè)值用來(lái)保存spawn子程序的退出狀態(tài),但是注意:只有使用了 process.close() 命令之后這 2 個(gè)參數(shù)才會(huì)被設(shè)置。
其他說(shuō)明
CR/LF約定
眾所周知的是:世界上有很多種回車(chē)換行約定,它們給我們?cè)斐闪撕芏嗦闊热纾?/p>
windows 中用 \r\n 表示回車(chē)換行
Linux like 系統(tǒng)中用 \r 表示回車(chē)換行
Mac 系統(tǒng)中用 \n 表示回車(chē)換行
這種種回車(chē)換行約定對(duì)代碼移植造成了很大的困難,幾乎所有全平臺(tái)支持的程序語(yǔ)言都有它們自己的解決方案,而 pexpect 的解決方案就是:
不管哪個(gè)平臺(tái),回車(chē)換行都替換成 \r\n
所以,如果我們要在expect中匹配回車(chē)換行符號(hào)的話(huà),就必須這么做:
process.expect('\r\n') # 想匹配一行里的最后一個(gè)單詞: process.expect('\w+\r\n') # 下面的匹配方式是錯(cuò)誤的(在其他腳本語(yǔ)言中是正確的,比如TCL語(yǔ)言的Expect實(shí)現(xiàn)中,這也是很容易搞混淆的地方): process.expect('\r')
$ * + 約定
正則表達(dá)式中, $ 符號(hào)表示從一行中的最后開(kāi)始匹配——但是在 pexpect 中是無(wú)效的。如果要匹配一行的最后,那么必須有一行數(shù)據(jù)存在,也就是有回車(chē)換行符,但是 pexpect 的處理不是按行來(lái)進(jìn)行的,它一次僅僅讀一個(gè)并且處理一個(gè)字符,而且不會(huì)處理【未來(lái)】的數(shù)據(jù)。
所以不管什么時(shí)候,都不要在 expect() 中用 $ 符號(hào)來(lái)匹配。
正因?yàn)閜expect一次僅僅處理一個(gè)字符,所以加號(hào) (+) 、星號(hào) (*) 的功能也無(wú)效了,比如:
# 無(wú)論何時(shí),都只會(huì)返回一個(gè)字符 process.expect(".+") # 無(wú)論何時(shí),都只會(huì)返回空字符 process.expect(".*")
程序調(diào)試
如果要調(diào)試pexpect,那么可以使用下面的方式:
str(processHandle) # 通過(guò) pexpect.spawn() 可以創(chuàng)建一個(gè)進(jìn)程,并通過(guò)操作這個(gè)進(jìn)程的句柄來(lái)控制程序。 # 但是如果將這個(gè)句柄用 str() 函數(shù)重載一下呢?它會(huì)顯示這個(gè)控制句柄的一系列內(nèi)部信息,比如: process = pexpect.spawn("ftp sw-tftp") print str(process) # <pexpect.spawn object at 0x2841cacc> version: 2.3 ($Revision: 399 $) command: /usr/bin/ftp args: ['/usr/bin/ftp', 'sw-tftp'] searcher: searcher_re: 0: EOF buffer (last 100 chars): before (last 100 chars): was 14494 bytes in 1 transfers. 221-Thank you for using the FTP service on sw-tftp. 221 Goodbye. after: <class 'pexpect.EOF'> match: <class 'pexpect.EOF'> match_index: 0 exitstatus: 0 flag_eof: True pid: 50733 child_fd: 3 closed: False timeout: 30 delimiter: <class 'pexpect.EOF'> logfile: None logfile_read: None logfile_send: None maxread: 2000 ignorecase: False searchwindowsize: None delaybeforesend: 0.05 delayafterclose: 0.1 delayafterterminate: 0.1
技巧和陷阱
循環(huán)匹配
Python 的 pexpect 模塊與 TCL 的 expect 相比有些功能明顯支持不足,其中就包括循環(huán)匹配。 TCL 的 expect 模塊可以給出一系列匹配關(guān)鍵字,然后通過(guò) continue 語(yǔ)句的設(shè)置保證同一個(gè) expect 可以在關(guān)鍵字列表中重新循環(huán)。
比如一個(gè) expect 有 3 個(gè)關(guān)鍵字,其中匹配到第二個(gè)關(guān)鍵字的時(shí)候會(huì)碰見(jiàn) continue 語(yǔ)句,那么下一次匹配就重復(fù)這個(gè) expect 過(guò)程,這是一個(gè)很有用的功能,比如超時(shí)時(shí)間設(shè)置為 10 秒,然后重復(fù) 3 次才會(huì)真正超時(shí)的情況。
可惜的是 Python 的 pexpect 沒(méi)有這樣的功能。但是想模擬這種情況也不是不可以,可以通過(guò) while 語(yǔ)句來(lái)完成,比如:
while True: index = process.expect([ pexpect.TIMEOUT, pexpect.EOF, ]} if index == 0: print "time is out" # 重新從開(kāi)始匹配 continue elif index == 1: print "Terminate." # 終止循環(huán) break
獲取 before 中內(nèi)容的戰(zhàn)略與清空 buffer
絕大多數(shù)情況下我們都會(huì)利用before變量來(lái)獲取命令執(zhí)行的結(jié)果。但是,你真的知道怎么用好 before 么?
before 中到底什么時(shí)候保存你所需的內(nèi)容?這個(gè)細(xì)節(jié)必須非常清楚,我們以一個(gè)調(diào)用一個(gè)命令為例子:
這里我們預(yù)計(jì)是在 linux 系統(tǒng)中,下面的 handle 是一個(gè) spawn 后的句柄,而 prompt 則是 bash 的提示符。我們預(yù)計(jì)做這樣的步驟:
匹配提示符,以此判斷系統(tǒng)已經(jīng)準(zhǔn)備好接受命令
發(fā)送命令
獲取命令執(zhí)行后的結(jié)果
handle.expect(prompt) handle.sendline("ls –l") handle.expect(prompt) output = handle.before
一共 4 個(gè)語(yǔ)句,就可以獲取 ls –l 命令的結(jié)果了,但是且慢,是否發(fā)現(xiàn)有什么不合理的地方?
第一句和第二句分別是匹配系統(tǒng)提示符和發(fā)送命令,這都是比較正常的。
但是為什么第三句是再次匹配系統(tǒng)提示符?在一般的想像下,發(fā)送命令之后,設(shè)備就會(huì)執(zhí)行并返回結(jié)果了,那么完全就可以用 handle.before 語(yǔ)句來(lái)獲取到這些內(nèi)容了才對(duì)???
實(shí)際上,從 before 這個(gè)單詞就可以大概明白,它并不是實(shí)時(shí)生效的,它里面的內(nèi)容,實(shí)際上是上一次 expect 匹配之后,除掉匹配到的關(guān)鍵字本身,系統(tǒng)緩存中剩余下來(lái)的全部?jī)?nèi)容。也就是說(shuō),如果第三句就是 output = handle.before的話(huà),那么它里面的內(nèi)容就是第一句的那個(gè) expect 中去掉 prompt 內(nèi)容后緩存中剩下來(lái)的內(nèi)容。顯然,這里面不會(huì)包括后面 ls –l 命令的內(nèi)容了。
那么想獲取 ls –l 的內(nèi)容,唯一的辦法是再增加一個(gè) expect 關(guān)鍵字匹配。這是非常關(guān)鍵的一點(diǎn)。
另外, pexpect 中的 buffer 是一個(gè)關(guān)鍵,但又不能被直接操作的變量,它保存的是運(yùn)行過(guò)程中每一個(gè) expect 之后的所有內(nèi)容,隨時(shí)被更新。而 before/after 都是直接源于它的,而 expect 的關(guān)鍵字匹配本身也是在 buffer 中做匹配的。
正因?yàn)樗闹匾裕瑢?duì)這個(gè)變量中的內(nèi)容需要特別的警惕。比如我們將登陸設(shè)備,發(fā)送命令,退出設(shè)備這3個(gè)步驟寫(xiě)進(jìn)3個(gè)函數(shù)的時(shí)候,最好保證每個(gè)步驟都不會(huì)影響下一個(gè)步驟,在每個(gè)步驟開(kāi)始的時(shí)候,最好做這樣的操作:
handle.buffer = ""
代碼實(shí)例
FTP服務(wù)器的登陸
下面的代碼比較簡(jiǎn)單,就是登陸到一個(gè) FTP 服務(wù)器,并自動(dòng)輸入密碼,等進(jìn)入服務(wù)器以后,先輸入幾個(gè)預(yù)定義的命令,然后將控制權(quán)交還給用戶(hù),用戶(hù)操作完成后按 <kbd>ctrl+]</kbd> 表示自己操作完成了,腳本再自動(dòng)退出 ftp 登陸。
#!/usr/bin/env python import sys import pexpect # FTP服務(wù)器的標(biāo)準(zhǔn)提示符 ftpPrompt = 'ftp>' # 啟動(dòng)FTP服務(wù)器,并將運(yùn)行期間的輸出都放到標(biāo)準(zhǔn)輸出中 process = pexpect.spawn('ftp sw-tftp') process.logfile_read = sys.stdout # 服務(wù)器登陸過(guò)程 process.expect('[Nn]ame') process.sendline('dev') process.expect('[Pp]assword') process.sendline('abcd1234') # 先自動(dòng)輸入一些預(yù)定命令 cmdList = ("passive", 'hash') for cmd in cmdList: process.expect(ftpPrompt) process.sendline(cmd) process.expect(ftpPrompt) # 在這里將FTP控制權(quán)交還給用戶(hù),用戶(hù)輸入完成后按 ctrl+] 再將控制權(quán)還給腳本 # ctrl+] 交還控制權(quán)給腳本是默認(rèn)值,用戶(hù)還可以設(shè)置其他的值,比如 ‘\x2a' # 就是用戶(hù)按星號(hào)的時(shí)候交還。這個(gè)值實(shí)際上是 ASCII 的16進(jìn)制碼,它們的對(duì)應(yīng)關(guān)系 # 可以自己去其他地方找一下,但是注意必須是16進(jìn)制的,并且前綴是 \x process.interact() # 當(dāng)用戶(hù)將控制權(quán)交還給腳本后,再由腳本退出ftp服務(wù)器 # 注意下面這個(gè)空的sendline()命令,它很重要。用戶(hù)將控制權(quán)交還給腳本的時(shí)候, # 腳本緩存里面是沒(méi)任何內(nèi)容的,所以也不可能匹配,這里發(fā)送一個(gè)回車(chē)符會(huì)從服務(wù)器取得 # 一些內(nèi)容,這樣就可以匹配了。 # 最后的EOF是確認(rèn)FTP連接完成的方法。 process.sendline() process.expect(ftpPrompt) process.sendline('by') process.expect(pexpect.EOF)
上面的腳本實(shí)際上缺少很多錯(cuò)誤處理,比如登陸以后用戶(hù)名或者密碼錯(cuò)誤,或者無(wú)法連接服務(wù)器之類(lèi)的,但是核心動(dòng)作已經(jīng)完整了。
以上這篇對(duì)Python Pexpect 模塊的使用說(shuō)明詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的遞歸下降分析器
這篇文章主要介紹了Python如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的遞歸下降分析器,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-08-08python實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11詳解Python中__str__和__repr__方法的區(qū)別
這篇文章主要介紹了__str__和__repr__方法的區(qū)別 ,__str__和__repr__是基本的內(nèi)置方法,使用時(shí)的區(qū)別也是Python學(xué)習(xí)當(dāng)中的基礎(chǔ),需要的朋友可以參考下2015-04-04python打包壓縮、讀取指定目錄下的指定類(lèi)型文件
這篇文章主要介紹了python打包壓縮、讀取指定目錄下的指定類(lèi)型文件,需要的朋友可以參考下2018-04-04NumPy.npy與pandas DataFrame的實(shí)例講解
今天小編就為大家分享一篇NumPy.npy與pandas DataFrame的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Python 裝飾器@,對(duì)函數(shù)進(jìn)行功能擴(kuò)展操作示例【開(kāi)閉原則】
這篇文章主要介紹了Python 裝飾器@,對(duì)函數(shù)進(jìn)行功能擴(kuò)展操作,結(jié)合實(shí)例形式分析了裝飾器的相關(guān)使用技巧,以及開(kāi)閉原則下的函數(shù)功能擴(kuò)展,需要的朋友可以參考下2019-10-10Pytorch抽取網(wǎng)絡(luò)層的Feature Map(Vgg)實(shí)例
今天小編就為大家分享一篇Pytorch抽取網(wǎng)絡(luò)層的Feature Map(Vgg)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08python爬蟲(chóng)中g(shù)et和post方法介紹以及cookie作用
本篇文章通過(guò)爬取163郵箱實(shí)例介紹了python爬蟲(chóng)中g(shù)et和post方法介紹以及cookie作用,對(duì)此有興趣的朋友學(xué)習(xí)下。2018-02-02python中使用mysql數(shù)據(jù)庫(kù)詳細(xì)介紹
這篇文章主要介紹了python中使用mysql數(shù)據(jù)庫(kù)詳細(xì)介紹,本文起講解了安裝mysql、安裝MySQL-python、mysql 的基本操作、python 操作mysql數(shù)據(jù)庫(kù)基礎(chǔ)等內(nèi)容,需要的朋友可以參考下2015-03-03Python實(shí)現(xiàn)發(fā)送與接收郵件的方法詳解
這篇文章主要介紹了Python實(shí)現(xiàn)發(fā)送與接收郵件的方法,結(jié)合實(shí)例形式分析了Python基于smtplib庫(kù)使用SMTP協(xié)議進(jìn)行郵件發(fā)送及基于poplib庫(kù)使用POP3服務(wù)器接收郵件的相關(guān)操作技巧,需要的朋友可以參考下2018-03-03