Python零基礎入門看完這一篇就夠了:零基礎入門筆記
學習python有一年多了,希望通過這篇兩萬字的學習筆記來複習了,也能讓後來者少走一點彎路。在慕課網
課程筆記的同時加入了一部分自己的經驗補充。
- [√] 慕課網Python開發環境搭建: 配開發環境
- [√] 廖雪峰老師在慕課網的課程: Python入門
Mark之前別忘了點個贊哦。
推薦前往我的個人部落格進行閱讀:http://blog.mtianyan.cn/
分章目錄,超寬程式碼顯示區。更爽哦。
知識點標註ctrl + f
搜尋知識點
檢視。重要及較難程式設計題採用天涯
標註。
Python開發環境搭建
搭建環境分為兩個版本:
- 基礎版,供初學者快速安裝體驗。
- 進階版, 供對於資料科學,機器學習有興趣者安裝。
推薦:
安裝進階版,一步到位。
基礎版:Windows下安裝python環境(2.7 | 3.x)
安裝包下載。
選擇download下的windows。點選進入。
下圖中紅框為64位版本。32位版本可以選擇Windows x86 executable installer
2.7版本的安裝包下載:
安裝python。
點選下一步下一步進行預設安裝即可。(跟平常裝個qq啥的沒兩樣)
安裝完成的測試。
win(即徽標鍵)
+ R
輸入cmd
:
開啟命令列。輸入python
不報錯的進入python控制檯下。
進階版:Windows下Anaconda2(Python2)和Anaconda3(Python3)的共存
轉載
原文地址:http://blog.csdn.net/infin1te/article/details/50445217
Anaconda是一個Python的科學計算髮行版,包含了超過300個流行的用於科學、數學、工程和資料分析的Python Packages。由於Python有2和3兩個版本,因此Anaconda也在Python2和Python3的基礎上推出了兩個發行版,即Anaconda2和Anaconda3。
以上文字摘自轉載部落格。通俗講就是一個python的各種科學計算包的大合集版本。省去了自己安裝大量基本包的過程。
Tips:
3.x版本建議選擇Python 3.5.1 |Anaconda 4.1.0 (64-bit)
以後如果要使用python進行TensorFlow windows版的配置可以省下時間。
這是博主自入python坑以來找到的最好的共存方法,沒有出過問任何題!!!
這是博主自入python坑以來找到的最好的共存方法,沒有出過問任何題!!!
這是博主自入python坑以來找到的最好的共存方法,沒有出過問任何題!!!
Linux下的python使用。
- Linux 預設安裝python,建議安裝IPython;
-
sudo apt-get install ipython
安裝Ipython(支援Tab鍵自動補齊) - 使用Vim來建立
.py
檔案 - 輸入
python
即可檢視當前版本
IPython是Python的互動式Shell,提供了程式碼自動補完,自動縮排,高亮顯示,執行Shell命令等非常有用的特性。特別是它的程式碼補完功能.
python檔案型別(常識)
python執行過程:
.py
檔案 –> python直譯器 –> 位元組碼檔案 –> python直譯器 –> 二進位制檔案 –> 記憶體、執行 –> 列印結果
位元組碼檔案:
-
.pyc
轉換方式:python -m py_compile xxx.py
作用:提高程式的載入速度 -
.pyo
(優化編譯的.pyc檔案)
轉換方式:python -O -m py_compile xxx.py
作用:提高程式的執行速度
eclipse下的python環境安裝。
新增python開發環境到eclipse:
- 點選
help
——install New Software
- 點選
add
,彈出新視窗: - Name:填PyDev
- Location:填 http://pydev.org/updates
- 確認後會出現 PyDev,勾選Pydev。
- pydev for eclipse–> next–> accept–>finish
- file–> new–> project –>Pydev下 pydevProject
python的dev下載地址:http://pydev.org/updates
Python的初次體驗
python語言介紹
全世界有幾百種程式語言,但是流行的只有十幾種,python就是其中一種。荷蘭人龜叔於1989年聖誕節創立。
特點:優雅,明確,簡單。
適合的領域:
- web網站和各種網路服務;
- 系統工具和指令碼;
作為膠水語言把其他語言開發的模組包裝起來方便使用。
Python是一門高階語言,所以不適合貼近硬體的程式碼:
- 比如驅動程式(首選C)
- 移動開發,有各自的語言,(objectC,swift/java)
- 遊戲開發(首選C/C++)。
Python實際應用:
YouTube,豆瓣,搜狐郵箱;Openstack開源雲端計算平臺。Google,Yahoo,NASA。
語言之間的對比:
C編譯為機器碼;JAVA編譯為位元組碼;python為解釋執行。
缺點: 執行慢,Python原始碼不能加密。
Python版本的選擇
博主建議選擇安裝環境篇的進階版:2.7版本與3.x版本共存。
3.x版本建議選擇Python 3.5.1 |Anaconda 4.1.0 (64-bit)
以後如果要使用python進行TensorFlow windows版的配置可以省下時間。
windows下安裝python
參考:搭建Python開發環境
第一個python程式
cmd下輸入python。進入互動式環境。
-
命令列模式啟動python:
python
-
命令列模式執行python檔案
python 目錄/xxx.py
-
命令列模式關閉python:
exit()
注意:不要使用word,或者windows下自帶的記事本來進行程式碼編寫。
推薦使用:
- 輕量級:sublime Text 或 editplus
- 重量級(較大工程) : pycharm Professional
2.7版本專屬:
print `hello,world!`
3.x版本(2.7版本也可以正常執行):
print ("hello,world!")
Python變數和資料型別
講解Python基本的資料型別.包括整數、浮點數、字串和布林型別,以及變數的概念和基本的資料運算。
資料型別
整數
在Python程式中,整數的表示方法和數學上的寫法一模一樣.
例如:1,100,-8080,0,等等。十六進位制用0x
字首和0-9
,a-f
表示.
例如:0xff00,0xa5b4c3d2,等等。
浮點數
浮點數也就是小數,之所以稱為浮點數: 因為按照科學記數法表示時,一個浮點數的小數點位置是可變的
比如,1.23×109和12.3×108是相等的。
浮點數可以用數學寫法: 如1.23,3.14,-9.01,等等。但是對於很大或很小的浮點數,就必須用科學計數法表示,把10用e替代,1.23×10^9就是1.23e9,或者12.3e8,0.000012可以寫成1.2e-5,等等。
整數和浮點數在計算機內部儲存的方式是不同的,整數運算永遠是精確的(除法難道也是精確的?是的!),而浮點數運算則可能會有四捨五入的誤差。
知識點:python2與3不同整除
python2.7下:/
和 //
都是整數除法。
例: 1/2結果為0.後面小數部分會直接去除掉。
python3.x下:
-
/
為浮點數除法(如:1/2=0.5
) -
//
為整數除法(如:1//2 = 0
)
a = 1
b = 2
print a+b
#python2.7下想要浮點數除法就得使用型別轉換。
print float(a)/b
字串
字串是以“或””括起來的任意文字,比如`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
是一個特殊的空值。
程式設計小任務:
- 計算十進位制整數 45678 和十六進位制整數 0x12fd2 之和。
- 請用字串表示出Learn Python in imooc。
- 請計算以下表示式的布林值(注意==表示判斷是否相等):
100 < 99
0xff == 255
題目答案:
print 45678+0x12fd2
print "Learn Python in imooc"
print 100<99
print 0xff == 255
執行結果:
123456
Learn Python in imooc
False
True
print語句
print語句可以向螢幕上輸出指定的文字。比如輸出`hello, world`,用程式碼實現如下:
print `hello, world`
注意:
-
當我們在Python互動式環境下編寫程式碼時,>>>是Python直譯器的提示符,不是程式碼的一部分。
-
當我們在文字編輯器中編寫程式碼時,千萬不要自己新增 >>>。
print語句也可以跟上多個字串,用逗號,
隔開,就可以連成一串輸出:
print `The quick brown fox`, `jumps over`, `the lazy dog`
執行結果:
The quick brown fox jumps over the lazy dog
print會依次列印每個字串,知識點:遇到逗號,
會輸出一個空格.
print也可以列印整數,或者計算結果:
>>> print 300
300 #執行結果
>>> print 100 + 200
300 #執行結果
漂亮做法:
>>> print `100 + 200 =`, 100 + 200
100 + 200 = 300 #執行結果
注意: 對於100 + 200,Python直譯器自動計算出結果300.
但是,`100 + 200 =`是字串而非數學公式,Python把它視為字串.
程式設計任務:請用兩種方式列印出 hello, python.
實現程式碼:
#input code
print `hello, python.`
print `hello,`,`python.`
執行結果:
hello, python.
hello, python.
註釋
Python的註釋以#
開頭,後面的文字直到行尾都算註釋
# 這一行全部都是註釋...
print `hello` # 這也是註釋
# 暫時不想執行下面一行程式碼:
# print `hello, python.`
註釋還有一個巧妙的用途,就是一些程式碼我們不想執行,但又不想刪除,就可以用註釋暫時遮蔽掉:
程式設計任務:
將程式碼編輯器中的 “print `hello`” 語句修改成註釋語句
實現程式碼:
# print `hello`
註釋:多行註釋
```
下面是一行被註釋程式碼
下面是兩行被註釋程式碼
```
什麼是變數
在Python中,變數的概念基本上和初中代數的方程變數是一致的。
例如,對於方程式y=x*x
,x
就是變數。
當x=2
時,計算結果是4
。當x=5
時,計算結果是25
。
只是在計算機程式中,變數不僅可以是數字,還可以是任意資料型別。
在Python程式中,變數是用一個變數名錶示。
知識點:變數名必須是大小寫英文、數字和下劃線 _
的組合,且不能用數字開頭。比如:
a = 1
t_007 = `T007`
變數a是一個整數。變數t_007是一個字串。
在Python中,等號=
是賦值語句,可以把任意資料型別賦值給變數,同一個變數可以反覆賦值,而且可以是不同型別的變數,例如:
a = 123 # a是整數
print a
a = `imooc` # a變為字串
print a
知識點: 這種變數本身型別不固定的語言稱之為動態語言,與之對應的是靜態語言。
靜態語言在定義變數時必須指定變數型別,如果賦值的時候型別不匹配,就會報錯。例如Java是靜態語言,賦值語句如下(// 表示註釋):
//這些是java程式碼
int a = 123; // a是整數型別變數
a = "mooc"; // 錯誤:不能把字串賦給整型變數
和靜態語言相比,動態語言更靈活,就是這個原因。
請不要把賦值語句的等號等同於數學的等號。比如下面的程式碼:
x = 10
x = 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`了。
程式設計任務:
等差數列可以定義為每一項與它的前一項的差等於一個常數,可以用變數 x1 表示等差數列的第一項,用 d 表示公差,請計算數列
1 4 7 10 13 16 19 …
前 100 項的和。
實現程式碼:
x1 = 1
d = 3
n = 100
x100 = x1+(100-1)*d
s2 = (x1+x100)*100/2
s = n*x1+n*(n-1)*d/2
print s,s2
等差數列公式:
(首項+尾項)*項數/2
項數*首項+項數*(項數-1)*公差/2
執行結果:
14950 14950
定義字串
字串可以用``
或者""
括起來表示。
如果字串本身包含`
怎麼辦?比如我們要表示字串 I`m OK
,這時,可以用" "
括起來表示:
"I`m OK"
`Learn "Python" in imooc`
類似的,知識點: 如果字串包含"
,我們就可以用` `
括起來表示:
如果字串既包含`
又包含"
怎麼辦?
知識點:轉義
這個時候,就需要對字串的某些特殊字元進行轉義
,Python字串用進行轉義。
要表示字串 Bob said "I`m OK".
由於 `
和"
會引起歧義,因此,我們在它前面插入一個表示這是一個普通字元,不代表字串的起始,因此,這個字串又可以表示為
`Bob said "I`m OK".`
# 在要保留原狀的字串前面加上右斜槓
注意:轉義字元 不計入字串的內容中。
常用的轉義字元還有:
-
表示換行
-
-
\
表示字元本身
程式設計任務:
請將下面兩行內容用Python的字串表示並列印出來:
Python was started in 1989 by "Guido".
Python is free and easy to learn.
s = `Python was started in 1989 by"Guido".
Python is free and easy to learn.`
print s
raw字串與多行字串
如果一個字串包含很多需要轉義的字元,對每一個字元都進行轉義會很麻煩。為了避免這種情況,我們可以在字串前面加個字首 r
,表示這是一個 raw 字串,裡面的字元就不需要轉義了。例如:
r`(~_~)/ (~_~)/`
解釋:
這個例子舉得不是很好。
可以看出raw加上之後。可能產生誤會的被修改為
\
(\
表示字元本身)
- 不加上r 只有
和
(
並沒有合成轉義字元。 - 加上r。
需要被轉義,經過轉義後顯示出來還是自己。
知識點: 個人小題(r的強大作用)
上圖效果可以看出r的強大作用。
但是r`我是一段字元`
表示法不能表示多行字串(r```一段字元```
),也不能表示包含`
和 "
的字串(為什麼?)
因為如果r`mtian`yan`
r遇到左邊第一個
`
,會繼續往後找閉合的標誌`
然後找到mtian的地方。它任務結束了。程式碼繼續往下執行。當掃到yan這裡他就會報錯。
???(更深層待續)
或者r"mtian"yan"
或導致r提前結束掉。後面的就無法繼續匹配到對應的。
知識點: 多行字串,可以用```...```
表示:
```Line 1
Line 2
Line 3```
#上面這個字串的表示方法和下面的是完全一樣的:
`Line 1
Line 2
Line 3`
還可以在多行字串前面新增 r ,把這個多行字串也變成一個raw字串:
r```Python is created by "Guido".
It is free and easy to learn.
Let`s start learn Python in imooc!```
程式設計任務:
請把下面的字串用r```...```
的形式改寫,並用print列印出來:
`"To be, or not to be": that is the question.
Whether it`s nobler in the mind to suffer.`
print r```"To be,or not to be":that is the question.
Whether it`s nobler in the mind to suffer.```
知識點: 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 ,中文就不能正常顯示。(這個應該是很早版本才會。筆者現在已經無法復現)
轉載: http://blog.csdn.net/lxdcyh/article/details/4018054
字串在Python內部的表示是unicode編碼,因此,在做編碼轉換時,通常需要以unicode作為中間編碼,即先將其他編碼的字串解碼decode
成unicode,再從unicode編碼encode
成另一種編碼。
-
decode的作用是將其他編碼的字串轉換成unicode編碼,如
str1.decode(`gb2312`)
,表示將gb2312編碼的字串str1轉換成unicode編碼。 -
encode的作用是將unicode編碼轉換成其他編碼的字串,如str2.encode(`gb2312`),表示將unicode編碼的字串str2轉換成gb2312編碼。
-
程式碼中字串的預設編碼與程式碼檔案本身的編碼一致。
如:s=`中文`
如果是在utf8的檔案中,該字串就是utf8編碼,如果是在gb2312的檔案中,則其編碼為gb2312。這種情況下,要進行編碼轉換,都需要先用decode方法將其轉換成unicode編碼,再使用encode方法將其轉換成其他編碼。通常,在沒有指定特定的編碼方式時,都是使用的系統預設編碼建立的程式碼檔案
如果字串是這樣定義:s=u`中文`
則該字串的編碼就被指定為unicode了,即python的內部編碼,而與程式碼檔案本身的編碼無關。因此,對於這種情況做編碼轉換,只需要直接使用encode
方法將其轉換成指定編碼即可。
如果一個字串已經是unicode了,再進行解碼則將出錯,因此通常要對其編碼方式是否為unicode進行判斷:
isinstance(s, unicode)
#用來判斷是否為unicode
用非unicode編碼形式的str來encode
會報錯
如何獲得系統的預設編碼?
#!/usr/bin/env python
#coding=utf-8
import sys
print sys.getdefaultencoding()
該段程式在Win10(1079)上輸出為:ascii
在某些IDE中,字串的輸出總是出現亂碼,甚至錯誤,其實是由於IDE的結果輸出控制檯自身不能顯示字串的編碼,而不是程式本身的問題。
如在UliPad(注:UliPad是wxPython的動力,導向和靈活的程式設計器)中執行如下程式碼:
s=u"中文"
print s
會提示:UnicodeEncodeError: `ascii` codec can`t encode characters in position 0-1: ordinal not in range(128)。
這是因為UliPad在控制檯資訊輸出視窗是按照ascii編碼輸出的(系統的預設編碼是ascii),而上面程式碼中的字串是Unicode編碼的,所以輸出時產生了錯誤。
將最後一句改為:print s.encode(`gb2312`)
則能正確輸出“中文”兩個字。
若最後一句改為:print s.encode(`utf8`)
則輸出:/xe4/xb8/xad/xe6/x96/x87,這是控制檯資訊輸出視窗按照ascii編碼輸出utf8編碼的字串的結果。
unicode(str,`gb2312`)
與str.decode(`gb2312`)
是一樣的,都是將gb2312編碼的str轉為unicode編碼
使用str.__class__
可以檢視str的編碼形式為str型別。
window預設編碼gbk;linux預設編碼utf8
原理說了半天,最後來個包治百病的吧:
(天涯):下面程式碼
#!/usr/bin/env python
#coding=utf-8
def getCoding(strInput):
```
獲取編碼格式
```
if isinstance(strInput, unicode):
return "unicode"
try:
strInput.decode("utf8")
return `utf8`
except:
pass
try:
strInput.decode("gbk")
return `gbk`
except:
pass
def tran2UTF8(strInput):
```
轉化為utf8格式
```
strCodingFmt = getCoding(strInput)
if strCodingFmt == "utf8":
return strInput
elif strCodingFmt == "unicode":
return strInput.encode("utf8")
elif strCodingFmt == "gbk":
return strInput.decode("gbk").encode("utf8")
def tran2GBK(strInput):
```
轉化為gbk格式
```
strCodingFmt = getCoding(strInput)
if strCodingFmt == "gbk":
return strInput
elif strCodingFmt == "unicode":
return strInput.encode("gbk")
elif strCodingFmt == "utf8":
return strInput.decode("utf8").encode("gbk")
s = "中文"
if isinstance(s, unicode):
#s=u"中文"
print s.encode(`gb2312`)
print "我是Unicode編碼的"
elif getCoding(s) == "utf8":
#s="中文"
print s.decode(`utf-8`).encode(`gb2312`)
print "我是utf-8編碼的"
else:
print s.decode(`gbk`).encode(`gbk`)
print "我是gbk編碼的"
上圖結果一:以utf-8格式儲存的py檔案。
圖二:以ascii格式儲存的py檔案。
編碼檢測包 chardet
知識點:因此,轉碼的時候一定要先搞明白,字串str是什麼編碼,然後decode成unicode,然後再encode成其他編碼
插入資料庫報錯的解決方案:
UnicodeDecodeError: ‘ascii’ codec can’t decode byte
import sys
reload(sys)
sys.setdefaultencoding(`utf8`)
Unicode字串除了多了一個 u 之外,與普通字串沒啥區別,轉義字元和多行表示法仍然有效:
轉義:
u`中文
日文
韓文`
#多行:
u```第一行
第二行```
#raw+多行:
ur```Python的Unicode字串支援"中文",
"日文",
"韓文"等多種語言```
如果中文字串在Python環境下遇到 UnicodeDecodeError,這是因為.py檔案儲存的格式有問題。可以在第一行新增註釋
# -*- coding: utf-8 -*-
#簡潔版
#coding=utf-8
目的是告訴Python直譯器,用UTF-8編碼讀取原始碼。
然後用Notepad++ 另存為… 並選擇UTF-8格式儲存。
程式設計任務:
用多行Unicode字串表示下面的唐詩並列印:
靜夜思
床前明月光,
疑是地上霜。
舉頭望明月,
低頭思故鄉。
知識點: https://www.python.org/dev/peps/pep-0263/
python定義檔案編碼到底用哪種?
# coding=<encoding name>
#!/usr/bin/python
# -*- coding: <encoding name> -*-
#!/usr/bin/python
# vim: set fileencoding=<encoding name> :
這些都可以只要第一二行能滿足如下正規表示式
^[ v]*#.*?coding[:=][ ]*([-_.a-zA-Z0-9]+)
# -*- coding: utf-8 -*-
# This Python file uses the following encoding: utf-8
# 花式標明
print ```靜夜思
床前明月光,
疑是地上霜。
舉頭望明月,
低頭思故鄉。
```
如果不標明檔案編碼或找不到。python會預設你是ASCII
整數和浮點數
Python支援對整數和浮點數直接進行四則混合運算,運算規則和數學上的四則運算規則完全一致。
基本的運算:
1 + 2 + 3 # ==> 6
4 * 5 - 6 # ==> 14
7.5 / 8 + 2.1 # ==> 3.0375
使用括號可以提升優先順序,這和數學運算完全一致,注意只能使用小括號,但是括號可以巢狀很多層:
(1 + 2) * 3 # ==> 9
(2.2 + 3.3) / (1.5 * (9 - 0.3)) # ==> 0.42145593869731807
和數學運算不同的地方是,Python的整數運算結果仍然是整數,浮點數運算結果仍然是浮點數:
1 + 2 # ==> 整數 3
1.0 + 2.0 # ==> 浮點數 3.0
但是整數和浮點數混合運算的結果就變成浮點數了:
1 + 2.0 # ==> 浮點數 3.0
為什麼要區分整數運算和浮點數運算呢?
這是因為整數運算的結果永遠是精確的,而浮點數運算的結果不一定精確,因為計算機記憶體再大,也無法精確表示出無限迴圈小數,比如 0.1 換成二進位制表示就是無限迴圈小數。
那整數的除法運算遇到除不盡的時候,結果難道不是浮點數嗎?我們來試一下:
11 / 4 # ==> 2
令很多初學者驚訝的是,Python的整數除法,即使除不盡,結果仍然是整數,餘數直接被扔掉。不過,Python提供了一個求餘的運算 % 可以計算餘數:
11 % 4 # ==> 3
如果我們要計算 11 / 4
的精確結果,按照“整數和浮點數混合運算的結果是浮點數”的法則,把兩個數中的一個變成浮點數再運算就沒問題了:
11.0 / 4 # ==> 2.75
程式設計任務:
請計算 2.5 + 10 / 4 ,並解釋計算結果為什麼不是期望的 5.0 ?
請修復上述運算,使得計算結果是 5.0
print 2.5 + 10.0 / 4
執行結果:
5.0
布林型別
我們已經瞭解了Python支援布林型別的資料,布林型別只有True和False兩種值,但是布林型別有以下幾種運算:
與運算:只有兩個布林值都為 True 時,計算結果才為 True。
True and True # ==> True
True and False # ==> False
False and True # ==> False
False and False # ==> False
或運算:只要有一個布林值為 True,計算結果就是 True。
True or True # ==> True
True or False # ==> True
False or True # ==> True
False or False # ==> False
非運算:把True變為False,或者把False變為True:
not True # ==> False
not False # ==> True
布林運算在計算機中用來做條件判斷,根據計算結果為True
或者False
,計算機可以自動執行不同的後續程式碼。
在Python中,布林型別還可以與其他資料型別做 and
、or
和not
運算,請看下面的程式碼:
知識點:Python把0、空字串“和None看成 False,其他數值和非空字串都看成 True。短路運算
a = True
print a and `a=T` or `a=F`
計算結果不是布林型別,而是字串 `a=T`
,這是為什麼呢?
因為Python把0、空字串“和None看成 False,其他數值和非空字串都看成 True,所以:
True and `a=T` 計算結果是 `a=T`
繼續計算 `a=T` or `a=F` 計算結果還是 `a=T`
要解釋上述結果,又涉及到 and 和 or 運算的一條重要法則:短路計算。
-
在計算
a and b
時,如果 a 是 False,則根據與運演算法則,整個結果必定為 False,因此返回 a;如果 a 是 True,則整個計算結果必定取決與 b,因此返回 b。 -
在計算
a or b
時,如果 a 是 True,則根據或運演算法則,整個計算結果必定為 True,因此返回 a;如果 a 是 False,則整個計算結果必定取決於 b,因此返回 b。
所以Python直譯器在做布林運算時,只要能提前確定計算結果,它就不會往後算了,直接返回結果。
編碼任務:
請執行如下程式碼,並解釋列印的結果:
a = `python`
print `hello,`, a or `world`
b = ``
print `hello,`, b or `world`
# -*- coding: utf-8 -*-
a = `python`
print `hello,`, a or `world`
#a為非空,則輸出a
b = ``
#b為空,輸出world
print `hello,`, b or `world`
執行結果:
hello, python
hello, world
Python集合型別:list和tuple
建立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 = []
程式設計任務
假設班裡有3名同學:Adam,Lisa和Bart,他們的成績分別是 95.5,85 和 59,請按照 名字, 分數, 名字, 分數… 的順序按照分數從高到低用一個list表示,然後列印出來。
L = [`Adam`, 95.5,`Lisa`, 85, `Bart`, 59]
print L
執行結果:
[`Adam`, 95.5, `Lisa`, 85, `Bart`, 59]
注:list本身就是有序的。所以直接列印即可。
Python按照索引訪問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表示:L = [95.5, 85, 59]
請按照索引分別列印出第一名、第二名、第三名,同時測試 print L[3]。
實現程式碼:
L = [95.5,85,59]
print L[0]
print L[1]
print L[2]
print L[3]
執行結果:
Traceback (most recent call last):
File "index.py", line 5, in
print L[3]
IndexError: list index out of range
95.5
85
59
知識點:正序從0開始,逆序從-1開始是最好一個list內容。
當索引數字為負數時,表示逆序讀出List中的內容,記住List的最後一個空間的編號為-1開始
倒序訪問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個元素。
使用倒序索引時,也要注意不要越界。
程式設計任務
三名同學的成績可以用一個list表示:L = [95.5, 85, 59]
請按照倒序索引分別列印出倒數第一、倒數第二、倒數第三。
實現程式碼:
L = [95.5, 85, 59]
print L[-1]
print L[-2]
print L[-3]
print L[-4]
執行結果:
Traceback (most recent call last):
File "index.py", line 5, in
print L[-4]
IndexError: list index out of range
59
85
95.5
list新增新元素(append insert)
現在,班裡有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 同學的成績比Bart好,但是比Lisa差,他應該排到第三名的位置,請用程式碼實現。
程式碼實現:
L = [`Adam`, `Lisa`, `Bart`]
L.insert(2,`paul`)
print L
執行結果:
[`Adam`, `Lisa`, `paul`, `Bart`]
正向第三名索引號為2.倒數第三名索引號為-3
list刪除元素(pop)
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`]
兩種方式:直接pop()
預設刪除第一個,括號內指定引數:索引,刪除索引位置上。
編碼任務
L = [`Adam`, `Lisa`, `Paul`, `Bart`]
Paul的索引是2,Bart的索引是3,如果我們要把Paul和Bart都刪掉,請解釋下面的程式碼為什麼不能正確執行:
L.pop(2)
L.pop(3)
怎樣調整程式碼可以把Paul和Bart都正確刪除掉?
解釋:因為語句是按順序執行的刪除了Paul之後。
索引號3已經越界。我們要刪除的Bart已經變成2了。
知識點:這教育我們刪除list時要秉著從前到後順序。
List替換元素
假設現在班裡仍然是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`
程式設計任務
班裡的同學按照分數排名是這樣的:L = [`Adam`, `Lisa`, `Bart`]
但是,在一次考試後,Bart同學意外取得第一,而Adam同學考了倒數第一。
請通過對list的索引賦值,生成新的排名。
實現程式碼:
L = [`Adam`, `Lisa`, `Bart`]
L[0]=`Bart`
L[-1]=`Adam`
print L
執行結果:
[`Bart`, `Lisa`, `Adam`]
建立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,順序包含0 – 9這10個數。
實現程式碼:
t = (0,1,2,3,4,5,6,7,8,9)
print t
執行結果:
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
建立單元素tuple
tuple和list一樣,可以包含 0 個、1個和任意多個元素。
包含多個元素的 tuple,前面我們已經建立過了。
包含 0 個元素的 tuple,也就是空tuple,直接用 ()表示:
>>> t = ()
>>> print t
()
建立包含1個元素的 tuple 呢?來試試:
>>> t = (1)
>>> print t
1
好像哪裡不對!t 不是 tuple ,而是整數1。為什麼呢?
知識點:單元素tuple的()
被當做優先順序。(1)
變成整數1.單元素括號結尾加,
因為()既可以表示tuple,又可以作為括號表示運算時的優先順序,結果 (1) 被Python直譯器計算出結果 1,導致我們得到的不是tuple,而是整數 1。
正是因為用()定義單元素的tuple有歧義,所以 Python 規定,單元素 tuple 要多加一個逗號,
,這樣就避免了歧義:
>>> t = (1,)
>>> print t
(1,)
Python在列印單元素tuple時,也自動新增了一個,
,為了更明確地告訴你這是一個tuple。
多元素 tuple 加不加這個額外的,
效果是一樣的:
>>> t = (1, 2, 3,)
>>> print t
(1, 2, 3)
程式設計任務
下面程式碼為什麼沒有建立出包含一個學生的 tuple:
t = (`Adam`)
print t
請修改程式碼,確保 t 是一個tuple。
因為單元素tuple的括號被當做是優先順序標誌。要加上額外,
標識這是一個元組。
實現程式碼:
t = (`Adam`,)
print t
執行結果:
(`Adam`,)
“可變”的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的每一個元素本身也不能變。
程式設計任務:
定義了tuple:t = (`a`, `b`, [`A`, `B`])
由於 t 包含一個list元素,導致tuple的內容是可變的。能否修改上述程式碼,讓tuple內容不可變?
解答:將裡面的list替換成一個不可變的元素。比如tuple。
實現程式碼:
t = (`a`, `b`, (`A`, `B`))
print t
執行結果:
(`a`, `b`, (`A`, `B`))
Python的條件判斷和迴圈語句
if語句
計算機之所以能做很多自動化的任務,因為它可以自己做條件判斷。
比如,輸入使用者年齡,根據年齡列印不同的內容,在Python程式中,可以用if語句實現:
age = 20
if 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 20
adult
程式設計任務
如果成績達到60分或以上,視為passed。
假設Bart同學的分數是75,請用if語句判斷是否能列印出 passed:
實現程式碼:
score = 75
if score>=60:
print `passed`
執行結果:
passed
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 後面有個:
。
程式設計任務
如果成績達到60分或以上,視為passed,否則視為failed。
假設Bart同學的分數是55,請用if語句列印出 passed 或者 failed:
實現程式碼:
score = 55
if score>=60:
print `passed`
else:
print `failed`
執行結果:
failed
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,執行完對應的程式碼塊,後面的條件判斷就直接忽略,不再執行了。
請思考下面的程式碼:
age = 8
if age >= 6:
print `teenager`
elif age >= 18:
print `adult`
else:
print `kid`
當 age = 8 時,結果正確,但 age = 20 時,為什麼沒有列印出 adult?
如果要修復,應該如何修復?
知識點解答: 因為當age=20.第一個條件>=6滿足就短路了。
因此我們在設定條件應該從嚴格到鬆泛.
age = 20
if age >= 18:
print `teenager`
elif age >= 6:
print `adult`
else:
print `kid`
程式設計任務
如果按照分數劃定結果:
- 90分或以上:excellent
- 80分或以上:good
- 60分或以上:passed
- 60分以下:failed
請編寫程式根據分數列印結果。
實現程式碼:
score = 85
if score>=90:
print `excellent`
elif score>=80:
print `good`
elif score>=60:
print `passed`
else:
print `failed`
執行結果:
good
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就非常容易了。
程式設計任務
班裡考試後,老師要統計平均成績,已知4位同學的成績用list表示如下:L = [75, 92, 59, 68]
請利用for迴圈計算出平均成績。
實現程式碼:
L = [75, 92, 59, 68]
sum = 0.0
for x in L:
sum =sum+x
print sum / 4
執行結果:
73.5
while迴圈
和 for 迴圈不同的另一種迴圈是 while
迴圈,while 迴圈不會迭代 list 或 tuple 的元素,而是根據表示式判斷迴圈是否結束。
比如要從 0 開始列印不大於 N 的整數:
N = 10
x = 0
while x < N:
print x
x = x + 1
while迴圈每次先判斷 x < N
,如果為True,則執行迴圈體的程式碼塊,否則,退出迴圈。
在迴圈體內,x = x + 1 會讓 x 不斷增加,最終因為 x < N 不成立而退出迴圈。
如果沒有這一個語句,while迴圈在判斷 x < N 時總是為True,就會無限迴圈下去,變成死迴圈,所以要特別留意while迴圈的退出條件。
程式設計任務
利用while迴圈計算100以內奇數的和。
實現程式碼:
sum = 0
x = 1
while x<=100:
sum=sum+x
x=x+2
print sum
知識點: 奇數只需要從1開始不斷加2都是奇數。
執行結果:
2500
break退出迴圈
用for
迴圈或者 while
迴圈時,如果要在迴圈體內直接退出迴圈,可以使用 break 語句
。
比如計算1至100的整數和,我們用while來實現:
sum = 0
x = 1
while True:
sum = sum + x
x = x + 1
if x > 100:
break
print sum
咋一看, while True 就是一個死迴圈,但是在迴圈體內,我們還判斷了 x > 100 條件成立時,用break語句退出迴圈,這樣也可以實現迴圈的結束。
程式設計任務
利用 while True 無限迴圈配合 break 語句,計算 1 + 2 + 4 + 8 + 16 + … 的前20項的和。
實現程式碼:
sum = 0
x = 1
n = 1
while True:
sum =sum+x
x =2*x
n =n+1
if n >20:
break
print sum
執行結果:
1048575
continue繼續迴圈
在迴圈過程中,可以用break
退出當前迴圈,還可以用continue
跳過後續迴圈程式碼,繼續下一次迴圈。
假設我們已經寫好了利用for迴圈計算平均分的程式碼:
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
現在老師只想統計及格分數的平均分,就要把 x < 60
的分數剔除掉,這時,利用continue
,可以做到當 x < 60
的時候,不繼續執行迴圈體的後續程式碼,直接進入下一次迴圈:
for x in L:
if x < 60:
continue
sum = sum + x
n = n + 1
coutinue: 跳過下面的程式碼。開始下一次迴圈。
程式設計任務
對已有的計算 0 – 100 的while迴圈進行改造,通過增加 continue 語句,使得只計算奇數的和:
sum = 0
x = 1
while True:
sum = sum + x
x = x + 1
if x > 100:
break
print sum
思路: if判斷到是偶數,continue跳過。
實現程式碼:
sum = 0
x = 0
while True:
x = x + 1
if x > 100:
break
if x%2==0:
continue
sum = sum+x
print sum
執行結果:
2500
多重迴圈(巢狀迴圈)
在迴圈內部,還可以巢狀迴圈,我們來看一個例子:
for x in [`A`, `B`, `C`]:
for y in [`1`, `2`, `3`]:
print x + y
x
每迴圈一次,y
就會迴圈 3 次,這樣,我們可以列印出一個全排列:
A1
A2
A3
B1
B2
B3
C1
C2
C3
程式設計任務
對100以內的兩位數,請使用一個兩重迴圈列印出所有十位數數字比個位數數字小的數,例如,23(2 < 3)。
程式碼實現。
tens_place = [1,2,3,4,5,6,7,8,9]
ones_place = [0,1,2,3,4,5,6,7,8,9]
for x in tens_place:
for y in ones_place:
if x<y:
print x*10 + y
執行結果:
12
13
14
15
16
17
18
19
23
略
重要的資料型別Dict和Set
什麼是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 的逗號可以省略。
知識點: 區別小課堂
- 單元素的tuple必須在後面多加一個逗號。
- dict最後的逗號可以省略
由於dict也是集合,len()
函式可以計算任意集合的大小:
>>> len(d)
3
知識點:注意: 一個 key-value 算一個,因此,dict大小為3。
程式設計任務
新來的Paul同學成績是 75 分,請編寫一個dict,把Paul同學的成績也加進去。
d = {
`Adam`: 95,
`Lisa`: 85,
`Bart`: 59
}
實現程式碼:
d = {
`Adam`: 95,
`Lisa`: 85,
`Bart`: 59,
`Paul`: 75
}
訪問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:
d = {
`Adam`: 95,
`Lisa`: 85,
`Bart`: 59
}
請列印出:
Adam: 95
Lisa: 85
Bart: 59
實現程式碼:
d = {
`Adam`: 95,
`Lisa`: 85,
`Bart`: 59
}
print `Adam:`,d[`Adam`]
print `Lisa:`,d.get(`Lisa`)
print `Bart:`,d[`Bart`]
執行結果:
Adam: 95
Lisa: 85
Bart: 59
dict的特點
知識點:dict查詢速度快。list查詢速度隨著元素增加而逐漸下降。
缺點:記憶體佔用大。list慢但記憶體佔用小。
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,可以根據分數來查詢名字,已知成績如下:
Adam: 95,
Lisa: 85,
Bart: 59.
實現程式碼:
d = {
95:`Adam`,
85:`Lisa`,
59:`Bart`
}
執行結果:無
更新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}
程式設計任務
請根據Paul的成績 72 更新下面的dict:
d = {
95: `Adam`,
85: `Lisa`,
59: `Bart`
}
實現程式碼:
d = {
95: `Adam`,
85: `Lisa`,
59: `Bart`
}
d[72] = `Paul`
print d
執行結果:
{72: `Paul`, 59: `Bart`, 85: `Lisa`, 95: `Adam`}
遍歷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的值。
注:這裡的key只是一個約定俗稱的變數,可以改為其他名字。但是推薦用key。
程式設計任務
請用 for 迴圈遍歷如下的dict,列印出 name: score 來。
d = {
`Adam`: 95,
`Lisa`: 85,
`Bart`: 59
}
實現程式碼:
d = {
`Adam`: 95,
`Lisa`: 85,
`Bart`: 59
}
for key in d:
print key+":",d[key]
執行結果:
Lisa: 85
Adam: 95
Bart: 59
什麼是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表示班裡的4位同學:Adam, Lisa, Bart, Paul
實現程式碼:
s = set([`Adam`, `Lisa`, `Bart`, `Paul`])
print s
執行結果:
set([`Lisa`, `Paul`, `Adam`, `Bart`])
訪問set
由於set儲存的是無序集合,所以我們沒法通過索引來訪問。
訪問 set中的某個元素實際上就是判斷一個元素是否在set中。
例如,儲存了班裡同學名字的set:
>>> s = set([`Adam`, `Lisa`, `Bart`, `Paul`])
我們可以用 in
操作符判斷:
Bart是該班的同學嗎?
>>> `Bart` in s
True
Bill是該班的同學嗎?
>>> `Bill` in s
False
bart是該班的同學嗎?
>>> `bart` in s
False
知識點:大小寫很重要,`Bart` 和 `bart`被認為是兩個不同的元素。
程式設計任務
由於上述set不能識別小寫的名字,請改進set,使得 `adam` 和 `bart`都能返回True。
既然大小寫是不同的。那我們的set中就把大小寫都包含。
實現程式碼:
s = set([`Adam`, `Lisa`, `Bart`, `Paul`,`adam`, `lisa`, `bart`, `paul`])
print `adam` in s
print `bart` in s
執行結果.
True
True
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並判斷使用者輸入的月份是否有效。
月份可以用字串`Jan`, `Feb`, ...
表示。
實現程式碼:
months = set([`Jan`, `Feb`, `Mar`, `Apr`, `May`, `Jun`, `Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`])
x1 = `Feb`
x2 = `Sun`
if x1 in months:
print `x1: ok`
else:
print `x1: error`
if x2 in months:
print `x2: ok`
else:
print `x2: error`
執行結果:
x1: ok
x2: 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的順序很可能是不同的,而且不同的機器上執行的結果也可能不同。
程式設計任務
請用 for 迴圈遍歷如下的set,列印出 name: score 來。
s = set([(`Adam`, 95), (`Lisa`, 85), (`Bart`, 59)])
上面這個set中的每一個元素又是一個字典。
set([ ])
是殼子。
(`Adam`, 95), (`Lisa`, 85), (`Bart`, 59)
才是真正的內容
實現程式碼:
s = set([(`Adam`, 95), (`Lisa`, 85), (`Bart`, 59)])
for name,score in s:
print name,`:`,score
執行結果:
Lisa : 85
Adam : 95
Bart : 59
更新set(add remove)
由於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()
前需要判斷。
程式設計任務
針對下面的set,給定一個list,對list中的每一個元素,如果在set中,就將其刪除,如果不在set中,就新增進去。
s = set([`Adam`, `Lisa`, `Paul`])
L = [`Adam`, `Lisa`, `Bart`, `Paul`]
實現程式碼:
s = set([`Adam`, `Lisa`, `Paul`])
L = [`Adam`, `Lisa`, `Bart`, `Paul`]
for name in L:
if name in s:
s.remove(name)
else:
s.add(name)
print s
函式定義與呼叫
Python之什麼是函式
我們知道圓的面積計算公式為:
S = πr²
當我們知道半徑r的值時,就可以根據公式計算出面積。假設我們需要計算3個不同大小的圓的面積:
r1 = 12.34
r2 = 9.08
r3 = 73.1
s1 = 3.14 * r1 * r1
s2 = 3.14 * r2 * r2
s3 = 3.14 * r3 * r3
當程式碼出現有規律的重複的時候,你就需要當心了,每次寫3.14 * x * x
不僅很麻煩,而且,如果要把3.14改成3.14159265359的時候,得全部替換。
有了函式,我們就不再每次寫s = 3.14 * x * x
,而是寫成更有意義的函式呼叫 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不但能非常靈活地定義函式,而且本身內建了很多有用的函式,可以直接呼叫。
程式設計任務
寫一個函式
實現程式碼:
s = area_of_circle(x)
area_of_circle(x)
執行結果:
呼叫函式,內建函式
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
,如果 x==y
,返回0
,如果 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`
程式設計任務
sum()函式接受一個list作為引數,並返回list所有元素之和。請計算
1*1 + 2*2 + 3*3 + ... + 100*100
。
實現程式碼:
L = []
L = []
x = 1
while x <= 100:
L.append(x * x)
x = x + 1
print sum(L)
執行結果:
338350
編寫函式
在Python中,定義一個函式要使用 def
語句,依次寫出函式名
、括號
、括號中的引數
和冒號
:,然後,在縮排塊中編寫函式體,函式的返回值用 return
語句返回。
我們以自定義一個求絕對值的 my_abs 函式為例:
def my_abs(x):
if x >= 0:
return x
else:
return -x
請注意,函式體內部的語句在執行時,一旦執行到return
時,函式就執行完畢,並將結果返回。因此,函式內部通過條件判斷和迴圈可以實現非常複雜的邏輯。
知識點; 如果沒有return語句,函式執行完畢後也會返回結果,只是結果為 None
。return None
可以簡寫為return
。
程式設計任務
請定義一個
square_of_sum
函式,它接受一個list,返回list中每個元素平方的和。
實現程式碼:
def square_of_sum(L):
sum = 0
for x in L:
sum = x*x+sum
return sum
print square_of_sum([1, 2, 3, 4, 5])
print square_of_sum([-5, 0, 5, 15, 25])
執行結果:
55
900
函式之返回”多值”
函式可以返回多個值嗎?答案是肯定的。
比如在遊戲中經常需要從一個點移動到另一個點,給出座標、位移和角度,就可以計算出新的座標:
math
包提供了sin()
和 cos()
函式,我們先用import
引用它:
import math
def 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, y
151.961524227 70.0
但其實這只是一種假象,Python函式返回的仍然是單一值:
>>> r = move(100, 100, 60, math.pi / 6)
>>> print r
(151.96152422706632, 70.0)
知識點:用print列印返回結果,原來返回值是一個tuple!
但是,在語法上,返回一個tuple可以省略括號,而多個變數可以同時接收一個tuple,按位置賦給對應的值,所以,知識點:Python的函式返回多值其實就是返回一個tuple,但寫起來更方便。
程式設計任務
一元二次方程的定義是:
ax² + bx + c = 0
請編寫一個函式,返回一元二次方程的兩個解。
注意:Python的math包提供了sqrt()函式用於計算平方根。
實現程式碼:
import math
def quadratic_equation(a, b, c):
t = math.sqrt(b*b - 4*a*c)
return (-b + t) / (2 * a),( -b - t )/ (2 * a)
print quadratic_equation(2, 3, 0)
print quadratic_equation(1, -6, 5)
執行結果:
(0.0, -1.5)
(5.0, 1.0)
遞迴函式
在函式內部,可以呼叫其他函式。知識點: 如果一個函式在內部呼叫自身本身,這個函式就是遞迴函式。
舉個例子,我們來計算階乘 n! = 1 * 2 * 3 * ... * 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)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
如果我們計算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)
。
程式設計任務(天涯)
漢諾塔 (http://baike.baidu.com/view/191666.htm) 的移動也可以看做是遞迴函式。
我們對柱子編號為a, b, c,將所有圓盤從a移到c可以描述為:
如果a只有一個圓盤,可以直接移動到c;
如果a有N個圓盤,可以看成a有1個圓盤(底盤) + (N-1)個圓盤,首先需要把 (N-1) 個圓盤移動到 b,然後,將 a的最後一個圓盤移動到c,再將b的(N-1)個圓盤移動到c。
請編寫一個函式,給定輸入 n, a, b, c,列印出移動的步驟:
move(n, a, b, c)
例如,輸入 move(2, `A`, `B`, `C`),列印出:
A --> B
A --> C
B --> C
實現程式碼:
def move(n, a, b, c):
if n ==1:
print a, `-->`, c
return
move(n-1, a, c, b)
print a, `-->`, c
move(n-1, b, a, c)
move(4, `A`, `B`, `C`)
執行結果:
A --> B
A --> C
B --> C
A --> B
C --> A
C --> B
A --> B
A --> C
B --> C
B --> A
C --> A
B --> C
A --> B
A --> C
B --> C
定義預設引數
定義函式的時候,還可以有預設引數。
例如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
個人: 這裡我們可以把自己想象成計算機。在自己感到為難不知道哪個是哪個的時候。
那麼恭喜你,計算機也不知道。
程式設計任務
請定義一個
greet()
函式,它包含一個預設引數,如果沒有傳入,列印`Hello, world.`
,如果傳入,列印`Hello, xxx.`
實現程式碼:
def greet(x = `World`):
print `Hello,`+x+`.`
greet()
greet(`mtianyan`)
執行結果:
Hello,World.
Hello,mtianyan.
知識點: 定義可變引數
如果想讓一個函式能接受任意個引數,我們就可以定義一個可變引數:
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
程式設計任務
請編寫接受可變引數的 average() 函式。
def average(*args):
sum = 0.0
if len(args) == 0:
return sum
for x in args:
sum = sum + x
return sum / len(args)
print average()
print average(1, 2)
print average(1, 2, 2, 3, 4)
執行結果:
0.0
1.5
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: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
。
程式設計任務
range()函式可以建立一個數列:
>>> range(1, 101)
[1, 2, 3, ..., 100]
請利用切片,取出:
- 前10個數;
- 3的倍數;
- 不大於50的5的倍數。
實現程式碼:
L = range(1, 101)
print L[:10]
print L[2::3]
print L[4:50:5]
執行結果:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
倒序切片
對於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。知識點:倒序切片包含起始索引,不包含結束索引。
程式設計任務
利用倒序切片對 1 – 100 的數列取出:
- 最後10個數;
- 最後10個5的倍數。
實現程式碼:
L = range(1, 101)
print L[-10:]
print L[-46::5]
對字串切片
字串 `xxx`
和 Unicode
字串 u`xxx`
也可以看成是一種list,每個元素就是一個字元。因此,字串也可以用切片操作,只是操作結果仍是字串:
>>> `ABCDEFG`[:3]
`ABC`
>>> `ABCDEFG`[-3:]
`EFG`
>>> `ABCDEFG`[::2]
`ACEG`
在很多程式語言中,針對字串提供了很多各種擷取函式,其實目的就是對字串切片。知識點:Python沒有針對字串的擷取函式,只需要切片一個操作就可以完成,非常簡單。
程式設計任務
字串有個方法
upper()
可以把字元變成大寫字母:
>>> `abc`.upper()
`ABC`
但它會把所有字母都變成大寫。請設計一個函式,它接受一個字串,然後返回一個僅首字母變成大寫的字串。
提示:利用切片操作簡化字串操作。
實現程式碼:
def firstCharUpper(s):
return s[0].upper() + s[1:]
print firstCharUpper(`hello`)
print firstCharUpper(`sunday`)
print firstCharUpper(`september`)
執行結果:
Hello
Sunday
September
各種迭代方式
什麼是迭代
在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 迴圈
。
迭代
與按下標訪問陣列
最大的不同是,後者是一種具體的迭代實現方式,而前者只關心迭代結果,根本不關心迭代內部是如何實現的。
程式設計任務
請用for迴圈迭代數列 1-100 並列印出7的倍數。
實現程式碼:
for i in range(1, 101):
if i % 7 == 0:
print i
執行結果:
7
14
21
28
35
42
49
56
63
70
77
84
91
98
索引迭代
知識點:Python中,迭代永遠是取出元素本身
,而非元素的索引。
對於有序集合,元素確實是有索引的。有的時候,我們確實想在 for 迴圈中拿到索引
,怎麼辦?
方法是使用 enumerate()
函式:
>>> L = [`Adam`, `Lisa`, `Bart`, `Paul`]
>>> for index, name in enumerate(L):
... print index, `-`, name
...
0 - Adam
1 - Lisa
2 - Bart
3 - 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,再迭代,就同時獲得了索引和元素本身。
程式設計任務(天涯)
zip()函式可以把兩個 list 變成一個 list:
>>> zip([10, 20, 30], [`A`, `B`, `C`])
[(10, `A`), (20, `B`), (30, `C`)]
在迭代 [`Adam`, `Lisa`, `Bart`, `Paul`]
時,如果我們想列印出名次 – 名字(名次從1開始),請考慮如何在迭代中列印出來。
提示:考慮使用zip()函式和range()函式
實現程式碼:
L = [`Adam`, `Lisa`, `Bart`, `Paul`]
for index, name in zip(range(1, len(L)+1), L):
print index, `-`, name
執行結果:
1 - Adam
2 - Lisa
3 - Bart
4 - Paul
迭代dict的value
迭代dict的value
我們已經瞭解了dict物件本身就是可迭代物件
,用 for 迴圈直接迭代 dict,可以每次拿到dict的一個key。
如果我們希望迭代 dict 物件的value,應該怎麼做?
知識點:values()把dict轉換成一個包含所有value的list
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() 發現它返回一個 <dictionary-valueiterator> 物件,這說明在Python中,for 迴圈可作用的迭代物件遠不止 list,tuple,str,unicode,dict等,知識點: 任何可迭代物件都可以作用於for迴圈,而內部如何迭代我們通常並不用關心。
如果一個物件說自己可迭代,那我們就直接用 for 迴圈去迭代它,知識點: 可見,迭代是一種抽象的資料操作,它不對迭代物件內部的資料有任何要求。
程式設計任務
給定一個dict:d = { `Adam`: 95, `Lisa`: 85, `Bart`: 59, `Paul`: 74 }
請計算所有同學的平均分。
實現程式碼:
d = { `Adam`: 95, `Lisa`: 85, `Bart`: 59, `Paul`: 74 }
sum = 0.0
for v in d.itervalues():
sum = sum + v
print sum / len(d)
執行結果:
78.25
迭代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 : 85
Adam : 95
Bart : 59
和 values()
有一個 itervalues()
類似,items()
也有一個對應的 iteritems()
,知識點: iteritems() 不把dict轉換成list,而是在迭代過程中不斷給出 tuple,所以, iteritems() 不佔用額外的記憶體。
程式設計任務
請根據dict:d = { `Adam`: 95, `Lisa`: 85, `Bart`: 59, `Paul`: 74 }
列印出 name : score
,最後再列印出平均分 average : score
。
實現程式碼:
d = { `Adam`: 95, `Lisa`: 85, `Bart`: 59, `Paul`: 74 }
sum = 0.0
for k, v in d.iteritems():
sum = sum + v
print k, `:`, v
print `average`, `:`, sum / len(d)
執行結果:
Lisa : 85
Paul : 74
Adam : 95
Bart : 59
average : 78.25
列表生成式:快速生成列表
生成列表
要生成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]
怎麼做?
- 方法一是迴圈:
>>> 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建立出來,十分有用,多寫幾次,很快就可以熟悉這種語法。
程式設計任務
請利用列表生成式生成列表
[1x2, 3x4, 5x6, 7x8, ..., 99x100]
提示:range(1, 100, 2)
可以生成list [1, 3, 5, 7, 9,...]
實現程式碼:
print [x * (x + 1) for x in range(1, 100, 2)]
執行結果:
[2, 12, 30, 56, 90, 132, 182, 240, 306, 380, 462, 552, 650, 756, 870, 992, 1122, 1260, 1406, 1560, 1722, 1892, 2070, 2256, 2450, 2652, 2862, 3080, 3306, 3540, 3782, 4032, 4290, 4556, 4830, 5112, 5402, 5700, 6006, 6320, 6642, 6972, 7310, 7656, 8010, 8372, 8742, 9120, 9506, 9900]
複雜表示式
使用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 `
`.join(tds)
print `</table>`
個人:<tr><td>%s</td><td>%s</td></tr>
中:
- 第一個%s是name的填充位置。
- 第二個%s為score的填充位置。
有多少個name和score,會通過迴圈生成多少個。<tr><th>Name</th><th>Score</th><tr>
設定表格頭
print `
`.join(tds)。列表裡的項通過
連線成字串。
注:字串可以通過%
進行格式化,用指定的引數替代 %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>
程式設計任務(天涯)
在生成的表格中,對於沒有及格的同學,請把分數標記為紅色。
提示:紅色可以用 <td style="color:red">
實現。
實現程式碼:
d = { `Adam`: 95, `Lisa`: 85, `Bart`: 59 }
def generate_tr(name, score):
if score < 60:
return `<tr><td>%s</td><td style="color:red">%s</td></tr>` % (name, score)
return `<tr><td>%s</td><td>%s</td></tr>` % (name, score)
tds = [generate_tr(name, score) for name, score in d.iteritems()]
print `<table border="1">`
print `<tr><th>Name</th><th>Score</th><tr>`
print `
`.join(tds)
print `</table>`
執行結果:
[圖片上傳失敗…(image-837d01-1514981535184)]
條件過濾
列表生成式的 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 的時候,才把迴圈的當前元素新增到列表中。
程式設計任務
請編寫一個函式,它接受一個 list,然後把list中的所有字串變成大寫後返回,非字串元素將被忽略。
提示:
- isinstance(x, str) 可以判斷變數 x 是否是字串;
- 字串的 upper() 方法可以返回大寫的字母。
實現程式碼:
def toUppers(L):
return [x.upper() for x in L if isinstance(x, str)]
print toUppers([`Hello`, `world`, 101])
執行結果:
[`HELLO`, `WORLD`]
多層表示式(知識點)
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)
程式設計任務(天涯)
利用 3 層for迴圈的列表生成式,找出對稱的 3 位數。例如,121 就是對稱數,因為從右到左倒過來還是 121。
實現程式碼:
print [100 * n1 + 10 * n2 + n3 for n1 in range(1, 10) for n2 in range(10) for n3 in range(10) if n1==n3]
執行結果:
101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525, 535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666, 676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808, 818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949, 959, 969, 979, 989, 999]
個人學習筆記,內容來自慕課網課程。請勿用作商業用途。
相關文章
- Git【入門】這一篇就夠了Git
- AJAX入門這一篇就夠了
- Python快速入門,看這一篇就夠了!Python
- Spring入門這一篇就夠了Spring
- 入門Hbase,看這一篇就夠了
- jQuery入門看這一篇就夠了jQuery
- Elasticsearch入門,看這一篇就夠了Elasticsearch
- MySQL入門看這一篇就夠了MySql
- MASA Blazor入門這一篇就夠了Blazor
- Mybatis入門看這一篇就夠了MyBatis
- Hibernate入門這一篇就夠了
- 快速零基礎入門 DockerDocker
- 零基礎入門Python的路徑Python
- Spring入門看這一篇就夠了Spring
- Struts2入門這一篇就夠了
- Zookeeper入門一篇就夠了
- 零基礎入門深度學習工作原理?看本文就對了!深度學習
- c語言入門這一篇就夠了-學習筆記(一萬字)C語言筆記
- 零基礎入門Serverless:Hello WorldServer
- 給零基礎小白的Python入門教程Python
- 過濾器入門看這一篇就夠了過濾器
- 組合語言零基礎入門學習筆記(一)組合語言筆記
- 【零基礎】PostgreSQL從入門到精通SQL
- python萌新:從零基礎入門到放棄Python
- (Python篇)零基礎入門第三篇Python
- Python爬蟲開發(一):零基礎入門Python爬蟲
- 入門Webpack,看這篇就夠了Web
- Zookeeper入門看這篇就夠了
- React入門看這篇就夠了React
- Python3入門,看這篇就夠了Python
- 零基礎入門Python教程4節與基礎語法Python
- 零基礎輕鬆入門——JAVA基礎學習Java
- 零基礎入門前端的修煉之道前端
- 零基礎學習Alfred(一):入門操作Alfred
- Java零基礎入門(三)流程控制Java
- 學Mybatis,入門看這一篇就夠你學的了!MyBatis
- 最新python3完全零基礎入門(目前最新)Python
- Python爬蟲五大零基礎入門教程Python爬蟲