一、python 變量和數(shù)據(jù)類型
1.整數(shù)
Python可以處理任意大小的整數(shù)鄙皇,當(dāng)然包括負(fù)整數(shù)芜赌,在Python程序中,整數(shù)的表示方法和數(shù)學(xué)上的寫法一模一樣伴逸,例
如:1
缠沈,100
,-8080
错蝴,0
洲愤,等等。
計(jì)算機(jī)由于使用二進(jìn)制顷锰,所以柬赐,有時(shí)候用十六進(jìn)制表示整數(shù)比較方便,十六進(jìn)制用0x
前綴和0-9官紫,a-f表示肛宋,例如:0xff00
,0xa5b4c3d2
束世,等等酝陈。
2.浮點(diǎn)型
浮點(diǎn)數(shù)也就是小數(shù),之所以稱為浮點(diǎn)數(shù)毁涉,是因?yàn)榘凑湛茖W(xué)記數(shù)法表示時(shí)沉帮,一個(gè)浮點(diǎn)數(shù)的小數(shù)點(diǎn)位置是可變的,比如,1.23x109和12.3x108是相等的遇西。浮點(diǎn)數(shù)可以用數(shù)學(xué)寫法馅精,
如1.23,3.14粱檀,-9.01洲敢,等等。但是對(duì)于很大或很小的浮點(diǎn)數(shù)茄蚯,就必須用科學(xué)計(jì)數(shù)法表示压彭,把10用e替代,1.23x10^9就是1.23e9渗常,或者12.3e8壮不,0.000012可以寫成1.2e-5,等等皱碘。
整數(shù)和浮點(diǎn)數(shù)在計(jì)算機(jī)內(nèi)部存儲(chǔ)的方式是不同的询一,整數(shù)運(yùn)算永遠(yuǎn)是精確的,而浮點(diǎn)數(shù)運(yùn)算則可能會(huì)有四舍五入的誤差癌椿。
3.字符串
字符串是以''或""括起來(lái)的任意文本健蕊,比如'abc',"xyz"等等踢俄。請(qǐng)注意缩功,''或""本身只是一種表示方式,不是字符串的一部分都办,因此嫡锌,字符串'abc'只有a,b琳钉,c這3個(gè)字符势木。
4.布爾值
布爾值和布爾代數(shù)的表示完全一致,一個(gè)布爾值只有True
歌懒、False
兩種值跟压,要么是True
,要么False
歼培,在Python中,可以直接用True茸塞、False
表示布爾值(請(qǐng)注意大小寫)躲庄,也可以通過(guò)布爾運(yùn)算計(jì)算出來(lái)。
布爾值可以用and
钾虐、or
和not
運(yùn)算噪窘。
and
運(yùn)算是與運(yùn)算,只有所有都為 True
效扫,and
運(yùn)算結(jié)果才是 True
倔监。
or運(yùn)算是或運(yùn)算直砂,只要其中有一個(gè)為 True,or 運(yùn)算結(jié)果就是 True浩习。
not運(yùn)算是非運(yùn)算静暂,它是一個(gè)單目運(yùn)算符,把 True 變成 False谱秽,F(xiàn)alse 變成 True洽蛀。
Python把0、空字符串''和None看成 False疟赊,其他數(shù)值和非空字符串都看成 True郊供。
5.空值
空值是Python里一個(gè)特殊的值,用None
表示近哟。None
不能理解為0驮审,因?yàn)?是有意義的,而None
是一個(gè)特殊的空值吉执。此外疯淫,Python還提供了列表、字典等多種數(shù)據(jù)類型鼠证,還允許創(chuàng)建自定義數(shù)據(jù)類型峡竣。
什么是變量
在Python中,變量的概念基本上和初中代數(shù)的方程變量是一致的量九。
例如适掰,對(duì)于方程式 y=x*x ,x就是變量荠列。當(dāng)x=2時(shí)类浪,計(jì)算結(jié)果是4,當(dāng)x=5時(shí)肌似,計(jì)算結(jié)果是25费就。
只是在計(jì)算機(jī)程序中,變量不僅可以是數(shù)字川队,還可以是任意數(shù)據(jù)類型力细。
在Python程序中,變量是用一個(gè)變量名表示固额,變量名必須是大小寫英文眠蚂、數(shù)字和_的組合,且不能用數(shù)字開頭不能和關(guān)鍵字重名斗躏,比如:
a = 1
變量a
是一個(gè)整數(shù)逝慧。
t_007 = 'T007'
變量t_007
是一個(gè)字符串。
在Python中,等號(hào)=是賦值語(yǔ)句笛臣,可以把任意數(shù)據(jù)類型賦值給變量云稚,同一個(gè)變量可以反復(fù)賦值,而且可以是不同類型的變量沈堡,例如:
a = 123 # a是整數(shù)
print a
a = 'imooc' # a變?yōu)樽址?
print a
這種變量本身類型不固定的語(yǔ)言稱之為動(dòng)態(tài)語(yǔ)言静陈,與之對(duì)應(yīng)的是靜態(tài)語(yǔ)言。
靜態(tài)語(yǔ)言在定義變量時(shí)必須指定變量類型踱蛀,如果賦值的時(shí)候類型不匹配窿给,就會(huì)報(bào)錯(cuò)。例如Java是靜態(tài)語(yǔ)言率拒,賦值語(yǔ)句如下(// 表示注釋):
int a = 123; // a是整數(shù)類型變量
a = "mooc"; // 錯(cuò)誤:不能把字符串賦給整型變量
和靜態(tài)語(yǔ)言相比崩泡,動(dòng)態(tài)語(yǔ)言更靈活,就是這個(gè)原因猬膨。
請(qǐng)不要把賦值語(yǔ)句的等號(hào)等同于數(shù)學(xué)的等號(hào)角撞。比如下面的代碼:
x = 10
x = x + 2
如果從數(shù)學(xué)上理解x = x + 2那無(wú)論如何是不成立的,在程序中勃痴,賦值語(yǔ)句先計(jì)算右側(cè)的表達(dá)式x + 2谒所,得到結(jié)果12,再賦給變量x沛申。由于x之前的值是10劣领,重新賦值后,x的值變成12铁材。
最后尖淘,理解變量在計(jì)算機(jī)內(nèi)存中的表示也非常重要。當(dāng)我們寫:a = 'ABC'時(shí)著觉,Python解釋器干了兩件事情:
- 在內(nèi)存中創(chuàng)建了一個(gè)'ABC'的字符串村生;
- 在內(nèi)存中創(chuàng)建了一個(gè)名為a的變量,并把它指向'ABC'饼丘。
也可以把一個(gè)變量a賦值給另一個(gè)變量b趁桃,這個(gè)操作實(shí)際上是把變量b指向變量a所指向的數(shù)據(jù),例如下面的代碼:
a = 'ABC'
b = a
a = 'XYZ'
print b
最后一行打印出變量b的內(nèi)容到底是'ABC'呢還是'XYZ'肄鸽?如果從數(shù)學(xué)意義上理解卫病,就會(huì)錯(cuò)誤地得出b和a相同,也應(yīng)該是'XYZ'典徘,但實(shí)際上b的值是'ABC'忽肛,讓我們一行一行地執(zhí)行代碼,就可以看到到底發(fā)生了什么事:
執(zhí)行a = 'ABC'
烂斋,解釋器創(chuàng)建了字符串 'ABC'
和變量 a
,并把a
指向 'ABC'
:
執(zhí)行b = a
,解釋器創(chuàng)建了變量 b
汛骂,并把b
指向 a
指向的字符串'ABC'
:
執(zhí)行a = 'XYZ'罕模,解釋器創(chuàng)建了字符串'XYZ',并把a(bǔ)的指向改為'XYZ'帘瞭,但b并沒有更改:
所以淑掌,最后打印變量b的結(jié)果自然是'ABC'了。 上機(jī)驗(yàn)證得變量修改值時(shí)不是修改原來(lái)內(nèi)存中的值而是指向一個(gè)新值或者創(chuàng)建一個(gè)新值
定義字符串
前面我們講解了什么是字符串蝶念。字符串可以用''
或者""
括起來(lái)表示抛腕。
如果字符串本身包含'
怎么辦?比如我們要表示字符串 I'm OK
媒殉,這時(shí)担敌,可以用" "
括起來(lái)表示:
"I'm OK"
類似的,如果字符串包含"
廷蓉,我們就可以用' '
括起來(lái)表示:
'Learn "Python" in imooc'
如果字符串既包含'
又包含"
怎么辦全封?
這個(gè)時(shí)候,就需要對(duì)字符串的某些特殊字符進(jìn)行“轉(zhuǎn)義”桃犬,Python字符串用\
進(jìn)行轉(zhuǎn)義刹悴。
要表示字符串 Bob said "I'm OK".
由于 '
和 "
會(huì)引起歧義,因此攒暇,我們?cè)谒懊娌迦胍粋€(gè)\
表示這是一個(gè)普通字符土匀,不代表字符串的起始,因此形用,這個(gè)字符串又可以表示為
'Bob said \"I\'m OK\".'
注意:轉(zhuǎn)義字符 \
不計(jì)入字符串的內(nèi)容中就轧。
常用的轉(zhuǎn)義字符還有:
\n
表示換行
\t
表示一個(gè)制表符
\\
表示 \ 字符本身
raw字符串與多行字符串
如果一個(gè)字符串包含很多需要轉(zhuǎn)義的字符,對(duì)每一個(gè)字符都進(jìn)行轉(zhuǎn)義會(huì)很麻煩尾序。
為了避免這種情況钓丰,我們可以在字符串前面加個(gè)前綴 r ,表示這是一個(gè) raw 字符串每币,里面的字符就不需要轉(zhuǎn)義了携丁。例如:
r'\(~_~)/ \(~_~)/'
但是r'...'
表示法不能表示多行字符串,也不能表示包含'
和 "
的字符串(為什么兰怠?)
如果要表示多行字符串梦鉴,可以用'''...'''
表示:
'''Line 1
Line 2
Line 3'''
上面這個(gè)字符串的表示方法和下面的是完全一樣的:
'Line 1\nLine 2\nLine 3'
還可以在多行字符串前面添加 r ,把這個(gè)多行字符串也變成一個(gè)raw字符串:
r'''Python is created by "Guido".
It is free and easy to learn.
Let's start learn Python in imooc!'''
Unicode字符串
字符串還有一個(gè)編碼問題揭保。
因?yàn)橛?jì)算機(jī)只能處理數(shù)字肥橙,如果要處理文本,就必須先把文本轉(zhuǎn)換為數(shù)字才能處理秸侣。
最早的計(jì)算機(jī)在設(shè)計(jì)時(shí)采用8個(gè)比特(bit)作為一個(gè)字節(jié)(byte)存筏,所以宠互,一個(gè)字節(jié)能表示的最大的整數(shù)就是255(二進(jìn)制11111111=十進(jìn)制255),0 - 255被用來(lái)表示大小寫英文字母椭坚、數(shù)字和一些符號(hào)予跌,這個(gè)編碼表被稱為ASCII編碼,比如大寫字母 A 的編碼是65善茎,小寫字母 z 的編碼是122券册。
如果要表示中文,顯然一個(gè)字節(jié)是不夠的垂涯,至少需要兩個(gè)字節(jié)烁焙,而且還不能和ASCII編碼沖突,所以耕赘,中國(guó)制定了GB2312編碼骄蝇,用來(lái)把中文編進(jìn)去。
類似的鞠苟,日文和韓文等其他語(yǔ)言也有這個(gè)問題乞榨。為了統(tǒng)一所有文字的編碼,Unicode應(yīng)運(yùn)而生当娱。
Unicode把所有語(yǔ)言都統(tǒng)一到一套編碼里吃既,這樣就不會(huì)再有亂碼問題了。
Unicode通常用兩個(gè)字節(jié)表示一個(gè)字符跨细,原有的英文編碼從單字節(jié)變成雙字節(jié)鹦倚,只需要把高字節(jié)全部填為0就可以。
因?yàn)镻ython的誕生比Unicode標(biāo)準(zhǔn)發(fā)布的時(shí)間還要早冀惭,所以最早的Python只支持ASCII編碼震叙,普通的字符串'ABC'在Python內(nèi)部都是ASCII編碼的。
Python在后來(lái)添加了對(duì)Unicode的支持散休,以Unicode表示的字符串用u'...'
表示媒楼,比如:
print u'中文'
中文
注意: 不加 u
,中文就不能正常顯示戚丸。
Unicode字符串除了多了一個(gè) u
之外划址,與普通字符串沒啥區(qū)別,轉(zhuǎn)義字符和多行表示法仍然有效:
轉(zhuǎn)義:
u'中文\n日文\n韓文'
多行:
u'''第一行
第二行'''
raw+多行:
ur'''Python的Unicode字符串支持"中文",
"日文",
"韓文"等多種語(yǔ)言'''
如果中文字符串在Python環(huán)境下遇到 UnicodeDecodeError限府,這是因?yàn)?py文件保存的格式有問題夺颤。可以在第一行添加注釋
# -*- coding: utf-8 -*-
目的是告訴Python解釋器胁勺,用UTF-8編碼讀取源代碼世澜。另存為... 并選擇UTF-8格式保存。
List 和Tuple 類型
1.創(chuàng)建list
Python內(nèi)置的一種數(shù)據(jù)類型是列表:list署穗。list是一種有序的集合寥裂,可以隨時(shí)添加和刪除其中的元素嵌洼。
比如,列出班里所有同學(xué)的名字封恰,就可以用一個(gè)list表示:
>>> ['Michael', 'Bob', 'Tracy']
['Michael', 'Bob', 'Tracy']
list是數(shù)學(xué)意義上的有序集合咱台,也就是說(shuō),list中的元素是按照順序排列的俭驮。
構(gòu)造list非常簡(jiǎn)單,按照上面的代碼春贸,直接用 [ ]
把list的所有元素都括起來(lái)混萝,就是一個(gè)list對(duì)象。通常萍恕,我們會(huì)把list賦值給一個(gè)變量逸嘀,這樣,就可以通過(guò)變量來(lái)引用list:
>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates # 打印classmates變量的內(nèi)容
['Michael', 'Bob', 'Tracy']
由于Python是動(dòng)態(tài)語(yǔ)言允粤,所以list中包含的元素并不要求都必須是同一種數(shù)據(jù)類型崭倘,我們完全可以在list中包含各種數(shù)據(jù):
>>> L = ['Michael', 100, True]
一個(gè)元素也沒有的list,就是空l(shuí)ist:
>>> empty_list = []
2.按照索引訪問list
由于list是一個(gè)有序集合类垫,所以司光,我們可以用一個(gè)list按分?jǐn)?shù)從高到低表示出班里的3個(gè)同學(xué):
>>> L = ['Adam', 'Lisa', 'Bart']
那我們?nèi)绾螐膌ist中獲取指定第 N 名的同學(xué)呢?方法是通過(guò)索引來(lái)獲取list中的指定元素悉患。
需要特別注意的是残家,索引從 0 開始,也就是說(shuō)售躁,第一個(gè)元素的索引是0坞淮,第二個(gè)元素的索引是1,以此類推陪捷。
因此回窘,要打印第一名同學(xué)的名字,用 L[0]:
>>> print L[0]
Adam
要打印第二名同學(xué)的名字市袖,用 L[1]:
>>> print L[1]
Lisa
要打印第三名同學(xué)的名字啡直,用 L[2]:
>>> print L[2]
Bart
要打印第四名同學(xué)的名字,用 L[3]:
>>> print L[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
報(bào)錯(cuò)了凌盯!IndexError意思就是索引超出了范圍付枫,因?yàn)樯厦娴膌ist只有3個(gè)元素,有效的索引是 0驰怎,1阐滩,2。
所以县忌,使用索引時(shí)掂榔,千萬(wàn)注意不要越界继效。
3.倒序訪問list
我們還是用一個(gè)list按分?jǐn)?shù)從高到低表示出班里的3個(gè)同學(xué):
>>> L = ['Adam', 'Lisa', 'Bart']
這時(shí),老師說(shuō)装获,請(qǐng)分?jǐn)?shù)最低的同學(xué)站出來(lái)憔足。
要寫代碼完成這個(gè)任務(wù),我們可以先數(shù)一數(shù)這個(gè) list鼎文,發(fā)現(xiàn)它包含3個(gè)元素纱兑,因此,最后一個(gè)元素的索引是2:
>>> print L[2]
Bart
有沒有更簡(jiǎn)單的方法精肃?
有秤涩!
Bart同學(xué)是最后一名,俗稱倒數(shù)第一司抱,所以筐眷,我們可以用 -1 這個(gè)索引來(lái)表示最后一個(gè)元素:
>>> print L[-1]
Bart
Bart同學(xué)表示躺槍。
類似的习柠,倒數(shù)第二用 -2 表示匀谣,倒數(shù)第三用 -3 表示,倒數(shù)第四用 -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]
報(bào)錯(cuò)了资溃,因?yàn)榈箶?shù)第四不存在武翎,一共只有3個(gè)元素。
使用倒序索引時(shí)肉拓,也要注意不要越界后频。
4.添加新元素
現(xiàn)在,班里有3名同學(xué):
>>> L = ['Adam', 'Lisa', 'Bart']
今天暖途,班里轉(zhuǎn)來(lái)一名新同學(xué) Paul卑惜,如何把新同學(xué)添加到現(xiàn)有的 list 中呢?
第一個(gè)辦法是用 list 的 append() 方法驻售,把新同學(xué)追加到 list 的末尾:
>>> L = ['Adam', 'Lisa', 'Bart']
>>> L.append('Paul')
>>> print L
['Adam', 'Lisa', 'Bart', 'Paul']
append()
總是把新的元素添加到 list 的尾部露久。
如果 Paul 同學(xué)表示自己總是考滿分,要求添加到第一的位置欺栗,怎么辦毫痕?
方法是用list的 insert()方法,它接受兩個(gè)參數(shù)迟几,第一個(gè)參數(shù)是索引號(hào)消请,第二個(gè)參數(shù)是待添加的新元素:
>>> L = ['Adam', 'Lisa', 'Bart']
>>> L.insert(0, 'Paul')
>>> print L
['Paul', 'Adam', 'Lisa', 'Bart']
L.insert(0, 'Paul')
的意思是,'Paul'將被添加到索引為 0 的位置上(也就是第一個(gè))类腮,而原來(lái)索引為 0 的Adam同學(xué)臊泰,以及后面的所有同學(xué),都自動(dòng)向后移動(dòng)一位蚜枢。
5.從list刪除元素
Paul同學(xué)剛來(lái)幾天又要轉(zhuǎn)走了缸逃,那么我們?cè)趺窗裀aul 從現(xiàn)有的list中刪除呢针饥?
如果Paul同學(xué)排在最后一個(gè),我們可以用list的pop()方法刪除:
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']
>>> L.pop()
'Paul'
>>> print L
['Adam', 'Lisa', 'Bart']
pop()
方法總是刪掉list的最后一個(gè)元素需频,并且它還返回這個(gè)元素丁眼,所以我們執(zhí)行 L.pop() 后,會(huì)打印出 'Paul'昭殉。
如果Paul同學(xué)不是排在最后一個(gè)怎么辦苞七?比如Paul同學(xué)排在第三:
>>> L = ['Adam', 'Lisa', 'Paul', 'Bart']
要把Paul踢出list,我們就必須先定位Paul的位置挪丢。由于Paul的索引是2莽鸭,因此,用 pop(2)把Paul刪掉:
>>> L.pop(2)
'Paul'
>>> print L
['Adam', 'Lisa', 'Bart']
6.替換元素
假設(shè)現(xiàn)在班里仍然是3名同學(xué):
>>> L = ['Adam', 'Lisa', 'Bart']
現(xiàn)在吃靠,Bart同學(xué)要轉(zhuǎn)學(xué)走了,碰巧來(lái)了一個(gè)Paul同學(xué)足淆,要更新班級(jí)成員名單巢块,我們可以先把Bart刪掉,再把Paul添加進(jìn)來(lái)巧号。
另一個(gè)辦法是直接用Paul把Bart給替換掉:
>>> L[2] = 'Paul'
>>> print L
L = ['Adam', 'Lisa', 'Paul']
對(duì)list中的某一個(gè)索引賦值族奢,就可以直接用新的元素替換掉原來(lái)的元素,list包含的元素個(gè)數(shù)保持不變丹鸿。
由于Bart還可以用 -1 做索引越走,因此,下面的代碼也可以完成同樣的替換工作:
>>> L[-1] = 'Paul'
7.創(chuàng)建tuple
tuple是另一種有序的列表靠欢,中文翻譯為“ 元組 ”廊敌。tuple 和 list 非常類似,但是门怪,tuple一旦創(chuàng)建完畢骡澈,就不能修改了。
同樣是表示班里同學(xué)的名稱掷空,用tuple表示如下:
>>> t = ('Adam', 'Lisa', 'Bart')
創(chuàng)建tuple和創(chuàng)建list唯一不同之處是用( )替代了[ ]肋殴。
現(xiàn)在,這個(gè) t 就不能改變了坦弟,tuple沒有 append()方法护锤,也沒有insert()和pop()方法。所以酿傍,新同學(xué)沒法直接往 tuple 中添加烙懦,老同學(xué)想退出 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和list一樣魄鸦,可以包含 0 個(gè)宴杀、1個(gè)和任意多個(gè)元素。
包含多個(gè)元素的 tuple拾因,前面我們已經(jīng)創(chuàng)建過(guò)了旺罢。
包含 0 個(gè)元素的 tuple,也就是空tuple绢记,直接用 ()表示:
>>> t = ()
>>> print t
()
創(chuàng)建包含1個(gè)元素的 tuple 呢扁达?來(lái)試試:
>>> t = (1)
>>> print t
1
好像哪里不對(duì)!t
不是 tuple
蠢熄,而是整數(shù)1跪解。為什么呢?
因?yàn)?)既可以表示tuple签孔,又可以作為括號(hào)表示運(yùn)算時(shí)的優(yōu)先級(jí)叉讥,結(jié)果 (1) 被Python解釋器計(jì)算出結(jié)果 1,導(dǎo)致我們得到的不是tuple饥追,而是整數(shù) 1图仓。
正是因?yàn)橛?)定義單元素的tuple有歧義,所以 Python 規(guī)定但绕,單元素 tuple 要多加一個(gè)逗號(hào)“,”救崔,這樣就避免了歧義:
>>> t = (1,)
>>> print t
(1,)
Python在打印單元素tuple時(shí),也自動(dòng)添加了一個(gè)“,”捏顺,為了更明確地告訴你這是一個(gè)tuple六孵。
多元素 tuple 加不加這個(gè)額外的“,”效果是一樣的:
>>> t = (1, 2, 3,)
>>> print t
(1, 2, 3)
8.“可變”的tuple
前面我們看到了tuple一旦創(chuàng)建就不能修改。現(xiàn)在幅骄,我們來(lái)看一個(gè)“可變”的tuple:
>>> t = ('a', 'b', ['A', 'B'])
注意到 t 有 3 個(gè)元素:'a'
狸臣,'b'
和一個(gè)list:['A', 'B']
。list作為一個(gè)整體是tuple的第3個(gè)元素昌执。list對(duì)象可以通過(guò) t[2]
拿到:
>>> L = t[2]
然后烛亦,我們把list的兩個(gè)元素改一改:
>>> L[0] = 'X'
>>> L[1] = 'Y'
再看看tuple的內(nèi)容:
>>> print t
('a', 'b', ['X', 'Y'])
不是說(shuō)tuple一旦定義后就不可變了嗎?怎么現(xiàn)在又變了懂拾?
別急煤禽,我們先看看定義的時(shí)候tuple包含的3個(gè)元素:
當(dāng)我們把list的元素'A'
和'B'
修改為'X'
和'Y'
后,tuple變?yōu)椋?/p>
表面上看岖赋,tuple的元素確實(shí)變了檬果,但其實(shí)變的不是 tuple 的元素,而是list的元素。
tuple一開始指向的list并沒有改成別的list选脊,所以杭抠,tuple所謂的“不變”是說(shuō),tuple的每個(gè)元素恳啥,指向永遠(yuǎn)不變偏灿。 即指向'a',就不能改成指向'b'钝的,指向一個(gè)list翁垂,就不能改成指向其他對(duì)象,但指向的這個(gè)list本身是可變的硝桩!
理解了“指向不變”后沿猜,要?jiǎng)?chuàng)建一個(gè)內(nèi)容也不變的tuple怎么做?那就必須保證tuple的每一個(gè)元素本身也不能變碗脊。
二啼肩、條件判斷和循環(huán)
if語(yǔ)句
計(jì)算機(jī)之所以能做很多自動(dòng)化的任務(wù),因?yàn)樗梢宰约鹤鰲l件判斷衙伶。
比如疟游,輸入用戶年齡,根據(jù)年齡打印不同的內(nèi)容痕支,在Python程序中,可以用if語(yǔ)句實(shí)現(xiàn):
age = 20
if age >= 18:
print 'your age is', age
print 'adult'
print 'END'
注意: Python代碼的縮進(jìn)規(guī)則蛮原。具有相同縮進(jìn)的代碼被視為代碼塊卧须,上面的3,4行 print 語(yǔ)句就構(gòu)成一個(gè)代碼塊(但不包括第5行的print)儒陨。如果 if 語(yǔ)句判斷為 True花嘶,就會(huì)執(zhí)行這個(gè)代碼塊。
縮進(jìn)請(qǐng)嚴(yán)格按照Python的習(xí)慣寫法:4個(gè)空格蹦漠,不要使用Tab椭员,更不要混合Tab和空格,否則很容易造成因?yàn)榭s進(jìn)引起的語(yǔ)法錯(cuò)誤笛园。
注意: if 語(yǔ)句后接表達(dá)式隘击,然后用:表示代碼塊開始。
如果你在Python交互環(huán)境下敲代碼研铆,還要特別留意縮進(jìn)埋同,并且退出縮進(jìn)需要多敲一行回車:
>>> age = 20
>>> if age >= 18:
... print 'your age is', age
... print 'adult'
...
your age is 20
adult
if-else
當(dāng) if 語(yǔ)句判斷表達(dá)式的結(jié)果為 True 時(shí),就會(huì)執(zhí)行 if 包含的代碼塊:
if age >= 18:
print 'adult'
如果我們想判斷年齡在18歲以下時(shí)棵红,打印出 'teenager'
凶赁,怎么辦?
方法是再寫一個(gè) if:
if age < 18:
print 'teenager'
或者用 not 運(yùn)算:
if not age >= 18:
print 'teenager'
細(xì)心的同學(xué)可以發(fā)現(xiàn),這兩種條件判斷是“非此即彼”的虱肄,要么符合條件1致板,要么符合條件2,因此咏窿,完全可以用一個(gè) if ... else ...
語(yǔ)句把它們統(tǒng)一起來(lái):
if age >= 18:
print 'adult'
else:
print 'teenager'
利用 if ... else ...
語(yǔ)句斟或,我們可以根據(jù)條件表達(dá)式的值為 True
或者 False
,分別執(zhí)行 if
代碼塊或者 else
代碼塊翰灾。
注意: else 后面有個(gè)“:”缕粹。
if-elif-else
有的時(shí)候,一個(gè) if ... else ...
還不夠用纸淮。比如平斩,根據(jù)年齡的劃分:
條件1:18歲或以上:adult
條件2:6歲或以上:teenager
條件3:6歲以下:kid
我們可以用一個(gè) if age >= 18
判斷是否符合條件1,如果不符合咽块,再通過(guò)一個(gè) if
判斷 age >= 6
來(lái)判斷是否符合條件2绘面,否則,執(zhí)行條件3:
if age >= 18:
print 'adult'
else:
if age >= 6:
print 'teenager'
else:
print 'kid'
這樣寫出來(lái)侈沪,我們就得到了一個(gè)兩層嵌套的 if ... else ...
語(yǔ)句揭璃。這個(gè)邏輯沒有問題,但是亭罪,如果繼續(xù)增加條件瘦馍,比如3歲以下是 baby:
if age >= 18:
print 'adult'
else:
if age >= 6:
print 'teenager'
else:
if age >= 3:
print 'kid'
else:
print 'baby'
這種縮進(jìn)只會(huì)越來(lái)越多,代碼也會(huì)越來(lái)越難看应役。
要避免嵌套結(jié)構(gòu)的 if ... else ...
情组,我們可以用 if ... 多個(gè)elif ... else ...
的結(jié)構(gòu),一次寫完所有的規(guī)則:
if age >= 18:
print 'adult'
elif age >= 6:
print 'teenager'
elif age >= 3:
print 'kid'
else:
print 'baby'
elif
意思就是 else if
箩祥。這樣一來(lái)院崇,我們就寫出了結(jié)構(gòu)非常清晰的一系列條件判斷。
特別注意: 這一系列條件判斷會(huì)從上到下依次判斷袍祖,如果某個(gè)判斷為 True底瓣,執(zhí)行完對(duì)應(yīng)的代碼塊,后面的條件判斷就直接忽略蕉陋,不再執(zhí)行了捐凭。
for循環(huán)
list或tuple可以表示一個(gè)有序集合。如果我們想依次訪問一個(gè)list中的每一個(gè)元素呢凳鬓?比如 list:
L = ['Adam', 'Lisa', 'Bart']
print L[0]
print L[1]
print L[2]
如果list只包含幾個(gè)元素柑营,這樣寫還行,如果list包含1萬(wàn)個(gè)元素村视,我們就不可能寫1萬(wàn)行print官套。
這時(shí),循環(huán)就派上用場(chǎng)了。
Python的 for 循環(huán)就可以依次把list或tuple的每個(gè)元素迭代出來(lái):
L = ['Adam', 'Lisa', 'Bart']
for name in L:
print name
注意: name 這個(gè)變量是在 for 循環(huán)中定義的奶赔,意思是惋嚎,依次取出list中的每一個(gè)元素,并把元素賦值給 name站刑,然后執(zhí)行for循環(huán)體(就是縮進(jìn)的代碼塊)另伍。
這樣一來(lái),遍歷一個(gè)list或tuple就非常容易了绞旅。
while循環(huán)
和 for 循環(huán)不同的另一種循環(huán)是 while 循環(huán)摆尝,while 循環(huán)不會(huì)迭代 list 或 tuple 的元素,而是根據(jù)表達(dá)式判斷循環(huán)是否結(jié)束因悲。
比如要從 0 開始打印不大于 N 的整數(shù):
N = 10
x = 0
while x < N:
print x
x = x + 1
while循環(huán)每次先判斷 x < N堕汞,如果為True,則執(zhí)行循環(huán)體的代碼塊晃琳,否則讯检,退出循環(huán)。
在循環(huán)體內(nèi)卫旱,x = x + 1
會(huì)讓 x 不斷增加人灼,最終因?yàn)?x < N
不成立而退出循環(huán)。
如果沒有這一個(gè)語(yǔ)句顾翼,while循環(huán)在判斷 x < N 時(shí)總是為True投放,就會(huì)無(wú)限循環(huán)下去,變成死循環(huán)适贸,所以要特別留意while循環(huán)的退出條件灸芳。
break退出循環(huán)
用 for 循環(huán)或者 while 循環(huán)時(shí),如果要在循環(huán)體內(nèi)直接退出循環(huán)取逾,可以使用 break 語(yǔ)句。
比如計(jì)算1至100的整數(shù)和苹支,我們用while來(lái)實(shí)現(xiàn):
sum = 0
x = 1
while True:
sum = sum + x
x = x + 1
if x > 100:
break
print sum
咋一看砾隅, while True
就是一個(gè)死循環(huán),但是在循環(huán)體內(nèi)债蜜,我們還判斷了 x > 100
條件成立時(shí)晴埂,用break
語(yǔ)句退出循環(huán),這樣也可以實(shí)現(xiàn)循環(huán)的結(jié)束寻定。
continue繼續(xù)循環(huán)
在循環(huán)過(guò)程中儒洛,可以用break退出當(dāng)前循環(huán),還可以用continue跳過(guò)后續(xù)循環(huán)代碼狼速,繼續(xù)下一次循環(huán)琅锻。
假設(shè)我們已經(jīng)寫好了利用for循環(huán)計(jì)算平均分的代碼:
L = [75, 98, 59, 81, 66, 43, 69, 85]
sum = 0.0
n = 0
for x in L:
sum = sum + x
n = n + 1
print sum / n
現(xiàn)在老師只想統(tǒng)計(jì)及格分?jǐn)?shù)的平均分,就要把 x < 60
的分?jǐn)?shù)剔除掉,這時(shí)恼蓬,利用 continue
惊完,可以做到當(dāng) x < 60的時(shí)候,不繼續(xù)執(zhí)行循環(huán)體的后續(xù)代碼处硬,直接進(jìn)入下一次循環(huán):
for x in L:
if x < 60:
continue
sum = sum + x
n = n + 1
多重循環(huán)
在循環(huán)內(nèi)部小槐,還可以嵌套循環(huán),我們來(lái)看一個(gè)例子:
for x in ['A', 'B', 'C']:
for y in ['1', '2', '3']:
print x + y
x 每循環(huán)一次荷辕,y 就會(huì)循環(huán) 3 次凿跳,這樣,我們可以打印出一個(gè)全排列:
A1
A2
A3
B1
B2
B3
C1
C2
C3
三疮方、Dict和Set類型
什么是dict
我們已經(jīng)知道控嗜,list 和 tuple 可以用來(lái)表示順序集合,例如案站,班里同學(xué)的名字:
['Adam', 'Lisa', 'Bart']
或者考試的成績(jī)列表:
[95, 85, 59]
但是躬审,要根據(jù)名字找到對(duì)應(yīng)的成績(jī),用兩個(gè) list 表示就不方便蟆盐。
如果把名字和分?jǐn)?shù)關(guān)聯(lián)起來(lái)承边,組成類似的查找表:
'Adam' ==> 95
'Lisa' ==> 85
'Bart' ==> 59
給定一個(gè)名字,就可以直接查到分?jǐn)?shù)石挂。
Python的 dict 就是專門干這件事的博助。用 dict 表示“名字”-“成績(jī)”的查找表如下:
d = {
'Adam': 95,
'Lisa': 85,
'Bart': 59
}
我們把名字稱為key,對(duì)應(yīng)的成績(jī)稱為value痹愚,dict
就是通過(guò) key 來(lái)查找 value富岳。
花括號(hào) {}
表示這是一個(gè)dict,然后按照 key: value
, 寫出來(lái)即可拯腮。最后一個(gè) key: value 的逗號(hào)可以省略窖式。
由于dict也是集合,len() 函數(shù)可以計(jì)算任意集合的大卸馈:
>>> len(d)
3
注意: 一個(gè) key-value 算一個(gè)萝喘,因此,dict大小為3琼懊。
訪問dict
我們已經(jīng)能創(chuàng)建一個(gè)dict阁簸,用于表示名字和成績(jī)的對(duì)應(yīng)關(guān)系:
d = {
'Adam': 95,
'Lisa': 85,
'Bart': 59
}
那么,如何根據(jù)名字來(lái)查找對(duì)應(yīng)的成績(jī)呢哼丈?
可以簡(jiǎn)單地使用 d[key]
的形式來(lái)查找對(duì)應(yīng)的 value启妹,這和 list 很像,不同之處是醉旦,list 必須使用索引返回對(duì)應(yīng)的元素饶米,而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'
注意: 通過(guò) key 訪問 dict 的value桨啃,只要 key 存在,dict就返回對(duì)應(yīng)的value咙崎。如果key不存在优幸,會(huì)直接報(bào)錯(cuò):KeyError。
要避免 KeyError 發(fā)生褪猛,有兩個(gè)辦法:
一是先判斷一下 key 是否存在网杆,用 in
操作符:
if 'Paul' in d:
print d['Paul']
如果 'Paul'
不存在,if語(yǔ)句判斷為False伊滋,自然不會(huì)執(zhí)行 print d['Paul']
碳却,從而避免了錯(cuò)誤。
二是使用dict本身提供的一個(gè) get 方法笑旺,在Key不存在的時(shí)候昼浦,返回None:
>>> print d.get('Bart')
59
>>> print d.get('Paul')
None
dict的特點(diǎn)
dict的第一個(gè)特點(diǎn)是查找速度快,無(wú)論dict有10個(gè)元素還是10萬(wàn)個(gè)元素筒主,查找速度都一樣关噪。而list的查找速度隨著元素增加而逐漸下降。
不過(guò)dict的查找速度快不是沒有代價(jià)的乌妙,dict的缺點(diǎn)是占用內(nèi)存大使兔,還會(huì)浪費(fèi)很多內(nèi)容,list正好相反藤韵,占用內(nèi)存小虐沥,但是查找速度慢。
由于dict是按 key 查找泽艘,所以欲险,在一個(gè)dict中,key不能重復(fù)匹涮。
dict的第二個(gè)特點(diǎn)就是存儲(chǔ)的key-value序?qū)κ菦]有順序的天试!這和list不一樣:
d = {
'Adam': 95,
'Lisa': 85,
'Bart': 59
}
當(dāng)我們?cè)噲D打印這個(gè)dict時(shí):
>>> print d
{'Lisa': 85, 'Adam': 95, 'Bart': 59}
打印的順序不一定是我們創(chuàng)建時(shí)的順序,而且然低,不同的機(jī)器打印的順序都可能不同喜每,這說(shuō)明dict內(nèi)部是無(wú)序的,不能用dict存儲(chǔ)有序的集合脚翘。
dict的第三個(gè)特點(diǎn)是作為 key 的元素必須不可變灼卢,Python的基本類型如字符串绍哎、整數(shù)来农、浮點(diǎn)數(shù)都是不可變的,都可以作為 key崇堰。但是list是可變的沃于,就不能作為 key涩咖。
可以試試用list作為key時(shí)會(huì)報(bào)什么樣的錯(cuò)誤。
不可變這個(gè)限制僅作用于key繁莹,value是否可變無(wú)所謂:
{
'123': [1, 2, 3], # key 是 str檩互,value是list
123: '123', # key 是 int,value 是 str
('a', 'b'): True # key 是 tuple咨演,并且tuple的每個(gè)元素都是不可變對(duì)象闸昨,value是 boolean
}
最常用的key還是字符串,因?yàn)橛闷饋?lái)最方便薄风。
更新dict
dict是可變的饵较,也就是說(shuō),我們可以隨時(shí)往dict中添加新的 key-value遭赂。比如已有dict:
d = {
'Adam': 95,
'Lisa': 85,
'Bart': 59
}
要把新同學(xué)'Paul'
的成績(jī) 72 加進(jìn)去循诉,用賦值語(yǔ)句:
>>> d['Paul'] = 72
再看看dict的內(nèi)容:
>>> print d
{'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 59}
如果 key 已經(jīng)存在,則賦值會(huì)用新的 value 替換掉原來(lái)的 value:
>>> d['Bart'] = 60
>>> print d
{'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 60}
遍歷dict
由于dict也是一個(gè)集合撇他,所以茄猫,遍歷dict和遍歷list類似,都可以通過(guò) for 循環(huán)實(shí)現(xiàn)困肩。
直接使用for循環(huán)可以遍歷 dict 的 key:
>>> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
>>> for key in d:
... print key
...
Lisa
Adam
Bart
由于通過(guò) key 可以獲取對(duì)應(yīng)的 value划纽,因此,在循環(huán)體內(nèi)僻弹,可以獲取到value的值阿浓。
什么是set
dict的作用是建立一組 key 和一組 value 的映射關(guān)系,dict的key是不能重復(fù)的蹋绽。
有的時(shí)候芭毙,我們只想要 dict 的 key,不關(guān)心 key 對(duì)應(yīng)的 value卸耘,目的就是保證這個(gè)集合的元素不會(huì)重復(fù)退敦,這時(shí),set就派上用場(chǎng)了蚣抗。
set 持有一系列元素侈百,這一點(diǎn)和 list 很像,但是set的元素沒有重復(fù)翰铡,而且是無(wú)序的钝域,這點(diǎn)和 dict 的 key很像。
創(chuàng)建 set 的方式是調(diào)用 set() 并傳入一個(gè) list锭魔,list的元素將作為set的元素:
>>> s = set(['A', 'B', 'C'])
可以查看 set 的內(nèi)容:
>>> print s
set(['A', 'C', 'B'])
請(qǐng)注意例证,上述打印的形式類似 list, 但它不是 list迷捧,仔細(xì)看還可以發(fā)現(xiàn)织咧,打印的順序和原始 list 的順序有可能是不同的胀葱,因?yàn)閟et內(nèi)部存儲(chǔ)的元素是無(wú)序的。
因?yàn)閟et不能包含重復(fù)的元素笙蒙,所以抵屿,當(dāng)我們傳入包含重復(fù)元素的 list 會(huì)怎么樣呢?
>>> s = set(['A', 'B', 'C', 'C'])
>>> print s
set(['A', 'C', 'B'])
>>> len(s)
3
結(jié)果
訪問set
由于set存儲(chǔ)的是無(wú)序集合捅位,所以我們沒法通過(guò)索引來(lái)訪問轧葛。
訪問 set中的某個(gè)元素實(shí)際上就是判斷一個(gè)元素是否在set中。
例如艇搀,存儲(chǔ)了班里同學(xué)名字的set:
>>> s = set(['Adam', 'Lisa', 'Bart', 'Paul'])
我們可以用 in
操作符判斷:
Bart是該班的同學(xué)嗎朝群?
>>> 'Bart' in s
True
Bill是該班的同學(xué)嗎?
>>> 'Bill' in s
False
bart是該班的同學(xué)嗎中符?
>>> 'bart' in s
False
看來(lái)大小寫很重要姜胖,'Bart' 和 'bart'被認(rèn)為是兩個(gè)不同的元素。顯示淀散,set會(huì)自動(dòng)去掉重復(fù)的元素右莱,原來(lái)的list有4個(gè)元素汉买,但set只有3個(gè)元素评肆。
set的特點(diǎn)
set的內(nèi)部結(jié)構(gòu)和dict很像,唯一區(qū)別是不存儲(chǔ)value鼠锈,因此郭膛,判斷一個(gè)元素是否在set中速度很快晨抡。
set存儲(chǔ)的元素和dict的key類似,必須是不變對(duì)象则剃,因此耘柱,任何可變對(duì)象是不能放入set中的。
最后棍现,set存儲(chǔ)的元素也是沒有順序的调煎。
set的這些特點(diǎn),可以應(yīng)用在哪些地方呢己肮?
星期一到星期日可以用字符串'MON', 'TUE', ... 'SUN'表示士袄。
假設(shè)我們讓用戶輸入星期一至星期日的某天,如何判斷用戶的輸入是否是一個(gè)有效的星期呢谎僻?
可以用 if 語(yǔ)句判斷娄柳,但這樣做非常繁瑣:
x = '???' # 用戶輸入的字符串
if x!= 'MON' and x!= 'TUE' and x!= 'WED' ... and x!= 'SUN':
print 'input error'
else:
print 'input ok'
注意:if
語(yǔ)句中的...
表示沒有列出的其它星期名稱,測(cè)試時(shí)艘绍,請(qǐng)輸入完整赤拒。
如果事先創(chuàng)建好一個(gè)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'
這樣一來(lái)需了,代碼就簡(jiǎn)單多了
遍歷set
由于 set 也是一個(gè)集合,所以般甲,遍歷 set 和遍歷 list 類似肋乍,都可以通過(guò) for 循環(huán)實(shí)現(xiàn)。
直接使用 for 循環(huán)可以遍歷 set 的元素:
>>> s = set(['Adam', 'Lisa', 'Bart'])
>>> for name in s:
... print name
...
Lisa
Adam
Bart
注意: 觀察 for 循環(huán)在遍歷set時(shí)敷存,元素的順序和list的順序很可能是不同的墓造,而且不同的機(jī)器上運(yùn)行的結(jié)果也可能不同。
更新set
由于set存儲(chǔ)的是一組不重復(fù)的無(wú)序元素锚烦,因此觅闽,更新set主要做兩件事:
一是把新的元素添加到set中,二是把已有元素從set中刪除涮俄。
添加元素時(shí)蛉拙,用set的add()方法:
>>> s = set([1, 2, 3])
>>> s.add(4)
>>> print s
set([1, 2, 3, 4])
如果添加的元素已經(jīng)存在于set中,add()不會(huì)報(bào)錯(cuò)彻亲,但是不會(huì)加進(jìn)去了:
>>> s = set([1, 2, 3])
>>> s.add(3)
>>> print s
set([1, 2, 3])
刪除set中的元素時(shí)孕锄,用set的remove()方法:
>>> s = set([1, 2, 3, 4])
>>> s.remove(4)
>>> print s
set([1, 2, 3])
如果刪除的元素不存在set中,remove()會(huì)報(bào)錯(cuò):
>>> s = set([1, 2, 3])
>>> s.remove(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 4
所以用add()可以直接添加苞尝,而remove()前需要判斷畸肆。
四、函數(shù)
什么是函數(shù)
我們知道圓的面積計(jì)算公式為:
S = πr2
當(dāng)我們知道半徑r的值時(shí)宙址,就可以根據(jù)公式計(jì)算出面積轴脐。假設(shè)我們需要計(jì)算3個(gè)不同大小的圓的面積:
r1 = 12.34
r2 = 9.08
r3 = 73.1
s1 = 3.14 * r1 * r1
s2 = 3.14 * r2 * r2
s3 = 3.14 * r3 * r3
當(dāng)代碼出現(xiàn)有規(guī)律的重復(fù)的時(shí)候,你就需要當(dāng)心了抡砂,每次寫3.14 * x * x不僅很麻煩大咱,而且,如果要把3.14改成3.14159265359的時(shí)候注益,得全部替換徽级。
有了函數(shù),我們就不再每次寫s = 3.14 * x * x
聊浅,而是寫成更有意義的函數(shù)調(diào)用 s = area_of_circle(x)
餐抢,而函數(shù) area_of_circle
本身只需要寫一次,就可以多次調(diào)用低匙。
抽象是數(shù)學(xué)中非常常見的概念旷痕。舉個(gè)例子:
計(jì)算數(shù)列的和,比如:1 + 2 + 3 + ... + 100
顽冶,寫起來(lái)十分不方便欺抗,于是數(shù)學(xué)家發(fā)明了求和符號(hào)∑,可以把1 + 2 + 3 + ... + 100
記作:
100
∑n
n=1
這種抽象記法非常強(qiáng)大强重,因?yàn)槲覀兛吹健凭涂梢岳斫獬汕蠛徒食剩皇沁€原成低級(jí)的加法運(yùn)算贸人。
而且,這種抽象記法是可擴(kuò)展的佃声,比如:
100
∑(n2+1)
n=1
還原成加法運(yùn)算就變成了:
(1 x 1 + 1) + (2 x 2 + 1) + (3 x 3 + 1) + ... + (100 x 100 + 1)
可見艺智,借助抽象,我們才能不關(guān)心底層的具體計(jì)算過(guò)程圾亏,而直接在更高的層次上思考問題十拣。
寫計(jì)算機(jī)程序也是一樣,函數(shù)就是最基本的一種代碼抽象的方式志鹃。
Python不但能非常靈活地定義函數(shù)夭问,而且本身內(nèi)置了很多有用的函數(shù),可以直接調(diào)用曹铃。
調(diào)用函數(shù)
Python內(nèi)置了很多有用的函數(shù)缰趋,我們可以直接調(diào)用。
要調(diào)用一個(gè)函數(shù)陕见,需要知道函數(shù)的名稱和參數(shù)埠胖,比如求絕對(duì)值的函數(shù) abs
,它接收一個(gè)參數(shù)淳玩。
可以直接從Python的官方網(wǎng)站查看文檔:
python_doc_html
也可以在交互式命令行通過(guò) help(abs)
查看abs函數(shù)的幫助信息直撤。
調(diào)用 abs 函數(shù):
>>> abs(100)
100
>>> abs(-20)
20
>>> abs(12.34)
12.34
調(diào)用函數(shù)的時(shí)候,如果傳入的參數(shù)數(shù)量不對(duì)蜕着,會(huì)報(bào)TypeError
的錯(cuò)誤谋竖,并且Python會(huì)明確地告訴你:abs()有且僅有1個(gè)參數(shù),但給出了兩個(gè):
>>> abs(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)
如果傳入的參數(shù)數(shù)量是對(duì)的承匣,但參數(shù)類型不能被函數(shù)所接受蓖乘,也會(huì)報(bào)TypeError
的錯(cuò)誤,并且給出錯(cuò)誤信息:str是錯(cuò)誤的參數(shù)類型:
>>> abs('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'
而比較函數(shù) cmp(x, y)
就需要兩個(gè)參數(shù)韧骗,如果 x<y
嘉抒,返回-1,如果 x==y
袍暴,返回 0些侍,如果 x>y
,返回 1:
>>> cmp(1, 2)
-1
>>> cmp(2, 1)
1
>>> cmp(3, 3)
0
Python內(nèi)置的常用函數(shù)還包括數(shù)據(jù)類型轉(zhuǎn)換函數(shù)政模,比如int()
函數(shù)可以把其他數(shù)據(jù)類型轉(zhuǎn)換為整數(shù):
>>> int('123')
123
>>> int(12.34)
12
str()
函數(shù)把其他類型轉(zhuǎn)換成 str:
>>> str(123)
'123'
>>> str(1.23)
'1.23'
編寫函數(shù)
在Python中岗宣,定義一個(gè)函數(shù)要使用 def
語(yǔ)句,依次寫出函數(shù)名淋样、括號(hào)耗式、括號(hào)中的參數(shù)和冒號(hào):
,然后,在縮進(jìn)塊中編寫函數(shù)體刊咳,函數(shù)的返回值用 return
語(yǔ)句返回彪见。
我們以自定義一個(gè)求絕對(duì)值的 my_abs
函數(shù)為例:
def my_abs(x):
if x >= 0:
return x
else:
return -x
請(qǐng)注意,函數(shù)體內(nèi)部的語(yǔ)句在執(zhí)行時(shí)娱挨,一旦執(zhí)行到return
時(shí)余指,函數(shù)就執(zhí)行完畢,并將結(jié)果返回让蕾。因此,函數(shù)內(nèi)部通過(guò)條件判斷和循環(huán)可以實(shí)現(xiàn)非常復(fù)雜的邏輯或听。
如果沒有return
語(yǔ)句探孝,函數(shù)執(zhí)行完畢后也會(huì)返回結(jié)果,只是結(jié)果為None
誉裆。
return None
可以簡(jiǎn)寫為return
顿颅。
返回多值
函數(shù)可以返回多個(gè)值嗎?答案是肯定的足丢。
比如在游戲中經(jīng)常需要從一個(gè)點(diǎn)移動(dòng)到另一個(gè)點(diǎn)粱腻,給出坐標(biāo)、位移和角度斩跌,就可以計(jì)算出新的坐標(biāo):
math包提供了sin()和 cos()函數(shù)绍些,我們先用import引用它:
import math
def move(x, y, step, angle):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny
這樣我們就可以同時(shí)獲得返回值:
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print x, y
151.961524227 70.0
但其實(shí)這只是一種假象,Python函數(shù)返回的仍然是單一值:
>>> r = move(100, 100, 60, math.pi / 6)
>>> print r
(151.96152422706632, 70.0)
用print打印返回結(jié)果耀鸦,原來(lái)返回值是一個(gè)tuple
柬批!
但是,在語(yǔ)法上袖订,返回一個(gè)tuple可以省略括號(hào)氮帐,而多個(gè)變量可以同時(shí)接收一個(gè)tuple,按位置賦給對(duì)應(yīng)的值洛姑,所以上沐,Python的函數(shù)返回多值其實(shí)就是返回一個(gè)tuple,但寫起來(lái)更方便楞艾。
遞歸函數(shù)
在函數(shù)內(nèi)部参咙,可以調(diào)用其他函數(shù)。如果一個(gè)函數(shù)在內(nèi)部調(diào)用自身本身硫眯,這個(gè)函數(shù)就是遞歸函數(shù)昂勒。
舉個(gè)例子,我們來(lái)計(jì)算階乘 n! = 1 * 2 * 3 * ... * n
舟铜,用函數(shù) 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
時(shí)需要特殊處理塘娶。
于是归斤,fact(n)
用遞歸的方式寫出來(lái)就是:
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
上面就是一個(gè)遞歸函數(shù)〉蟀叮可以試試:
>>> fact(1)
1
>>> fact(5)
120
>>> fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L
如果我們計(jì)算fact(5)脏里,可以根據(jù)函數(shù)定義看到計(jì)算過(guò)程如下:
===> 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
遞歸函數(shù)的優(yōu)點(diǎn)是定義簡(jiǎn)單,邏輯清晰虹曙。理論上迫横,所有的遞歸函數(shù)都可以寫成循環(huán)的方式,但循環(huán)的邏輯不如遞歸清晰酝碳。
使用遞歸函數(shù)需要注意防止棧溢出矾踱。在計(jì)算機(jī)中,函數(shù)調(diào)用是通過(guò)棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的疏哗,每當(dāng)進(jìn)入一個(gè)函數(shù)調(diào)用呛讲,棧就會(huì)加一層棧幀,每當(dāng)函數(shù)返回返奉,棧就會(huì)減一層棧幀贝搁。由于棧的大小不是無(wú)限的,所以芽偏,遞歸調(diào)用的次數(shù)過(guò)多雷逆,會(huì)導(dǎo)致棧溢出。
可以試試計(jì)算 fact(10000)污尉。
定義默認(rèn)參數(shù)
定義函數(shù)的時(shí)候关面,還可以有默認(rèn)參數(shù)。
例如Python自帶的 int()
函數(shù)十厢,其實(shí)就有兩個(gè)參數(shù)等太,我們既可以傳一個(gè)參數(shù),又可以傳兩個(gè)參數(shù):
>>> int('123')
123
>>> int('123', 8)
83
int()
函數(shù)的第二個(gè)參數(shù)是轉(zhuǎn)換進(jìn)制蛮放,如果不傳缩抡,默認(rèn)是十進(jìn)制 (base=10),如果傳了包颁,就用傳入的參數(shù)瞻想。
可見,函數(shù)的默認(rèn)參數(shù)的作用是簡(jiǎn)化調(diào)用娩嚼,你只需要把必須的參數(shù)傳進(jìn)去蘑险。但是在需要的時(shí)候,又可以傳入額外的參數(shù)來(lái)覆蓋默認(rèn)參數(shù)值岳悟。
我們來(lái)定義一個(gè)計(jì)算 x 的N次方的函數(shù):
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
假設(shè)計(jì)算平方的次數(shù)最多佃迄,我們就可以把 n 的默認(rèn)值設(shè)定為 2:
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
這樣一來(lái)泼差,計(jì)算平方就不需要傳入兩個(gè)參數(shù)了:
>>> power(5)
25
由于函數(shù)的參數(shù)按從左到右的順序匹配,所以默認(rèn)參數(shù)只能定義在必需參數(shù)的后面:
# OK:
def fn1(a, b=1, c=2):
pass
# Error:
def fn2(a=1, b):
pass
定義可變參數(shù)
如果想讓一個(gè)函數(shù)能接受任意個(gè)參數(shù)呵俏,我們就可以定義一個(gè)可變參數(shù):
def fn(*args):
print args
可變參數(shù)的名字前面有個(gè) * 號(hào)堆缘,我們可以傳入0個(gè)、1個(gè)或多個(gè)參數(shù)給可變參數(shù):
>>> fn()
()
>>> fn('a')
('a',)
>>> fn('a', 'b')
('a', 'b')
>>> fn('a', 'b', 'c')
('a', 'b', 'c')
可變參數(shù)也不是很神秘普碎,Python解釋器會(huì)把傳入的一組參數(shù)組裝成一個(gè)tuple傳遞給可變參數(shù)吼肥,因此,在函數(shù)內(nèi)部麻车,直接把變量 args 看成一個(gè) tuple 就好了缀皱。
定義可變參數(shù)的目的也是為了簡(jiǎn)化調(diào)用。假設(shè)我們要計(jì)算任意個(gè)數(shù)的平均值动猬,就可以定義一個(gè)可變參數(shù):
def average(*args):
...
這樣啤斗,在調(diào)用的時(shí)候,可以這樣寫:
>>> average()
0
>>> average(1, 2)
1.5
>>> average(1, 2, 2, 3, 4)
2.4
* 星號(hào)函數(shù)參數(shù)解包
定義一個(gè)四個(gè)參數(shù)的函數(shù)
def test(a, b, c, d):
print a, b, c, d
通過(guò) *
號(hào)來(lái)解包元組來(lái)實(shí)現(xiàn)參數(shù)輸入
>>> test(*(1, 2, 3, 4))
1 2 3 4
**
和 *
一樣是解包對(duì)象是字典
切片
對(duì)list進(jìn)行切片
取一個(gè)list的部分元素是非常常見的操作枣察。比如争占,一個(gè)list如下:
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']
取前3個(gè)元素燃逻,應(yīng)該怎么做序目?
笨辦法:
>>> [L[0], L[1], L[2]]
['Adam', 'Lisa', 'Bart']
之所以是笨辦法是因?yàn)閿U(kuò)展一下,取前N個(gè)元素就沒轍了伯襟。
取前N個(gè)元素猿涨,也就是索引為0-(N-1)的元素,可以用循環(huán):
>>> r = []
>>> n = 3
>>> for i in range(n):
... r.append(L[i])
...
>>> r
['Adam', 'Lisa', 'Bart']
對(duì)這種經(jīng)常取指定索引范圍的操作姆怪,用循環(huán)十分繁瑣叛赚,因此,Python提供了切片(Slice)操作符稽揭,能大大簡(jiǎn)化這種操作俺附。
對(duì)應(yīng)上面的問題,取前3個(gè)元素溪掀,用一行代碼就可以完成切片:
>>> L[0:3]
['Adam', 'Lisa', 'Bart']
L[0:3]
表示事镣,從索引0開始取,直到索引3為止揪胃,但不包括索引3璃哟。即索引0,1喊递,2随闪,正好是3個(gè)元素。
如果第一個(gè)索引是0骚勘,還可以省略:
>>> L[:3]
['Adam', 'Lisa', 'Bart']
也可以從索引1開始铐伴,取出2個(gè)元素出來(lái):
>>> L[1:3]
['Adam', 'Lisa']
只用一個(gè) : ,表示從頭到尾:
>>> L[:]
['Adam', 'Lisa', 'Bart', 'Paul']
因此,L[:]實(shí)際上復(fù)制出了一個(gè)新list盛杰。
切片操作還可以指定第三個(gè)參數(shù):
>>> L[::2]
['Adam', 'Bart']
第三個(gè)參數(shù)表示每N個(gè)取一個(gè)挽荡,上面的 L[::2] 會(huì)每?jī)蓚€(gè)元素取出一個(gè)來(lái),也就是隔一個(gè)取一個(gè)即供。
把list換成tuple定拟,切片操作完全相同,只是切片的結(jié)果也變成了tuple逗嫡。
倒序切片
對(duì)于list青自,既然Python支持L[-1]取倒數(shù)第一個(gè)元素,那么它同樣支持倒數(shù)切片驱证,試試:
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']
>>> L[-2:]
['Bart', 'Paul']
>>> L[:-2]
['Adam', 'Lisa']
>>> L[-3:-1]
['Lisa', 'Bart']
>>> L[-4:-1:2]
['Adam', 'Bart']
記住倒數(shù)第一個(gè)元素的索引是-1延窜。倒序切片包含起始索引,不包含結(jié)束索引抹锄。
對(duì)字符串切片
字符串 'xxx'
和 Unicode
字符串 u'xxx'
也可以看成是一種list逆瑞,每個(gè)元素就是一個(gè)字符。因此伙单,字符串也可以用切片操作获高,只是操作結(jié)果仍是字符串:
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[-3:]
'EFG'
>>> 'ABCDEFG'[::2]
'ACEG'
在很多編程語(yǔ)言中,針對(duì)字符串提供了很多各種截取函數(shù)吻育,其實(shí)目的就是對(duì)字符串切片念秧。Python沒有針對(duì)字符串的截取函數(shù),只需要切片一個(gè)操作就可以完成布疼,非常簡(jiǎn)單摊趾。
迭代
什么是迭代
在Python中,如果給定一個(gè)list或tuple游两,我們可以通過(guò)for循環(huán)來(lái)遍歷這個(gè)list或tuple砾层,這種遍歷我們成為迭代(Iteration)。
在Python中贱案,迭代是通過(guò) for ... in
來(lái)完成的肛炮,而很多語(yǔ)言比如C或者Java,迭代list是通過(guò)下標(biāo)完成的轰坊,比如Java代碼:
for (i=0; i<list.length; i++) {
n = list[i];
}
可以看出铸董,Python的for循環(huán)抽象程度要高于Java的for循環(huán)。
因?yàn)?Python 的 for循環(huán)不僅可以用在list或tuple上肴沫,還可以作用在其他任何可迭代對(duì)象上粟害。
因此,迭代操作就是對(duì)于一個(gè)集合颤芬,無(wú)論該集合是有序還是無(wú)序悲幅,我們用 for 循環(huán)總是可以依次取出集合的每一個(gè)元素套鹅。
*注意: 集合是指包含一組元素的數(shù)據(jù)結(jié)構(gòu),我們已經(jīng)介紹的包括:
- 有序集合:list汰具,tuple卓鹿,str和unicode;
- 無(wú)序集合:set
- 無(wú)序集合并且具有 key-value 對(duì):dict
而迭代是一個(gè)動(dòng)詞留荔,它指的是一種操作吟孙,在Python中,就是for
循環(huán)聚蝶。
迭代與按下標(biāo)訪問數(shù)組最大的不同是杰妓,后者是一種具體的迭代實(shí)現(xiàn)方式,而前者只關(guān)心迭代結(jié)果碘勉,根本不關(guān)心迭代內(nèi)部是如何實(shí)現(xiàn)的巷挥。
索引迭代
Python中,迭代永遠(yuǎn)是取出元素本身验靡,而非元素的索引倍宾。
對(duì)于有序集合,元素確實(shí)是有索引的胜嗓。有的時(shí)候高职,我們確實(shí)想在 for 循環(huán)中拿到索引,怎么辦兼蕊?
方法是使用 enumerate() 函數(shù):
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']
>>> for index, name in enumerate(L):
... print index, '-', name
...
0 - Adam
1 - Lisa
2 - Bart
3 - Paul
使用 enumerate()
函數(shù)初厚,我們可以在for循環(huán)中同時(shí)綁定索引index和元素name件蚕。但是孙技,這不是 enumerate()
的特殊語(yǔ)法。實(shí)際上排作,enumerate()
函數(shù)把:
['Adam', 'Lisa', 'Bart', 'Paul']
變成了類似:
[(0, 'Adam'), (1, 'Lisa'), (2, 'Bart'), (3, 'Paul')]
因此牵啦,迭代的每一個(gè)元素實(shí)際上是一個(gè)tuple:
for t in enumerate(L):
index = t[0]
name = t[1]
print index, '-', name
如果我們知道每個(gè)tuple元素都包含兩個(gè)元素,for循環(huán)又可以進(jìn)一步簡(jiǎn)寫為:
for index, name in enumerate(L):
print index, '-', name
這樣不但代碼更簡(jiǎn)單妄痪,而且還少了兩條賦值語(yǔ)句哈雏。
可見,索引迭代也不是真的按索引訪問衫生,而是由 enumerate()
函數(shù)自動(dòng)把每個(gè)元素變成 (index, element)
這樣的tuple裳瘪,再迭代,就同時(shí)獲得了索引和元素本身罪针。
迭代dict的value
我們已經(jīng)了解了dict對(duì)象本身就是可迭代對(duì)象彭羹,用 for 循環(huán)直接迭代 dict,可以每次拿到dict的一個(gè)key泪酱。
如果我們希望迭代 dict 對(duì)象的value派殷,應(yīng)該怎么做还最?
dict 對(duì)象有一個(gè) values()
方法,這個(gè)方法把dict轉(zhuǎn)換成一個(gè)包含所有value的list毡惜,這樣拓轻,我們迭代的就是 dict的每一個(gè) value:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
print d.values()
# [85, 95, 59]
for v in d.values(): print v
# 85
# 95
# 59
如果仔細(xì)閱讀Python的文檔,還可以發(fā)現(xiàn)经伙,dict除了values()
方法外扶叉,還有一個(gè) 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
那這兩個(gè)方法有何不同之處呢辜梳?
- values() 方法實(shí)際上把一個(gè) dict 轉(zhuǎn)換成了包含 value 的list。
- 但是 itervalues() 方法不會(huì)轉(zhuǎn)換泳叠,它會(huì)在迭代過(guò)程中依次從 dict 中取出 value作瞄,所以 itervalues() 方法比 values() 方法節(jié)省了生成 list 所需的內(nèi)存。
- 打印 itervalues() 發(fā)現(xiàn)它返回一個(gè) <dictionary-valueiterator> 對(duì)象危纫,這說(shuō)明在Python中宗挥,for 循環(huán)可作用的迭代對(duì)象遠(yuǎn)不止 list,tuple种蝶,str契耿,unicode,dict等螃征,任何可迭代對(duì)象都可以作用于for循環(huán)搪桂,而內(nèi)部如何迭代我們通常并不用關(guān)心。
如果一個(gè)對(duì)象說(shuō)自己可迭代盯滚,那我們就直接用 for 循環(huán)去迭代它踢械,可見,迭代是一種抽象的數(shù)據(jù)操作魄藕,它不對(duì)迭代對(duì)象內(nèi)部的數(shù)據(jù)有任何要求内列。
迭代dict的key和value
我們了解了如何迭代 dict 的key和value,那么背率,在一個(gè) for 循環(huán)中话瞧,能否同時(shí)迭代 key和value?答案是肯定的寝姿。
首先交排,我們看看 dict 對(duì)象的 items() 方法返回的值:
>>> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
>>> print d.items()
[('Lisa', 85), ('Adam', 95), ('Bart', 59)]
可以看到,items()
方法把dict對(duì)象轉(zhuǎn)換成了包含tuple
的list
饵筑,我們對(duì)這個(gè)list進(jìn)行迭代埃篓,可以同時(shí)獲得key和value:
>>> for key, value in d.items():
... print key, ':', value
...
Lisa : 85
Adam : 95
Bart : 59
和 values()
有一個(gè) itervalues()
類似, items()
也有一個(gè)對(duì)應(yīng)的 iteritems()
翻翩,iteritems()
不把dict轉(zhuǎn)換成list都许,而是在迭代過(guò)程中不斷給出 tuple稻薇,所以, iteritems()
不占用額外的內(nèi)存胶征。
列表生成式
生成列表
要生成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]
但如果要生成[1x1, 2x2, 3x3, ..., 10x10]
怎么做?方法一是循環(huán):
>>> L = []
>>> for x in range(1, 11):
... L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
但是循環(huán)太繁瑣睛低,而列表生成式則可以用一行語(yǔ)句代替循環(huán)生成上面的list:
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
這種寫法就是Python特有的列表生成式案狠。利用列表生成式,可以以非常簡(jiǎn)潔的代碼生成 list钱雷。
寫列表生成式時(shí)骂铁,把要生成的元素 x * x 放到前面,后面跟 for 循環(huán)罩抗,就可以把list創(chuàng)建出來(lái)拉庵,十分有用,多寫幾次套蒂,很快就可以熟悉這種語(yǔ)法钞支。
復(fù)雜表達(dá)式
使用for循環(huán)的迭代不僅可以迭代普通的list,還可以迭代dict操刀。
假設(shè)有如下的dict:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
完全可以通過(guò)一個(gè)復(fù)雜的列表生成式把它變成一個(gè) 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>'
注:字符串可以通過(guò) %
進(jìn)行格式化烁挟,用指定的參數(shù)替代 %s
。字符串的join()
方法可以把一個(gè) list 拼接成一個(gè)字符串骨坑。
把打印出來(lái)的結(jié)果保存為一個(gè)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>
條件過(guò)濾
列表生成式的 for 循環(huán)后面還可以加上 if 判斷。例如:
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
如果我們只想要偶數(shù)的平方欢唾,不改動(dòng) range()
的情況下且警,可以加上 if 來(lái)篩選:
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
有了 if 條件,只有 if 判斷為 True 的時(shí)候匈辱,才把循環(huán)的當(dāng)前元素添加到列表中振湾。
多層表達(dá)式
for循環(huán)可以嵌套杀迹,因此亡脸,在列表生成式中,也可以用多層 for 循環(huán)來(lái)生成列表树酪。
對(duì)于字符串 'ABC' 和 '123'浅碾,可以使用兩層循環(huán),生成全排列:
>>> [m + n for m in 'ABC' for n in '123']
['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
翻譯成循環(huán)代碼就像下面這樣:
L = []
for m in 'ABC':
for n in '123':
L.append(m + n)