尋夢新聞LINE@每日推播熱門推薦文章,趣聞不漏接❤️
全世界大概有幾百種編程語言,而其中著名的只有十幾種。Python就是著名編程語言之一。它起源於「龜叔」(Guido van Rossum,荷蘭人)在1989年聖誕節間的無聊,而為了打發時間所創造。
可見業餘時間比工作時間創造出來的東西更容易出名哈,當然,這是開玩笑的。「龜叔」賦予Python「優雅、明確、簡單」的特點。
那麼,Python適合做什麼呢?它主要適用領域:Web網站和各種網路服務(YouTube、Instagram、douban、openstack);系統工具和腳本;作為「膠水」語言把其它語言開發的模塊包裝起來方便使用。
優點是顯著的,但是也少不了缺點。Python不適用的領域:貼近硬件的代碼(首選C);移動開發(iOS/Android都有各自的開發語言);因為不能做到告訴渲染,所以不適合遊戲開發(首選C/C++)。
Python和其它語言對比
語言 | 類型 | 運行速度 | 代碼量 |
---|---|---|---|
C | 編譯為機器碼 | 非常快 | 非常多 |
Java | 編譯為字節碼 | 快 | 多 |
Python | 解釋執行 | 慢 | 少 |
CPU越來越快,程序的運行速度瓶頸往往不在Python的執行,更多受到網路速度和硬盤速度的制約。
另外,Python發布程序即發布源代碼。
安裝Python
前往官網下載對應平台對應工具。另外Python2.7版本和3.3版本並不兼容,所以開發時請注意使用Python的版本。
作為Mac OS X使用者,其實更推薦PyCharm IDE。安裝之後直接使用即可。
數據類型
計算機顧名思義就是可以做數學計算的機器,因此,計算機程序理所當然地可以處理各種數值。但是,計算機能處理的遠不止數值,還可以處理文本、圖形、音頻、視頻、網頁等各種各樣的數據,不同的數據,需要定義不同的數據類型。在Python中,能夠直接處理的數據類型有以下幾種:
整數
Python可以處理任意大小的整數,當然包括負整數,在Python程序中,整數的表示方法和數學上的寫法一模一樣,例如:1,100,-8080,0,等等。
計算機由於使用二進制,所以,有時候用十六進制表示整數比較方便,十六進制用0x前綴和0-9,a-f表示,例如:0xff00,0xa5b4c3d2,等等。
浮點數
浮點數也就是小數,之所以稱為浮點數,是因為按照科學記數法表示時,一個浮點數的小數點位置是可變的,比如,1.23×10^9 和 12.3×10^8 是相等的。浮點數可以用數學寫法,如1.23,3.14,-9.01,等等。但是對於很大或很小的浮點數,就必須用科學計數法表示,把10用e替代,1.23×10^9 就是1.23e9,或者12.3e8,0.000012可以寫成1.2e-5,等等。
整數和浮點數在計算機內部存儲的方式是不同的,整數運算永遠是精確的(除法難道也是精確的?是的!),而浮點數運算則可能會有四舍五入的誤差。
字符串
字符串是以’’或」」括起來的任意文本,比如’abc’,」xyz」等等。請注意,’’或」」本身只是一種表示方式,不是字符串的一部分,因此,字符串’abc’只有a,b,c這3個字符。
布爾值
布爾值和布爾代數的表示完全一致,一個布爾值只有True、False兩種值,要麼是True,要麼是False,在Python中,可以直接用True、False表示布爾值(請注意大小寫),也可以通過布爾運算計算出來。
布爾值可以用and、or和not運算。
and運算是與運算,只有所有都為 True,and運算結果才是 True。
or運算是或運算,只要其中有一個為 True,or 運算結果就是 True。
not運算是非運算,它是一個單目運算符,把 True 變成 False,False 變成 True。
空值
空值是Python里一個特殊的值,用None表示。None不能理解為0,因為0是有意義的,而None是一個特殊的空值。
此外,Python還提供了列表、字典等多種數據類型,還允許創建自定義數據類型。
print 語句
print語句可以向螢幕上輸出指定的文字。比如輸出’hello, world’,用代碼做到如下:
>>> print 'hello, world'
注意:
1.當我們在Python交互式環境下編寫代碼時,>>>是Python解釋器的提示符,不是代碼的一部分。
2.當我們在文本編輯器中編寫代碼時,千萬不要自己添加 >>>。
print語句也可以跟上多個字符串,用逗號「,」隔開,就可以連成一串輸出:
>>> print 'The quick brown fox', 'jumps over', 'the lazy dog'The quick brown fox jumps over the lazy dog
print會依次列印每個字符串,遇到逗號「,」會輸出一個空格,因此,輸出的字符串是這樣拼起來的:
print也可以列印整數,或者計算結果:
>>> print 300300 #運行結果>>> print 100 + 200300 #運行結果
因此,我們可以把計算100 + 200的結果列印得更漂亮一點:
>>> print '100 + 200 =', 100 + 200100 + 200 = 300 #運行結果
注意: 對於100 + 200,Python解釋器自動計算出結果300,但是,’100 + 200 =’是字符串而非數學公式,Python把它視為字符串,請自行解釋上述列印結果。
Python的註釋
任何時候,我們都可以給程序加上註釋。註釋是用來說明代碼的,給自己或別人看,而程序運行的時候,Python解釋器會直接忽略掉註釋,所以,有沒有註釋不影響程序的執行結果,但是影響到別人能不能看懂你的代碼。
Python的註釋以#
開頭,後面的文字直到行尾都算註釋
# 這一行全部都是註釋...print 'hello' # 這也是註釋
註釋還有一個巧妙的用途,就是一些代碼我們不想運行,但又不想刪除,就可以用註釋暫時屏蔽掉:
# 暫時不想運行下面一行代碼: # print 'hello, python.'
變量
在Python中,變量的概念基本上和初中代數的方程變量是一致的。
例如,對於方程式y=x*x
,x就是變量。當x=2
時,計算結果是4,當x=5
時,計算結果是25。
只是在計算機程序中,變量不僅可以是數字,還可以是任意數據類型。
在Python程序中,變量是用一個變量名表示,變量名必須是 大小寫英文、數字和下劃線(_)的組合,且不能用數字開頭 ,比如:
a = 1
變量a
是一個整數。
t_007 = 'T007'
變量t_007
是一個字符串。
在Python中,等號=是賦值語句,可以把任意數據類型賦值給變量,同一個變量可以反復賦值,而且可以是不同類型的變量,例如:
a = 123 # a是整數print a a = 'Chars' # a變為字符串print a
這種變量本身類型不固定的語言稱之為動態語言,與之對應的是靜態語言。
靜態語言在定義變量時必須指定變量類型,如果賦值的時候類型不匹配,就會報錯。例如Java是靜態語言,賦值語句如下(// 表示註釋):
int a = 123; // a是整數類型變量a = "Chars"; // 錯誤:不能把字符串賦給整型變量
和靜態語言相比,動態語言更靈活,就是這個原因。
請不要把賦值語句的等號等同於數學的等號。比如下面的代碼:
x = 10x = x + 2
如果從數學上理解x = x + 2
那無論如何是不成立的,在程序中,賦值語句先計算右側的表達式x + 2
,得到結果12,再賦給變量x。由於x之前的值是10,重新賦值後,x的值變成12。
最後,理解變量在計算機內存中的表示也非常重要。當我們寫:a = 'ABC'
時,Python解釋器幹了兩件事情:
- 在內存中創建了一個’ABC’的字符串;
- 在內存中創建了一個名為a的變量,並把它指向’ABC’。
也可以把一個變量a賦值給另一個變量b,這個操作實際上是把變量b指向變量a所指向的數據,例如下面的代碼:
a = 'ABC'b = a a = 'XYZ'print b
最後一行列印出變量b的內容到底是'ABC'
呢還是'XYZ'
?如果從數學意義上理解,就會錯誤地得出b和a相同,也應該是'XYZ'
,但實際上b的值是'ABC'
,讓我們一行一行地執行代碼,就可以看到到底發生了什麼事:
執行a = 'ABC'
,解釋器創建了字符串'ABC'
和變量a
,並把a指向'ABC'
:
執行b = a
,解釋器創建了變量 b,並把b指向 a 指向的字符串'ABC'
:
執行a = 'XYZ'
,解釋器創建了字符串'XYZ'
,並把a的指向改為'XYZ'
,但b並沒有更改:
所以,最後列印變量b的結果自然是'ABC'
了。
字符串
定義字符串
前面我們講解了什麼是字符串。字符串可以用''
或者""
括起來表示。
如果字符串本身包含’怎麼辦?比如我們要表示字符串I'm OK
,這時,可以用" "
括起來表示:
"I'm OK"
類似的,如果字符串包含」,我們就可以用’ ‘括起來表示:
'Learn "Python" in Chars's Blog'
如果字符串既包含’又包含」怎麼辦?
這個時候,就需要對字符串的某些特殊字符進行「轉義」,Python字符串用\
進行轉義。
要表示字符串Bob said "I'm OK"
.
由於 ‘ 和 「 會引起歧義,因此,我們在它前面插入一個\
表示這是一個普通字符,不代表字符串的起始,因此,這個字符串又可以表示為
'Bob said \"I\'m OK\".'
注意:轉義字符 \ 不計入字符串的內容中。
常用的轉義字符還有:
\n 表示換行\t 表示一個制表符\\ 表示 \ 字符本身
raw字符串與多行字符串
如果一個字符串包含很多需要轉義的字符,對每一個字符都進行轉義會很麻煩。為了避免這種情況,我們可以在字符串前面加個前綴r
,表示這是一個 raw 字符串,里面的字符就不需要轉義了。例如:
r'\(~_~)/ \(~_~)/'
但是r'...'
表示法不能表示多行字符串,也不能表示包含'
和"
的字符串(為什麼?)
如果要表示多行字符串,可以用'''...'''
表示:
'''Line 1 Line 2 Line 3'''
上面這個字符串的表示方法和下面的是完全一樣的:
'Line 1\nLine 2\nLine 3'
還可以在多行字符串前面添加r
,把這個多行字符串也變成一個raw字符串:
r'''Python is created by "Guido". It is free and easy to learn. Let's start learn Python in Chars's Blog!'''
Unicode字符串
字符串還有一個編碼問題。
因為計算機只能處理數字,如果要處理文本,就必須先把文本轉換為數字才能處理。最早的計算機在設計時採用8個比特(bit)作為一個字節(byte),所以,一個字節能表示的最大的整數就是255(二進制11111111=十進制255),0 – 255被用來表示大小寫英文字母、數字和一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母 A 的編碼是65,小寫字母 z 的編碼是122。
如果要表示中文,顯然一個字節是不夠的,至少需要兩個字節,而且還不能和ASCII編碼衝突,所以,中國制定了GB2312編碼,用來把中文編進去。
類似的,日文和韓文等其他語言也有這個問題。為了統一所有文字的編碼,Unicode應運而生。Unicode把所有語言都統一到一套編碼里,這樣就不會再有亂碼問題了。
Unicode通常用兩個字節表示一個字符,原有的英文編碼從單字節變成雙字節,只需要把高字節全部填為0就可以。
因為Python的誕生比Unicode標準發布的時間還要早,所以最早的Python只支持ASCII編碼,普通的字符串’ABC’在Python內部都是ASCII編碼的。
Python在後來添加了對Unicode的支持,以Unicode表示的字符串用u’…’表示,比如:
print u'中文'中文
注意: 不加 u ,中文就不能正常顯示。
Unicode字符串除了多了一個 u 之外,與普通字符串沒什麼區別,轉義字符和多行表示法仍然有效:
轉義:
u'中文\n日文\n韓文'
多行:
u'''第一行 第二行'''
raw+多行:
ur'''Python的Unicode字符串支持"中文", "日文", "韓文"等多種語言'''
如果中文字符串在Python環境下遇到UnicodeDecodeError
,這是因為.py文件保存的格式有問題。可以在第一行添加註釋
# -*- coding: utf-8 -*-
目的是告訴Python解釋器,用UTF-8編碼讀取源代碼。然後用Notepad++ 另存為… 並選擇UTF-8格式保存。
List
創建list
Python內置的一種數據類型是列表:list。list是一種有序的集合,可以隨時添加和刪除其中的元素。
比如,列出班里所有同學的名字,就可以用一個list表示:
>>> ['Michael', 'Bob', 'Tracy'] ['Michael', 'Bob', 'Tracy']
list是數學意義上的有序集合,也就是說,list中的元素是按照順序排列的。
構造list非常簡單,按照上面的代碼,直接用 [ ] 把list的所有元素都括起來,就是一個list對象。通常,我們會把list賦值給一個變量,這樣,就可以通過變量來引用list:
>>> classmates = ['Michael', 'Bob', 'Tracy']>>> classmates # 列印classmates變量的內容['Michael', 'Bob', 'Tracy']
由於Python是動態語言,所以list中包含的元素並不要求都必須是同一種數據類型,我們完全可以在list中包含各種數據:
>>> L = ['Michael', 100, True]
一個元素也沒有的list,就是空list:
>>> empty_list = []
按照索引訪問list
由於list是一個有序集合,所以,我們可以用一個list按分數從高到低表示出班里的3個同學:
>>> L = ['Adam', 'Lisa', 'Bart']
那我們如何從list中獲取指定第 N 名的同學呢?方法是通過索引來獲取list中的指定元素。
需要特別注意的是,索引從 0 開始,也就是說,第一個元素的索引是0,第二個元素的索引是1,以此類推。
因此,要列印第一名同學的名字,用 L[0]:
>>> print L[0] Adam
要列印第二名同學的名字,用 L[1]:
>>> print L[1] Lisa
要列印第三名同學的名字,用 L[2]:
>>> print L[2] Bart
要列印第四名同學的名字,用 L[3]:
>>> print L[3] Traceback (most recent call last): File "<stdin>", line 1, in <module>IndexError: list index out of range
報錯了!IndexError意思就是索引超出了範圍,因為上面的list只有3個元素,有效的索引是 0,1,2。
所以,使用索引時,千萬注意不要越界。
倒序訪問list
我們還是用一個list按分數從高到低表示出班里的3個同學:
>>> L = ['Adam', 'Lisa', 'Bart']
這時,老師說,請分數最低的同學站出來。
要寫代碼完成這個任務,我們可以先數一數這個 list,發現它包含3個元素,因此,最後一個元素的索引是2:
>>> print L[2] Bart
有沒有更簡單的方法?有!
Bart同學是最後一名,俗稱倒數第一,所以,我們可以用 -1 這個索引來表示最後一個元素:
>>> print L[-1] Bart
Bart同學表示躺槍。
類似的,倒數第二用 -2 表示,倒數第三用 -3 表示,倒數第四用 -4 表示:
>>> print L[-2] Lisa >>> print L[-3] Adam >>> print L[-4] Traceback (most recent call last): File "<stdin>", line 1, in <module>IndexError: list index out of range
L[-4] 報錯了,因為倒數第四不存在,一共只有3個元素。
使用倒序索引時,也要注意不要越界。
添加新元素
現在,班里有3名同學:
>>> L = ['Adam', 'Lisa', 'Bart']
今天,班里轉來一名新同學 Paul,如何把新同學添加到現有的 list 中呢?
第一個辦法是用 list 的 append() 方法,把新同學追加到 list 的末尾:
>>> L = ['Adam', 'Lisa', 'Bart']>>> L.append('Paul')>>> print L ['Adam', 'Lisa', 'Bart', 'Paul']
append()總是把新的元素添加到 list 的尾部。
如果 Paul 同學表示自己總是考滿分,要求添加到第一的位置,怎麼辦?
方法是用list的 insert()方法,它接受兩個參數,第一個參數是索引號,第二個參數是待添加的新元素:
>>> L = ['Adam', 'Lisa', 'Bart']>>> L.insert(0, 'Paul')>>> print L ['Paul', 'Adam', 'Lisa', 'Bart']
L.insert(0, ‘Paul’) 的意思是,’Paul’將被添加到索引為 0 的位置上(也就是第一個),而原來索引為 0 的Adam同學,以及後面的所有同學,都自動向後移動一位。
刪除元素
Paul同學剛來幾天又要轉走了,那麼我們怎麼把Paul 從現有的list中刪除呢?
如果Paul同學排在最後一個,我們可以用list的pop()方法刪除:
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']>>> L.pop()'Paul'>>> print L ['Adam', 'Lisa', 'Bart']
pop()方法總是刪掉list的最後一個元素,並且它還返回這個元素,所以我們執行 L.pop() 後,會列印出 ‘Paul’。
如果Paul同學不是排在最後一個怎麼辦?比如Paul同學排在第三:
>>> L = ['Adam', 'Lisa', 'Paul', 'Bart']
要把Paul踢出list,我們就必須先定位Paul的位置。由於Paul的索引是2,因此,用 pop(2)把Paul刪掉:
>>> L.pop(2)'Paul'>>> print L ['Adam', 'Lisa', 'Bart']
替換元素
假設現在班里仍然是3名同學:
>>> L = ['Adam', 'Lisa', 'Bart']
現在,Bart同學要轉學走了,碰巧來了一個Paul同學,要更新班級成員名單,我們可以先把Bart刪掉,再把Paul添加進來。
另一個辦法是直接用Paul把Bart給替換掉:
>>> L[2] = 'Paul'>>> print L L = ['Adam', 'Lisa', 'Paul']
對list中的某一個索引賦值,就可以直接用新的元素替換掉原來的元素,list包含的元素個數保持不變。
由於Bart還可以用 -1 做索引,因此,下面的代碼也可以完成同樣的替換工作:
>>> L[-1] = 'Paul'
Tuple
創建tuple
tuple是另一種有序的列表,中文翻譯為「 元組 」。tuple 和 list 非常類似,但是,tuple一旦創建完畢,就不能修改了。
同樣是表示班里同學的名稱,用tuple表示如下:
>>> t = ('Adam', 'Lisa', 'Bart')
創建tuple和創建list唯一不同之處是用( )替代了[ ]。
現在,這個 t 就不能改變了,tuple沒有 append()方法,也沒有insert()和pop()方法。所以,新同學沒法直接往 tuple 中添加,老同學想退出 tuple 也不行。
獲取 tuple 元素的方式和 list 是一模一樣的,我們可以正常使用 t[0],t[-1]等索引方式訪問元素,但是不能賦值成別的元素,不信可以試試:
>>> t[0] = 'Paul'Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: 'tuple' object does not support item assignment
創建單元素tuple
tuple和list一樣,可以包含 0 個、1個和任意多個元素。
包含多個元素的 tuple,前面我們已經創建過了。
包含 0 個元素的 tuple,也就是空tuple,直接用 ()表示:
>>> t = ()>>> print t ()
創建包含1個元素的 tuple 呢?來試試:
>>> t = (1)>>> print t1
好像哪里不對!t 不是 tuple ,而是整數1。為什麼呢?
因為()既可以表示tuple,又可以作為括號表示運算時的優先級,結果 (1) 被Python解釋器計算出結果 1,導致我們得到的不是tuple,而是整數 1。
正是因為用()定義單元素的tuple有歧義,所以 Python 規定,單元素 tuple 要多加一個逗號「,」,這樣就避免了歧義:
>>> t = (1,)>>> print t (1,)
可變的tuple
前面我們看到了tuple一旦創建就不能修改。現在,我們來看一個「可變」的tuple:
>>> t = ('a', 'b', ['A', 'B'])
注意到 t 有 3 個元素:’a’,’b’和一個list:[‘A’, ‘B’]。list作為一個整體是tuple的第3個元素。list對象可以通過 t[2] 拿到:
>>> L = t[2]
然後,我們把list的兩個元素改一改:
>>> L[0] = 'X'>>> L[1] = 'Y'
再看看tuple的內容:
>>> print t ('a', 'b', ['X', 'Y'])
不是說tuple一旦定義後就不可變了嗎?怎麼現在又變了?
別急,我們先看看定義的時候tuple包含的3個元素:
當我們把list的元素’A’和’B’修改為’X’和’Y’後,tuple變為:
表面上看,tuple的元素確實變了,但其實變的不是 tuple 的元素,而是list的元素。
tuple一開始指向的list並沒有改成別的list,所以,tuple所謂的「不變」是說,tuple的每個元素,指向永遠不變。即指向’a’,就不能改成指向’b’,指向一個list,就不能改成指向其他對象,但指向的這個list本身是可變的!
理解了「指向不變」後,要創建一個內容也不變的tuple怎麼做?那就必須保證tuple的每一個元素本身也不能變。
條件判斷和循環
條件判斷
計算機之所以能做很多自動化的任務,因為它可以自己做條件判斷。
if語句
比如,輸入用戶年齡,根據年齡列印不同的內容,在Python程序中,可以用if語句做到:
age = 20if age >= 18: print 'your age is', age print 'adult'print 'END'
注意: Python代碼的縮進規則。具有相同縮進的代碼被視為代碼塊,上面的3,4行 print 語句就構成一個代碼塊(但不包括第5行的print)。如果 if 語句判斷為 True,就會執行這個代碼塊。
縮進請嚴格按照Python的習慣寫法:4個空格,不要使用Tab,更不要混合Tab和空格,否則很容易造成因為縮進引起的語法錯誤。
注意: if 語句後接表達式,然後用:表示代碼塊開始。
如果你在Python交互環境下敲代碼,還要特別留意縮進,並且退出縮進需要多敲一行回車:
>>> age = 20>>> if age >= 18:... print 'your age is', age... print 'adult'... your age is 20adult
if-else語句
當 if 語句判斷表達式的結果為 True 時,就會執行 if 包含的代碼塊:
if age >= 18: print 'adult'
如果我們想判斷年齡在18歲以下時,列印出 ‘teenager’,怎麼辦?
方法是再寫一個 if:
if age < 18: print 'teenager'
或者用 not 運算:
if not age >= 18: print 'teenager'
細心的讀者可以發現,這兩種條件判斷是「非此即彼」的,要麼符合條件1,要麼符合條件2,因此,完全可以用一個if ... else ...
語句把它們統一起來:
if age >= 18: print 'adult'else: print 'teenager'
利用if ... else ...
語句,我們可以根據條件表達式的值為 True 或者 False ,分別執行 if 代碼塊或者 else 代碼塊。
注意: else 後面有個「:」。
if-elif-else語句
有的時候,一個if ... else ...
還不夠用。比如,根據年齡的劃分:
條件1:18歲或以上:adult 條件2:6歲或以上:teenager 條件3:6歲以下:kid
我們可以用一個 if age >= 18 判斷是否符合條件1,如果不符合,再通過一個 if 判斷 age >= 6 來判斷是否符合條件2,否則,執行條件3:
if age >= 18: print 'adult'else: if age >= 6: print 'teenager' else: print 'kid'
這樣寫出來,我們就得到了一個兩層嵌套的if ... else ...
語句。這個邏輯沒有問題,但是,如果繼續增加條件,比如3歲以下是 baby:
if age >= 18: print 'adult'else: if age >= 6: print 'teenager' else: if age >= 3: print 'kid' else: print 'baby'
這種縮進只會越來越多,代碼也會越來越難看。
要避免嵌套結構的if ... else ...
,我們可以用if ...
多個elif ... else ...
的結構,一次寫完所有的規則:
if age >= 18: print 'adult'elif age >= 6: print 'teenager'elif age >= 3: print 'kid'else: print 'baby'
elif 意思就是 else if。這樣一來,我們就寫出了結構非常清晰的一系列條件判斷。
特別注意: 這一系列條件判斷會從上到下依次判斷,如果某個判斷為 True,執行完對應的代碼塊,後面的條件判斷就直接忽略,不再執行了。
循環
for循環
list或tuple可以表示一個有序集合。如果我們想依次訪問一個list中的每一個元素呢?比如 list:
L = ['Adam', 'Lisa', 'Bart']print L[0]print L[1]print L[2]
如果list只包含幾個元素,這樣寫還行,如果list包含1萬個元素,我們就不可能寫1萬行print。
這時,循環就派上用場了。
Python的 for 循環就可以依次把list或tuple的每個元素迭代出來:
L = ['Adam', 'Lisa', 'Bart']for name in L: print name
注意: name 這個變量是在 for 循環中定義的,意思是,依次取出list中的每一個元素,並把元素賦值給 name,然後執行for循環體(就是縮進的代碼塊)。
這樣一來,遍歷一個list或tuple就非常容易了。
while循環
和 for 循環不同的另一種循環是 while 循環,while 循環不會迭代 list 或 tuple 的元素,而是根據表達式判斷循環是否結束。
比如要從 0 開始列印不大於 N 的整數:
N = 10x = 0while x < N: print x x = x + 1
while循環每次先判斷 x < N,如果為True,則執行循環體的代碼塊,否則,退出循環。
在循環體內,x = x + 1 會讓 x 不斷增加,最終因為 x < N 不成立而退出循環。
如果沒有這一個語句,while循環在判斷 x < N 時總是為True,就會無限循環下去,變成死循環,所以要特別留意while循環的退出條件。
break退出循環
用 for 循環或者 while 循環時,如果要在循環體內直接退出循環,可以使用 break 語句。
比如計算1至100的整數和,我們用while來做到:
sum = 0x = 1while True: sum = sum + x x = x + 1 if x > 100: breakprint sum
怎麼一看, while True 就是一個死循環,但是在循環體內,我們還判斷了 x > 100 條件成立時,用break語句退出循環,這樣也可以做到循環的結束。
continue繼續循環
在循環過程中,可以用break退出當前循環,還可以用continue跳過後續循環代碼,繼續下一次循環。
假設我們已經寫好了利用for循環計算平均分的代碼:
L = [75, 98, 59, 81, 66, 43, 69, 85] sum = 0.0n = 0for x in L: sum = sum + x n = n + 1print sum / n
現在老師只想統計及格分數的平均分,就要把 x < 60 的分數剔除掉,這時,利用 continue,可以做到當 x < 60的時候,不繼續執行循環體的後續代碼,直接進入下一次循環:
for x in L: if x < 60: continue sum = sum + x n = n + 1
多重循環
在循環內部,還可以嵌套循環,我們來看一個例子:
for x in ['A', 'B', 'C']: for y in ['1', '2', '3']: print x + y
x 每循環一次,y 就會循環 3 次。
Dict類型
我們已經知道,list 和 tuple 可以用來表示順序集合,例如,班里同學的名字:
['Adam', 'Lisa', 'Bart']
或者考試的成績列表:
[95, 85, 59]
但是,要根據名字找到對應的成績,用兩個 list 表示就不方便。
如果把名字和分數關聯起來,組成類似的查找表:
'Adam' ==> 95'Lisa' ==> 85'Bart' ==> 59
給定一個名字,就可以直接查到分數。
Python的 dict 就是專門幹這件事的。用 dict 表示「名字」-「成績」的查找表如下:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59}
我們把名字稱為key,對應的成績稱為value,dict就是通過 key 來查找 value。
花括號 {} 表示這是一個dict,然後按照 key: value, 寫出來即可。最後一個 key: value 的逗號可以省略。
由於dict也是集合,len() 函數可以計算任意集合的大小:
>>> len(d)3
注意: 一個 key-value 算一個,因此,dict大小為3。
訪問Dict
我們已經能創建一個dict,用於表示名字和成績的對應關係:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59}
那麼,如何根據名字來查找對應的成績呢?
可以簡單地使用 d[key] 的形式來查找對應的 value,這和 list 很像,不同之處是,list 必須使用索引返回對應的元素,而dict使用key:
>>> print d['Adam']95>>> print d['Paul'] Traceback (most recent call last): File "index.py", line 11, in <module> print d['Paul']KeyError: 'Paul'
注意: 通過 key 訪問 dict 的value,只要 key 存在,dict就返回對應的value。如果key不存在,會直接報錯:KeyError。
要避免 KeyError 發生,有兩個辦法:
一是先判斷一下 key 是否存在,用 in 操作符:
if 'Paul' in d: print d['Paul']
如果 ‘Paul’ 不存在,if語句判斷為False,自然不會執行 print d[‘Paul’] ,從而避免了錯誤。
二是使用dict本身提供的一個 get 方法,在Key不存在的時候,返回None:
>>> print d.get('Bart')59>>> print d.get('Paul')None
Dict特點
dict的第一個特點是查找速度快,無論dict有10個元素還是10萬個元素,查找速度都一樣。而list的查找速度隨著元素增加而逐漸下降。
不過dict的查找速度快不是沒有代價的,dict的缺點是占用內存大,還會浪費很多內容,list正好相反,占用內存小,但是查找速度慢。
由於dict是按 key 查找,所以,在一個dict中,key不能重復。
dict的第二個特點就是存儲的key-value序對是沒有順序的!這和list不一樣:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59}
當我們試圖列印這個dict時:
>>> print d {'Lisa': 85, 'Adam': 95, 'Bart': 59}
列印的順序不一定是我們創建時的順序,而且,不同的機器列印的順序都可能不同,這說明dict內部是無序的,不能用dict存儲有序的集合。
dict的第三個特點是作為 key 的元素必須不可變,Python的基本類型如字符串、整數、浮點數都是不可變的,都可以作為 key。但是list是可變的,就不能作為 key。
可以試試用list作為key時會報什麼樣的錯誤。
不可變這個限制僅作用於key,value是否可變無所謂:
{ '123': [1, 2, 3], # key 是 str,value是list 123: '123', # key 是 int,value 是 str ('a', 'b'): True # key 是 tuple,並且tuple的每個元素都是不可變對象,value是 boolean}
最常用的key還是字符串,因為用起來最方便。
更新Dict
dict是可變的,也就是說,我們可以隨時往dict中添加新的 key-value。比如已有dict:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59}
要把新同學’Paul’的成績 72 加進去,用賦值語句:
>>> d['Paul'] = 72
再看看dict的內容:
>>> print d {'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 59}
如果 key 已經存在,則賦值會用新的 value 替換掉原來的 value:
>>> d['Bart'] = 60>>> print d {'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 60}
遍歷Dict
由於dict也是一個集合,所以,遍歷dict和遍歷list類似,都可以通過 for 循環做到。
直接使用for循環可以遍歷 dict 的 key:
>>> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }>>> for key in d:... print key... Lisa Adam Bart
由於通過 key 可以獲取對應的 value,因此,在循環體內,可以獲取到value的值。
Set類型
dict的作用是建立一組 key 和一組 value 的映射關係,dict的key是不能重復的。
有的時候,我們只想要 dict 的 key,不關心 key 對應的 value,目的就是保證這個集合的元素不會重復,這時,set就派上用場了。
set 持有一系列元素,這一點和 list 很像,但是set的元素沒有重復,而且是無序的,這點和 dict 的 key很像。
創建 set 的方式是調用 set() 並傳入一個 list,list的元素將作為set的元素:
>>> s = set(['A', 'B', 'C'])
可以查看 set 的內容:
>>> print s set(['A', 'C', 'B'])
請注意,上述列印的形式類似 list, 但它不是 list,仔細看還可以發現,列印的順序和原始 list 的順序有可能是不同的,因為set內部存儲的元素是無序的。
因為set不能包含重復的元素,所以,當我們傳入包含重復元素的 list 會怎麼樣呢?
>>> s = set(['A', 'B', 'C', 'C'])>>> print s set(['A', 'C', 'B'])>>> len(s)3
結果顯示,set會自動去掉重復的元素,原來的list有4個元素,但set只有3個元素。
訪問Set
由於set存儲的是無序集合,所以我們沒法通過索引來訪問。
訪問 set中的某個元素實際上就是判斷一個元素是否在set中。
例如,存儲了班里同學名字的set:
>>> s = set(['Adam', 'Lisa', 'Bart', 'Paul'])
我們可以用 in 操作符判斷:
Bart是該班的同學嗎?
>>> 'Bart' in sTrue
Bill是該班的同學嗎?
>>> 'Bill' in sFalse
bart是該班的同學嗎?
>>> 'bart' in sFalse
看來大小寫很重要,’Bart’ 和 ‘bart’被認為是兩個不同的元素。
Set的特點
set的內部結構和dict很像,唯一區別是不存儲value,因此,判斷一個元素是否在set中速度很快。
set存儲的元素和dict的key類似,必須是不變對象,因此,任何可變對象是不能放入set中的。
最後,set存儲的元素也是沒有順序的。
set的這些特點,可以應用在哪些地方呢?
星期一到星期日可以用字符串’MON’, ‘TUE’, … ‘SUN’表示。
假設我們讓用戶輸入星期一至星期日的某天,如何判斷用戶的輸入是否是一個有效的星期呢?
可以用 if 語句判斷,但這樣做非常繁瑣:
x = '???' # 用戶輸入的字符串if x!= 'MON' and x!= 'TUE' and x!= 'WED' ... and x!= 'SUN': print 'input error'else: print 'input ok'
注意:if 語句中的…表示沒有列出的其它星期名稱,測試時,請輸入完整。
如果事先創建好一個set,包含’MON’ ~ ‘SUN’:
weekdays = set(['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'])
再判斷輸入是否有效,只需要判斷該字符串是否在set中:
x = '???' # 用戶輸入的字符串if x in weekdays: print 'input ok'else: print 'input error'
這樣一來,代碼就簡單多了。
遍歷Set
由於 set 也是一個集合,所以,遍歷 set 和遍歷 list 類似,都可以通過 for 循環做到。
直接使用 for 循環可以遍歷 set 的元素:
>>> s = set(['Adam', 'Lisa', 'Bart'])>>> for name in s:... print name... Lisa Adam Bart
注意: 觀察 for 循環在遍歷set時,元素的順序和list的順序很可能是不同的,而且不同的機器上運行的結果也可能不同。
更新Set
由於set存儲的是一組不重復的無序元素,因此,更新set主要做兩件事:
一是把新的元素添加到set中,二是把已有元素從set中刪除。
添加元素時,用set的add()方法:
>>> s = set([1, 2, 3])>>> s.add(4)>>> print s set([1, 2, 3, 4])
如果添加的元素已經存在於set中,add()不會報錯,但是不會加進去了:
>>> s = set([1, 2, 3])>>> s.add(3)>>> print s set([1, 2, 3])
刪除set中的元素時,用set的remove()方法:
>>> s = set([1, 2, 3, 4])>>> s.remove(4)>>> print s set([1, 2, 3])
如果刪除的元素不存在set中,remove()會報錯:
>>> s = set([1, 2, 3])>>> s.remove(4) Traceback (most recent call last): File "<stdin>", line 1, in <module>KeyError: 4
所以用add()可以直接添加,而remove()前需要判斷。
函數
我們知道圓的面積計算公式為:
S = πr²
當我們知道半徑r的值時,就可以根據公式計算出面積。假設我們需要計算3個不同大小的圓的面積:
r1 = 12.34r2 = 9.08r3 = 73.1s1 = 3.14 * r1 * r1s2 = 3.14 * r2 * r2s3 = 3.14 * r3 * r3
當代碼出現有規律的重復的時候,你就需要當心了,每次寫3.14xx不僅很麻煩,而且,如果要把3.14改成3.14159265359的時候,得全部替換。
有了函數,我們就不再每次寫s = 3.14xx,而是寫成更有意義的函數調用 s = area_of_circle(x),而函數 area_of_circle 本身只需要寫一次,就可以多次調用。
抽象是數學中非常常見的概念。舉個例子:
計算數列的和,比如:1 + 2 + 3 + … + 100,寫起來十分不方便,於是數學家發明了求和符號∑,可以把1 + 2 + 3 + … + 100記作:
100 ∑n n=1
這種抽象記法非常強大,因為我們看到∑就可以理解成求和,而不是還原成低級的加法運算。
而且,這種抽象記法是可擴展的,比如:
100 ∑(n²+1) n=1
還原成加法運算就變成了:
(1 x 1 + 1) + (2 x 2 + 1) + (3 x 3 + 1) + … + (100 x 100 + 1)
可見,借助抽象,我們才能不關心底層的具體計算過程,而直接在更高的層次上思考問題。
寫計算機程序也是一樣,函數就是最基本的一種代碼抽象的方式。
Python不但能非常靈活地定義函數,而且本身內置了很多有用的函數,可以直接調用。
調用函數
Python內置了很多有用的函數,我們可以直接調用。
要調用一個函數,需要知道函數的名稱和參數,比如求絕對值的函數 abs,它接收一個參數。
可以直接從Python的官方網站查看文檔:
http://docs.python.org/2/library/functions.html#abs
也可以在交互式命令行通過 help(abs) 查看abs函數的幫助信息。
調用 abs 函數:
>>> abs(100)100>>> abs(-20)20>>> abs(12.34)12.34
調用函數的時候,如果傳入的參數數量不對,會報TypeError的錯誤,並且Python會明確地告訴你:abs()有且僅有1個參數,但給出了兩個:
>>> abs(1, 2) Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: abs() takes exactly one argument (2 given)
如果傳入的參數數量是對的,但參數類型不能被函數所接受,也會報TypeError的錯誤,並且給出錯誤信息:str是錯誤的參數類型:
>>> abs('a') Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: bad operand type for abs(): 'str'
而比較函數 cmp(x, y) 就需要兩個參數,如果 x y,返回 1:
>>> cmp(1, 2) -1>>> cmp(2, 1)1>>> cmp(3, 3)0
Python內置的常用函數還包括數據類型轉換函數,比如 int()函數可以把其他數據類型轉換為整數:
>>> int('123')123>>> int(12.34)12
str()函數把其他類型轉換成 str:
>>> str(123)'123'>>> str(1.23)'1.23'
編寫函數
在Python中,定義一個函數要使用 def 語句,依次寫出函數名、括號、括號中的參數和冒號:,然後,在縮進塊中編寫函數體,函數的返回值用 return 語句返回。
我們以自定義一個求絕對值的 my_abs 函數為例:
def my_abs(x): if x >= 0: return x else: return -x
請注意,函數體內部的語句在執行時,一旦執行到return時,函數就執行完畢,並將結果返回。因此,函數內部通過條件判斷和循環可以做到非常複雜的邏輯。
如果沒有return語句,函數執行完畢後也會返回結果,只是結果為 None。
return None可以簡寫為return。
返回多值
函數可以返回多個值嗎?答案是肯定的。
比如在遊戲中經常需要從一個點移動到另一個點,給出坐標、位移和角度,就可以計算出新的坐標:
# math
包提供了sin()和 cos()函數,我們先用import
引用它:
import mathdef move(x, y, step, angle): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny
這樣我們就可以同時獲得返回值:
>>> x, y = move(100, 100, 60, math.pi / 6)>>> print x, y151.961524227 70.0
但其實這只是一種假象,Python函數返回的仍然是單一值:
>>> r = move(100, 100, 60, math.pi / 6)>>> print r (151.96152422706632, 70.0)
用print列印返回結果,原來返回值是一個tuple!
但是,在語法上,返回一個tuple可以省略括號,而多個變量可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函數返回多值其實就是返回一個tuple,但寫起來更方便。
遞歸函數
在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。
舉個例子,我們來計算階乘 n! = 123…n,用函數 fact(n)表示,可以看出:
fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n
所以,fact(n)可以表示為 n * fact(n-1),只有n=1時需要特殊處理。
於是,fact(n)用遞歸的方式寫出來就是:
def fact(n): if n==1: return 1 return n * fact(n - 1)
上面就是一個遞歸函數。可以試試:
>>> fact(1)1>>> fact(5)120>>> fact(100)93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L
如果我們計算fact(5),可以根據函數定義看到計算過程如下:
===> fact(5) ===> 5 * fact(4) ===> 5 * (4 * fact(3)) ===> 5 * (4 * (3 * fact(2))) ===> 5 * (4 * (3 * (2 * fact(1)))) ===> 5 * (4 * (3 * (2 * 1))) ===> 5 * (4 * (3 * 2)) ===> 5 * (4 * 6) ===> 5 * 24 ===> 120
遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。
使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構做到的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。可以試試計算 fact(10000)。
定義默認參數
定義函數的時候,還可以有默認參數。
例如Python自帶的 int() 函數,其實就有兩個參數,我們既可以傳一個參數,又可以傳兩個參數:
>>> int('123')123>>> int('123', 8)83
int()函數的第二個參數是轉換進制,如果不傳,默認是十進制 (base=10),如果傳了,就用傳入的參數。
可見,函數的默認參數的作用是簡化調用,你只需要把必須的參數傳進去。但是在需要的時候,又可以傳入額外的參數來覆蓋默認參數值。
我們來定義一個計算 x 的N次方的函數:
def power(x, n): s = 1 while n > 0: n = n - 1 s = s * x return s
假設計算平方的次數最多,我們就可以把 n 的默認值設定為 2:
def power(x, n=2): s = 1 while n > 0: n = n - 1 s = s * x return s
這樣一來,計算平方就不需要傳入兩個參數了:
>>> power(5)25
由於函數的參數按從左到右的順序匹配,所以默認參數只能定義在必需參數的後面:
# OK:def fn1(a, b=1, c=2): pass# Error:def fn2(a=1, b): pass
定義可變參數
如果想讓一個函數能接受任意個參數,我們就可以定義一個可變參數:
def fn(*args): print args
可變參數的名字前面有個 * 號,我們可以傳入0個、1個或多個參數給可變參數:
>>> fn() () >>> fn('a') ('a',) >>> fn('a', 'b') ('a', 'b') >>> fn('a', 'b', 'c') ('a', 'b', 'c')
可變參數也不是很神秘,Python解釋器會把傳入的一組參數組裝成一個tuple傳遞給可變參數,因此,在函數內部,直接把變量 args 看成一個 tuple 就好了。
定義可變參數的目的也是為了簡化調用。假設我們要計算任意個數的平均值,就可以定義一個可變參數:
def average(*args): ...
這樣,在調用的時候,可以這樣寫:
>>> average()0>>> average(1, 2)1.5>>> average(1, 2, 2, 3, 4)2.4
切片
對list進行切片
取一個list的部分元素是非常常見的操作。比如,一個list如下:
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']
取前3個元素,應該怎麼做?
笨辦法:
>>> [L[0], L[1], L[2]] ['Adam', 'Lisa', 'Bart']
之所以是笨辦法是因為擴展一下,取前N個元素就沒轍了。
取前N個元素,也就是索引為0-(N-1)的元素,可以用循環:
>>> r = []>>> n = 3>>> for i in range(n): ... r.append(L[i]) ... >>> r ['Adam', 'Lisa', 'Bart']
對這種經常取指定索引範圍的操作,用循環十分繁瑣,因此,Python提供了切片(Slice)操作符,能大大簡化這種操作。
對應上面的問題,取前3個元素,用一行代碼就可以完成切片:
>>> L[0:3] ['Adam', 'Lisa', 'Bart']
L[0:3]表示,從索引0開始取,直到索引3為止,但不包括索引3。即索引0,1,2,正好是3個元素。
如果第一個索引是0,還可以省略:
>>> L[:3] ['Adam', 'Lisa', 'Bart']
也可以從索引1開始,取出2個元素出來:
>>> L[1:3] ['Lisa', 'Bart']
只用一個 : ,表示從頭到尾:
>>> L[:] ['Adam', 'Lisa', 'Bart', 'Paul']
因此,L[:]實際上復制出了一個新list。
切片操作還可以指定第三個參數:
>>> L[::2] ['Adam', 'Bart']
第三個參數表示每N個取一個,上面的 L[::2] 會每兩個元素取出一個來,也就是隔一個取一個。
把list換成tuple,切片操作完全相同,只是切片的結果也變成了tuple。
倒序切片
對於list,既然Python支持L[-1]取倒數第一個元素,那麼它同樣支持倒數切片,試試:
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']>>> L[-2:] ['Bart', 'Paul']>>> L[:-2] ['Adam', 'Lisa']>>> L[-3:-1] ['Lisa', 'Bart']>>> L[-4:-1:2] ['Adam', 'Bart']
記住倒數第一個元素的索引是-1。倒序切片包含起始索引,不包含結束索引。
對字符串切片
字符串 ‘xxx’和 Unicode字符串 u’xxx’也可以看成是一種list,每個元素就是一個字符。因此,字符串也可以用切片操作,只是操作結果仍是字符串:
>>> 'ABCDEFG'[:3]'ABC'>>> 'ABCDEFG'[-3:]'EFG'>>> 'ABCDEFG'[::2]'ACEG'
在很多編程語言中,針對字符串提供了很多各種截取函數,其實目的就是對字符串切片。Python沒有針對字符串的截取函數,只需要切片一個操作就可以完成,非常簡單。
迭代
在Python中,如果給定一個list或tuple,我們可以通過for循環來遍歷這個list或tuple,這種遍歷我們成為迭代(Iteration)。
在Python中,迭代是通過 for … in 來完成的,而很多語言比如C或者Java,迭代list是通過下標完成的,比如Java代碼:
for (i=0; i<list.length; i++) { n = list[i]; }
可以看出,Python的for循環抽象程度要高於Java的for循環。
因為 Python 的 for循環不僅可以用在list或tuple上,還可以作用在其他任何可迭代對象上。
因此,迭代操作就是對於一個集合,無論該集合是有序還是無序,我們用 for 循環總是可以依次取出集合的每一個元素。
注意: 集合是指包含一組元素的數據結構,我們已經介紹的包括:
-
有序集合:list,tuple,str和unicode;
-
無序集合:set
-
無序集合併且具有 key-value 對:dict而迭代是一個動詞,它指的是一種操作,在Python中,就是 for 循環。
迭代與按下標訪問數組最大的不同是,後者是一種具體的迭代做到方式,而前者只關心迭代結果,根本不關心迭代內部是如何做到的。
索引迭代
Python中,迭代永遠是取出元素本身,而非元素的索引。
對於有序集合,元素確實是有索引的。有的時候,我們確實想在 for 循環中拿到索引,怎麼辦?
方法是使用 enumerate() 函數:
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']>>> for index, name in enumerate(L):... print index, '-', name... 0 - Adam1 - Lisa2 - Bart3 - Paul
使用 enumerate() 函數,我們可以在for循環中同時綁定索引index和元素name。但是,這不是 enumerate() 的特殊語法。實際上,enumerate() 函數把:
['Adam', 'Lisa', 'Bart', 'Paul']
變成了類似:
[(0, 'Adam'), (1, 'Lisa'), (2, 'Bart'), (3, 'Paul')]
因此,迭代的每一個元素實際上是一個tuple:
for t in enumerate(L): index = t[0] name = t[1] print index, '-', name
如果我們知道每個tuple元素都包含兩個元素,for循環又可以進一步簡寫為:
for index, name in enumerate(L): print index, '-', name
這樣不但代碼更簡單,而且還少了兩條賦值語句。
可見,索引迭代也不是真的按索引訪問,而是由 enumerate() 函數自動把每個元素變成 (index, element) 這樣的tuple,再迭代,就同時獲得了索引和元素本身。
迭代Dict的value
我們已經了解了dict對象本身就是可迭代對象,用 for 循環直接迭代 dict,可以每次拿到dict的一個key。
如果我們希望迭代 dict 對象的value,應該怎麼做?
dict 對象有一個 values() 方法,這個方法把dict轉換成一個包含所有value的list,這樣,我們迭代的就是 dict的每一個 value:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }print d.values()# [85, 95, 59]for v in d.values(): print v# 85# 95# 59
如果仔細閱讀Python的文檔,還可以發現,dict除了values()方法外,還有一個 itervalues() 方法,用 itervalues() 方法替代 values() 方法,迭代效果完全一樣:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }print d.itervalues()# <dictionary-valueiterator object at 0x106adbb50>for v in d.itervalues(): print v# 85# 95# 59
那這兩個方法有何不同之處呢?
- values() 方法實際上把一個 dict 轉換成了包含 value 的list。
- 但是 itervalues() 方法不會轉換,它會在迭代過程中依次從 dict 中取出 value,所以 itervalues() 方法比 values() 方法節省了生成 list 所需的內存。
- 列印 itervalues() 發現它返回一個 對象,這說明在Python中,for 循環可作用的迭代對象遠不止 list,tuple,str,unicode,dict等,任何可迭代對象都可以作用於for循環,而內部如何迭代我們通常並不用關心。
如果一個對象說自己可迭代,那我們就直接用 for 循環去迭代它,可見,迭代是一種抽象的數據操作,它不對迭代對象內部的數據有任何要求。
迭代Dict的key和value
我們了解了如何迭代 dict 的key和value,那麼,在一個 for 循環中,能否同時迭代 key和value?答案是肯定的。
首先,我們看看 dict 對象的 items() 方法返回的值:
>>> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }>>> print d.items() [('Lisa', 85), ('Adam', 95), ('Bart', 59)]
可以看到,items() 方法把dict對象轉換成了包含tuple的list,我們對這個list進行迭代,可以同時獲得key和value:
>>> for key, value in d.items():... print key, ':', value... Lisa : 85Adam : 95Bart : 59
和 values() 有一個 itervalues() 類似, items() 也有一個對應的 iteritems(),iteritems() 不把dict轉換成list,而是在迭代過程中不斷給出 tuple,所以, iteritems() 不占用額外的內存。
列表
列表生成
要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
,我們可以用range(1, 11)
:
>>> range(1, 11)[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
但如果要生成[1×1, 2×2, 3×3, …, 10×10]怎麼做?方法一是循環:
>>> L = []>>> for x in range(1, 11):... L.append(x * x)... >>> L [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
但是循環太繁瑣,而列表生成式則可以用一行語句代替循環生成上面的list:
>>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
這種寫法就是Python特有的列表生成式。利用列表生成式,可以以非常簡潔的代碼生成 list。
寫列表生成式時,把要生成的元素 x * x 放到前面,後面跟 for 循環,就可以把list創建出來,十分有用,多寫幾次,很快就可以熟悉這種語法。
複雜表達式
使用for循環的迭代不僅可以迭代普通的list,還可以迭代dict。
假設有如下的dict:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
完全可以通過一個複雜的列表生成式把它變成一個 HTML 表格:
tds = ['<tr><td>%s</td><td>%s</td></tr>' % (name, score) for name, score in d.iteritems()]print '<table>'print '<tr><th>Name</th><th>Score</th><tr>'print '\n'.join(tds)print '</table>'
註:字符串可以通過 % 進行格式化,用指定的參數替代 %s。字符串的join()方法可以把一個 list 拼接成一個字符串。
把列印出來的結果保存為一個html文件,就可以在瀏覽器中看到效果了:
<table border="1"><tr><th>Name</th><th>Score</th><tr><tr><td>Lisa</td><td>85</td></tr><tr><td>Adam</td><td>95</td></tr><tr><td>Bart</td><td>59</td></tr></table>
Name | Score |
---|---|
Lisa | 85 |
Adam | 95 |
Bart | 59 |
條件過濾
列表生成式的 for 循環後面還可以加上 if 判斷。例如:
>>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
如果我們只想要偶數的平方,不改動 range()的情況下,可以加上 if 來篩選:
>>> [x * x for x in range(1, 11) if x % 2 == 0] [4, 16, 36, 64, 100]
有了 if 條件,只有 if 判斷為 True 的時候,才把循環的當前元素添加到列表中。
多層表達式
for循環可以嵌套,因此,在列表生成式中,也可以用多層 for 循環來生成列表。
對於字符串 ‘ABC’ 和 ‘123’,可以使用兩層循環,生成全排列:
>>> [m + n for m in 'ABC' for n in '123'] ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
翻譯成循環代碼就像下面這樣:
L = []for m in 'ABC': for n in '123': L.append(m + n)
End.