本書適合學(xué)習(xí)Python3的入門讀者,也適用對編程一無所知,但渴望用編程改變世界的朋友們!本書提倡理解為主,應(yīng)用為王。因此,只要有可能,小甲魚(作者)都會通過生動的實例來讓大家理解概念。 雖然這是一本入門書籍,但本書的“野心”可并不止于“初級水平”的教學(xué)。本書前半部分是基礎(chǔ)的語法特性講解,后半部分圍繞著Python3在爬蟲、Tkinter和游戲開發(fā)等實例上的應(yīng)用。 編程知識深似海,小甲魚沒辦法僅通過一本書將所有的知識都灌輸給你,但能夠做到的是培養(yǎng)你對編程的興趣,提高你編寫代碼的水平,以及鍛煉你的自學(xué)能力。最后,本書貫徹的核心理念是: 實用、好玩,還有參與。
本書適合入門學(xué)習(xí)Python3的讀者,也適用于對編程一無所知,但渴望用編程改變世界的朋友!本書提倡理解為主,應(yīng)用為王。本書前半部分講解Python3的基礎(chǔ)語法和高級特性,后半部分圍繞著Python3在爬蟲、Tkinter和游戲開發(fā)等實例上的應(yīng)用。本書著重培養(yǎng)讀者對編程的興趣,提高你編寫代碼的水平,以及鍛煉讀者的自學(xué)能力。*后,本書貫徹的核心理念是:實用、好玩,還有參
前言
Life is short. You need Python.
——Bruce Eckel上邊這句話是Python社區(qū)的名言,翻譯過來就是“人生苦短,我用Python”。我和Python結(jié)緣于一次服務(wù)器的調(diào)試,從此便一發(fā)不可收拾。我從來沒有遇到一門編程語言可以如此干凈、簡潔,如果你有處女座情節(jié),你一定會愛上這門語言。使用Python,可以說是很難寫出丑陋的代碼。我從來沒想過一門編程語言可以如此簡單,它太適合零基礎(chǔ)的朋友踏入編程的大門了,如果我有一個八歲的孩子,我一定會毫不猶豫地使用Python引導(dǎo)他學(xué)習(xí)編程,因為面對它,永遠(yuǎn)不缺乏樂趣。Python雖然簡單,其設(shè)計卻十分嚴(yán)謹(jǐn)。盡管Python可能沒有C或C 這類編譯型語言運行速度那么快,但是C和C 需要你無時無刻地關(guān)注數(shù)據(jù)類型、內(nèi)存溢出、邊界檢查等問題。而Python,它就像一個貼心的仆人,私底下為你都一一處理好,從來不用你操心這些,這讓你可以將全部心思放在程序的設(shè)計邏輯之上。有人說,完成相同的一個任務(wù),使用匯編語言需要1000行代碼,使用C語言需要500行,使用Java只需要100行,而使用Python,可能只要20行就可以了。這就是Python,使用它來編程,你可以節(jié)約大量編寫代碼的時間。既然Python如此簡單,會不會學(xué)了之后沒什么實際作用呢?事實上你并不用擔(dān)心這個問題,因為Python可以說是一門“萬金油”語言,在Web應(yīng)用開發(fā)、系統(tǒng)網(wǎng)絡(luò)運維、科學(xué)與數(shù)字計算、3D游戲開發(fā)、圖形界面開發(fā)、網(wǎng)絡(luò)編程中都有它的身影。目前越來越多的IT企業(yè),在招聘欄中都有“精通Python語言優(yōu)先考慮”的字樣。另外,就連Google都在大規(guī)模使用Python。好了,我知道過多的溢美之詞反而會使大家反感,所以我必須就此打住,剩下的就留給大家自己體驗吧。接下來簡單地介紹一下這本書。一年前,出版社的編輯老師無意間看到了我的一個同名的教學(xué)視頻,建議我以類似的風(fēng)格撰寫一本書。當(dāng)時我是受寵若驚的,也很興奮。剛開始寫作就遇到了不小的困難——如何將視頻中口語化的描述轉(zhuǎn)變?yōu)槲淖。?dāng)然,我希望盡可能地保留原有的幽默和風(fēng)趣——畢竟學(xué)習(xí)是要快樂的。這確實需要花不少時間去修改,但我覺得這是值得的。 本書不假設(shè)你擁有任何一方面的編程基礎(chǔ),所以本書不但適合有一定編程基礎(chǔ),想學(xué)習(xí)Python3的讀者,也適合此前對編程一無所知,但渴望用編程改變世界的朋友!本書提倡理解為主,應(yīng)用為王。因此,只要有可能,我都會通過生動的實例來讓大家理解概念。雖然這是一本入門書籍,但本書的“野心”可并不止于“初級水平”的教學(xué)。本書前半部分是基礎(chǔ)的語法特性講解,后半部分圍繞著Python3在爬蟲、Tkinter和游戲開發(fā)等實例上的應(yīng)用。編程知識深似海,沒辦法僅通過一本書將所有的知識都灌輸給你,但我能夠做到的是培養(yǎng)你對編程的興趣,提高你編寫代碼的水平,以及鍛煉你的自學(xué)能力。最后,本書貫徹的核心理念是: 實用、好玩,還有參與。本書對應(yīng)的系列視頻教程,可以在http://blog.fishc.com/category/python下載得到,也可掃描以下二維碼關(guān)注微信號進(jìn)行觀看。
編者2016年7月
目錄
第1章就這么愉快地開始吧
1.1獲得Python
1.2從IDLE啟動Python
1.3失敗的嘗試
1.4嘗試點兒新的東西
1.5為什么會這樣
第2章用Python設(shè)計第一個游戲
2.1第一個小游戲
2.2縮進(jìn)
2.3BIF
第3章成為高手前必須知道的一些基礎(chǔ)知識
3.1變量
3.2字符串
3.3原始字符串
3.4長字符串
3.5改進(jìn)我們的小游戲
3.6條件分支
3.7while循環(huán)
3.8引入外援
3.9閑聊數(shù)據(jù)類型
3.9.1整型
3.9.2浮點型
3.9.3布爾類型
3.9.4類型轉(zhuǎn)換
3.9.5獲得關(guān)于類型的信息
3.10常用操作符
3.10.1算術(shù)操作符
3.10.2優(yōu)先級問題
3.10.3比較操作符
3.10.4邏輯操作符
第4章了不起的分支和循環(huán)
4.1分支和循環(huán)
4.2課堂小練習(xí)
4.3結(jié)果分析
4.4Python可以有效避免“懸掛else”
4.5條件表達(dá)式(三元操作符)
4.6斷言
4.7while循環(huán)語句
4.8for循環(huán)語句
4.9range()
4.10break語句
4.11continue語句
第5章列表、元組和字符串
5.1列表: 一個“打了激素”的數(shù)組
5.1.1創(chuàng)建列表
5.1.2向列表添加元素
5.1.3從列表中獲取元素
5.1.4從列表刪除元素
5.1.5列表分片
5.1.6列表分片的進(jìn)階玩法
5.1.7一些常用操作符
5.1.8列表的小伙伴們
5.1.9關(guān)于分片“拷貝”概念的補(bǔ)充
5.2元組: 戴上了枷鎖的列表
5.2.1創(chuàng)建和訪問一個元組
5.2.2更新和刪除元組
5.3字符串
5.3.1各種內(nèi)置方法
5.3.2格式化
5.4序列
第6章函數(shù)
6.1Python的樂高積木
6.1.1創(chuàng)建和調(diào)用函數(shù)
6.1.2函數(shù)的參數(shù)
6.1.3函數(shù)的返回值
6.2靈活即強(qiáng)大
6.2.1形參和實參
6.2.2函數(shù)文檔
6.2.3關(guān)鍵字參數(shù)
6.2.4默認(rèn)參數(shù)
6.2.5收集參數(shù)
6.3我的地盤聽我的
6.3.1函數(shù)和過程
6.3.2再談?wù)劮祷刂?br />
6.3.3函數(shù)變量的作用域
6.4內(nèi)嵌函數(shù)和閉包
6.4.1global關(guān)鍵字
6.4.2內(nèi)嵌函數(shù)
6.4.3閉包(closure)
6.5lambda表達(dá)式
6.6遞歸
6.6.1遞歸是“神馬”
6.6.2寫一個求階乘的函數(shù)
6.6.3這幫小兔崽子
6.6.4漢諾塔
第7章字典和集合
7.1字典: 當(dāng)索引不好用時
7.1.1創(chuàng)建和訪問字典
7.1.2各種內(nèi)置方法
7.2集合: 在我的世界里,你就是唯一
7.2.1創(chuàng)建集合
7.2.2訪問集合
7.2.3不可變集合
第8章永久存儲
8.1文件: 因為懂你,所以永恒
8.1.1打開文件
8.1.2文件對象的方法
8.1.3文件的關(guān)閉
8.1.4文件的讀取和定位
8.1.5文件的寫入
8.1.6一個任務(wù)
8.2文件系統(tǒng): 介紹一個高大上的東西
8.3pickle: 腌制一缸美味的泡菜
第9章異常處理
9.1你不可能總是對的
9.2tryexcept語句
9.2.1針對不同異常設(shè)置多個except
9.2.2對多個異常統(tǒng)一處理
9.2.3捕獲所有異常
9.3tryfinally語句
9.4raise語句
9.5豐富的else語句
9.6簡潔的with語句
第10章圖形用戶界面入門
10.1導(dǎo)入EasyGui
10.2使用EasyGui
10.3修改默認(rèn)設(shè)置
第11章類和對象
11.1給大家介紹對象
11.2對象=屬性 方法
11.3面向?qū)ο缶幊?br />
11.3.1self是什么
11.3.2你聽說過Python的魔法方法嗎
11.3.3公有和私有
11.4繼承
11.4.1調(diào)用未綁定的父類方法
11.4.2使用super函數(shù)
11.5多重繼承
11.6組合
11.7類、類對象和實例對象
11.8到底什么是綁定
11.9一些相關(guān)的BIF
第12章魔法方法
12.1構(gòu)造和析構(gòu)
12.1.1__init__(self[, ...])
12.1.2__new__(cls[, ...])
12.1.3__del__(self)
12.2算術(shù)運算
12.2.1算術(shù)操作符
12.2.2反運算
12.2.3增量賦值運算
12.2.4一元操作符
12.3簡單定制
12.4屬性訪問
12.5描述符(property的原理)
12.6定制序列
12.7迭代器
12.8生成器(亂入)
第13章模塊
13.1模塊就是程序
13.2命名空間
13.3導(dǎo)入模塊
13.4__name__='__main__'
13.5搜索路徑
13.6包
13.7像個極客一樣去思考
第14章論一只爬蟲的自我修養(yǎng)
14.1入門
14.2實戰(zhàn)
14.2.1下載一只貓
14.2.2翻譯文本
14.3隱藏
14.3.1修改UserAgent
14.3.2延遲提交數(shù)據(jù)
14.3.3使用代理
14.4Beautiful Soup
14.5正則表達(dá)式
14.5.1re模塊
14.5.2通配符
14.5.3反斜杠
14.5.4字符類
14.5.5重復(fù)匹配
14.5.6特殊符號及用法
14.5.7元字符
14.5.8貪婪和非貪婪
14.5.9反斜杠 普通字母=特殊含義
14.5.10編譯正則表達(dá)式
14.5.11編譯標(biāo)志
14.5.12實用的方法
14.6異常處理
14.6.1URLError
14.6.2HTTPError
14.6.3處理異常
14.7安裝Scrapy
14.8Scrapy爬蟲之初窺門徑
14.8.1Scrapy框架
14.8.2創(chuàng)建一個Scrapy項目
14.8.3定義Item容器
14.8.4編寫爬蟲
14.8.5爬
14.8.6取
14.8.7在Shell中嘗試Selector選擇器
14.8.8使用XPath
14.8.9提取數(shù)據(jù)
14.8.10使用item
14.8.11存儲內(nèi)容
第15章GUI的最終選擇: Tkinter
15.1Tkinter之初體驗
15.2Label組件
15.3Button組件
15.4Checkbutton組件
15.5Radiobutton組件
15.6LabelFrame組件
15.7Entry組件
15.8Listbox組件
15.9Scrollbar組件
15.10Scale組件
15.11Text組件
15.11.1Indexes用法
15.11.2Marks用法
15.11.3Tags用法
15.12Canvas組件
15.13Menu組件
15.14Menubutton組件
15.15OptionMenu組件
15.16Message組件
15.17Spinbox組件
15.18PanedWindow組件
15.19Toplevel組件
15.20事件綁定
15.21事件序列
15.21.1type
15.21.2modifier
15.22Event對象
15.23布局管理器
15.23.1pack
15.23.2grid
15.23.3place
15.24標(biāo)準(zhǔn)對話框
15.24.1messagebox(消息對話框)
15.24.2filedialog(文件對話框)
15.24.3colorchooser(顏色選擇對話框)
第16章Pygame: 游戲開發(fā)
16.1安裝Pygame
16.2初步嘗試
16.3解惑
16.3.1什么是Surface對象
16.3.2將一個圖像繪制到另一個圖像上是怎么一回事
16.3.3移動圖像是怎么一回事
16.3.4如何控制游戲的速度
16.3.5Pygame的效率高不高
16.3.6我應(yīng)該從哪里獲得幫助
16.4事件
16.5提高游戲的顏值
16.5.1顯示模式
16.5.2全屏才是王道
16.5.3使窗口尺寸可變
16.5.4圖像的變換
16.5.5裁剪圖像
16.5.6轉(zhuǎn)換圖片
16.5.7透明度分析
16.6繪制基本圖形
16.6.1繪制矩形
16.6.2繪制多邊形
16.6.3繪制圓形
16.6.4繪制橢圓形
16.6.5繪制弧線
16.6.6繪制線段
16.7動畫精靈
16.7.1創(chuàng)建精靈
16.7.2移動精靈
16.8碰撞檢測
16.8.1嘗試自己寫碰撞檢測函數(shù)
16.8.2sprite模塊提供的碰撞檢測函數(shù)
16.8.3實現(xiàn)完美碰撞檢測
16.9播放聲音和音效
16.10響應(yīng)鼠標(biāo)
16.10.1設(shè)置鼠標(biāo)的位置
16.10.2自定義鼠標(biāo)光標(biāo)
16.10.3讓小球響應(yīng)光標(biāo)的移動頻率
16.11響應(yīng)鍵盤
16.12結(jié)束游戲
16.12.1發(fā)生碰撞后獲得隨機(jī)速度
16.12.2減少“抖動”現(xiàn)象的發(fā)生
16.12.3游戲勝利
16.12.4更好地結(jié)束游戲
16.13經(jīng)典飛機(jī)大戰(zhàn)
16.13.1游戲設(shè)定
16.13.2主模塊
16.13.3我方飛機(jī)
16.13.4響應(yīng)鍵盤
16.13.5飛行效果
16.13.6敵方飛機(jī)
16.13.7提升敵機(jī)速度
16.13.8碰撞檢測
16.13.9完美碰撞檢測
16.13.10一個BUG
16.13.11發(fā)射子彈
16.13.12設(shè)置敵機(jī)“血槽”
16.13.13中彈效果
16.13.14繪制得分
16.13.15暫停游戲
16.13.16控制難度
16.13.17全屏炸彈
16.13.18發(fā)放補(bǔ)給包
16.13.19超級子彈
16.13.20三次機(jī)會
16.13.21結(jié)束畫面
參考文獻(xiàn)
第5章列表、元組和字符串
5.1列表: 一個“打了激素”的數(shù)組有時候需要把一堆東西暫時存儲起來,因為它們有某種直接或者間接的聯(lián)系,需要把它們放在某種“組”或者“集合”中,因為將來可能用得上。很多接觸過編程的朋友都知道或者聽說過數(shù)組。數(shù)組這個概念呢,就是把一大堆同種類型的數(shù)據(jù)挨個兒擺在一塊兒,然后通過數(shù)組下標(biāo)進(jìn)行索引。但數(shù)組有一個基本要求,就是你所放在一起的數(shù)據(jù)必須類型一致。由于Python的變量沒有數(shù)據(jù)類型,也就是說,Python是沒有數(shù)組的。但是呢,Python加入了更為強(qiáng)大的列表。Python的列表有多強(qiáng)大?如果把數(shù)組比作是一個集裝箱的話,那么Python的列表就是一個工廠的倉庫了。列表真的非常有用,基本上所有的Python程序都要使用到列表,包括之前的打飛機(jī)游戲,里邊的小飛機(jī)可以全部扔到一個列表中統(tǒng)一管理。5.1.1創(chuàng)建列表創(chuàng)建列表和創(chuàng)建普通變量一樣,用中括號括起一堆數(shù)據(jù)就可以了,數(shù)據(jù)之間用逗號隔開,這樣一個普普通通的列表就創(chuàng)建成功了:
number = [1, 2, 3, 4, 5]
我們說列表是打了激素的數(shù)組不是沒有道理的,可以創(chuàng)建一個魚龍混雜的列表:
mix = [1, "小甲魚", 3.14, [1, 2, 3]]
可以看到上邊這個列表里有整型、字符串、浮點型數(shù)據(jù),甚至還可以包含著另一個列表。當(dāng)然,如果實在想不到要往列表里邊塞什么數(shù)據(jù)的時候,可以先創(chuàng)建一個空列表:
empty = []
5.1.2向列表添加元素列表相當(dāng)靈活,所以它的內(nèi)容不可能總是固定的,現(xiàn)在就來教大家如何向列表添加元素吧。要向列表添加元素,可以使用append()方法:
number = [1, 2, 3, 4, 5]
number.append(6)
number
[1, 2, 3, 4, 5, 6]
可以看到,參數(shù)6已經(jīng)被添加到列表number的末尾了。有讀者可能會問,這個方法調(diào)用怎么跟平時的BIF內(nèi)置函數(shù)調(diào)用不一樣呢?嗯,因為append()不是一個BIF,它是屬于列表對象的一個方法。中間這個“.”,大家暫時可以理解為范圍的意思: append()這個方法是屬于一個叫作number的列表對象的。關(guān)于對象的知識,咱暫時只需要理解這么多,后邊再給大家介紹對象。同理,我們可以把數(shù)字7和8添加進(jìn)去,但是我們發(fā)現(xiàn)似乎不能用append()同時添加多個元素:
number.append(7, 8)
Traceback (most recent call last):
File "pyshell#122", line 1, in module
number.append(7, 8)
TypeError: append() takes exactly one argument (2 given)
這時候就可以使用extend()方法向列表末尾添加多個元素:
number.extend(7, 8)
Traceback (most recent call last):
File "pyshell#123", line 1, in module
number.extend(7, 8)
TypeError: extend() takes exactly one argument (2 given)
哎呀,怎么又報錯了呢?!嗯,其實小甲魚是故意的。extend()方法事實上使用一個列表來擴(kuò)展另一個列表,所以它的參數(shù)應(yīng)該是一個列表:
number.extend([7, 8])
number
[1, 2, 3, 4, 5, 6, 7, 8]
好,我們又再一次向世界證明我們成功了!但是又發(fā)現(xiàn)了一個問題,到目前為止,我們都是往列表的末尾添加數(shù)據(jù),那如果我想“插隊”呢?當(dāng)然沒問題,想要往列表的任意位置插入元素,就要使用insert()方法。insert()方法有兩個參數(shù): 第一個參數(shù)代表在列表中的位置,第二個參數(shù)是在這個位置處插入一個元素。不妨來試一下,讓數(shù)字0出現(xiàn)在列表number的最前邊:
number.insert(1, 0)
number
[1, 0, 2, 3, 4, 5, 6, 7, 8]
等等,不是說好插入到第一個位置嗎?怎么插入后0還是排在1的后邊呢?其實是這樣的: 凡是順序索引,Python均從0開始,同時這也是大多數(shù)編程語言約定俗成的規(guī)范。那么大家知道為什么要用0來表示第一個數(shù)嗎?是因為計算機(jī)本身就是二進(jìn)制的,在二進(jìn)制的世界里只有兩個數(shù): 0和1,當(dāng)然,0就是二進(jìn)制里的第一個數(shù)了,所以嘛,秉承著這樣的傳統(tǒng),0也就習(xí)慣用于表示第一個數(shù)。所以,正確的做法應(yīng)該是:
number = [1, 2, 3, 4, 5, 6, 7, 8]
number.insert(0, 0)
number
[0, 1, 2, 3, 4, 5, 6, 7, 8]
5.1.3從列表中獲取元素跟數(shù)組一樣,可以通過元素的索引值(index)從列表獲取單個元素,注意,列表索引值是從0開始的:
name = ["雞蛋", "鴨蛋", "鵝蛋", "李狗蛋"]
name[0]
'雞蛋'
name[3]
'李狗蛋'
那按照這個方法讓“李狗蛋”和“鴨蛋”的位置互調(diào):
name[1], name[3] = name[3], name[1]
name
['雞蛋', '李狗蛋', '鵝蛋', '鴨蛋']
5.1.4從列表刪除元素從列表刪除元素,這里也介紹三種方法: remove()、del和pop()。先演示一下用remove()刪除元素:
name.remove("李狗蛋")
name
['雞蛋', '鵝蛋', '鴨蛋']
使用remove()刪除元素,你并不需要知道這個元素在列表中的具體位置,只需要知道該元素存在列表中就可以了。如果要刪除的東西根本不在列表中,程序就會報錯:
name.remove("陳鴨蛋")
Traceback (most recent call last):
File "pyshell#138", line 1, in module
name.remove("陳鴨蛋")
ValueError: list.remove(x): x not in list
remove()方法并不能指定刪除某個位置的元素,這時就要用del來實現(xiàn):
del name[1]
name
['雞蛋', '鴨蛋']
注意,del是一個語句,不是一個列表的方法,所以你不必在它后邊加上小括號()。另外,如果你想刪除整個列表,還可以直接用del加列表名刪除:
del name
name
Traceback (most recent call last):
File "pyshell#142", line 1, in module
name
NameError: name 'name' is not defined
最后,演示用pop()方法“彈出”元素:
name = ["雞蛋", "鴨蛋", "鵝蛋", "李狗蛋"]
name.pop()
'李狗蛋'
name.pop()
'鵝蛋'
name
['雞蛋', '鴨蛋']
大家看到了,pop()方法默認(rèn)是彈出列表中的最后一個元素。但這個pop()方法其實還可以靈活運用,當(dāng)你為它加上一個索引值作為參數(shù)的時候,它會彈出這個索引值對應(yīng)的元素:
name = ["雞蛋", "鴨蛋", "鵝蛋", "李狗蛋"]
name.pop(2)
'鵝蛋'
name
['雞蛋', '鴨蛋', '李狗蛋']
5.1.5列表分片利用索引值,每次可以從列表獲取一個元素,但是人總是貪心的,如果需要一次性獲取多個元素,有沒有辦法實現(xiàn)呢?利用列表分片(slice),可以方便地實現(xiàn)這個要求:
name = ["雞蛋", "鴨蛋", "鵝蛋", "李狗蛋"]
name[0:2]
['雞蛋', '鴨蛋']
很簡單對吧?只不過是用一個冒號隔開兩個索引值,左邊是開始位置,右邊是結(jié)束位置。這里要注意的一點是,結(jié)束位置上的元素是不包含的。利用列表分片,得到一個原來列表的拷貝,原來列表并沒有發(fā)生改變。列表分片也可以簡寫,我們說過Python就是以簡潔聞名于世,所以你能想到的“便捷方案”,Python的作者以及Python社區(qū)的小伙伴們都已經(jīng)想到了,并付諸實踐,你要做的就是驗證一下是否可行:
name[:2]
['雞蛋', '鴨蛋']
name[1:]
['鴨蛋', '鵝蛋', '李狗蛋']
name[:]
['雞蛋', '鴨蛋', '鵝蛋', '李狗蛋']
如果沒有開始位置,Python會默認(rèn)開始位置是0。同樣道理,如果要得到從指定索引值到列表末尾的所有元素,把結(jié)束位置省去即可。如果沒有放入任何索引值,而只有一個冒號,將得到整個列表的拷貝。再一次強(qiáng)調(diào): 列表分片就是建立原列表的一個拷貝(或者說副本),所以如果你想對列表做出某些修改,但同時還想保持原來的那個列表,那么直接使用分片的方法來獲取拷貝就很方便了。5.1.6列表分片的進(jìn)階玩法分片操作實際上還可以接收第三個參數(shù),其代表的是步長,默認(rèn)情況下(不指定它的時候)該值為1,來試試將其改成2會有什么效果?
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list1[0:9:2]
[1, 3, 5, 7, 9]
如果將步長改成2,那么每前進(jìn)兩個元素才取一個出來。其實還可以直接寫成list1[::2]。如果步長的值是負(fù)數(shù),例如-1,結(jié)果會怎樣呢?不妨試試看:
list1[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1]
是不是很有意思?這里步長設(shè)置為-1,就相當(dāng)于復(fù)制一個反轉(zhuǎn)的列表。5.1.7一些常用操作符
此前學(xué)過的大多數(shù)操作符都可以運用到列表上:
list1 = [123]
list2 = [234]
list1 list2
False
list3 = ['abc']
list4 = ['bcd']
list3 list4
True
我們發(fā)現(xiàn)列表還是挺聰明的,竟然會懂得比較大小。那如果列表中不止一個元素呢?結(jié)果又會如何?
list1 = [123, 456]
list2 = [234, 123]
list1 list2
False
怎么會這樣?list1列表的和是123 456=579,按理應(yīng)該比list2列表的和234 123=357要大,為什么list1>list2還會返回False呢?思考片刻后得出結(jié)論: Python的列表原來并沒有我們想象中那么“智能”(注: 在后邊講“魔法方法”的章節(jié)會教大家如何把列表改變得更加聰明),當(dāng)列表包含多個元素的時候,默認(rèn)是從第一個元素開始比較,只要有一個PK贏了,就算整個列表贏了。字符串比較也是同樣的道理(字符串比較的是第一個字符對應(yīng)的ASCII碼值的大小)。我們知道字符串可以用加號( )來進(jìn)行拼接,用乘號(*)來復(fù)制自身若干次。它們在列表身上也是可以實現(xiàn)的:
list1 = [123, 456]
list2 = [234, 123]
list3 = list1 list2
list3
[123, 456, 234, 123]
加號( )也叫連接操作符,它允許我們把多個列表對象合并在一起,其實就相當(dāng)于extend()方法實現(xiàn)的效果。一般情況下建議大家使用extend()方法來擴(kuò)展列表,因為這樣顯得更為規(guī)范和專業(yè)。另外,連接操作符并不能實現(xiàn)列表添加新元素的操作:
list1 = [123, 456]
list2 = list1 789
Traceback (most recent call last):
File "pyshell#177", line 1, in module
list2 = list1 789
TypeError: can only concatenate list (not "int") to list
所以如果要添加一個元素到列表中,用什么方法?嗯,可以用append()或者insert()方法,希望大家還記得。乘號(*)也叫重復(fù)操作符,重復(fù)操作符可以用于列表中:
list1 = [123]
list1 * 3
[123, 123, 123]
當(dāng)然復(fù)合賦值運算符也可以用于列表:
list1 *= 5
list1
[123, 123, 123, 123, 123]
另外有個成員關(guān)系操作符大家也不陌生,我們是在談for循環(huán)的時候認(rèn)識它的,成員關(guān)系操作符就是in和not in!
list1 = ["小豬", "小貓", "小狗", "小甲魚"]
"小甲魚" in list1
True
"小護(hù)士" not in list1
True
之前說過列表里邊可以包含另一個列表,那么對于列表中的列表元素,能不能使用in和not in測試呢?試試便知:
list1 = ["小豬", "小貓", ["小甲魚", "小護(hù)士"], "小狗"]
"小甲魚" in list1
False
"小護(hù)士" not in list1
True
可見,in和not in只能判斷一個層次的成員關(guān)系,這跟break和continue語句只能跳出一個層次的循環(huán)是一個道理。那要判斷列表里邊的列表的元素,應(yīng)該先進(jìn)入一層列表:
"小甲魚" in list1[2]
True
"小護(hù)士" not in list1[2]
False
順便說一下,前面提到使用索引號去訪問列表中的值,那么對于列表中的值,應(yīng)該如何訪問呢?大家應(yīng)該猜到了,其實跟C語言訪問二維數(shù)組的方法相似:
list1[2][0]
'小甲魚'
5.1.8列表的小伙伴們接下來認(rèn)識一下列表的小伙伴們,那么列表有多少小伙伴呢?不妨讓Python自己告訴我們:
dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
產(chǎn)生了一個熟悉又陌生的列表,很多熟悉的方法似曾相識,例如append()、extend()、insert()、pop()、remove()都是學(xué)過的,F(xiàn)在再給大家介紹幾個常用的方法。count()這個方法的作用是計算它的參數(shù)在列表中出現(xiàn)的次數(shù):
list1 = [1, 1, 2, 3, 5, 8, 13, 21]
list1.count(1)
2
index()這個方法會返回它的參數(shù)在列表中的位置:
list1.index(1)
0
可以看到,這里是返回第一個目標(biāo)(1)在list1中的位置,index()方法還有兩個參數(shù),用于限定查找的范圍。因此可以這樣查找第二個目標(biāo)在list1的位置:
start = list1.index(1) 1
stop = len(list1)
list1.index(1, start, stop)
1
reverse()方法的作用是將整個列表原地翻轉(zhuǎn),就是排最后的放到最前邊,排最前的放到最后,那么排倒數(shù)第二的就排在第二,以此類推:
list1 = [1, 2, 3, 4, 5, 6, 7, 8]
list1.reverse()
list1
[8, 7, 6, 5, 4, 3, 2, 1]
sort()這個方法是用指定的方式對列表的成員進(jìn)行排序,默認(rèn)不需要參數(shù),從小到大排隊:
list1 = [8, 9, 3, 5, 2, 6, 10, 1, 0]
list1.sort()
list1
[0, 1, 2, 3, 5, 6, 8, 9, 10]
那如果需要從大到小排隊呢?很簡單,先調(diào)用sort()方法,列表會先從小到大排好隊,然后調(diào)用reverse()方法原地翻轉(zhuǎn)就可以啦。什么?太麻煩?好吧,大家真是越來越懶了……很好,大家離天才又近了一步小甲魚個人認(rèn)為“懶”是創(chuàng)新發(fā)明的根源和動力。。其實,sort()這個方法其實有三個參數(shù),其形式為sort(func, key, reverse)。func和key參數(shù)用于設(shè)置排序的算法和關(guān)鍵字,默認(rèn)是使用歸并排序,算法問題不在這里討論,有興趣的朋友可以看一下小甲魚另一本不錯的教程: 《數(shù)據(jù)結(jié)構(gòu)和算法》(C語言)。這里要討論sort()方法的第三個參數(shù): reverse,沒錯,就是剛剛學(xué)的那個reverse()方法的那個reverse。不過這里作為sort()的一個默認(rèn)參數(shù),它的默認(rèn)值是sort(reverse=False),表示不顛倒順序。因此,只需要把False改為True,列表就相當(dāng)于從大到小排序:
list1 = [8, 9, 3, 5, 2, 6, 10, 1, 0]
list1.sort(reverse=True)
list1
[10, 9, 8, 6, 5, 3, 2, 1, 0]
5.1.9關(guān)于分片“拷貝”概念的補(bǔ)充上一節(jié)提到使用分片創(chuàng)建列表的拷貝:
list1 = [1, 3, 2, 9, 7, 8]
list2 = list1[:]
list2
[1, 3, 2, 9, 7, 8]
list3 = list1
list3
[1, 3, 2, 9, 7, 8]
看似一樣,對吧?但事實上呢?利用列表的一個小伙伴做以下修改,大家看看差別:
list1.sort()
list1
[1, 2, 3, 7, 8, 9]
list2
[1, 3, 2, 9, 7, 8]
list3
[1, 2, 3, 7, 8, 9]
可以看到list1已經(jīng)從小到大排好了序,那list2和list3呢?使用分片方式得到的list2很有原則、很有格調(diào),并不會因為list1的改變而改變,這個原理我待會兒跟大家說; 接著看list3……看,真正的墻頭草是list3,它竟然跟著list1改變了,這是為什么呢?不知道大家還記不記得在講解變量的時候說過,Python的變量就像一個標(biāo)簽,就一個名字而已……還是給大家畫個圖好理解,如圖51所示。
圖51拷貝列表
這下大家應(yīng)該明白了吧,為一個列表指定另一個名字的做法,只是向同一個列表增加一個新的標(biāo)簽而已,真正的拷貝是要使用分片的方法。這個也是初學(xué)者最容易混淆的地方,大家以后寫代碼時一定要注意哦。
5.2元組: 戴上了枷鎖的列表早在三百多年前,孟德斯鳩在《輪法的精神》里邊就提到“一切擁有權(quán)力的人都容易濫用權(quán)力,這是萬古不變的一條經(jīng)驗!钡欠彩菗碛写髾(quán)力的人,都想用自身的實踐證明孟德斯鳩是一個只會說屁話的家伙,但是他們好像都失敗了……由于列表過分強(qiáng)大,Python的作者覺得這樣似乎不妥,于是發(fā)明了列表的“表親”——元組。元組和列表最大的區(qū)別就是你可以任意修改列表中的元素,可以任意插入或者刪除一個元素,而對元組是不行的,元組是不可改變的(像字符串一樣),所以你也別指望對元組進(jìn)行原地排序等高級操作了。5.2.1創(chuàng)建和訪問一個元組元組和列表,除了不可改變這個顯著特征之外,還有一個明顯的區(qū)別是,創(chuàng)建列表用的是中括號,而創(chuàng)建元組大部分時候用的是小括號(注意,我這里說的是大部分):
tuple1 = (1, 2, 3, 4, 5, 6, 7, 8)
tuple1
(1, 2, 3, 4, 5, 6, 7, 8)
訪問元組的方式與列表無異:
tuple1[1]
2
tuple1[5:]
(6, 7, 8)
tuple1[:5]
(1, 2, 3, 4, 5)
也使用分片的方式來復(fù)制一個元組:
tuple2 = tuple1[:]
tuple2
(1, 2, 3, 4, 5, 6, 7, 8)
如果你試圖修改元組的一個元素,那么抱歉,Python會很不開心:
tuple1[1] = 1
Traceback (most recent call last):
File "pyshell#7", line 1, in module
tuple1[1] = 1
TypeError: 'tuple' object does not support item assignment
我很好奇如果問你,列表的標(biāo)志性符號是中括號([]),那么元組的標(biāo)志性符號是什么?你會怎么回答呢?小甲魚相信百分之九十的朋友都會不假思索地回答: 小括號啊,有部分比較激進(jìn)的朋友還可能會補(bǔ)充一句“小甲魚你傻?”好吧,這個問題其實也是大部分初學(xué)者所忽略和容易上當(dāng)?shù)模覀儗嶒炓幌拢?nbsp;
temp = (1)
type(temp)
class 'int'
還記得type()方法吧,作用是返回參數(shù)的類型,這里它返回說temp變量是整型(int)。再試試:
temp = 1, 2, 3
type(temp)
class 'tuple'
噢,發(fā)現(xiàn)了吧?就算沒有小括號,temp還是元組類型,所以逗號(,)才是關(guān)鍵,小括號只是起到補(bǔ)充的作用。但是你如果想要創(chuàng)建一個空元組,那么你就直接使用小括號即可:
temp = ()
type(temp)
class 'tuple'
所以這里要注意的是,如果要創(chuàng)建的元組中只有一個元素,請在它后邊加上一個逗號(,),這樣可以明確告訴Python你要的是一個元組,不要拿什么整型、浮點型來忽悠你:
temp1 = (1)
type(temp1)
class 'int'
temp2 = (1, )
type(temp2)
class 'tuple'
temp3 = 1,
type(temp3)
class 'tuple'
為了證明逗號(,)起到了決定性作用,再給大家舉個例子:
8 * (8)
64
8 * (8,)
(8, 8, 8, 8, 8, 8, 8, 8)
5.2.2更新和刪除元組有朋友可能會說,剛才不是你自己說“元組是板上釘釘不能修改的嗎”?你現(xiàn)在又來談更新一個元組,小甲魚你這不是自己打臉嗎?大家不要激動……我們只是討論一個相對含蓄的做法(直接在同一個元組上更新是不可行的,除非你學(xué)習(xí)了后邊的“魔法方法”章節(jié))。不知道大家還記不記得以前是如何更新一個字符串的?沒錯,是通過拷貝現(xiàn)有的字符串片段構(gòu)造一個新的字符串的方式解決的,對元組也是使用同樣的方法:
temp = ("小雞", "小鴨", "小豬")
temp = temp[:2] ("小甲魚",) temp[2:]
temp
('小雞', '小鴨', '小甲魚', '小豬')
上面的代碼需要在“小鴨”和“小豬”中間插入“小甲魚”,那么通過分片的方法讓元組拆分為兩部分,然后再使用連接操作符( )合并成一個新元組,最后將原來的變量名(temp)指向連接好的新元組。不妨可以把這樣的做法稱為“貍貓換太子”。在這里就要注意了,逗號是必需的,小括號也是必需的!在談到列表的時候,小甲魚跟大家說有三個方法可以刪除列表里邊的元素,但是對于元組是不可變的原則來說,單獨刪除一個元素是不可能的,當(dāng)然你可以用剛才小甲魚教給大家更新元組的方法,間接地刪除一個元素:
temp = temp[:2] temp[3:]
temp
('小雞', '小鴨', '小豬')
如果要刪除整個元組,只要使用del語句即可顯式地刪除一個元組:
del temp
temp
Traceback (most recent call last):
File "pyshell#30", line 1, in module
temp
NameError: name 'temp' is not defined
其實在日常使用中,很少使用del去刪除整個元組,因為Python的回收機(jī)制會在這個元組不再被使用到的時候自動刪除。最后小結(jié)一下哪些操作符可以使用在元組上,拼接操作符和重復(fù)操作符剛剛演示過了,關(guān)系操作符、邏輯操作符和成員關(guān)系操作符in和not in也可以直接應(yīng)用在元組上,這跟列表是一樣的,大家自己實踐一下就知道了。關(guān)于列表和元組,我們今后會談得更多,目前,就先聊到這里。
5.3字符串或許現(xiàn)在又回過頭來談字符串,有些朋友可能會覺得沒必要。其實關(guān)于字符串,還有很多你可能不知道的秘密,由于字符串在日常使用中是如此常見,因此小甲魚抱著負(fù)責(zé)任的態(tài)度在本節(jié)把所知道的都倒出來跟大家分享一下。關(guān)于創(chuàng)建和訪問字符串,前面已經(jīng)介紹過了。不過學(xué)了列表和元組,我們知道了分片的概念,事實上也可以應(yīng)用于字符串之上:
str1 = "I love fishc.com!"
str1[:6]
'I love'
接觸過C語言的朋友應(yīng)該知道,在C語言中,字符串和字符是兩個不同的概念(C語言用單引號表示字符,雙引號表示字符串)。但在Python并沒有字符這個類型,在Python看來,所謂字符,就是長度為1的字符串。當(dāng)要訪問字符串的其中一個字符的時候,只需用索引列表或元組的方法來索引字符串即可:
str1[5]
'e'
字符串跟元組一樣,都是屬于“一言既出、駟馬難追”的家伙。所以一旦定下來就不能直接對它們進(jìn)行修改了,如果必須要修改,我們就需要委曲求全……
str1[:6] " 插入的字符串" str1[6:]
'I love 插入的字符串 fishc.com!'
但是大家要注意,這種通過拼接舊字符串的各個部分得到新字符串的方式并不是真正意義上的改變原始字符串,原來的那個“家伙”還在,只是將變量指向了新的字符串(舊的字符串一旦失去了變量的引用,就會被Python的垃圾回收機(jī)制釋放掉)。像比較操作符、邏輯操作符、成員關(guān)系操作符等的操作跟列表和元組是一樣的,這里就不再啰唆了。5.3.1各種內(nèi)置方法列表和元組都有它們的方法,大家可能覺得列表的方法已經(jīng)非常多了,其實字符串更多呢。表51總結(jié)了字符串的所有方法及對應(yīng)的含義。
表51Python字符串的方法
方法含義
capitalize()把字符串的第一個字符改為大寫
casefold()把整個字符串的所有字符改為小寫
center(width)將字符串居中,并使用空格填充至長度width的新字符串
count(sub[, start[, end]])返回sub在字符串里邊出現(xiàn)的次數(shù),start和end參數(shù)表示范圍,可選
encode(encoding='utf8', errors='strict')以encoding指定的編碼格式對字符串進(jìn)行編碼
endswith(sub[, start[, end]])檢查字符串是否以sub子字符串結(jié)束,如果是返回True,否則返回False。start和end參數(shù)表示范圍,可選
expandtabs([tabsize=8])把字符串中的tab符號(\t)轉(zhuǎn)換為空格,如不指定參數(shù),默認(rèn)的空格數(shù)是tabsize=8
find(sub[, start[, end]])檢測sub是否包含在字符串中,如果有則返回索引值,否則返回-1,start和end參數(shù)表示范圍,可選續(xù)表
方法含義
index(sub[, start[, end]])跟find方法一樣,不過如果sub不在string中會產(chǎn)生一個異常
isalnum()如果字符串至少有一個字符并且所有字符都是字母或數(shù)字則返回True,否則返回False
isalpha()如果字符串至少有一個字符并且所有字符都是字母則返回True,否則返回False
isdecimal()如果字符串只包含十進(jìn)制數(shù)字則返回True,否則返回False
isdigit()如果字符串只包含數(shù)字則返回True,否則返回False
islower()如果字符串中至少包含一個區(qū)分大小寫的字符,并且這些字符都是小寫,則返回True,否則返回False
isnumeric()如果字符串中只包含數(shù)字字符,則返回True,否則返回False
isspace()如果字符串中只包含空格,則返回True,否則返回Falseistitle()如果字符串是標(biāo)題化(所有的單詞都是以大寫開始,其余字母均小寫),則返回True,否則返回Falseisupper()如果字符串中至少包含一個區(qū)分大小寫的字符,并且這些字符都是大寫,則返回True,否則返回Falsejoin(sub)以字符串作為分隔符,插入到sub中所有的字符之間ljust(width)返回一個左對齊的字符串,并使用空格填充至長度為width的新字符串lower()轉(zhuǎn)換字符串中所有大寫字符為小寫lstrip()去掉字符串左邊的所有空格partition(sub)找到子字符串sub,把字符串分成一個3元組(pre_sub, sub, fol_sub),如果字符串中不包含sub則返回('原字符串', ' ', ' ')replace(old, new[, count])把字符串中的old子字符串替換成new子字符串,如果count指定,則替換不超過count次rfind(sub[, start[, end]])類似于find()方法,不過是從右邊開始查找rindex(sub[, start[, end]])類似于index()方法,不過是從右邊開始查找rjust(width)返回一個右對齊的字符串,并使用空格填充至長度為width的新字符串rpartition(sub)類似于partition()方法,不過是從右邊開始查找rstrip()刪除字符串末尾的空格split(sep=None, maxsplit=-1)不帶參數(shù)默認(rèn)是以空格為分隔符切片字符串,如果maxsplit參數(shù)有設(shè)置,則僅分隔maxsplit個子字符串,返回切片后的子字符串拼接的列表splitlines(([keepends]))按照'\n'分隔,返回一個包含各行作為元素的列表,如果keepends參數(shù)指定,則返回前keepends行startswith(prefix[, start[, end]])檢查字符串是否以prefix開頭,是則返回True,否則返回False。start和end參數(shù)可以指定范圍檢查,可選strip([chars])刪除字符串前邊和后邊所有的空格,chars參數(shù)可以定制刪除的字符,可選swapcase()翻轉(zhuǎn)字符串中的大小寫title()返回標(biāo)題化(所有的單詞都是以大寫開始,其余字母均小寫)的字符串translate(table)根據(jù)table的規(guī)則(可以由str.maketrans('a', 'b')定制)轉(zhuǎn)換字符串中的字符upper()轉(zhuǎn)換字符串中的所有小寫字符為大寫zfill(width)返回長度為width的字符串,原字符串右對齊,前邊用0填充。
這里選幾個常用的給大家演示一下用法,首先是casefold()方法,它的作用是將字符串的所有字符變?yōu)樾懀?nbsp;
str1 = "FishC"
str1.casefold()
'fishc'
count(sub[, start[, end]])方法之前試過了,就是查找sub子字符串出現(xiàn)的次數(shù),可選參數(shù)(注: 在Python文檔中,用方括號([])括起來表示為可選)start和end表示查找的范圍:
str1 = "AbcABCabCabcABCabc"
str1.count('ab', 0, 15)
2
如果要查找某個子字符串在該字符串中的位置,可以使用find(sub[, start[, end]])或index(sub[, start[, end]])方法。如果找到了,則返回值是第一個字符的索引值; 如果找不到,則find()方法會返回-1,而index()方法會拋出異常(注: 異常是可以被捕獲并處理的錯誤,目前你可以認(rèn)為就是錯誤):
str1 = "I love fishc.com"
str1.find("fishc")
7
str1.find("good")
-1
str1.index("fishc")
7
str1.index("good")
Traceback (most recent call last):
File "pyshell#12", line 1, in module
str1.index("good")
ValueError: substring not found
今后你可能會在很多文檔中看到j(luò)oin(sub)的身影,程序員喜歡用它來連接字符串,但它的用法也許會讓你感到詫異。join是以字符串作為分隔符,插入到sub字符串中所有的字符之間:
'x'.join("Test")
'Txexsxt'
'_'.join("FishC")
'F_i_s_h_C'
為什么說“程序員喜歡用join()來連接字符串”,我們不是有很好用的連接符號( )嗎?這是因為當(dāng)使用連接符號( )去拼接大量的字符串時是非常低效率的,因為加號連接會引起內(nèi)存復(fù)制以及垃圾回收操作。所以對于大量的字符串拼接來說,使用join()方法的效率要高一些:
'I' ' ' 'love' ' ' 'fishc.com'
'I love fishc.com'
' '.join(['I', 'love', 'fishc.com'])
'I love fishc.com'
replace(old, new[, count])方法如其名,就是替換指定的字符串:
str1 = "I love you"
str1.replace("you", "fishc.com")
'I love fishc.com'
split(sep=None, maxsplit=1)跟join()正好相反,split()用于拆分字符串:
str1 = ' '.join(['I', 'love', 'fishc.com'])
str1
'I love fishc.com'
str1.split()
['I', 'love', 'fishc.com']
str2 = '_'.join("FishC")
str2.split(sep='_')
['F', 'i', 's', 'h', 'C']
5.3.2格式化前面介紹了Python字符串大部分方法的使用,但唯獨漏了一個format()方法。因為小甲魚覺得format()方法跟本節(jié)的話題如出一轍,都是關(guān)于字符串的格式化,所以放一塊來講解。
那什么是字符串的格式化,又為什么需要對字符串進(jìn)行格式化呢?舉個小例子給大家聽: 某天小甲魚召開了魚C國際互聯(lián)安全大會,到會的朋友有來自世界各地的各界精英人士,有小烏龜、喵星人、旺星人,當(dāng)然還有米奇和唐老鴨。哇噻,那氣勢簡直跟小甲魚開了個動物園一樣……但是問題來了,大家交流起來簡直是雞同鴨講,不知所云!但是最后聰明的小甲魚還是把問題給解決了,其實也很簡單,各界都找一個翻譯就行了,統(tǒng)一都翻譯成普通話,那么問題就解決了……最后我們這個大會當(dāng)然取得了卓越的成功并記入了吉尼斯動物大全。舉這個例子就是想跟大家說,格式化字符串,就是按照統(tǒng)一的規(guī)格去輸出一個字符串。如果規(guī)格不統(tǒng)一,就很可能造成誤會,例如十六進(jìn)制的10跟十進(jìn)制的10或二進(jìn)制的10完全是不同的概念(十六進(jìn)制10等于十進(jìn)制16,二進(jìn)制10等于十進(jìn)制2)。字符串格式化,正是幫助我們糾正并規(guī)范這類問題而存在的。1. format()format()方法接受位置參數(shù)和關(guān)鍵字參數(shù)(位置參數(shù)和關(guān)鍵字參數(shù)在函數(shù)章節(jié)有詳細(xì)講解),二者均傳遞到一個叫作replacement字段。而這個replacement字段在字符串內(nèi)由大括號({})表示。先看一個例子:
"{0} love {1}.{2}".format("I", "FishC", "com")
'I love FishC.com'
怎么回事呢?仔細(xì)看,字符串中的{0}、{1}和{2}應(yīng)該跟位置有關(guān),依次被format()的三個參數(shù)替換,那么format()的三個參數(shù)就叫作位置參數(shù)。那什么是關(guān)鍵字參數(shù)呢,再來看一個例子:
"{a} love .{c}".format(a="I", b="FishC", c="com")
'I love FishC.com'
{a}、和{c}就相當(dāng)于三個標(biāo)簽,format()將參數(shù)中等值的字符串替換進(jìn)去,這就是關(guān)鍵字參數(shù)啦。另外,你也可以綜合位置參數(shù)和關(guān)鍵字參數(shù)在一起使用:
"{0} love .{c}".format("I", b="FishC", c="com")
'I love FishC.com'
但要注意的是,如果將位置參數(shù)和關(guān)鍵字參數(shù)綜合在一起使用,那么位置參數(shù)必須在關(guān)鍵字參數(shù)之前,否則就會出錯:
"{a} love .{0}".format(a="I", b="FishC", "com")
SyntaxError: non-keyword arg after keyword arg
如果要把大括號打印出來,你有辦法嗎?沒錯,這跟字符串轉(zhuǎn)義字符有點像,只需要用多一層大括號包起來即可(要打印轉(zhuǎn)義字符(\),只需用轉(zhuǎn)義字符轉(zhuǎn)義本身(\\)):
"{{0}}".format("不打印")
'{0}'
位置參數(shù)“不打印”沒有被輸出,這是因為{0}的特殊功能被外層的大括號({})剝奪,因此沒有字段可以輸出。注意,這并不會產(chǎn)生錯誤哦。最后來看另一個例子:
"{0}: {1:.2f}".format("圓周率", 3.14159)
'圓周率: 3.14'
可以看到,位置參數(shù){1}跟平常有些不同,后邊多了個冒號。在替換域中,冒號表示格式化符號的開始,“.2”的意思是四舍五入到保留兩位小數(shù)點,而f的意思是定點數(shù),所以按照格式化符號的要求打印出了3.14。2. 格式化操作符: %剛才講的是字符串的格式化方法,現(xiàn)在來談?wù)勛址毾淼囊粋操作符: %,有人說,這不是求余數(shù)的操作符嗎?是的,沒錯。當(dāng)%的左右均為數(shù)字的時候,那么它表示求余數(shù)的操作; 但當(dāng)它出現(xiàn)在字符中的時候,它表示的是格式化操作符。表52列舉了Python的格式化符號及含義。
表52Python格式化符號及含義
符號含義
%c格式化字符及其ASCII碼%s格式化字符串%d格式化整數(shù)%o格式化無符號八進(jìn)制數(shù)%x格式化無符號十六進(jìn)制數(shù)%X格式化無符號十六進(jìn)制數(shù)(大寫)%f格式化浮點數(shù)字,可指定小數(shù)點后的精度%e用科學(xué)計數(shù)法格式化浮點數(shù)%E作用同%e,用科學(xué)計數(shù)法格式化浮點數(shù)%g根據(jù)值的大小決定使用%f或%e%G作用同%g,根據(jù)值的大小決定使用%f或%E
下面給大家舉幾個例子參考:
'%c' % 97
'a'
'%c%c%c%c%c' % (70, 105, 115, 104, 67)
'FishC'
'%d轉(zhuǎn)換為八進(jìn)制是: %o' % (123, 123)
'123轉(zhuǎn)換為八進(jìn)制是: 173'
'%f用科學(xué)計數(shù)法表示為: %e' % (149500000, 149500000)
'149500000.000000用科學(xué)計數(shù)法表示為: 1.495000e 08'
Python還提供了格式化操作符的輔助指令,如表53所示。
表53格式化操作符的輔助指令
符號含義
m.nm是顯示的最小總寬度,n是小數(shù)點后的位數(shù)結(jié)果左對齊 在正數(shù)前面顯示加號( )#在八進(jìn)制數(shù)前面顯示'0o',在十六進(jìn)制數(shù)前面顯示'0x64'或'0X64'0顯示的數(shù)字前面填充'0'代替空格
同樣給大家舉幾個例子供參考:
'%5.1f' % 27.658
' 27.7'
'%.2e' % 27.658
'2.77e 01'
'%10d' % 5
' 5'
'%-10d' % 5
'5 '
'%010d' % 5
'0000000005'
'%#X' % 100
'0X64'
3. Python的轉(zhuǎn)義字符及含義Python的部分轉(zhuǎn)義字符已經(jīng)使用了一段時間,是時候來給它做個總結(jié)了,見表54。
表54轉(zhuǎn)義字符及含義
符號說明符號說明
\'單引號\r回車符\"雙引號\f換頁符\a發(fā)出系統(tǒng)響鈴聲\o八進(jìn)制數(shù)代表的字符\b退格符\x十六進(jìn)制數(shù)代表的字符\n換行符\0表示一個空字符\t橫向制表符(TAB)\\反斜杠\v縱向制表符
5.4序列聰明的你可能已經(jīng)發(fā)現(xiàn),小甲魚把列表、元組和字符串放在一塊兒來講解是有道理的,因為它們之間有很多共同點: 都可以通過索引得到每一個元素。 默認(rèn)索引值總是從0開始(當(dāng)然靈活的Python還支持負(fù)數(shù)索引)。 可以通過分片的方法得到一個范圍內(nèi)的元素的集合。 有很多共同的操作符(重復(fù)操作符、拼接操作符、成員關(guān)系操作符)。我們把它們統(tǒng)稱為: 序列!下面介紹一些關(guān)于序列的常用BIF(內(nèi)置方法)。1. list([iterable])list()方法用于把一個可迭代對象轉(zhuǎn)換為列表,很多朋友經(jīng)常聽到“迭代”這個詞,但要是讓你解釋的時候,很多朋友就含糊其詞了: 迭代……迭代就是for循環(huán)嘛……這里小甲魚幫大家科普一下: 所謂迭代,是重復(fù)反饋過程的活動,其目的通常是為了接近并到達(dá)所需的目標(biāo)或結(jié)果。每一次對過程的重復(fù)被稱為一次“迭代”,而每一次迭代得到的結(jié)果會被用來作為下一次迭代的初始值……就目前來說,迭代還就是一個for循環(huán),但今后會介紹到迭代器,那個功能,那叫一個驚艷!好了,這里說list()方法要么不帶參數(shù),要么帶一個可迭代對象作為參數(shù),而這個序列天生就是可迭代對象(迭代這個概念實際上就是從序列中泛化而來的)。還是通過幾個例子給大家講解吧:
# 創(chuàng)建一個空列表
a = list()
a
[]
# 將字符串的每個字符迭代存放到列表中
b = list("FishC")
b
['F', 'i', 's', 'h', 'C']
# 將元組中的每個元素迭代存放到列表中
c = list((1, 1, 2, 3, 5, 8, 13))
c
[1, 1, 2, 3, 5, 8, 13]
事實上這個list()方法大家自己也可以動手實現(xiàn)對不對?很簡單嘛,實現(xiàn)過程大概就是新建一個列表,然后循環(huán)通過索引迭代參數(shù)的每一個元素并加入列表,迭代完畢后返回列表即可。大家課后不妨自己動動手來嘗試一下。2. tuple([iterable]) tuple()方法用于把一個可迭代對象轉(zhuǎn)換為元組,具體的用法和list()一樣,這里就不啰唆了。3. str(obj)str()方法用于把obj對象轉(zhuǎn)換為字符串,這個方法在前面結(jié)合int()和float()方法給大家講過,還記得吧?4. len(sub)len()方法用于返回sub參數(shù)的長度:
str1 = "I love fishc.com"
len(str1)
16
list1 = [1, 1, 2, 3, 5, 8, 13]
len(list1)
7
tuple1 = "這", "是", "一", "個", "元組"
len(tuple1)
5
5. max()max()方法用于返回序列或者參數(shù)集合中的最大值,也就是說,max()的參數(shù)可以是一個序列,返回值是該序列中的最大值; 也可以是多個參數(shù),那么max()將返回這些參數(shù)中最大的一個:
list1 = [1, 18, 13, 0, -98, 34, 54, 76, 32]
max(list1)
76
str1 = "I love fishc.com"
max(str1)
'v'
max(5, 8, 1, 13, 5, 29, 10, 7)
29
6. min(…)min()方法跟max()用法一樣,但效果相反: 返回序列或者參數(shù)集合中的最小值。這里需要注意的是,使用max()方法和min()方法都要保證序列或參數(shù)的數(shù)據(jù)類型統(tǒng)一,否則會出錯。
list1 = [1, 18, 13, 0, -98, 34, 54, 76, 32]
list1.append("x")
max(list1)
Traceback (most recent call last):
File "pyshell#22", line 1, in module
max(list1)
TypeError: unorderable types: str() int()
min(123, 'oo', 456, 'xx')
Traceback (most recent call last):
File "pyshell#23", line 1, in module
min(123, 'oo', 456, 'xx')
TypeError: unorderable types: str() int()
人家說: 外行看熱鬧,內(nèi)行看門道。分析一下這個錯誤信息。Python這里說“TypeError: unorderable types: str() > int()”,意思是說不能拿字符串和整型進(jìn)行比較。這說明了什么呢?你看,str() > int(),說明max()方法和min()方法的內(nèi)部實現(xiàn)事實上類似于之前提到的,通過索引得到每一個元素,然后將各個元素進(jìn)行對比。所以不妨根據(jù)猜想寫出可能的代碼:
# 猜想下max(tuple1)的實現(xiàn)方式
temp = tuple1[0]
for each in tuple1:
if each temp:
temp = each
return temp
由此可見,Python的內(nèi)置方法其實也沒啥了不起的,有些我們也可以自己實現(xiàn),對吧?所以只要認(rèn)真跟著本書的內(nèi)容學(xué)習(xí)下去,很多看似如狼似虎的問題,將來都能迎刃而解!7. sum(iterable[, start])sum()方法用于返回序列iterable的總和,用法跟max()和min()一樣。但sum()方法有一個可選參數(shù)(start),如果設(shè)置該參數(shù),表示從該值開始加起,默認(rèn)值是0:
tuple1 = 1, 2, 3, 4, 5
sum(tuple1)
15
sum(tuple1, 10)
25
8. sorted(iterable, key=None, reverse=False)sorted()方法用于返回一個排序的列表,大家還記得列表的內(nèi)建方法sort()嗎?它們的實現(xiàn)效果一致,但列表的內(nèi)建方法sort()是實現(xiàn)列表原地排序; 而sorted()是返回一個排序后的新列表。
list1 = [1, 18, 13, 0, -98, 34, 54, 76, 32]
list2 = list1[:]
list1.sort()
list1
[-98, 0, 1, 13, 18, 32, 34, 54, 76]
sorted(list2)
[-98, 0, 1, 13, 18, 32, 34, 54, 76]
list2
[1, 18, 13, 0, -98, 34, 54, 76, 32]
9. reversed(sequence)reversed()方法用于返回逆向迭代序列的值。同樣的道理,實現(xiàn)效果跟列表的內(nèi)建方法reverse()一致。區(qū)別是列表的內(nèi)建方法是原地翻轉(zhuǎn),而reversed()是返回一個翻轉(zhuǎn)后的迭代器對象。你沒看錯,它不是返回一個列表,是返回一個迭代器對象:
list1 = [1, 18, 13, 0, -98, 34, 54, 76, 32]
reversed(list1)
list_reverseiterator object at 0x000000000324F518
for each in reversed(list1):
print(each, end=',')
32,76,54,34,-98,0,13,18,1,
10. enumerate(iterable)enumerate()方法生成由二元組(二元組就是元素數(shù)量為二的元組)構(gòu)成的一個迭代對象,每個二元組是由可迭代參數(shù)的索引號及其對應(yīng)的元素組成的。舉個例子你就明白了:
str1 = "FishC"
for each in enumerate(str1):
print(each)
(0, 'F')
(1, 'i')
(2, 's')
(3, 'h')
(4, 'C')
11. zip(iter1 [,iter2 [...]])zip()方法用于返回由各個可迭代參數(shù)共同組成的元組,舉個例子比較容易理解:
list1 = [1, 3, 5, 7, 9]
str1 = "FishC"
for each in zip(list1, str1):
print(each)
(1, 'F')
(3, 'i')
(5, 's')
(7, 'h')
(9, 'C')
tuple1 = (2, 4, 6, 8, 10)
for each in zip(list1, str1, tuple1):
print(each)
(1, 'F', 2)
(3, 'i', 4)
(5, 's', 6)
(7, 'h', 8)
(9, 'C', 10)