Python零基礎入門看完這一篇就夠了:零基礎入門筆記

天涯明月笙發表於2018-01-03

學習python有一年多了,希望通過這篇兩萬字的學習筆記來複習了,也能讓後來者少走一點彎路。在慕課網課程筆記的同時加入了一部分自己的經驗補充。

  • [√] 慕課網Python開發環境搭建: 配開發環境
  • [√] 廖雪峰老師在慕課網的課程: Python入門

Mark之前別忘了點個贊哦。
推薦前往我的個人部落格進行閱讀:http://blog.mtianyan.cn/
分章目錄,超寬程式碼顯示區。更爽哦。
知識點標註ctrl + f 搜尋知識點檢視。重要及較難程式設計題採用天涯標註。

Python開發環境搭建

搭建環境分為兩個版本:

  • 基礎版,供初學者快速安裝體驗。
  • 進階版, 供對於資料科學,機器學習有興趣者安裝。

推薦:安裝進階版,一步到位。

基礎版:Windows下安裝python環境(2.7 | 3.x)

官網: https://www.python.org/

安裝包下載。

選擇download下的windows。點選進入。

download下的windows

下圖中紅框為64位版本。32位版本可以選擇Windows x86 executable installer

選擇3.7下的executable installer

2.7版本的安裝包下載:

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

mark

Python的初次體驗

python語言介紹

全世界有幾百種程式語言,但是流行的只有十幾種,python就是其中一種。荷蘭人龜叔於1989年聖誕節創立。

特點:優雅,明確,簡單

適合的領域:

  • web網站和各種網路服務;
  • 系統工具和指令碼;

作為膠水語言把其他語言開發的模組包裝起來方便使用。

Python是一門高階語言,所以不適合貼近硬體的程式碼:

  • 比如驅動程式(首選C)
  • 移動開發,有各自的語言,(objectC,swift/java)
  • 遊戲開發(首選C/C++)。

Python實際應用:

YouTube,豆瓣,搜狐郵箱;Openstack開源雲端計算平臺。Google,Yahoo,NASA。

語言之間的對比:

C編譯為機器碼;JAVA編譯為位元組碼;python為解釋執行。

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-9a-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表示布林值(請注意大小寫),也可以通過布林運算計算出來。

布林值可以用andornot運算。

and運算是與運算,只有所有都為 True,and運算結果才是 True。

or運算是或運算,只要其中有一個為 True,or 運算結果就是 True。

not運算是非運算,它是一個單目運算子,把 True 變成 False,False 變成 True。

空值

空值是Python裡一個特殊的值,用None表示。

None不能理解為0,因為0是有意義的,而None是一個特殊的空值。

程式設計小任務:

  1. 計算十進位制整數 45678 和十六進位制整數 0x12fd2 之和。
  2. 請用字串表示出Learn Python in imooc。
  3. 請計算以下表示式的布林值(注意==表示判斷是否相等):
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*xx就是變數。

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直譯器幹了兩件事情:

  1. 在記憶體中建立了一個`ABC`的字串;

  2. 在記憶體中建立了一個名為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`

圖2

執行a = `XYZ`,直譯器建立了字串`XYZ`,並把a的指向改為`XYZ`,但b並沒有更改:

圖3

所以,最後列印變數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`(~_~)/ (~_~)/`
mark

解釋:

這個例子舉得不是很好。
可以看出raw加上之後。可能產生誤會的被修改為\(\ 表示 字元本身)

  • 不加上r 只有(並沒有合成轉義字元。
  • 加上r。需要被轉義,經過轉義後顯示出來還是自己。

知識點: 個人小題(r的強大作用)

mark

上圖效果可以看出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 ,中文就不能正常顯示。(這個應該是很早版本才會。筆者現在已經無法復現)
mark

轉載: 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()  
mark

該段程式在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編碼的字串的結果。

mark

unicode(str,`gb2312`)str.decode(`gb2312`)是一樣的,都是將gb2312編碼的str轉為unicode編碼

使用str.__class__可以檢視str的編碼形式為str型別。

window預設編碼gbk;linux預設編碼utf8

mark

原理說了半天,最後來個包治百病的吧:
(天涯):下面程式碼

#!/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編碼的"

mark

上圖結果一:以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中,布林型別還可以與其他資料型別做 andornot運算,請看下面的程式碼:

知識點: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 運算的一條重要法則:短路計算

  1. 在計算 a and b時,如果 a 是 False,則根據與運演算法則,整個結果必定為 False,因此返回 a;如果 a 是 True,則整個計算結果必定取決與 b,因此返回 b。

  2. 在計算 a or b 時,如果 a 是 True,則根據或運演算法則,整個計算結果必定為 True,因此返回 a;如果 a 是 False,則整個計算結果必定取決於 b,因此返回 b。

mark

所以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個元素:

mark

當我們把list的元素`A`和`B`修改為`X`和`Y`後,tuple變為:

mark

表面上看,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)
mark

執行結果:

(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),如果傳了,就用傳入的引數。

可見,函式的預設引數的作用是簡化呼叫,你只需要把必須的引數傳進去。但是在需要的時候,又可以傳入額外的引數來覆蓋預設引數值。

我們來定義一個計算 xN次方的函式:

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]

請利用切片,取出:

  1. 前10個數;
  2. 3的倍數;
  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迴圈不僅可以用在listtuple上,還可以作用在其他任何可迭代物件上。

因此,迭代操作就是對於一個集合,無論該集合是有序還是無序,我們用 for 迴圈總是可以依次取出集合的每一個元素。

注意: 集合是指包含一組元素的資料結構,我們已經介紹的包括:

  1. 有序集合:list,tuple,知識點: str和unicode
  2. 無序集合:set
  3. 無序集合並且具有 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

那這兩個方法有何不同之處呢?

  1. values() 方法實際上把一個 dict 轉換成了包含 value 的list。
  2. 但是 itervalues() 方法不會轉換,它會在迭代過程中依次從 dict 中取出 value,所以 itervalues() 方法比 values() 方法節省了生成 list 所需的記憶體。
  3. 列印 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中的所有字串變成大寫後返回,非字串元素將被忽略。

提示:

  1. isinstance(x, str) 可以判斷變數 x 是否是字串;
  2. 字串的 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]

個人學習筆記,內容來自慕課網課程。請勿用作商業用途。


相關文章