2016年10月26日 12:00:53css
今天開始着手python的學習,但願能高效快速的學完!html
Python基礎(上)... 7前端
實驗簡介... 7java
1、實驗說明... 8python
1. 環境登陸... 8mysql
2. 環境介紹... 8linux
3. 環境使用... 8git
2、Hello World! 8程序員
一、Python命令行... 8web
二、寫一段小程序... 9
三、腳本... 10
3、基本數據類型... 11
一、變量不須要聲明... 11
二、回收變量名... 12
三、經常使用數據類型... 12
4、序列... 13
一、元素的引用... 14
二、其餘引用方式... 15
三、字符串是元組... 15
5、運算... 16
一、數學運算... 16
二、判斷... 16
三、邏輯運算... 17
6、縮進和選擇... 17
一、縮進... 17
二、if語句... 18
做業... 20
Python基礎(下)... 20
1、循環... 20
一、for循環... 20
二、while循環... 22
三、中斷循環... 22
2、函數... 23
一、函數的定義... 23
二、函數調用和參數傳遞... 24
3、面向對象的基本概念... 26
一、相近對象,歸爲類... 26
二、動做... 27
三、子類... 29
4、面向對象的進一步拓展... 30
一、調用類的其它信息... 30
二、__init__() 方法... 31
三、對象的性質... 32
5、反過頭來看看... 33
一、list是一個類... 33
二、運算符是特殊方法... 34
三、下一步... 36
做業... 36
Python進階(上)... 37
Python進階(上)... 37
1、詞典... 37
一、基本概念... 38
二、詞典元素的循環調用... 39
三、詞典的經常使用方法... 39
2、文本文件的輸入輸出... 40
一、建立文件對象... 40
二、文件對象的方法... 40
3、模塊... 41
一、引入模塊... 41
二、搜索路徑... 42
三、模塊包... 43
4、函數的參數傳遞... 43
一、關鍵字傳遞... 43
二、參數默認值... 44
三、包裹傳遞... 44
四、解包裹... 45
五、混合... 46
做業... 46
Python進階(下)... 47
Python進階(下)... 47
1、循環設計... 47
一、range() 47
二、enumerate() 47
三、zip() 48
2、循環對象... 49
一、什麼是循環對象... 49
二、迭代器... 50
三、生成器... 50
四、表推導... 52
3、函數對象... 52
一、lambda函數... 52
二、函數做爲參數傳遞... 53
三、map()函數... 53
四、filter()函數... 54
五、reduce()函數... 54
4、錯誤處理... 55
一、異常處理... 55
二、拋出異常... 58
5、動態類型... 59
一、動態類型... 59
二、從動態類型看函數的參數傳遞... 61
做業... 62
Python深刻(上)... 62
1、特殊方法與多範式... 63
一、運算符... 63
二、內置函數... 64
三、表(list)元素引用... 64
四、函數... 64
2、上下文管理器... 65
一、關閉文件... 65
二、自定義... 66
3、對象的屬性... 67
一、屬性的__dict__系統... 68
二、特性... 70
三、使用特殊方法__getattr__. 72
四、即時生成屬性的其餘方式... 73
做業... 73
Python深刻(下)... 73
1、閉包... 74
一、函數對象的做用域... 74
二、閉包... 75
三、閉包與並行運算... 77
2、裝飾器... 78
一、裝飾函數和方法... 78
二、含參的裝飾器... 81
三、裝飾類... 82
3、內存管理... 83
一、對象的內存使用... 83
二、對象引用對象... 88
三、引用減小... 91
四、垃圾回收... 92
五、分代回收... 94
六、孤立的引用環... 96
做業... 97
Python補充... 98
1、序列的方法... 98
2、Python小技巧... 102
一、import模塊... 102
二、查詢... 104
三、使用中文(以及其它非ASCII編碼) 105
四、表示2進制,8進制和16進制數字... 105
五、註釋... 105
六、搜索路徑... 106
七、腳本與命令行結合... 107
八、安裝非標準包... 107
3、Python內置函數清單... 108
一、數學運算... 108
二、類型轉換... 109
三、序列操做... 110
四、類、對象、屬性... 110
五、編譯、執行... 111
六、其餘... 111
4、字符串格式化(%操做符)... 111
一、模板... 111
二、格式符... 112
做業... 114
Python標準庫(上)... 114
1、正則表達式 (re包) 114
一、語法... 114
二、正則表達式的函數... 116
三、寫一個正則表達式... 117
2、時間與日期 (time, datetime包) 119
一、time包... 119
二、datetime包... 120
3、路徑與文件 (os.path包, glob包) 122
一、os.path包... 122
二、glob包... 123
4、文件管理 (部分os包,shutil包) 124
一、os包... 125
二、shutil包... 126
5、存儲對象 (pickle包,cPickle包) 126
一、pickle包... 127
二、cPickle包... 128
做業... 129
Python標準庫(中)... 129
1、子進程 (subprocess包) 129
一、subprocess以及經常使用的封裝函數... 129
二、Popen() 131
三、子進程的文本流控制... 132
2、信號 (signal包) 134
一、定義信號名... 134
二、預設信號處理函數... 135
三、定時發出SIGALRM信號... 136
四、發送信號... 137
3、多線程與同步 (threading包) 137
一、多線程售票以及同步... 138
二、OOP建立線程... 140
三、其餘... 142
4、進程信息 (部分os包) 143
一、進程信息... 143
二、saved UID和saved GID.. 144
5、多進程初步 (multiprocessing包) 146
一、threading和multiprocessing. 146
二、Pipe和Queue. 149
做業... 152
Python標準庫(下)... 152
1、多進程探索 (multiprocessing包) 152
一、進程池... 153
二、共享資源... 155
2、數學與隨機數 (math包,random包) 158
一、math包... 158
二、random包... 159
3、循環器 (itertools) 161
一、無窮循環器... 161
二、函數式工具... 162
三、組合工具... 163
四、groupby() 163
五、其它工具... 164
4、數據庫 (sqlite3) 165
一、建立數據庫... 165
二、插入數據... 167
三、查詢... 168
四、更新與刪除... 169
做業... 169
Python網絡... 170
1、原始Python服務器... 170
一、TCP/IP和socket 171
二、TCP socket 173
三、基於TCP socket的HTTP服務器... 175
四、深刻HTTP服務器程序... 178
五、使用瀏覽器實驗... 179
六、探索的方向... 183
2、Python服務器進化... 183
一、支持POST.. 183
二、使用SocketServer 188
三、SimpleHTTPServer: 使用靜態文件來回應請求... 191
四、CGIHTTPServer:使用靜態文件或者CGI來回應請求... 194
做業... 198
Django(上)... 198
1、安裝Django. 199
2、啓動... 199
3、第一個網頁... 201
4、增長APP.. 204
5、增長APP頁面... 206
6、鏈接數據庫... 207
7、創立模型... 209
8、顯示數據... 211
做業... 214
Django(中)... 214
1、模板初體驗... 214
... 216
2、流程... 216
3、循環與選擇... 217
4、模板繼承... 219
5、html表格... 220
6、POST方法... 222
7、存儲數據... 224
8、表格對象... 226
做業... 228
Django(下)... 229
1、默認界面... 229
2、複雜模型... 230
3、自定義界面... 232
4、Inline顯示... 235
5、列表頁的顯示... 237
6、建立用戶... 240
7、用戶登陸... 242
8、登出... 244
9、views.py中的用戶... 244
10、模板中的用戶... 247
11、用戶註冊... 247
12、apache安裝... 248
十3、靜態文件... 250
做業... 252
Python基礎(上)
實驗簡介
小提醒
建議
1、實驗說明
1. 環境登陸
無需密碼自動登陸,系統用戶名shiyanlou
2. 環境介紹
本實驗環境採用Ubuntu Linux桌面環境,實驗中會用到桌面上的程序:
1.命令行終端: Linux命令行終端,打開後會進入Bash環境,能夠使用Linux命令;
2.Python:實驗樓環境已經安裝好Python 2.7.6;
3.GVim:很是好用的Vim編輯器,最簡單的用法能夠參考課程Vim編輯器;
4.Gedit及Brackets:若是您對GVim的使用不熟悉,能夠用這兩個做爲代碼編輯器,其中Brackets很是適用於前端代碼開發。
3. 環境使用
使用命令行終端運行所需命令進行操做,使用編輯器輸入實驗所需的代碼及文件。
「實驗記錄」頁面能夠在「個人主頁」中查看,每次實驗的截圖及筆記,以及有效學習時間(指的是在實驗桌面內操做的時間,若是沒有操做,系統會記錄爲發呆時間)。這些都是您在實驗樓學習的真實性證實。
2、Hello World!
1、Python命令行
實驗環境已經安裝好了Python, 在Linux命令行輸入:
$python
將直接進入python。而後在命令行提示符>>>後面輸入:
>>>print('Hello World!')
能夠看到,隨後在屏幕上輸出:
Hello World!
print是一個經常使用函數,其功能就是輸出括號中得字符串。
(在Python 2.x中,print還能夠是一個關鍵字,可寫成print 'Hello World!',但這在3.x中行不通 )
2、寫一段小程序
另外一個使用Python的方法,是寫一個Python程序。用文本編輯器寫一個 .py 結尾的文件,好比說 hello.py
在hello.py中寫入以下,並保存:
print('Hello World!')
退出文本編輯器,而後在命令行輸入:
$python hello.py
來運行hello.py。能夠看到Python隨後輸出:
Hello World!
3、腳本
咱們還能夠把Python程序hello.py改爲一個可執行的腳本,能夠直接執行:
#!/usr/bin/env python
print('Hello World!')
須要修改上面程序的權限爲可執行:
chmod 755 hello.py
而後再命令行中,輸入:
./hello.py
就能夠直接運行了:
3、基本數據類型
1、變量不須要聲明
Python的變量不須要聲明,你能夠直接輸入:
>>>a = 10
那麼你的內存裏就有了一個變量a, 它的值是10,它的類型是integer (整數)。 在此以前你不須要作什麼特別的聲明,而數據類型是Python自動決定的。
>>>print a
>>>print type(a)
那麼會有以下輸出:
10
<type 'int'>
這裏,咱們學到一個內置函數type(), 用以查詢變量的類型。
2、回收變量名
若是你想讓 a 存儲不一樣的數據,你不須要刪除原有變量就能夠直接賦值。
>>>a = 1.3
>>>print a,type(a)
會有以下輸出:
1.3 <type 'float'>
咱們看到print的另外一個用法,也就是print後跟多個輸出,以逗號分隔。
3、經常使用數據類型
變量 |
數據類型 |
a=10 |
int 整數 |
a=1.3 |
float 浮點數 |
a=True |
真值(True/False) |
a='Hello!' |
字符串 |
以上是最經常使用的數據類型,對於字符串來講,也能夠用雙引號。
(此外還有分數,字符,複數等其餘數據類型,有興趣的能夠學習一下)
4、序列
sequence(序列)是一組有順序的元素的集合
(嚴格的說,是對象的集合,但鑑於咱們尚未引入「對象」概念,暫時說元素)
序列能夠包含一個或多個元素,也能夠沒有任何元素。
咱們以前所說的基本數據類型,均可以做爲序列的元素。元素還能夠是另外一個序列,以及咱們之後要介紹的其餘對象。
序列有兩種:tuple(定值表; 也有翻譯爲元組) 和 list (表)
>>>s1 = (2, 1.3, 'love', 5.6, 9, 12, False) # s1是一個tuple
>>>s2 = [True, 5, 'smile'] # s2是一個list
>>>print s1,type(s1)
>>>print s2,type(s2)
tuple和list的主要區別在於,一旦創建,tuple的各個元素不可再變動,而list的各個元素能夠再變動。
dh tuple相似於靜態數組,已經定下來了,而list像動態數組,至於怎麼加,裏面有多少個內容,再看看
一個序列做爲另外一個序列的元素:
>>>s3 = [1,[3,4,5]]
空序列:
>>>s4 = []
1、元素的引用
序列元素的下標從0開始:
>>>print s1[0]
>>>print s2[2]
>>>print s3[1][2] #dh這是提取子數組裏的序號
因爲list的元素可變動,你能夠對list的某個元素賦值:
>>>s2[1] = 3.0
>>>print s2
若是你對tuple作這樣的操做,會獲得錯誤提示。
因此,能夠看到,序列的引用經過s[int]實現,(int爲下標)。
2、其餘引用方式
範圍引用: 基本樣式 [下限:上限:步長]
>>>print s1[:5] # 從開始到下標4 (下標5的元素 不包括在內)
>>>print s1[2:] # 從下標2到最後
>>>print s1[0:5:2] # 從下標0到下標4 (下標5不包括在內),每隔2取一個元素 (下標爲0,2,4的元素)
>>>print s1[2:0:-1] # 從下標2到下標1
dh這裏若是要到底,能夠這樣寫:print s1[3::-1],意思是不加這個上限
從上面能夠看到,在範圍引用的時候,若是寫明上限,那麼這個上限自己不包括在內。
尾部元素引用:
>>>print s1[-1] # 序列最後一個元素
>>>print s1[-3] # 序列倒數第三個元素
一樣,若是s1[0:-1], 那麼最後一個元素不會被引用 (再一次,不包括上限元素自己)。
3、字符串是元組
字符串是一種特殊的元素,所以能夠執行元組的相關操做。
>>>str = 'abcdef'
>>>print str[2:4]
5、運算
暫時只介紹這些運算符的基本用法,方便咱們展開後面的內容,高級應用暫時不介紹。
1、數學運算
>>>print 1+9 # 加法
>>>print 1.3-4 # 減法
>>>print 3*5 # 乘法
>>>print 4.5/1.5 # 除法
>>>print 3**2 # 乘方
>>>print 10%3 # 求餘數
2、判斷
判斷是真仍是假,返回True/False:
>>>print 5==6 # =, 相等
>>>print 8.0!=8.0 # !=, 不等
>>>print 3<3, 3<=3 # <, 小於; <=, 小於等於
>>>print 4>5, 4>=0 # >, 大於; >=, 大於等於
>>>print 5 in [1,3,5] # 5是list [1,3,5]的一個元素
還有is, is not等, 暫時不深刻。
3、邏輯運算
True/False之間的運算:
>>>print True and True, True and False # and, 「與」運算, 二者都爲真纔是真
>>>print True or False # or, "或"運算, 其中之一爲真即爲真
>>>print not True # not, 「非」運算, 取反
能夠和上一部分結合作一些練習,好比:
>>>print 5==6 or 3>=3
6、縮進和選擇
1、縮進
Python最具特點的是用縮進來標明成塊的代碼。我下面以if選擇結構來舉例。if後面跟隨條件,若是條件成立,則執行歸屬於 if 的一個代碼塊。
先看C語言的表達方式(注意,這是C,不是Python!)
if ( i > 0 )
{
x = 1;
y = 2;
}
若是i > 0的話,咱們將進行括號中所包括的兩個賦值操做。括號中包含的就是塊操做,它隸屬於if。
在Python中,一樣的目的,這段話是這樣的:
if i > 0:
x = 1
y = 2
在Python中, 去掉了i > 0周圍的括號,去除了每一個語句句尾的分號,表示塊的花括號也消失了。
多出來了if ...以後的 :(冒號), 還有就是x = 1 和 y =2前面有四個空格的縮進。經過縮進,Python識別出這兩個語句是隸屬於if。
Python這樣設計的理由純粹是爲了程序好看。
2、if語句
寫一個完整的程序,命名爲ifDemo.py。這個程序用於實現if結構。
i = 1
x = 1
if i > 0:
x = x+1
print x
用cd命令進入該文件所在目錄,而後輸入命令運行它:
$python ifDemo.py # 運行
程序運行到 if 的時候,條件爲True,所以執行x = x+1。
print x語句沒有縮進,那麼就是if以外。
若是將第一句改爲i = -1,那麼 if 遇到假值 (False), x = x+1隸屬於 if , 這一句跳過。print x沒有縮進,是 if 以外,不跳過,繼續執行。
這種以四個空格的縮進來表示隸屬關係的書寫方式,之後還會看到。強制縮進加強了程序的可讀性。
複雜一些的 if 選擇:
i = 1
if i > 0:
print 'positive i'
i = i + 1
elif i == 0:
print 'i is 0'
i = i * 10
else:
print 'negative i'
i = i - 1
print 'new i:',i
這裏有三個塊,分別屬於if,elif,else引領。
Python檢測條件,若是發現 if 的條件爲假,那麼跳事後面緊跟的塊,檢測下一個 elif 的條件; 若是仍是假,那麼執行else塊。
經過上面的結構將程序分出三個分支。程序根據條件,只執行三個分支中的一個。
整個 if 能夠放在另外一個 if 語句中,也就是 if 結構的嵌套使用:
i = 5
if i > 1:
print 'i bigger than 1'
print 'good'
if i > 2:
print 'i bigger than 2'
print 'even better'
if i > 2 後面的塊相對於該 if 縮進了四個空格,以代表其隸屬於該 if ,而不是外層的 if 。
做業
按照過程在實驗樓環境中把本節內容中的Python代碼所有運行一遍,並截圖保存。
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
for循環須要預先設定好循環的次數(n),而後執行隸屬於for的語句n次。
基本構造是:
forin元素序列:
statement
舉例來講,咱們編輯一個叫forDemo.py的文件:
forin'life'a[3,4.4,]:
print a
這個循環就是每次從表[3,4.4,'life'] 中取出一個元素(回憶:表是一種序列),而後將這個元素賦值給a,以後執行隸屬於for的操做(print)。
介紹一個新的Python函數range(),來幫助你創建表。
idx = range(5)
printidx
能夠看到idx是[0,1,2,3,4]
這個函數的功能是新建一個表。這個表的元素都是整數,從0開始,下一個元素比前一個大1,直到函數中所寫的上限(不包括該上限自己)。
(關於range(),還有豐富用法,有興趣能夠查閱, Python 3中, range()用法有變化)
dh n1=range(1,6)#這裏注意range的使用,能夠按照想要的範圍來搞
>>> n1
[1, 2, 3, 4, 5]
>>> n1=range(6)
>>> n1
[0, 1, 2, 3, 4, 5]
舉例:
forinarange(10):
print a**2
while的用法是:
while條件:
statement
while會不停地循環執行隸屬於它的語句,直到條件爲假(False)。
舉例:
i = 0
whilei < 10:
print i
i = i + 1
forinirange(10):
if i == 2:
continue
print i
當循環執行到 i = 2 的時候,if 條件成立,觸發continue, 跳過本次執行(不執行print),繼續進行下一次執行(i = 3)。
forinirange(10):
if i == 2:
break
print i
當循環執行到i = 2的時候,if條件成立,觸發break, 整個循環中止。
函數最重要的目的是方便咱們重複使用相同的一段程序。
將一些操做隸屬於一個函數,之後你想實現相同的操做的時候,只用調用函數名就能夠,而不須要重複敲全部的語句。
首先,咱們要定義一個函數, 以說明這個函數的功能。
def square_sum(a,b):
22 c = a**+ b**
return c
這個函數的功能是求兩個數的平方和。
首先,def,這個關鍵字通知python:我在定義一個函數。square_sum是函數名。
括號中的a, b是函數的參數,是對函數的輸入。參數能夠有多個,也能夠徹底沒有(但括號要保留)。
咱們已經在循環和選擇中見過冒號和縮進來表示的隸屬關係。
定義過函數後,就能夠在後面程序中使用這一函數:
printsquare_sum(3,4)
Python經過位置,知道3對應的是函數定義中的第一個參數a, 4對應第二個參數b,而後把參數傳遞給函數square_sum。
(Python有豐富的參數傳遞方式,還有關鍵字傳遞、表傳遞、字典傳遞等,基礎教程將只涉及位置傳遞)
函數通過運算,返回值25, 這個25被print打印出來。
咱們再看下面兩個例子:
1a =
def change_integer(a):
1 a = a +
return a
print#注意觀察結果change_integer(a)
print#注意觀察結果a
#===(Python中 "#" 後面跟的內容是註釋,不執行 )
123b = [,,]
def change_list(b):
001 b[] = b[] +
return b
print#注意觀察結果change_list(b)
print#注意觀察結果b
第一個例子,咱們將一個整數變量傳遞給函數,函數對它進行操做,但原整數變量a不發生變化。
第二個例子,咱們將一個表傳遞給函數,函數進行操做,原來的表b發生變化。
對於基本數據類型的變量,變量傳遞給函數後,函數會在內存中複製一個新的變量,從而不影響原來的變量。(咱們稱此爲值傳遞)
可是對於表來講,表傳遞給函數的是一個指針,指針指向序列在內存中的位置,在函數中對錶的操做將在原有內存中進行,從而影響原有變量。 (咱們稱此爲指針傳遞),指針是C/C++語言中的重要概念,有關指針的概念能夠到網上搜索相關資料。
Python使用類(class)和對象(object),進行面向對象(object-oriented programming,簡稱OOP)的編程。
面向對象的最主要目的是提升程序的重複使用性。咱們這麼早切入面向對象編程的緣由是,Python的整個概念是基於對象的。瞭解OOP是進一步學習Python的關鍵。
下面是對面向對象的一種理解,基於分類。
在人類認知中,會根據屬性相近把東西歸類,而且給類別命名。好比說,鳥類的共同屬性是有羽毛,經過產卵生育後代。任何一隻特別的鳥都在鳥類的原型基礎上的。
面向對象就是模擬了以上人類認知過程。在Python語言,爲了聽起來酷,咱們把上面說的「東西」稱爲對象(object)。
先定義鳥類:
class Bird(object):
True have_feather =
'egg' way_of_reproduction =
咱們定義了一個類別(class),就是鳥(Bird)。在隸屬於這個類比的語句塊中,咱們定義了兩個變量,一個是有羽毛(have_feather),一個是生殖方式(way_of_reproduction),這兩個變量對應咱們剛纔說的屬性(attribute)。咱們暫時先不說明括號以及其中的內容,記爲問題1。
假設我養了一隻小雞,叫summer。它是個對象,且屬於鳥類。使用前面定義的類:
summer = Bird()
printsummer.way_of_reproduction
經過第一句建立對象,並說明summer是類別鳥中的一個對象,summer就有了鳥的類屬性,對屬性的引用是經過 對象.屬性(object.attribute) 的形式實現的。
可憐的summer,你就是個有毛產的蛋貨,好不精緻。
平常認知中,咱們在經過屬性識別類別的時候,有時根據這個東西能作什麼事情來區分類別。好比說,鳥會移動。這樣,鳥就和房屋的類別區分開了。這些動做會帶來必定的結果,好比移動致使位置的變化。
這樣的一些「行爲」屬性爲方法(method)。Python中經過在類的內部定義函數,來講明方法。
class Bird(object):
True have_feather =
'egg' way_of_reproduction =
def move(self, dx, dy):
00 position = [,]
00 position[] = position[] + dx
11 position[] = position[] + dy
return position
summer = Bird()
print'after move:'58,summer.move(,)
咱們從新定義了鳥這個類別。鳥新增一個方法屬性,就是表示移動的方法move。(我認可這個方法很傻,你能夠在看過下一講以後定義個有趣些的方法)
(它的參數中有一個self,它是爲了方便咱們引用對象自身。方法的第一個參數必須是self,不管是否用到。有關self的內容稍後介紹)
另外兩個參數,dx, dy表示在x、y兩個方向移動的距離。move方法會最終返回運算過的position。
在最後調用move方法的時候,咱們只傳遞了dx和dy兩個參數,不須要傳遞self參數(由於self只是爲了內部使用)。
個人summer能夠跑了。
類別自己還能夠進一步細分紅子類。
好比說,鳥類能夠進一步分紅雞,大雁,黃鸝。
在OOP中,咱們經過繼承(inheritance)來表達上述概念。
class Chicken(Bird):
'walk' way_of_move =
True possible_in_KFC =
class Oriole(Bird):
'fly' way_of_move =
False possible_in_KFC =
summer = Chicken()
printsummer.have_feather
print58summer.move(,)
新定義的雞(Chicken)類的,增長了兩個屬性:移動方式(way_of_move),可能在KFC找到(possible_in_KFC)。
在類定義時,括號裏爲了Bird。這說明,Chicken是屬於鳥類(Bird)的一個子類,即Chicken繼承自Bird。天然而然,Bird就是Chicken的父類。Chicken將享有Bird的全部屬性。儘管我只聲明瞭summer是雞類,它經過繼承享有了父類的屬性(不管是變量屬性have_feather仍是方法屬性move)
新定義的黃鸝(Oriole)類,一樣繼承自鳥類。在建立一個黃鸝對象時,該對象自動擁有鳥類的屬性。
經過繼承製度,咱們能夠減小程序中的重複信息和重複語句。若是咱們分別定義兩個類,而不繼承自鳥類,就必須把鳥類的屬性分別輸入到雞類和黃鸝類的定義中。整個過程會變得繁瑣,所以,面向對象提升了程序的可重複使用性。
(回到問題1, 括號中的object,當括號中爲object時,說明這個類沒有父類(到頭了))
將各類各樣的東西分類,從而瞭解世界,從人類祖先開始,咱們就在練習了這個認知過程,面向對象是符合人類思惟習慣的。所謂面向過程,也就是執行完一個語句再執行下一個,更多的是機器思惟。經過面向對象的編程,咱們能夠更方便的表達思惟中的複雜想法。
剛纔提到,在定義方法時,必須有self這一參數。這個參數表示某個對象。對象擁有類的全部性質(若是學習過Java 或C++等語言的便會知道,self至關於this),那麼咱們能夠經過self,調用類屬性。
class Human(object):
'hahahaha' laugh =
def show_laugh(self):
print self.laugh
def laugh_100th(self):
forin100 irange():
self.show_laugh()
li_lei = Human()
li_lei.laugh_100th()
這裏有一個類屬性laugh。在方法show_laugh()中,經過self.laugh,調用了該屬性的值。
還能夠用相同的方式調用其它方法。方法show_laugh(),在方法laugh_100th中()被調用。
在對象中對類屬性進行賦值的時候,實際上會在對象定義的做用域中添加一個屬性(若是還不存在的話),並不會影響到相應類中定義的同名類屬性。但若是是修改類屬性的內容(好比類屬性是個字典,修改字典內容)時會影響到全部對象實例,由於這個類屬性的內容是共享的。
__init__() 是一個特殊方法(special method)。Python有一些特殊方法。Python會特殊的對待它們。特殊方法的特色是名字先後有兩個下劃線。
若是你在類中定義了__init__() 這個方法,建立對象時,Python會自動調用這個方法。這個過程也叫初始化。
class happyBird(Bird):
def __init__(self,more_words):
print'We are happy birds.' ,more_words
'Happy,Happy!'summer = happyBird()
這裏繼承了Bird類。屏幕上打印:
Wearehappybirds.HappyHappy,!
咱們看到,儘管咱們只是建立了summer對象,但__init__() 方法被自動調用了。最後一行的語句(summer = happyBird...)先建立了對象,而後執行:
summer.__init__more_words()
'Happy,Happy!' 被傳遞給了__init__() 的參數more_words
咱們講到了許多屬性,但這些屬性是類的屬性。全部屬於該類的對象會共享這些屬性。好比說,鳥都有羽毛,雞都不會飛。
在一些狀況下,咱們定義對象的性質,用於記錄該對象的特別信息。好比說,人這個類。性別是某我的的一個性質,不是全部的人類都是男,或者都是女。這個性質的值隨着對象的不一樣而不一樣。李雷是人類的一個對象,性別是男;韓美美也是人類的一個對象,性別是女。
當定義類的方法時,必需要傳遞一個self的參數。這個參數指代的就是類的一個對象。咱們能夠經過操縱self,來修改某個對象的性質。好比用類來新建一個對象,即下面例子中的li_lei, 那麼li_lei就被self表示。咱們經過賦值給self.attribute,給li_lei這一對象增長一些性質,好比說性別的男女。self會傳遞給各個方法。在方法內部,能夠經過引用self.attribute,查詢或修改對象的性質。
這樣,在類屬性的以外,又給每一個對象增添了各自特點的性質,從而能描述多樣的世界。
class Human(object):
def __init__(self, input_gender):
self.gender = input_gender
def printGender(self):
print self.gender
'male'# 這裏,'male'做爲參數傳遞給__init__()方法的input_gender變量。li_lei = Human()
print#這一行結果與下一行對比li_lei.gender
#這一行結果與上一行對比li_lei.printGender()
在初始化中,將參數input_gender,賦值給對象的性質,即self.gender。
li_lei擁有了對象性質gender。gender不是一個類屬性。Python在創建了li_lei這一對象以後,使用li_lei.gender這一對象性質,專門儲存屬於對象li_lei的特有信息。
對象的性質也能夠被其它方法調用,調用方法與類屬性的調用類似,正如在printGender()方法中的調用。
從最初的「Hello World」,走到面向對象。該回過頭來看看,教程中是否遺漏了什麼。
咱們以前提到一句話,"Everything is Object". 那麼咱們就深刻體驗一下這句話。
須要先要介紹兩個內置函數:dir() 和 help() 。
dir()用來查詢一個類或者對象全部屬性。你能夠嘗試一下:
printlist>>>dir()
help()用來查詢的說明文檔。你能夠嘗試一下:
printhelp>>>(list)
(list是Python內置的一個類,對應於咱們以前講解過的列表)
在上面以及看到,表是Python已經定義好的一個類。當咱們新建一個表時,好比:
>>12535>nl = [,,,,]
實際上,nl是類list的一個對象。
實驗一些list的方法:
>>5# 計數,看總共有多少個5>print nl.count()
>>3# 查詢 nl 的第一個3的下標>print nl.index()
>>6# 在 nl 的最後增添一個新元素6>nl.append()
>># 對nl的元素排序>nl.sort()
>># 從nl中去除最後一個元素,並將該元素返回。>print nl.pop()
>>2# 從nl中去除第一個2>nl.remove()
>>09# 在下標爲0的位置插入9>nl.insert(,)
總之,list是一個類。每一個列表都屬於該類。
Python補充中有list經常使用方法的附錄。
使用dir(list)的時候,能看到一個屬性,是add()。從形式上看是特殊方法(下劃線,下劃線)。它特殊在哪呢?
這個方法定義了"+"運算符對於list對象的意義,兩個list的對象相加時,會進行的操做。
print[1,2,3][5,6,9]>>>+
運算符,好比+, -, >, <, 以及下標引用[start:end]等等,從根本上都是定義在類內部的方法。
嘗試一下:
print[1,2,3]-[3,4]>>>
會有錯誤信息,說明該運算符「-」沒有定義。如今咱們繼承list類,添加對"-"的定義:
class superList(list):
def __sub__(self, b):
# 這裏,self是supeList的對象。因爲superList繼承於list,它能夠利用和list[:]相同的引用方法來表示整個對象。 a = self[:]
b = b[:]
while0 len(b) >:
element_b = b.pop()
ifin element_ba:
a.remove(element_b)
return a
print12334superList([,,]) - superList([,])
內置函數len()用來返回list所包含的元素的總數。內置函數__sub__() 定義了「-」的操做:從第一個表中去掉第二個表中出現的元素。若是__sub__() 已經在父類中定義,你又在子類中定義了,那麼子類的對象會參考子類的定義,而不會載入父類的定義。任何其餘的屬性也是這樣。
(教程最後也會給出一個特殊方法的清單)
定義運算符對於複雜的對象很是有用。舉例來講,人類有多個屬性,好比姓名,年齡和身高。咱們能夠把人類的比較(>, <, =)定義成只看年齡。這樣就能夠根據本身的目的,將本來不存在的運算增長在對象上了。
但願你已經對Python有了一個基本瞭解。你可能躍躍欲試,要寫一些程序練習一下。這會對你頗有好處。
可是,Python的強大很大一部分緣由在於,它提供有不少已經寫好的,能夠現成用的對象。咱們已經看到了內置的好比說list,還有tuple等等。它們用起來很方便。在Python的標準庫裏,還有大量能夠用於操做系統互動,Internet開發,多線程,文本處理的對象。而在全部的這些的這些的基礎上,又有不少外部的庫包,定義了更豐富的對象,好比numpy, tkinter, django等用於科學計算,GUI開發,web開發的庫,定義了各類各樣的對象。對於通常用戶來講,使用這些庫,要比本身去從頭開始容易得多。咱們要開始攀登巨人的肩膀了。
謝謝你的關注,
歡迎來到Python的世界。
一、寫一個判斷閏年的函數,參數爲年、月、日。如果是閏年,返回True,並截圖。
二、使用help(list)或dir(list)仔細查詢list類的屬性和文檔。
dh作到這裏,由於已經編輯了不少.py的文件,批量移動,複習下以前的linux知識
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
Python基礎介紹了基本概念,特別是對象和類。
進階教程對基礎教程的進一步拓展,說明Python的細節。但願在進階教程以後,你對Python有一個更全面的認識。
以前咱們說了,列表是Python裏的一個類。一個特定的表,好比說nl = [1,3,8],就是這個類的一個對象。咱們能夠調用這個對象的一些方法,好比 nl.append(15)。 咱們要介紹一個新的類,詞典 (dictionary)。與列表類似,詞典也能夠儲存多個元素。這種儲存多個元素的對象稱爲容器(container)。
常見的建立詞典的方法:
>>'tom':11'sam':57'lily':100>dic = {,,}
>>>print type(dic)
詞典和表相似的地方,是包含有多個元素,每一個元素以逗號分隔。但詞典的元素包含有兩部分,鍵和值,常見的是以字符串來表示鍵,也能夠使用數字或者真值來表示鍵(不可變的對象能夠做爲鍵)。值能夠是任意對象。鍵和值二者一一對應。
好比上面的例子中,‘tom’對應11,'sam對應57,'lily'對應100
與表不一樣的是,詞典的元素沒有順序。你不能經過下標引用元素。詞典是經過鍵來引用。
>>'tom'>print dic[]
>>'tom'30>dic[] =
>>>print dic
構建一個新的空的詞典:
>>>dic = {}
>>>print dic
在詞典中增添一個新元素的方法:
>>'lilei'99>dic[] =
>>>print dic
這裏,咱們引用一個新的鍵,並賦予它對應的值。
'lilei''lily''sam''tom'dic = {: 90,: 100,: 57,: 90}
forinkeydic:
print dic[key]
在循環中,dict的每一個鍵,被提取出來,賦予給key變量。
經過print的結果,咱們能夠再次確認,dic中的元素是沒有順序的。
>># 返回dic全部的鍵>print dic.keys()
>># 返回dic全部的值>print dic.values()
>># 返回dic全部的元素(鍵值對)>print dic.items()
>># 清空dic,dict變爲{}>dic.clear()
另外有一個很經常使用的用法:
>>'tom'# 刪除 dic 的‘tom’元素>del dic[]
del是Python中保留的關鍵字,用於刪除對象。
與表相似,你能夠用len()查詢詞典中的元素總數。
>>>print len(dic)
Python具備基本的文本文件讀寫功能。Python的標準庫提供有更豐富的讀寫功能。
文本文件的讀寫主要經過open()所構建的文件對象來實現。
咱們打開一個文件,並使用一個對象來表示該文件:
open對象名=(文件名,模式)
最經常使用的模式有:
好比:
>>"test.txt""r">f = open(,)
讀取:
content= f.read(N) #讀取N bytes的數據
content= f.readline() #讀取一行
content= f.readlines() #讀取全部行,儲存在列表中,每一個元素是一行。
寫入:
'I like apple!\n'# 將'I like apple'寫入文件並換行f.write()
關閉文件:
# 不要忘記關閉文件f.close()
咱們以前看到了函數和對象。從本質上來講,它們都是爲了更好的組織已經有的程序,以方便重複利用。
模塊(module)也是爲了一樣的目的。在Python中,一個.py文件就構成一個模塊。經過模塊,你能夠調用其它文件中的程序。
咱們先寫一個first.py文件,內容以下:
def laugh():
print'HaHaHaHa'
再寫一個second.py,並引入first中的程序:
import#將first文件引入first
forin10irange():
first.laugh()
在second.py中,咱們使用了first.py中定義的laugh()函數。
引入模塊後,能夠經過 模塊.對象 的方式來調用引入模塊中的某個對象。上面例子中,first爲引入的模塊,laugh()是咱們所引入的對象。
Python中還有其它的引入方式:
importas# 引入模塊a,並將模塊a重命名爲bab
fromimport# 從模塊a中引入function1對象。調用a中對象時,咱們不用再說明模塊,即直接使用function1,而不是a.function1。afunction1
fromimport# 從模塊a中引入全部對象。調用a中對象時,咱們不用再說明模塊,即直接使用對象,而不是a.對象。a*
這些引用方式,能夠方便後面的程序書寫。
Python會在如下路徑中搜索它想要尋找的模塊:
若是你有自定義的模塊,或者下載的模塊,能夠根據狀況放在相應的路徑,以便Python能夠找到。
能夠將功能類似的模塊放在同一個文件夾(好比說this_dir)中,構成一個模塊包。經過
importmodulethis_dir.
引入this_dir文件夾中的module模塊。
該文件夾中必須包含一個 __init__.py 的文件,提醒Python,該文件夾爲一個模塊包。__init__.py 能夠是一個空文件。
咱們已經接觸過函數(function)的參數(arguments)傳遞。當時咱們根據位置,傳遞對應的參數。咱們將接觸更多的參數傳遞方式。
回憶一下位置傳遞:
def f(a,b,c):
return a+b+c
123print(f(,,))
在調用 f 時,1,2,3根據位置分別傳遞給了a,b,c。
有些狀況下,用位置傳遞會感受比較死板。關鍵字(keyword)傳遞是根據每一個參數的名字傳遞參數。關鍵字並不用遵照位置的對應關係。依然沿用上面f的定義,更改調用方式:
print(f(c=3,b=2,a=1))
關鍵字傳遞能夠和位置傳遞混用。但位置參數要出如今關鍵字參數以前:
print(f(1,c=3,b=2))
在定義函數的時候,使用形如a=19的方式,能夠給參數賦予默認值(default)。若是該參數最終沒有被傳遞值,將使用該默認值。
def f(a,b,c=10):
return a+b+c
32print(f(,))
321print(f(,,))
在第一次調用函數f時, 咱們並無足夠的值,c沒有被賦值,c將使用默認值10.
第二次調用函數的時候,c被賦值爲1,再也不使用默認值。
在定義函數時,咱們有時候並不知道調用的時候會傳遞多少個參數。這時候,包裹(packing)位置參數,或者包裹關鍵字參數,來進行參數傳遞,會很是有用。
下面是包裹位置傳遞的例子:
def func(*name):
print type(name)
print name
146func(,,)
567123func(,,,,,)
兩次調用,儘管參數個數不一樣,都基於同一個func定義。在func的參數表中,全部的參數被name收集,根據位置合併成一個元組(tuple),這就是包裹位置傳遞。
爲了提醒Python參數,name是包裹位置傳遞所用的元組名,在定義func時,在name前加*號。
下面是包裹關鍵字傳遞的例子:
def func(**dict):
print type(dict)
print dict
19func(a=,b=)
2111func(m=,n=,c=)
與上面一個例子相似,dict是一個字典,收集全部的關鍵字,傳遞給函數func。爲了提醒Python,參數dict是包裹關鍵字傳遞所用的字典,在dict前加* *。
包裹傳遞的關鍵在於定義函數時,在相應元組或字典前加 * 或 * * 。
* 和 **,也能夠在調用的時候使用,即解包裹(unpacking), 下面爲例:
def func(a,b,c):
print a,b,c
134args = (,,)
func(*args)
在這個例子中,所謂的解包裹,就是在傳遞tuple時,讓tuple的每個元素對應一個位置參數。在調用func時使用 * ,是爲了提醒Python:我想要把args拆成分散的三個元素,分別傳遞給a,b,c。(設想一下在調用func時,args前面沒有 * 會是什麼後果?)
相應的,也存在對詞典的解包裹,使用相同的func定義,而後:
'a''b''c'dict = {:1,:2,:3}
func(**dict)
在傳遞詞典dict時,讓詞典的每一個鍵值對做爲一個關鍵字傳遞給func。
在定義或者調用參數時,參數的幾種傳遞方式能夠混合。但在過程當中要當心先後順序。基本原則是:先位置,再關鍵字,再包裹位置,再包裹關鍵字,而且根據上面所說的原理細細分辨。
注意:請注意定義時和調用時的區分。包裹和解包裹並非相反操做,是兩個相對獨立的過程。
tom, 12, 86
Lee, 15, 99
Lucy, 11, 58
Joseph, 19, 56
再從 record.txt 中讀取文件並打印。
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
在「Python基礎(下)」一節,咱們已經討論了Python基本的循環語法。這一節,咱們將接觸更加靈活的循環方式。
在Python中,for循環後的in跟隨一個序列的話,循環每次使用的序列元素,而不是序列的下標。
以前咱們已經使用過 range() 來控制for循環。如今,咱們繼續開發range的功能,以實現下標對循環的控制:
'abcdefghijk'S =
forinirange(0,len(S),2):
print S[i]
在該例子中,咱們利用 len() 函數和 range() 函數,用 i 做爲 S 序列的下標來控制循環。在range函數中,分別定義上限,下限和每次循環的步長。這就和C語言中的for循環相相似了。
利用enumerate()函數,能夠在每次循環中同時獲得下標和元素:
'abcdefghijk'S =
forindex(,char) in enumerate(S):
printindex
print char
實際上,enumerate()在每次循環中,返回的是一個包含兩個元素的定值表(tuple),兩個元素分別賦予index和char。
若是你多個等長的序列,而後想要每次循環時從各個序列分別取出一個元素,能夠利用zip()方便地實現:
ta = [1,2,3]
tb = [9,8,7]
'a''b''c'tc = [,,]
forin(a,b,c)zip(ta,tb,tc):
print (a,b,c)
每次循環時,從各個序列分別從左到右取出一個元素,合併成一個tuple,而後tuple的元素賦予給a,b,c 。
zip()函數的功能,就是從多個列表中,依次各取出一個元素。每次取出的(來自不一樣列表的)元素合成一個元組,合併成的元組放入zip()返回的列表中。zip()函數起到了聚合列表的功能。
咱們能夠分解聚合後的列表,以下:
ta = [1,2,3]
tb = [9,8,7]
# cluster
zipped = zip(ta,tb)
print(zipped)
# decompose
na, nb = zip(*zipped)
print(na, nb)
這一講的主要目的是爲了你們在讀Python程序的時候對循環對象有一個基本概念。
循環對象的並非隨着Python的誕生就存在的,但它的發展迅速,特別是Python 3x的時代,循環對象正在成爲循環的標準形式。
dh循環對象的核心機制,做用什麼的,還不是徹底領會
循環對象是這樣一個對象,它包含有一個next()方法 ( __next__() 方法,在python 3x中 ), 這個方法的目的是進行到下一個結果,而在結束一系列結果以後,舉出StopIteration錯誤。
當一個循環結構(好比for)調用循環對象時,它就會每次循環的時候調用next()方法,直到StopIteration出現,for循環接收到,就知道循環已經結束,中止調用next()。
假設咱們有一個test.txt的文件:
1234
abcd
efg
咱們運行一下python命令行:
>>'test.txt'>f = open()
>>>f.next()
>>>f.next()
...
不斷輸入f.next(),直到最後出現StopIteration 。
open()返回的其實是一個循環對象,包含有next()方法。而該next()方法每次返回的就是新的一行的內容,到達文件結尾時舉出StopIteration。這樣,咱們至關於手工進行了循環。
自動進行的話,就是:
forin'test.txt'lineopen():
print line
在這裏,for結構自動調用next()方法,將該方法的返回值賦予給line。循環知道出現StopIteration的時候結束。
相對於序列,用循環對象的好處在於:不用在循環尚未開始的時候,就生成好要使用的元素。所使用的元素能夠在循環過程當中逐次生成。這樣,節省了空間,提升了效率,編程更靈活。
從技術上來講,循環對象和for循環調用之間還有一箇中間層,就是要將循環對象轉換成迭代器(iterator)。這一轉換是經過使用iter()函數實現的。但從邏輯層面上,經常能夠忽略這一層,因此循環對象和迭代器經常相互指代對方。
生成器(generator)的主要目的是構成一個用戶自定義的循環對象。
生成器的編寫方法和函數定義相似,只是在return的地方改成yield。生成器中能夠有多個yield。當生成器遇到一個yield時,會暫停運行生成器,返回yield後面的值。當再次調用生成器的時候,會從剛纔暫停的地方繼續運行,直到下一個yield。生成器自身又構成一個循環器,每次循環使用一個yield返回的值。
下面是一個生成器:
def gen():
100 a =
yield a
8 a = a*
yield a
yield1000
該生成器共有三個yield, 若是用做循環器時,會進行三次循環。
forinigen():
print i
再考慮以下一個生成器:
def gen():
forin4 irange():
yield i
它又能夠寫成生成器表達式(Generator Expression):
forinG = (xxrange(4))
生成器表達式是生成器的一種簡便的編寫方式。讀者可進一步查閱。
表推導(list comprehension)是快速生成表的方法。它的語法簡單,頗有實用價值。
假設咱們生成表 L :
L = []
forinxrange(10):
L.append(x**2)
以上產生了表L,但實際上有快捷的寫法,也就是表推導的方式:
forinL = [x**2xrange(10)]
這與生成器表達式相似,只不過用的是中括號。
(表推導的機制其實是利用循環對象,有興趣能夠查閱。)
秉承着一切皆對象的理念,咱們再次回頭來看函數(function)。函數也是一個對象,具備屬性(能夠使用dir()查詢)。做爲對象,它還能夠賦值給其它對象名,或者做爲參數傳遞。
在展開以前,咱們先提一下lambda函數。能夠利用lambda函數的語法,定義函數。lambda例子以下:
lambdafunc =x,y: x + y
print34func(,)
lambda生成一個函數對象。該函數參數爲x,y,返回值爲x+y。函數對象賦給func。func的調用與正常函數無異。
以上定義能夠寫成如下形式:
def func(x, y):
return x + y
函數能夠做爲一個對象,進行參數傳遞。函數名(好比func)即該對象。好比說:
def test(f, a, b):
print'test'
print f(a, b)
35test(func,,)
test函數的第一個參數f就是一個函數對象。將func傳遞給f,test中的f()就擁有了func()的功能。
咱們所以能夠提升程序的靈活性。能夠使用上面的test函數,帶入不一樣的函數參數。好比:
dh,這裏存疑,由於運行有問題,之後回來再看
test((lambda x,y: x**2 + y), 6, 9)
map()是Python的內置函數。它的第一個參數是一個函數對象。
re31356= map((lambda x: x+),[,,,])
這裏,map()有兩個參數,一個是lambda所定義的函數對象,一個是包含有多個元素的表。map()的功能是將函數對象依次做用於表的每個元素,每次做用的結果儲存於返回的表re中。map經過讀入的函數(這裏是lambda函數)來操做數據(這裏「數據」是表中的每個元素,「操做」是對每一個數據加3)。
在Python 3.X中,map()的返回值是一個循環對象。能夠利用list()函數,將該循環對象轉換成表。
若是做爲參數的函數對象有多個參數,可以使用下面的方式,向map()傳遞函數參數的多個參數:
map123679re =((lambda x,y: x+y),[,,],[,,])
map()將每次從兩個表中分別取出一個元素,帶入lambda所定義的函數。
filter函數的第一個參數也是一個函數對象。它也是將做爲參數的函數對象做用於多個元素。若是函數對象返回的是True,則該次的元素被儲存於返回的表中。 filter經過讀入的函數來篩選數據。一樣,在Python 3.X中,filter返回的不是表,而是循環對象。
filter函數的使用以下例:
def func(a):
if100 a >:
returnTrue
else :
returnFalse
print1056101500filter(func,[,,,])
reduce函數的第一個參數也是函數,但有一個要求,就是這個函數自身能接收兩個參數。reduce能夠累進地將函數做用於各個參數。以下例:
printlambda12579reduce((x,y: x+y),[,,,,])
reduce的第一個參數是lambda函數,它接收兩個參數x,y, 返回x+y。
reduce將表中的前兩個元素(1和2)傳遞給lambda函數,獲得3。該返回值(3)將做爲lambda函數的第一個參數,而表中的下一個元素(5)做爲lambda函數的第二個參數,進行下一次的對lambda函數的調用,獲得8。依次調用lambda函數,每次lambda函數的第一個參數是上一次運算結果,而第二個參數爲表中的下一個元素,直到表中沒有剩餘元素。
上面例子,至關於(((1+2)+5)+7)+9
提醒: reduce()函數在3.0裏面不能直接用的,它被定義在了functools包裏面,須要引入包。
在項目開發中,異常處理是不可或缺的。異常處理幫助人們debug,經過更加豐富的信息,讓人們更容易找到bug的所在。異常處理還能夠提升程序的容錯性。
咱們以前在講循環對象的時候,曾提到一個StopIteration的異常,該異常是在循環對象窮盡全部元素時的報錯。
咱們以它爲例,來講明基本的異常處理。
一個包含異常的程序:
re = iter(range(5))
forinirange(100):
print re.next()
print'HaHaHaHa'
首先,咱們定義了一個循環對象re,該循環對象將進行5次循環,每次使用序列的一個元素。
在隨後的for循環中,咱們手工調用next()函數。當循環進行到第6次的時候,re.next()不會再返回元素,而是拋出(raise)StopIteration的異常。整個程序將會中斷。
咱們能夠修改以上異常程序,直到完美的沒有bug。但另外一方面,若是咱們在寫程序的時候,知道這裏可能犯錯以及可能的犯錯類型,咱們能夠針對該異常類型定義好」應急預案「。
5re = iter(range())
try:
forin100 irange():
print re.next()
exceptStopIteration:
print'here is end ' ,i
print'HaHaHaHa'
在try程序段中,咱們放入容易犯錯的部分。咱們能夠跟上except,來講明若是在try部分的語句發生StopIteration時,程序該作的事情。若是沒有發生異常,則except部分被跳過。
隨後,程序將繼續運行,而不是完全中斷。
完整的語法結構以下:
try:
...
exceptexception1:
...
exceptexception2:
...
except:
...
else:
...
finally:
...
若是try中有異常發生時,將執行異常的歸屬,執行except。異常層層比較,看是不是exception1, exception2...,直到找到其歸屬,執行相應的except中的語句。若是except後面沒有任何參數,那麼表示全部的exception都交給這段程序處理。好比:
try:
2 print(a*)
exceptTypeError:
"TypeError" print()
except:
"Not Type Error & Error noted" print()
因爲a沒有定義,因此是NameError。異常最終被except:部分的程序捕捉。
若是沒法將異常交給合適的對象,異常將繼續向上層拋出,直到被捕捉或者形成主程序報錯。好比下面的程序:
def test_func():
try :
10 m =/
except NameError:
"Catch NameError in the sub-function" print()
try:
test_func()
exceptZeroDivisionError:
"Catch error in the main program" print()
子程序的try...except...結構沒法處理相應的除以0的錯誤,因此錯誤被拋給上層的主程序。
若是try中沒有異常,那麼except部分將跳過,執行else中的語句。
finally是不管是否有異常,最後都要作的一些事情。
流程以下:
咱們也能夠本身寫一個拋出異常的例子:
print'Lalala'
raiseStopIteration
print'Hahaha'
這個例子不具有任何實際意義。只是爲了說明raise語句的做用。
StopIteration是一個類。拋出異常時,會自動有一箇中間環節,就是生成StopIteration的一個對象。Python實際上拋出的,是這個對象。固然,也能夠自行生成對象:
StopIterationraise()
動態類型(dynamic typing)是Python另外一個重要的核心概念。咱們以前說過,Python的變量(variable)不須要聲明,而在賦值時,變量能夠從新賦值爲任意值。這些都與動態類型的概念相關。
在咱們接觸的對象中,有一類特殊的對象,是用於存儲數據的。常見的該類對象包括各類數字,字符串,表,詞典。在C語言中,咱們稱這樣一些數據結構爲變量。而在Python中,這些是對象。
對象是儲存在內存中的實體。但咱們並不能直接接觸到該對象。咱們在程序中寫的對象名,只是指向這一對象的引用(reference)。
引用和對象分離,是動態類型的核心。引用能夠隨時指向一個新的對象:
a3=
a'at'=
第一個語句中,3是儲存在內存中的一個整數對象。經過賦值,引用a指向對象3。
第二個語句中,內存中創建對象‘at’,是一個字符串(string)。引用a指向了'at'。此時,對象3再也不有引用指向它。Python會自動將沒有引用指向的對象銷燬(destruct),釋放相應內存。
(對於小的整數和短字符串,Python會緩存這些對象,而不是頻繁的創建和銷燬。)
a5=
b= a
a2= a +
再看這個例子。經過前兩個句子,咱們讓a,b指向同一個整數對象5( b = a的含義是讓引用b指向引用a所指的那一個對象)。但第三個句子實際上對引用a從新賦值,讓a指向一個新的對象7。此時a,b分別指向不一樣的對象。咱們看到,即便是多個引用指向同一個對象,若是一個引用值發生變化,那麼其實是讓這個引用指向一個新的引用,並不影響其餘的引用的指向。從效果上看,就是各個引用各自獨立,互不影響。
其它數據對象也是如此:
L1123= [,,]
L2= L1
L11=
但注意如下狀況:
L1 = [1,2,3]
L2 = L1
L1[0] = 10
print L2
在該狀況下,咱們再也不對L1這一引用賦值,而是對L1所指向的表的元素賦值。結果是,L2也同時發生變化。
緣由何在呢?由於L1,L2的指向沒有發生變化,依然指向那個表。表其實是包含了多個引用的對象(每一個引用是一個元素,好比L1[0],L1[1]..., 每一個引用指向一個對象,好比1,2,3), 。而L1[0] = 10這一賦值操做,並非改變L1的指向,而是對L1[0], 也就是表對象的一部份(一個元素),進行操做,因此全部指向該對象的引用都受到影響。
(與之造成對比的是,咱們以前的賦值操做都沒有對對象自身發生做用,只是改變引用指向。)
列表能夠經過引用其元素,改變對象自身(in-place change)。這種對象類型,稱爲可變數據對象(mutable object),詞典也是這樣的數據類型。
而像以前的數字和字符串,不能改變對象自己,只能改變引用的指向,稱爲不可變數據對象(immutable object)。
咱們以前學的元組(tuple),儘管能夠調用引用元素,但不能夠賦值,所以不能改變對象自身,因此也算是immutable object。
函數的參數傳遞,本質上傳遞的是引用。好比說:
def f(x):
100 x =
print x
1a =
f(a)
printa
參數x是一個新的引用,指向a所指的對象。若是參數是不可變(immutable)的對象,a和x引用之間相互獨立。對參數x的操做不會影響引用a。這樣的傳遞相似於C語言中的值傳遞。
若是傳遞的是可變(mutable)的對象,那麼改變函數參數,有可能改變原對象。全部指向原對象的引用都會受影響,編程的時候要對此問題留心。好比說:
def f(x):
0100 x[] =
print x
123a = [,,]
f(a)
printa
動態類型是Python的核心機制之一。能夠在應用中慢慢熟悉。
xl135= [,,]
yl91213= [,,]
L210 = [ x**for (x,y) in zip(xl,yl) if y >]
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
到如今爲止,Python學習已經能夠告一段落。下面的部分,我想討論Python的高級語法和底層實現。這一部分的內容並非使用Python所必須的。但若是你想從事一些大型的Python開發(好比製做Python工具、寫一個框架等),你會但願對這一部份內容有所的瞭解。
Python 一切皆對象,但同時,Python仍是一個多範式語言(multi-paradigm),你不只能夠使用面向對象的方式來編寫程序,還能夠用面向過程的方式來編寫相同功能的程序(還有函數式、聲明式等,咱們暫不深刻)。Python的多範式依賴於Python對象中的特殊方法(special method)。
特殊方法名的先後各有兩個下劃線。特殊方法又被成爲魔法方法(magic method),定義了許多Python 語法和表達方式,正如咱們在下面的例子中將要看到的。當對象中定義了特殊方法的時候,Python也會對它們有「特殊優待」。好比定義了__init__()方法的類,會在建立對象的時候自動執行__init__()方法中的操做。
(能夠經過dir()來查看對象所擁有的特殊方法,好比dir(1))。
Python的運算符是經過調用對象的特殊方法實現的。好比:
'abc''xyz'# 鏈接字符串+
實際執行了以下操做:
'abc''xyz'.__add__()
因此,在Python中,兩個對象是否能進行加法運算,首先就要看相應的對象是否有__add__()方法。一旦相應的對象有__add__()方法,即便這個對象從數學上不可加,咱們均可以用加法的形式,來表達obj.__add__()所定義的操做。在Python中,運算符起到簡化書寫的功能,但它依靠特殊方法實現。
Python不強制用戶使用面向對象的編程方法。用戶能夠選擇本身喜歡的使用方式(好比選擇使用+符號,仍是使用更加面向對象的__add__()方法)。特殊方法寫起來老是要更費事一點。
與運算符相似,許多內置函數也都是調用對象的特殊方法。好比:
# 返回表中元素的總數len([1,2,3])
實際上作的是:
[1,2,3].__len__()
相對與__len__(),內置函數len()也起到了簡化書寫的做用。
下面是咱們常見的表元素引用方式:
li [1, 2, 3, 4, 5, 6]=
print(li[3])
上面的程序運行到li[3]的時候,Python發現並理解[]符號,而後調用__getitem__()方法。
li [1, 2, 3, 4, 5, 6]=
print(li.__getitem__(3))
咱們已經說過,在Python中,函數也是一種對象。實際上,任何一個有__call__()特殊方法的對象都被看成是函數。好比下面的例子:
class SampleMore(object):
def __call__(self, a):
return5 a +
# A function objectadd = SampleMore()
2# Call function print(add())
245# Pass around function objectmap(add, [,,])
add爲SampleMore類的一個對象,當被調用時,add執行加5的操做。add還能夠做爲函數對象,被傳遞給map()函數。
固然,咱們還能夠使用更「優美」的方式,想一想是什麼。
上下文管理器(context manager)是Python2.5開始支持的一種語法,用於規定某個對象的使用範圍。一旦進入或者離開該使用範圍,會有特殊操做被調用 (好比爲對象分配或者釋放內存)。它的語法形式是with...as...
咱們會進行這樣的操做:打開文件,讀寫,關閉文件。程序員常常會忘記關閉文件。上下文管理器能夠在不須要文件的時候,自動關閉文件。
下面咱們看一下兩段程序:
# without context manager
open"new.txt""w"f =(,)
print# whether the file is open(f.closed)
"Hello World!"f.write()
f.close()
print(f.closed)
以及:
# with context manager
with"new.txt""w"asopen(,)f:
print(f.closed)
"Hello World!" f.write()
print(f.closed)
兩段程序實際上執行的是相同的操做。咱們的第二段程序就使用了上下文管理器 (with...as...)。上下文管理器有隸屬於它的程序塊。當隸屬的程序塊執行結束的時候(也就是再也不縮進),上下文管理器自動關閉了文件 (咱們經過f.closed來查詢文件是否關閉)。咱們至關於使用縮進規定了文件對象f的使用範圍。
上面的上下文管理器基於f對象的__exit__()特殊方法(還記得咱們如何利用特殊方法來實現各類語法?參看特殊方法與多範式)。當咱們使用上下文管理器的語法時,咱們實際上要求Python在進入程序塊以前調用對象的__enter__()方法,在結束程序塊的時候調用__exit__()方法。對於文件對象f來講,它定義了__enter__()和__exit__()方法(能夠經過dir(f)看到)。在f的__exit__()方法中,有self.close()語句。因此在使用上下文管理器時,咱們就不用明文關閉f文件了。
任何定義了__enter__()和__exit__()方法的對象均可以用於上下文管理器。文件對象f是內置對象,因此f自動帶有這兩個特殊方法,不須要自定義。
下面,咱們自定義用於上下文管理器的對象,就是下面的myvow:
# customized object
class VOW(object):
def __init__(self, text):
self.text = text
def __enter__(self):
"I say: "# add prefix self.text =+ self.text
return# note: return an object self
def __exit__(self,exc_type,exc_value,traceback):
"!"# add suffix self.text = self.text +
with"I'm fine"asVOW()myvow:
print(myvow.text)
print(myvow.text)
咱們的運行結果以下:
I'm finesay: I
I say: I'm fine!
咱們能夠看到,在進入上下文和離開上下文時,對象的text屬性發生了改變(最初的text屬性是"I'm fine")。
__enter__()返回一個對象。上下文管理器會使用這一對象做爲as所指的變量,也就是myvow。在__enter__()中,咱們爲myvow.text增長了前綴 ("I say: ")。在__exit__()中,咱們爲myvow.text增長了後綴("!")。
注意: __exit__()中有四個參數。當程序塊中出現異常(exception),__exit__()的參數中exc_type, exc_value, traceback用於描述異常。咱們能夠根據這三個參數進行相應的處理。若是正常運行結束,這三個參數都是None。在咱們的程序中,咱們並無用到這一特性。
因爲上下文管理器帶來的便利,它是一個值得使用的工具。
Python一切皆對象(object),每一個對象均可能有多個屬性(attribute)。Python的屬性有一套統一的管理方案。
對象的屬性可能來自於其類定義,叫作類屬性(class attribute)。類屬性可能來自類定義自身,也可能根據類定義繼承來的。一個對象的屬性還多是該對象實例定義的,叫作對象屬性(object attribute)。
對象的屬性儲存在對象的__dict__屬性中。__dict__爲一個詞典,鍵爲屬性名,對應的值爲屬性自己。咱們看下面的類和對象。chicken類繼承自bird類,而summer爲chicken類的一個對象。
class bird(object):
True feather =
class chicken(bird):
False fly =
def __init__(self, age):
self.age = age
2summer = chicken()
print(bird.__dict__)
print(chicken.__dict__)
print(summer.__dict__)
下面爲咱們的輸出結果:
'__dict__''__dict__''bird''__module__''__main__''__weakref__''__weakref__''bird''feather'True'__doc__'{: <attributeofobjects>,:,: <attributeofobjects>,:,: None}
'fly'False'__module__''__main__''__doc__''__init__'function __init__ at 0x2b91db476d70>}{:,:,: None,: <
'age'2{:}
第一行爲bird類的屬性,好比feather。第二行爲chicken類的屬性,好比fly和__init__方法。第三行爲summer對象的屬性,也就是age。有一些屬性,好比__doc__,並非由咱們定義的,而是由Python自動生成。此外,bird類也有父類,是object類(正如咱們的bird定義,class bird(object))。這個object類是Python中全部類的父類。
能夠看到,Python中的屬性是分層定義的,好比這裏分爲object/bird/chicken/summer這四層。當咱們須要調用某個屬性的時候,Python會一層層向上遍歷,直到找到那個屬性。(某個屬性可能出現再不一樣的層被重複定義,Python向上的過程當中,會選取先遇到的那一個,也就是比較低層的屬性定義)。
當咱們有一個summer對象的時候,分別查詢summer對象、chicken類、bird類以及object類的屬性,就能夠知道summer對象全部的__dict__,就能夠找到經過對象summer能夠調用和修改的全部屬性了。下面兩種屬性修改方法等效:
'age'summer.__dict__[] = 3
print'age'(summer.__dict__[])
summer.age = 5
print(summer.age)
(上面的狀況中,咱們已經知道了summer對象的類爲chicken,而chicken類的父類爲bird。若是隻有一個對象,而不知道它的類以及其餘信息的時候,咱們能夠利用__class__屬性找到對象的類,而後調用類的__base__屬性來查詢父類) 。
同一個對象的不一樣屬性之間可能存在依賴關係。當某個屬性被修改時,咱們但願依賴於該屬性的其餘屬性也同時變化。這時,咱們不能經過__dict__的方式來靜態的儲存屬性。Python提供了多種即時生成屬性的方法。其中一種稱爲特性(property)。特性是特殊的屬性。好比咱們爲chicken類增長一個特性adult。當對象的age超過1時,adult爲True;不然爲False:
class bird(object):
True feather =
class chicken(bird):
False fly =
def __init__(self, age):
self.age = age
def getAdult(self):
if1.0returnTrue self.age >:
elsereturnFalse :
# property is built-in adult = property(getAdult)
2summer = chicken()
print(summer.adult)
0.5summer.age =
print(summer.adult)
特性使用內置函數property()來建立。property()最多能夠加載四個參數。前三個參數爲函數,分別用於處理查詢特性、修改特性、刪除特性。最後一個參數爲特性的文檔,能夠爲一個字符串,起說明做用。
咱們使用下面一個例子進一步說明:
class num(object):
def __init__(self, value):
self.value = value
def getNeg(self):
return -self.value
def setNeg(self, value):
self.value = -value
def delNeg(self):
"value also deleted" print()
del self.value
"I'm negative" neg = property(getNeg, setNeg, delNeg,)
1.1x = num()
print(x.neg)
-22x.neg =
print(x.value)
print(num.neg.__doc__)
delx.neg
上面的num爲一個數字,而neg爲一個特性,用來表示數字的負數。當一個數字肯定的時候,它的負數老是肯定的;而當咱們修改一個數的負數時,它自己的值也應該變化。這兩點由getNeg和setNeg來實現。而delNeg表示的是,若是刪除特性neg,那麼應該執行的操做是刪除屬性value。property()的最後一個參數("I'm negative")爲特性negative的說明文檔。
咱們能夠用__getattr__(self, name)來查詢即時生成的屬性。當咱們查詢一個屬性時,若是經過__dict__方法沒法找到該屬性,那麼Python會調用對象的__getattr__方法,來即時生成該屬性。好比:
class bird(object):
True feather =
class chicken(bird):
False fly =
def __init__(self, age):
self.age = age
def __getattr__(self, name):
if'adult' name ==:
if1.0returnTrue self.age >:
elsereturnFalse :
elseraise :AttributeError(name)
2summer = chicken()
print(summer.adult)
0.5summer.age =
print(summer.adult)
print(summer.male)
每一個特性須要有本身的處理函數,而__getattr__能夠將全部的即時生成屬性放在同一個函數中處理。__getattr__能夠根據函數名區別處理不一樣的屬性。好比上面咱們查詢屬性名male的時候,raise AttributeError。
(Python中還有一個__getattribute__特殊方法,用於查詢任意屬性。__getattr__只能用來查詢不在__dict__系統中的屬性)
__setattr__(self, name, value)和__delattr__(self, name)可用於修改和刪除屬性。它們的應用面更廣,可用於任意屬性。
即時生成屬性還能夠使用其餘的方式,好比descriptor ( descriptor類其實是property()函數的底層,property()實際上建立了一個該類的對象 ) 。有興趣能夠進一步查閱。
嘗試下面的操做,看看效果,再想一想它的對應運算符:
.8.__mul__.0(1)(2)
True.__or__False()
嘗試下面的操做,想一下它的對應內置函數:
-1.__abs__()()
.3.__int__(2)()
嘗試看下面的操做,想一想它的對應:
li.__setitem__(3, 0)
'a''b''a'{:1,:2}.__delitem__()
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
閉包(closure)是函數式編程的重要的語法結構。函數式編程是一種編程範式 (而面向過程編程和麪向對象編程也都是編程範式)。在面向過程編程中,咱們見到過函數(function);在面向對象編程中,咱們見過對象(object)。函數和對象的根本目的是以某種邏輯方式組織代碼,並提升代碼的可重複使用性(reusability)。閉包也是一種組織代碼的結構,它一樣提升了代碼的可重複使用性。
不一樣的語言實現閉包的方式不一樣。Python以函數對象爲基礎,爲閉包這一語法結構提供支持的 (咱們在特殊方法與多範式中,已經屢次看到Python使用對象來實現一些特殊的語法)。Python一切皆對象,函數這一語法結構也是一個對象。在函數對象中,咱們像使用一個普通對象同樣使用函數對象,好比更改函數對象的名字,或者將函數對象做爲參數進行傳遞。
和其餘對象同樣,函數對象也有其存活的範圍,也就是函數對象的做用域。函數對象是使用def語句定義的,函數對象的做用域與def所在的層級相同。好比下面代碼,咱們在 line_conf 函數的隸屬範圍內定義的函數line,就只能在 line_conf 的隸屬範圍內調用。
def line_conf():
def line(x):
return21 *x+
5# within the scope print(line())
line_conf()
5# out of the scopeprint(line())
line函數定義了一條直線(y = 2x + 1)。能夠看到,在 line_conf() 中能夠調用line函數,而在做用域以外調用line將會有下面的錯誤:
'line'isnotNameError: namedefined
說明這時已經在做用域以外。
一樣,若是使用lambda定義函數,那麼函數對象的做用域與lambda所在的層級相同。
函數是一個對象,因此能夠做爲某個函數的返回結果。
def line_conf():
def line(x):
return21 *x+
return# return a function object line
my_line = line_conf()
5print(my_line())
上面的代碼能夠成功運行。line_conf 的返回結果被賦給line對象。上面的代碼將打印11。
若是line()的定義中引用了外部的變量,會發生什麼呢?
def line_conf():
15 b =
def line(x):
return2 *x+b
return# return a function object line
5b =
my_line = line_conf()
5print(my_line())
咱們能夠看到,line定義的隸屬程序塊中引用了高層級的變量b,但b信息存在於line的定義以外 (b的定義並不在line的隸屬程序塊中)。咱們稱b爲line的環境變量。事實上,line做爲line_conf 的返回值時,line中已經包括b的取值(儘管b並不隸屬於line)。
上面的代碼將打印25,也就是說,line所參照的b值是函數對象定義時可供參考的b值,而不是使用時的b值。
一個函數和它的環境變量合在一塊兒,就構成了一個閉包(closure)。在Python中,所謂的閉包是一個包含有環境變量取值的函數對象。環境變量取值被保存在函數對象的__closure__屬性中。好比下面的代碼:
def line_conf():
15 b =
def line(x):
return2 *x+b
return# return a function object line
5b =
my_line = line_conf()
print(my_line.__closure__)
0print(my_line.__closure__[].cell_contents)
__closure__裏包含了一個元組(tuple)。這個元組中的每一個元素是cell類型的對象。咱們看到第一個cell包含的就是整數15,也就是咱們建立閉包時的環境變量b的取值。
下面看一個閉包的實際例子:
def line_conf(a, b):
def line(x):
return a*x + b
return line
11line1 = line_conf(,)
45line2 = line_conf(,)
55print(line1(), line2())
這個例子中,函數line與環境變量a,b構成閉包。在建立閉包的時候,咱們經過line_conf 的參數a,b說明了這兩個環境變量的取值,這樣,咱們就肯定了函數的最終形式(y = x + 1和y = 4x + 5)。咱們只須要變換參數a,b,就能夠得到不一樣的直線表達函數。由此,咱們能夠看到,閉包也具備提升代碼可複用性的做用。
若是沒有閉包,咱們須要每次建立直線函數的時候同時說明a,b,x。這樣,咱們就須要更多的參數傳遞,也減小了代碼的可移植性。利用閉包,咱們實際上建立了泛函。line函數定義一種普遍意義的函數。這個函數的一些方面已經肯定(必須是直線),但另外一些方面(好比a和b參數待定)。隨後,咱們根據line_conf傳遞來的參數,經過閉包的形式,將最終函數肯定下來。
dh既然這樣,那麼閉包的應用知足其形式便可,平常的不少函數均可以借用此
閉包有效的減小了函數所需定義的參數數目。這對於並行運算來講有重要的意義。在並行運算的環境下,咱們可讓每臺電腦負責一個函數,而後將一臺電腦的輸出和下一臺電腦的輸入串聯起來。最終,咱們像流水線同樣工做,從串聯的電腦集羣一端輸入數據,從另外一端輸出數據。這樣的情境最適合只有一個參數輸入的函數。閉包就能夠實現這一目的。
並行運算正稱爲一個熱點。這也是函數式編程又熱起來的一個重要緣由。函數式編程早在1950年代就已經存在,但應用並不普遍。然而,咱們上面描述的流水線式的工做並行集羣過程,正適合函數式編程。因爲函數式編程這一自然優點,愈來愈多的語言也開始加入對函數式編程範式的支持。
裝飾器(decorator)是一種高級Python語法。裝飾器能夠對一個函數、方法或者類進行加工。在Python中,咱們有多種方法對函數和類進行加工,好比在Python閉包中,咱們見到函數對象做爲某一個函數的返回結果。相對於其它方式,裝飾器語法簡單,代碼可讀性高。所以,裝飾器在Python項目中有普遍的應用。
裝飾器最先在Python 2.5中出現,它最初被用於加工函數和方法這樣的可調用對象(callable object,這樣的對象定義有__call__方法)。在Python 2.6以及以後的Python版本中,裝飾器被進一步用於加工類。
咱們先定義兩個簡單的數學函數,一個用來計算平方和,一個用來計算平方差:
# get square sum
def square_sum(a, b):
return22 a**+ b**
# get square diff
def square_diff(a, b):
return22 a**- b**
34print(square_sum(,))
34print(square_diff(,))
在擁有了基本的數學功能以後,咱們可能想爲函數增長其它的功能,好比打印輸入。咱們能夠改寫函數來實現這一點:
# modify: print input
# get square sum
def square_sum(a, b):
"intput:" print(, a, b)
return22 a**+ b**
# get square diff
def square_diff(a, b):
"input" print(, a, b)
return22 a**- b**
34print(square_sum(,))
34print(square_diff(,))
咱們修改了函數的定義,爲函數增長了功能。
如今,咱們使用裝飾器來實現上述修改:
def decorator(F):
def new_F(a, b):
"input" print(, a, b)
return F(a, b)
return new_F
# get square sum
@decorator
def square_sum(a, b):
return22 a**+ b**
# get square diff
@decorator
def square_diff(a, b):
return22 a**- b**
34print(square_sum(,))
34print(square_diff(,))
裝飾器能夠用def的形式定義,如上面代碼中的decorator。裝飾器接收一個可調用對象做爲輸入參數,並返回一個新的可調用對象。裝飾器新建了一個可調用對象,也就是上面的new_F。new_F中,咱們增長了打印的功能,並經過調用F(a, b)來實現原有函數的功能。
dh這裏decorator用別的來代替也是可行的
定義好裝飾器後,咱們就能夠經過@語法使用了。在函數square_sum和square_diff定義以前調用@decorator,咱們實際上將square_sum或square_diff傳遞給decorator,並將decorator返回的新的可調用對象賦給原來的函數名(square_sum或square_diff)。 因此,當咱們調用square_sum(3, 4)的時候,就至關於:
square_sum = decorator(square_sum)
square_sum(3, 4)
咱們知道,Python中的變量名和對象是分離的。變量名能夠指向任意一個對象。從本質上,裝飾器起到的就是這樣一個從新指向變量名的做用(name binding),讓同一個變量名指向一個新返回的可調用對象,從而達到修改可調用對象的目的。
與加工函數相似,咱們能夠使用裝飾器加工類的方法。
若是咱們有其餘的相似函數,咱們能夠繼續調用decorator來修飾函數,而不用重複修改函數或者增長新的封裝。這樣,咱們就提升了程序的可重複利用性,並增長了程序的可讀性。
在上面的裝飾器調用中,好比@decorator,該裝飾器默認它後面的函數是惟一的參數。裝飾器的語法容許咱們調用decorator時,提供其它參數,好比@decorator(a)。這樣,就爲裝飾器的編寫和使用提供了更大的靈活性。
# a new wrapper layer
def pre_str(pre=''):
# old decorator
def decorator(F):
def new_F(a, b):
"input" print(pre +, a, b)
return F(a, b)
return new_F
return decorator
# get square sum
@pre_str('^_^')
def square_sum(a, b):
return22 a**+ b**
# get square diff
@pre_str('T_T')
def square_diff(a, b):
return22 a**- b**
34print(square_sum(,))
34print(square_diff(,))
上面的pre_str是容許參數的裝飾器。它其實是對原有裝飾器的一個函數封裝,並返回一個裝飾器。咱們能夠將它理解爲一個含有環境參量的閉包。當咱們使用@pre_str('^_^')調用的時候,Python可以發現這一層的封裝,並把參數傳遞到裝飾器的環境中。該調用至關於:
square_sum'^_^'= pre_str() (square_sum)
在上面的例子中,裝飾器接收一個函數,並返回一個函數,從而起到加工函數的效果。在Python 2.6之後,裝飾器被拓展到類。一個裝飾器能夠接收一個類,並返回一個類,從而起到加工類的效果。
def decorator(aClass):
class newClass:
def __init__(self, age):
0 self.total_display =
self.wrapped = aClass(age)
def display(self):
1 self.total_display +=
"total display" print(, self.total_display)
self.wrapped.display()
return newClass
@decorator
class Bird:
def __init__(self, age):
self.age = age
def display(self):
"My age is" print(,self.age)
5eagleLord = Bird()
forin3irange():
eagleLord.display()
在decorator中,咱們返回了一個新類newClass。在新類中,咱們記錄了原來類生成的對象(self.wrapped),並附加了新的屬性total_display,用於記錄調用display的次數。咱們也同時更改了display方法。
經過修改,咱們的Bird類能夠顯示調用display的次數了。
裝飾器的核心做用是name binding。這種語法是Python多編程範式的又一個體現。大部分Python用戶都不怎麼須要定義裝飾器,但有可能會使用裝飾器。鑑於裝飾器在Python項目中的普遍使用,瞭解這一語法是很是有益的。
語言的內存管理是語言設計的一個重要方面。它是決定語言性能的重要因素。不管是C語言的手工管理,仍是Java的垃圾回收,都成爲語言最重要的特徵。這裏以Python語言爲例子,說明一門動態類型的、面向對象的語言的內存管理方式。
賦值語句是語言最多見的功能了。但即便是最簡單的賦值語句,也能夠頗有內涵。Python的賦值語句就很值得研究。
a1=
整數1爲一個對象。而a是一個引用。利用賦值語句,引用a指向對象1。Python是動態類型的語言(參考動態類型),對象與引用分離。Python像使用「筷子」那樣,經過引用來接觸和翻動真正的食物——對象。
引用和對象:
爲了探索對象在內存的存儲,咱們能夠求助於Python的內置函數id()。它用於返回對象的身份(identity)。其實,這裏所謂的身份,就是該對象的內存地址。
1a =
print(id(a))
printhex((id(a)))
在個人計算機上,它們返回的是:
11246696
'0xab9c68'
分別爲內存地址的十進制和十六進制表示。
在Python中,整數和短小的字符,Python都會緩存這些對象,以便重複使用。當咱們建立多個等於1的引用時,其實是讓全部這些引用指向同一個對象。
a = 1
b = 1
print(id(a))
print(id(b))
上面程序返回:
11246696
11246696
可見a和b其實是指向同一個對象的兩個引用。
爲了檢驗兩個引用指向同一個對象,咱們能夠用is關鍵字。is用於判斷兩個引用所指的對象是否相同。
# True
1a =
1b =
printis(ab)
# True
"good"a =
"good"b =
printis(ab)
# False
"very good morning"a =
"very good morning"b =
printis(ab)
# False
a = []
b = []
printis(ab)
上面的註釋爲相應的運行結果。能夠看到,因爲Python緩存了整數和短字符串,所以每一個對象只存有一份。好比,全部整數1的引用都指向同一對象。即便使用賦值語句,也只是創造了新的引用,而不是對象自己。長的字符串和其它對象能夠有多個相同的對象,能夠使用賦值語句建立出新的對象。
在Python中,每一個對象都有存有指向該對象的引用總數,即引用計數(reference count)。
咱們能夠使用sys包中的getrefcount(),來查看某個對象的引用計數。須要注意的是,當使用某個引用做爲參數,傳遞給getrefcount()時,參數實際上建立了一個臨時的引用。所以,getrefcount()所獲得的結果,會比指望的多1。
fromimportsysgetrefcount
123a = [,,]
print(getrefcount(a))
b = a
print(getrefcount(b))
因爲上述緣由,兩個getrefcount將返回2和3,而不是指望的1和2。
Python的一個容器對象(container),好比表、詞典等,能夠包含多個對象。實際上,容器對象中包含的並非元素對象自己,是指向各個元素對象的引用。
咱們也能夠自定義一個對象,並引用其它對象:
class from_obj(object):
def __init__(self, to_obj):
self.to_obj = to_obj
123b = [,,]
a = from_obj(b)
print(id(a.to_obj))
print(id(b))
能夠看到,a引用了對象b。
對象引用對象,是Python最基本的構成方式。即便是a = 1這一賦值方式,其實是讓詞典的一個鍵值"a"的元素引用整數對象1。該詞典對象用於記錄全部的全局引用。該詞典引用了整數對象1。咱們能夠經過內置函數globals()來查看該詞典。
當一個對象A被另外一個對象B引用時,A的引用計數將增長1。
fromimportsysgetrefcount
123a = [,,]
print(getrefcount(a))
b = [a, a]
print(getrefcount(a))
因爲對象b引用了兩次a,a的引用計數增長了2。
容器對象的引用可能構成很複雜的拓撲結構。咱們能夠用objgraph包來繪製其引用關係,好比:
x [1, 2, 3]=
y [x, dict(key1=x)]=
z [y, (x, y)]=
importobjgraph
objgraph.show_refs([z], filename='ref_topo.png')
objgraph是Python的一個第三方包。安裝以前須要安裝xdot。
installsudo apt-getxdot
installhttpsudo pip-i://mirrors.aliyuncs.com/pypi/simple objgraph
兩個對象可能相互引用,從而構成所謂的引用環(reference cycle)。
a []=
b [a]=
a.append(b)
即便是一個對象,只須要本身引用本身,也能構成引用環。
a []=
a.append(a)
print(getrefcount(a))
引用環會給垃圾回收機制帶來很大的麻煩,我將在後面詳細敘述這一點。
某個對象的引用計數可能減小。好比,能夠使用del關鍵字刪除某個引用:
fromimportsysgetrefcount
123a = [,,]
b = a
print(getrefcount(b))
dela
print(getrefcount(b))
del也能夠用於刪除容器元素中的元素,好比:
123a = [,,]
del0a[]
print(a)
若是某個引用指向對象A,當這個引用被從新定向到某個其餘對象B時,對象A的引用計數減小:
fromimportsysgetrefcount
123a = [,,]
b = a
print(getrefcount(b))
1a =
print(getrefcount(b))
吃太多,總會變胖,Python也是這樣。當Python中的對象愈來愈多,它們將佔據愈來愈大的內存。不過你不用太擔憂Python的體形,它會乖巧的在適當的時候「減肥」,啓動垃圾回收(garbage collection),將沒用的對象清除。在許多語言中都有垃圾回收機制,好比Java和Ruby。儘管最終目的都是塑造苗條的提醒,但不一樣語言的減肥方案有很大的差別 (這一點能夠對比本文和Java內存管理與垃圾回收)。
從基本原理上,當Python的某個對象的引用計數降爲0時,說明沒有任何引用指向該對象,該對象就成爲要被回收的垃圾了。好比某個新建對象,它被分配給某個引用,對象的引用計數變爲1。若是引用被刪除,對象的引用計數爲0,那麼該對象就能夠被垃圾回收。好比下面的表:
a [1, 2, 3]=
dela
del a後,已經沒有任何引用指向以前創建的[1, 2, 3]這個表。用戶不可能經過任何方式接觸或者動用這個對象。這個對象若是繼續待在內存裏,就成了不健康的脂肪。當垃圾回收啓動時,Python掃描到這個引用計數爲0的對象,就將它所佔據的內存清空。
然而,減肥是個昂貴而費力的事情。垃圾回收時,Python不能進行其它的任務。頻繁的垃圾回收將大大下降Python的工做效率。若是內存中的對象很少,就沒有必要總啓動垃圾回收。因此,Python只會在特定條件下,自動啓動垃圾回收。當Python運行時,會記錄其中分配對象(object allocation)和取消分配對象(object deallocation)的次數。當二者的差值高於某個閾值時,垃圾回收纔會啓動。
咱們能夠經過gc模塊的get_threshold()方法,查看該閾值:
importgc
print(gc.get_threshold())
返回(700, 10, 10),後面的兩個10是與分代回收相關的閾值,後面能夠看到。700便是垃圾回收啓動的閾值。能夠經過gc中的set_threshold()方法從新設置。
咱們也能夠手動啓動垃圾回收,即便用gc.collect()。
Python同時採用了分代(generation)回收的策略。這一策略的基本假設是,存活時間越久的對象,越不可能在後面的程序中變成垃圾。咱們的程序每每會產生大量的對象,許多對象很快產生和消失,但也有一些對象長期被使用。出於信任和效率,對於這樣一些「長壽」對象,咱們相信它們的用處,因此減小在垃圾回收中掃描它們的頻率。
小傢伙要多檢查:
Python將全部的對象分爲0,1,2三代。全部的新建對象都是0代對象。當某一代對象經歷過垃圾回收,依然存活,那麼它就被納入下一代對象。垃圾回收啓動時,必定會掃描全部的0代對象。若是0代通過必定次數垃圾回收,那麼就啓動對0代和1代的掃描清理。當1代也經歷了必定次數的垃圾回收後,那麼會啓動對0,1,2,即對全部對象進行掃描。
這兩個次數即上面get_threshold()返回的(700, 10, 10)返回的兩個10。也就是說,每10次0代垃圾回收,會配合1次1代的垃圾回收;而每10次1代的垃圾回收,纔會有1次的2代垃圾回收。
一樣能夠用set_threshold()來調整,好比對2代對象進行更頻繁的掃描。
importgc
gc.set_threshold(700, 10, 5)
引用環的存在會給上面的垃圾回收機制帶來很大的困難。這些引用環可能構成沒法使用,但引用計數不爲0的一些對象。
a []=
b [a]=
a.append(b)
dela
delb
上面咱們先建立了兩個表對象,並引用對方,構成一個引用環。刪除了a,b引用以後,這兩個對象不可能再從程序中調用,就沒有什麼用處了。可是因爲引用環的存在,這兩個對象的引用計數都沒有降到0,不會被垃圾回收。
孤立的引用環:
爲了回收這樣的引用環,Python複製每一個對象的引用計數,能夠記爲gc_ref。假設,每一個對象i,該計數爲gc_ref_i。Python會遍歷全部的對象i。對於每一個對象i引用的對象j,將相應的gc_ref_j減1。
遍歷後的結果:
在結束遍歷後,gc_ref不爲0的對象,和這些對象引用的對象,以及繼續更下游引用的對象,須要被保留。而其它的對象則被垃圾回收。
參考答案:
def xiangfan(x):
def kaifang(x):
def juedui(x):
return abs(x)
return0.5 juedui(x)**
return -kaifang(x)
print-4xiangfan()
參考答案:
def juedui(x):
return abs(x)
def kaifang(F):
def new_F(x):
return0.5 F(x)**
return new_F
def xiangfan(F):
def new_F(x):
return -F(x)
return new_F
func=xiangfan(kaifang(juedui))
print-4func()
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
在快速教程中,咱們瞭解了最基本的序列(sequence)。回憶一下,序列包含有定值表(tuple)和表(list)。此外,字符串(string)是一種特殊的定值表。表的元素能夠更改,定值表一旦創建,其元素不可更改。
任何的序列均可以引用其中的元素(item)。
下面的內建函數(built-in function)可用於序列(表,定值表,字符串):
# s爲一個序列
len(s) 返回:序列中包含元素的個數
min(s) 返回:序列中最小的元素
max(s) 返回:序列中最大的元素
TrueTrueall(s) 返回:,若是全部元素都爲的話
TrueTrueany(s) 返回:,若是任一元素爲的話
下面的方法主要起查詢功能,不改變序列自己, 可用於表和定值表:
sums() 返回:序列中全部元素的和
xi#爲元素值,爲下標(元素在序列中的位置)
s.countxxs() 返回:在中出現的次數
s.indexxxs() 返回:在中第一次出現的下標
因爲定值表的元素不可變動,下面方法只適用於表:
ll2#爲一個表,爲另外一個表
l.extendl2ll2() 在表的末尾添加表的全部元素
l.appendxlx() 在的末尾附加元素
l.sortl() 對中的元素排序
l.reversel() 將中的元素逆序
l.popll() 返回:表的最後一個元素,並在表中刪除該元素
dell[i] 刪除該元素
(以上這些方法都是在原來的表的上進行操做,會對原來的表產生影響,而不是返回一個新表。)
下面是一些用於字符串的方法。儘管字符串是定值表的特殊的一種,但字符串(string)類有一些方法是改變字符串的。這些方法的本質不是對原有字符串進行操做,而是刪除原有字符串,再創建一個新的字符串,因此並不與定值表的特色相矛盾。
#str爲一個字符串,sub爲str的一個子字符串。s爲一個序列,它的元素都是字符串。width爲一個整數,用於說明新生成字符串的寬度。
str.count(sub) 返回:sub在str中出現的次數
-1str.find(sub) 返回:從左開始,查找sub在str中第一次出現的位置。若是str中不包含sub,返回
str.index(sub) 返回:從左開始,查找sub在str中第一次出現的位置。若是str中不包含sub,舉出錯誤
-1str.rfind(sub) 返回:從右開始,查找sub在str中第一次出現的位置。若是str中不包含sub,返回
str.rindex(sub) 返回:從右開始,查找sub在str中第一次出現的位置。若是str中不包含sub,舉出錯誤
Truestr.isalnum() 返回:,若是全部的字符都是字母或數字
Truestr.isalpha() 返回:,若是全部的字符都是字母
Truestr.isdigit() 返回:,若是全部的字符都是數字
Truestr.istitle() 返回:,若是全部的詞的首字母都是大寫
Truestr.isspace() 返回:,若是全部的字符都是空格
Truestr.islower() 返回:,若是全部的字符都是小寫字母
Truestr.isupper() 返回:,若是全部的字符都是大寫字母
','str.split([sep, [max]]) 返回:從左開始,以空格爲分割符(separator),將str分割爲多個子字符串,總共分割max次。將所得的子字符串放在一個表中返回。能夠str.split()的方式使用逗號或者其它分割符
','str.rsplit([sep, [max]]) 返回:從右開始,以空格爲分割符(separator),將str分割爲多個子字符串,總共分割max次。將所得的子字符串放在一個表中返回。能夠str.rsplit()的方式使用逗號或者其它分割符
str.join(s) 返回:將s中的元素,以str爲分割符,合併成爲一個字符串。
str.strip([sub]) 返回:去掉字符串開頭和結尾的空格。也能夠提供參數sub,去掉位於字符串開頭和結尾的sub
str.replace(sub, new_sub) 返回:用一個新的字符串new_sub替換str中的sub
str.capitalize() 返回:將str第一個字母大寫
str.lower() 返回:將str所有字母改成小寫
str.upper() 返回:將str所有字母改成大寫
str.swapcase() 返回:將str大寫字母改成小寫,小寫改成大寫
str.title() 返回:將str的每一個詞(以空格分隔)的首字母大寫
str.center(width) 返回:長度爲width的字符串,將原字符串放入該字符串中心,其它空餘位置爲空格。
str.ljust(width) 返回:長度爲width的字符串,將原字符串左對齊放入該字符串,其它空餘位置爲空格。
str.rjust(width) 返回:長度爲width的字符串,將原字符串右對齊放入該字符串,其它空餘位置爲空格。
在這裏列舉一些我使用Python時積累的小技巧。這些技巧是我在使用Python過程當中常用的。以前很零碎的記在筆記本中,如今整理出來,和你們分享,也做爲Python快速教程的一個補充。
在Python常用import聲明,以使用其餘模塊(也就是其它.py文件)中定義的對象。
當咱們編寫Python庫模塊的時候,咱們每每運行一些測試語句。當這個程序做爲庫被import的時候,咱們並不須要運行這些測試語句。一種解決方法是在import以前,將模塊中的測試語句註釋掉。Python有一種更優美的解決方法,就是使用__name__。
下面是一個簡單的庫程序TestLib.py。當直接運行TestLib.py時,__name__爲"__main__"。若是被import的話,__name__爲"TestLib"。
def lib_func(a):
return10 a +
def lib_func_another(b):
return20 b +
if'__main__'__name__ ==:
101 test =
print(lib_func(test))
print'__name__: '#注意觀察,__name__
咱們在user.py中import上面的TestLib:
importTestLib
120print(TestLib.lib_func())
你能夠嘗試不在TestLib.py中使用if __name__=='__main__', 並對比運行結果。
好比:
importasTestLibt
120print(t.lib_func())
這樣的好處是減少所引用模塊的內存佔用。
好比:
fromimportTestLiblib_func
120print(lib_func())
好比:
fromimportTestLib*
120print(lib_func())
當咱們想要知道某個函數會接收哪些參數的時候,能夠使用下面方法查詢。
importinspect
print(inspect.getargspec(func))
除了使用dir()來查詢對象的屬性以外,咱們能夠使用下面內置(built-in)函數來確認一個對象是否具備某個屬性:
# attr_name是一個字符串hasattr(obj, attr_name)
例如:
a = [1,2,3]
print'append'(hasattr(a,))
123a = [,,]
print__class__a.
print__class__a..__name__
咱們能夠用 __base__ 屬性來查詢某個類的父類:
cls.__base__
例如:
printlist(.__base__)
在Python程序的第一行加入#coding=utf8,例如:
#coding=utf8
print"你好嗎?"()
也能用如下方式:
#-*- coding: UTF-8 -*-
print"你好嗎?"()
在2.6以上版本,以以下方式表示:
print# 二進制,以0b開頭(0b1110)
print# 八進制,以0o開頭(0o10)
print# 十六進制,以0x開頭(0x2A)
若是是更早版本,能夠用以下方式:
printint"1110"2((,))
printint"10"8((,))
printint"2A"16((,))
一行內的註釋能夠以#開始。
多行的註釋能夠以'''開始,以'''結束,好比:
'''
This is demo
'''
def func():
# print something
"Hello world!"# use print() function print()
# main
func()
註釋應該和所在的程序塊對齊。
當咱們import的時候,Python會在搜索路徑中查找模塊(module)。好比上面import TestLib,就要求TestLib.py在搜索路徑中。
咱們能夠經過下面方法來查看搜索路徑:
importsys
print(sys.path)
咱們能夠在Python運行的時候增長或者刪除sys.path中的元素。另外一方面,咱們能夠經過在shell中增長PYTHONPATH環境變量,來爲Python增長搜索路徑。
下面咱們增長/home/vamei/mylib到搜索路徑中:
:/home/vamei/mylib$export PYTHONPATH=$PYTHONPATH
你能夠將正面的這行命令加入到~/.bashrc中。這樣,咱們就長期的改變了搜索路徑。
能夠使用下面方法運行一個Python腳本,在腳本運行結束後,直接進入Python命令行。這樣作的好處是腳本的對象不會被清空,能夠經過命令行直接調用。dh直接進入命令行,原來腳本的內容還在
$python-i script.py
Python的標準庫隨着Python一塊兒安裝。當咱們須要非標準包時,就要先安裝。
這是安裝Python附加包的一個好的起點。你能夠在Linux repository中查找可能存在的Python包 (好比在Ubuntu Software Center中搜索matplot)。
好比使用以下方法來安裝、卸載或者升級web.py:
installhttp$pip-i://mirrors.aliyuncs.com/pypi/simple web.py
uninstall$pipweb.py
installhttp--upgrade web.py$pip-i://mirrors.aliyuncs.com/pypi/simple
若是你的Python安裝在一個非標準的路徑(使用$which python來確認python可執行文件的路徑)中,好比/home/vamei/util/python/bin中,你能夠使用下面方法設置pip的安裝包的路徑:
installhttp--install-option="--prefix=/home/vamei/util/" web.py$pip-i://mirrors.aliyuncs.com/pypi/simple
若是上面方法都無法找到你想要的庫,你可能須要從源碼開始編譯。Google每每是最好的起點。
Python內置(built-in)函數隨着python解釋器的運行而建立。在Python的程序中,你能夠隨時調用這些函數,不須要定義。最多見的內置函數是:
print"Hello World!"()
在Python教程中,咱們已經提到下面一些內置函數:
type() dir() help() len() len() open() range() enumerate() zip() iter() map() filter() reduce()
下面我採起的都是實際的參數,你能夠直接在命令行嘗試效果。
abs5# 取絕對值,也就是5(-)
2.6# 四捨五入取整,也就是3.0round()
23# 至關於2**3,若是是pow(2, 3, 5),至關於2**3 % 5pow(,)
2.33.2# 比較兩個數的大小cmp(,)
92# 返回除法結果和餘數divmod(,)
1529# 求最大值max([,,,])
9242# 求最小值min([,,-,])
21912# 求和sum([,-,,])
dh>>> bin(0)
'0b0'
>>> bin(2)
'0b10'
>>> bin(002)
'0b10'
>>> bin(200)
'0b11001000'
>>> bin(20000)
'0b100111000100000'
>>> hex(20)
'0x14'
>>> hex(15)
'0xf'
>>> hex(16)
'0x10'
>>> oct(8)
'010'
>>> oct(18)
'022'
int"5"# 轉換爲整數 integer()
2# 轉換爲浮點數 floatfloat()
"23"# 轉換爲長整數 long integerlong()
2.3# 轉換爲字符串 stringstr()
39# 返回複數 3 + 9icomplex(,)
ord"A"# "A"字符對應的數值()
chr65# 數值65對應的字符()
65# 數值65對應的unicode字符unichr()
0# 轉換爲相應的真假值,在Python中,0至關於False .在Python中,下列對象都至關於False:** [], (), {}, 0, None, 0.0, '' **bool()
56# 返回一個字符串,表示56的二進制數bin()
hex56# 返回一個字符串,表示56的十六進制數()
oct56# 返回一個字符串,表示56的八進制數()
123# 轉換爲表 listlist((,,))
234# 轉換爲定值表 tupletuple([,,])
521# 構建下標對象 sliceslice(,,-)
1"hello"123# 構建詞典 dictionarydict(a=,b=,c=[,,])
True1"hello!"# 是否全部的元素都至關於True值all([,,])
""0FalseNone# 是否有任意一個元素至關於True值any([,,, [],])
153# 返回正序的序列,也就是[1,3,5]sorted([,,])
153# 返回反序的序列,也就是[3,5,1]reversed([,,])
# define class
class Me(object):
def test(self):
print"Hello!"
def new_test():
print"New Hello!"
me = Me()
"test"# 檢查me對象是否有test屬性hasattr(me,)
"test"# 返回test屬性getattr(me,)
"test"test# 將test屬性設置爲new_testsetattr(me,, new_)
"test"# 刪除test屬性delattr(me,)
# me對象是否爲Me類生成的對象 (一個instance)isinstance(me, Me)
# Me類是否爲object類的子類issubclass(Me, object)
# 返回對象的字符串表達repr(me)
"print('Hello')"'test.py''exec'# 編譯字符串成爲code對象compile(,,)
eval"1 + 1"# 解釋字符串表達式。參數也能夠是compile()返回的code對象()
exec"print('Hello')"# 解釋並執行字符串,print('Hello')。參數也能夠是compile()返回的code對象()
"Please input:"# 等待輸入input()
# 返回全局命名空間,好比全局變量名,全局函數名globals()
# 返回局部命名空間locals()
在許多編程語言中都包含有格式化字符串的功能,好比C和Fortran語言中的格式化輸入輸出。Python中內置有對字符串進行格式化的操做%。
格式化字符串時,Python使用一個字符串做爲模板。模板中有格式符,這些格式符爲真實值預留位置,並說明真實數值應該呈現的格式。Python用一個tuple將多個值傳遞給模板,每一個值對應一個格式符。
好比下面的例子:
print"I'm %s. I'm %d year old"'Vamei'99(% (,))
上面的例子中,"I'm %s. I'm %d year old" 爲咱們的模板。%s爲第一個格式符,表示一個字符串。%d爲第二個格式符,表示一個整數。('Vamei', 99)的兩個元素'Vamei'和99爲替換%s和%d的真實值。
在模板和tuple之間,有一個%號分隔,它表明了格式化操做。
整個"I'm %s. I'm %d year old" % ('Vamei', 99) 實際上構成一個字符串表達式。咱們能夠像一個正常的字符串那樣,將它賦值給某個變量。好比:
"I'm %s. I'm %d year old"'Vamei'99a =% (,)
print(a)
咱們還能夠用詞典來傳遞真實值。以下:
print"I'm %(name)s. I'm %(age)d year old"'name''Vamei''age'(% {:,:99})
能夠看到,咱們對兩個格式符進行了命名。命名使用()括起來。每一個命名對應詞典的一個key。
格式符爲真實值預留位置,並控制顯示的格式。格式符能夠包含有一個類型碼,用以控制顯示的類型,以下:
%s 字符串(採用str()的顯示)
%r 字符串(採用repr()的顯示)
%c 單個字符
%b 二進制整數
%d 十進制整數
%i 十進制整數
%o 八進制整數
%x 十六進制整數
%e 指數(基底寫爲e)
%E 指數(基底寫爲E)
%f 浮點數
%F 浮點數,與上相同
%g 指數(e)或浮點數(根據顯示長度)
%G 指數(E)或浮點數(根據顯示長度)
"%"%% 字符
能夠用以下的方式,對格式進行進一步的控制:
(name)flagswidth%[][][].[precision]typecode
(name)爲命名
flags能夠有+,-,' '或0。+表示右對齊。-表示左對齊。' '爲一個空格,表示在正數的左側填充一個空格,從而與負數對齊。0表示使用0填充。
width表示顯示寬度
precision表示小數點後精度
好比:
print"%+10x"10(%)
print"%04d"5(%)
print"%6.3f"2.3(%)
上面的width, precision爲兩個整數。咱們能夠利用*,來動態代入這兩個量。好比:
print"%.*f"(% (4, 1.2))
Python實際上用4來替換*。因此實際的模板爲"%.4f"。
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
Python有一套頗有用的標準庫(standard library)。標準庫會隨着Python解釋器,一塊兒安裝在你的電腦中的。它是Python的一個組成部分。這些標準庫是Python爲你準備好的利器,可讓編程事半功倍。
我將根據我我的的使用經驗中,挑選出標準庫比較經常使用的包(package)介紹。
我將從正則表達式開始講Python的標準庫。正則表達式是文字處理中經常使用的工具,並且不須要額外的系統知識或經驗。咱們會把系統相關的包放在後面講解。
正則表達式(regular expression)主要功能是從字符串(string)中經過特定的模式(pattern),搜索想要找到的內容。
以前,咱們簡介了字符串相關的處理函數。咱們能夠經過這些函數實現簡單的搜索功能,好比說從字符串「I love you」中搜索是否有「you」這一子字符串。但有些時候,咱們只是模糊地知道咱們想要找什麼,而不能具體說出我是在找「you」,好比說,我想找出字符串中包含的數字,這些數字能夠是0到9中的任何一個。這些模糊的目標能夠做爲信息寫入正則表達式,傳遞給Python,從而讓Python知道咱們想要找的是什麼。
在Python中使用正則表達式須要標準庫中的一個包re。
importre
'[0-9]''abcd4ef'm = re.search(,)
0print(m.group())
re.search()接收兩個參數,第一個'[0-9]'就是咱們所說的正則表達式,它告訴Python的是:「聽着,我從字符串想要找的是從0到9的一個數字字符」。
re.search()若是從第二個參數找到符合要求的子字符串,就返回一個對象m,你能夠經過m.group()的方法查看搜索到的結果。若是沒有找到符合要求的字符,re.search()會返回None。
若是你熟悉Linux或者Perl, 你應該已經熟悉正則表達式。當咱們打開Linux shell的時候,能夠用正則表達式去查找或着刪除咱們想要的文件,好比說:
0-90-9$rm book[][].txt
這就是要刪除相似於book02.txt的文件。book[0-9][0-9].txt所包含的信息是,以book開頭,後面跟兩個數字字符,以後跟有".txt"的文件名。若是不符合條件的文件名,好比說:
bo12.txt
book1.txt
book99.text
都不會被選中。
Perl中內建有正則表達式的功能,聽說是全部正則表達式系統中最強的,這也是Perl成爲系統管理員利器的一個緣由。
string# 搜索整個字符串,直到發現符合的子字符串。#dh這個時候尚未徹底匹配全部的,m = re.search(pattern,)
string# 從頭開始檢查字符串是否符合正則表達式。必須從字符串的第一個字符開始就相符。m = re.match(pattern,)
能夠從這兩個函數中選擇一個進行搜索。上面的例子中,咱們若是使用re.match()的話,則會獲得None,由於字符串的起始爲‘a’, 不符合'[0-9]'的要求。
對於返回的m, 咱們使用m.group()來調用結果。(咱們會在後面更詳細解釋m.group())
咱們還能夠在搜索以後將搜索到的子字符串進行替換:
str= re.sub(pattern, replacement, string)
# 在string中利用正則變換pattern進行搜索,對於搜索到的字符串,用另外一字符串replacement替換。返回替換後的字符串。
此外,經常使用的正則表達式函數還有:
split# 根據正則表達式分割字符串, 將分割後的全部子字符串放在一個表(list)中返回re.()
# 根據正則表達式搜索字符串,將全部符合的子字符串放在一個表(list)中返回re.findall()
(在熟悉了上面的函數後,能夠看一下re.compile(),以便於提升搜索效率。)
關鍵在於將信息寫成一個正則表達式。咱們先看正則表達式的經常使用語法:
. 任意的一個字符
a|b 字符a或字符b
[afg] a或者f或者g的一個字符
[0-4] 0-4範圍內的一個字符
-f-f[a] a範圍內的一個字符
[^m] 不是m的一個字符
\s 一個空格
\S 一個非空格
\d [0-9]
\D [^0-9]
\w [0-9a-zA-Z]
\W [^0-9a-zA-Z]
緊跟在單個字符以後,表示多個這樣相似的字符:
* 重複>=0次
+ 重複>=1次
? 重複0或者1次
1-31-31-3{m} 重複m次。好比說a{4}至關於aaaa,再好比說[]{2}至關於[][]
{m, n} 重複m到n次。好比說a{2, 5}表示a重複2到5次。小於m次的重複,或者大於n次的重複都不符合條件。
正則表達 |
相符的字符串舉例 |
[0-9]{3,5} |
9678 |
a?b |
b |
a+b |
aaaaab |
^ 字符串的起始位置
$ 字符串的結尾位置
正則表達 |
相符的字符串舉例 |
不相符字符串 |
^ab.*c$ |
abeec |
cabeec (若是用re.search(), 將沒法找到。) |
咱們有可能對搜索的結果進行進一步精簡信息。好比下面一個正則表達式:
output_(\d{4})
該正則表達式用括號()包圍了一個小的正則表達式,\d{4}。 這個小的正則表達式被用於從結果中篩選想要的信息(在這裏是四位數字)。這樣被括號圈起來的正則表達式的一部分,稱爲羣(group)。
咱們能夠m.group(number)的方法來查詢羣。group(0)是整個正則表達的搜索結果,group(1)是第一個羣……
importre
"output_(\d{4})""output_1986.txt"m = re.search(,)
1print(m.group())
咱們還能夠將羣命名,以便更好地使用m.group查詢:
importre
"output_(?P<year>\d{4})""output_1986.txt"#(?P<name>...) 爲group命名m = re.search(,)
"year"print(m.group())
Python具備良好的時間和日期管理功能。實際上,計算機只會維護一個掛鐘時間(wall clock time),這個時間是從某個固定時間起點到如今的時間間隔。時間起點的選擇與計算機相關,但一臺計算機的話,這一時間起點是固定的。其它的日期信息都是從這一時間計算獲得的。此外,計算機還能夠測量CPU實際上運行的時間,也就是處理器時間(processor clock time),以測量計算機性能。當CPU處於閒置狀態時,處理器時間會暫停。
time包基於C語言的庫函數(library functions)。Python的解釋器一般是用C編寫的,Python的一些函數也會直接調用C語言的庫函數。
timeimport
print# wall clock time, unit: second(time.time())
print# processor clock time, unit: second(time.clock())
time.sleep()能夠將程序置於休眠狀態,直到某時間間隔以後再喚醒程序,讓程序繼續運行。
timeimport
print'start'()
10# sleep for 10 secondstime.sleep()
print'wake up'()
當咱們須要定時地查看程序運行狀態時,就能夠利用該方法。
time包還定義了struct_time對象。該對象其實是將掛鐘時間轉換爲年、月、日、時、分、秒……等日期信息,存儲在該對象的各個屬性中(tm_year, tm_mon, tm_mday...)。下面方法能夠將掛鐘時間轉換爲struct_time對象:
st= time.gmtime() #返回struct_time格式的UTC時間
st= time.localtime() #返回struct_time格式的當地時間,當地時區根據系統環境決定。
s = time.mktime(st) #將struct_time格式轉換成wall clock time
datetime包是基於time包的一個高級包, 爲咱們提供了多一層的便利。
datetime能夠理解爲date和time兩個組成部分。date是指年月日構成的日期(至關於日曆),time是指時分秒微秒構成的一天24小時中的具體時間(至關於手錶)。你能夠將這兩個分開管理(datetime.date類,datetime.time類),也能夠將二者合在一塊兒(datetime.datetime類)。因爲其構造大同小異,咱們將只介紹datetime.datetime類。
好比說我如今看到的時間,是2012年9月3日21時30分,咱們能夠用以下方式表達:
importdatetime
2012932130t = datetime.datetime(,,,,)
print(t)
所返回的t有以下屬性:
hour, minute, second, microsecond
# weekday表示周幾year, month, day, weekday
datetime包還定義了時間間隔對象(timedelta)。一個時間點(datetime)加上一個時間間隔(timedelta)能夠獲得一個新的時間點(datetime)。好比今天的上午3點加上5個小時獲得今天的上午8點。同理,兩個時間點相減會獲得一個時間間隔。
import datetime
t = datetime.datetime(2012,9,3,21,30)
t_next = datetime.datetime(2012,9,5,23,30)
delta1 = datetime.timedelta(seconds = 600)
delta2 = datetime.timedelta(weeks = 3)
print(t + delta1)
print(t + delta2)
print(t_next - t)
在給datetime.timedelta傳遞參數(如上的seconds和weeks)的時候,還能夠是days, hours, milliseconds, microseconds。
兩個datetime對象還能夠進行比較。好比使用上面的t和t_next:
print(t > t_next)
假如咱們有一個的字符串,咱們如何將它轉換成爲datetime對象呢?
一個方法是用上一講的正則表達式來搜索字符串。但時間信息實際上有很明顯的特徵,咱們能夠用格式化讀取的方式讀取時間信息。
from datetime import datetime
format"output-%Y-%m-%d-%H%M%S.txt"=
"output-1997-12-23-030000.txt"str =
formatt = datetime.strptime(str,)
咱們經過format來告知Python咱們的str字符串中包含的日期的格式。在format中,%Y表示年所出現的位置, %m表示月份所出現的位置……。
反過來,咱們也能夠調用datetime對象的strftime()方法,來將datetime對象轉換爲特定格式的字符串。好比上面所定義的t_next,
printformat(t_next.strftime())
具體的格式寫法可參閱官方文檔。 若是是Linux系統,也可查閱date命令的手冊($man date),二者相通。
os.path包主要是處理路徑字符串,好比說'/home/vamei/doc/file.txt',提取出有用信息。
import os.path
'/home/vamei/doc/file.txt'path =
print# 查詢路徑中包含的文件名(os.path.basename(path))
print# 查詢路徑中包含的目錄(os.path.dirname(path))
split# 將路徑分割成文件名和目錄兩個部分,放在一個表中返回info = os.path.(path)
'/''home''vamei''doc''file1.txt'# 使用目錄名和文件名構成一個路徑字符串path2 = os.path.join(,,,,)
p_list = [path, path2]
print# 查詢多個路徑的共同部分(os.path.commonprefix(p_list))
此外,還有下面的方法:
# 去除路徑path中的冗餘。好比'/home/vamei/../.'被轉化爲'/home'os.path.normpath(path)
os.path還能夠查詢文件的相關信息(metadata)。文件的相關信息不存儲在文件內部,而是由操做系統維護的,關於文件的一些信息(好比文件類型,大小,修改時間)。
importos.path
'/home/vamei/doc/file.txt'path =
# 查詢文件是否存在print(os.path.exists(path))
# 查詢文件大小print(os.path.getsize(path))
# 查詢文件上一次讀取的時間print(os.path.getatime(path))
# 查詢文件上一次修改的時間print(os.path.getmtime(path))
# 路徑是否指向常規文件print(os.path.isfile(path))
# 路徑是否指向目錄文件print(os.path.isdir(path))
(實際上,這一部份相似於Linux中的ls命令的某些功能)
glob包最經常使用的方法只有一個, glob.glob()。該方法的功能與Linux中的ls類似,接受一個Linux式的文件名格式表達式(filename pattern expression),列出全部符合該表達式的文件(與正則表達式相似),將全部文件名放在一個表中返回。因此glob.glob()是一個查詢目錄下文件的好方法。
該文件名錶達式的語法與Python自身的正則表達式不一樣 (你能夠同時看一下fnmatch包,它的功能是檢測一個文件名是否符合Linux的文件名格式表達式)。 以下:
Filename Pattern Expression |
Python Regular Expression |
* |
.* |
? |
. |
[0-9] |
same |
[a-e] |
same |
[^mnp] |
same |
咱們能夠用該命令找出/home/vamei下的全部文件:
globimport
print'/home/vamei/*'(glob.glob())
在操做系統下,用戶能夠經過操做系統的命令來管理文件。Python標準庫則容許咱們從Python內部管理文件。相同的目的,咱們有了兩條途徑。儘管在Python調用標準庫的方式不如操做系統命令直接,但有它本身的優點。你能夠利用Python語言,併發揮其餘Python工具,造成組合的文件管理功能。Python or Shell? 這是留給用戶的選擇。本文中會盡可能將二者類似的功能相對應。
os包包括各類各樣的函數,以實現操做系統的許多功能。這個包很是龐雜。os包的一些命令就是用於文件管理。咱們這裏列出最經常使用的:
好比說咱們要新建目錄new:
importos
'/home/vamei/new'os.mkdir()
好比咱們想複製文件a.txt:
importshutil
'a.txt''b.txt'shutil.copy(,)
結合本章以及以前的內容,咱們把Python打形成一個文件管理的利器了。
在以前對Python對象的介紹中,我提到過Python「一切皆對象」的哲學,在Python中,不管是變量仍是函數,都是一個對象。當Python運行時,對象存儲在內存中,隨時等待系統的調用。然而,內存裏的數據會隨着計算機關機和消失,如何將對象保存到文件,並儲存在硬盤上呢?
計算機的內存中存儲的是二進制的序列 (固然,在Linux眼中,是文本流)。咱們能夠直接將某個對象所對應位置的數據抓取下來,轉換成文本流 (這個過程叫作serialize),而後將文本流存入到文件中。因爲Python在建立對象時,要參考對象的類定義,因此當咱們從文本中讀取對象時,必須在手邊要有該對象的類定義,才能懂得如何去重建這一對象。從文件讀取時,對於Python的內建(built-in)對象 (好比說整數、詞典、表等等),因爲其類定義已經載入內存,因此不須要咱們再在程序中定義類。但對於用戶自行定義的對象,就必需要先定義類,而後才能從文件中載入對象 (好比面向對象的基本概念中的對象那個summer)。
對於上述過程,最經常使用的工具是Python中的pickle包。
importpickle
# define class
class Bird(object):
True have_feather =
'egg' way_of_reproduction =
# construct an objectsummer = Bird()
# serialize objectpicklestring = pickle.dumps(summer)
使用pickle.dumps()方法能夠將對象summer轉換成了字符串 picklestring(也就是文本流)。隨後咱們能夠用普通文本的存儲方法來將該字符串儲存在文件(文本文件的輸入輸出)。
固然,咱們也能夠使用pickle.dump()的方法,將上面兩部合二爲一:
importpickle
# define class
class Bird(object):
True have_feather =
'egg' way_of_reproduction =
# construct an objectsummer = Bird()
'a.pkl'fn =
with'w'as# open file with write-modeopen(fn,)f:
# serialize and save object picklestring = pickle.dump(summer, f)
對象summer存儲在文件a.pkl
首先,咱們要從文本中讀出文本,存儲到字符串 (文本文件的輸入輸出)。而後使用pickle.loads(str)的方法,將字符串轉換成爲對象。要記得,此時咱們的程序中必須已經有了該對象的類定義。
此外,咱們也能夠使用pickle.load()的方法,將上面步驟合併:
importpickle
# define the class before unpickle
class Bird(object):
True have_feather =
'egg' way_of_reproduction =
'a.pkl'fn =
with'r'asopen(fn,)f:
# read file and build object summer = pickle.load(f)
cPickle包的功能和用法與pickle包幾乎徹底相同 (其存在差異的地方實際上不多用到),不一樣在於cPickle是基於c語言編寫的,速度是pickle包的1000倍。對於上面的例子,若是想使用cPickle包,咱們均可以將import語句改成:
importascPicklepickle
就不須要再作任何改動了。
有一個文件,文件名爲output_1981.10.21.txt 。下面使用Python: 讀取文件名中的日期時間信息,並找出這一天是周幾。將文件更名爲output_YYYY-MM-DD-W.txt (YYYY:四位的年,MM:兩位的月份,DD:兩位的日,W:一位的周幾,並假設週一爲一週第一天)。
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
這裏的內容以Linux進程基礎和Linux文本流爲基礎。subprocess包主要功能是執行外部的命令和程序。好比說,我須要使用wget下載文件。我在Python中調用wget程序。從這個意義上來講,subprocess的功能與shell相似。
當咱們運行python的時候,咱們都是在建立並運行一個進程。一個進程能夠fork一個子進程,並讓這個子進程exec另一個程序。在Python中,咱們經過標準庫中的subprocess包來fork一個子進程,並運行一個外部的程序。
subprocess包中定義有數個建立子進程的函數,這些函數分別以不一樣的方式建立子進程,因此咱們能夠根據須要來從中選取一個使用。另外subprocess還提供了一些管理標準流(standard stream)和管道(pipe)的工具,從而在進程間使用文本通訊。
使用subprocess包中的函數建立子進程的時候,要注意:
(1)、subprocess.call()
父進程等待子進程完成
返回退出信息(returncode,至關於exit code)
(2)、subprocess.check_call()
父進程等待子進程完成
返回0
檢查退出信息,若是returncode不爲0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性,可用try...except...來檢查(見Python錯誤處理)。
(3)、subprocess.check_output()
父進程等待子進程完成
返回子進程向標準輸出的輸出結果
檢查退出信息,若是returncode不爲0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性和output屬性,output屬性爲標準輸出的輸出結果,可用try...except...來檢查。
這三個函數的使用方法相相似,咱們以subprocess.call()來講明:
importsubprocess
"ls""-l"rc = subprocess.call([,])
咱們將程序名(ls)和所帶的參數(-l)一塊兒放在一個表中傳遞給subprocess.call()
能夠經過一個shell來解釋一整個字符串:
importsubprocess
"ls -l"Trueout = subprocess.call(, shell=)
"cd .."Trueout = subprocess.call(, shell=)
咱們使用了shell=True這個參數。這個時候,咱們使用一整個字符串,而不是一個表來運行子進程。Python將先運行一個shell,再用這個shell來解釋這整個字符串。
shell命令中有一些是shell的內建命令,這些命令必須經過shell運行,$cd。shell=True容許咱們運行這樣一些命令。
實際上,咱們上面的三個函數都是基於Popen()的封裝(wrapper)。這些封裝的目的在於讓咱們容易使用子進程。當咱們想要更個性化咱們的需求的時候,就要轉向Popen類,該類生成的對象用來表明子進程。
與上面的封裝不一樣,Popen對象建立後,主程序不會自動等待子進程完成。咱們必須調用對象的wait()方法,父進程纔會等待 (也就是阻塞block):
importsubprocess
"ping""-c""5""www.google.com"child = subprocess.Popen([,,,])
"parent process"print()
從運行結果中看到,父進程在開啓子進程以後並無等待child的完成,而是直接運行print。
對比等待的狀況:
importsubprocess
"ping""-c""5""www.google.com"child = subprocess.Popen([,,,])
child.wait()
"parent process"print()
此外,你還能夠在父進程中對子進程進行其它操做,好比咱們上面例子中的child對象:
子進程的PID存儲在child.pid。
(沿用child子進程) 子進程的標準輸入,標準輸出和標準錯誤也能夠經過以下屬性表示:
stdinchild.
stdoutchild.
stderrchild.
咱們能夠在Popen()創建子進程的時候改變標準輸入、標準輸出和標準錯誤,並能夠利用subprocess.PIPE將多個子進程的輸入和輸出鏈接在一塊兒,構成管道(pipe):
import subprocess
"ls""-l"stdoutchild1 = subprocess.Popen([,],=subprocess.PIPE)
"wc"stdinstdoutstdoutchild2 = subprocess.Popen([],=child1.,=subprocess.PIPE)
out = child2.communicate()
print(out)
subprocess.PIPE實際上爲文本流提供一個緩存區。child1的stdout將文本輸出到緩存區,隨後child2的stdin從該PIPE中將文本讀取走。child2的輸出文本也被存放在PIPE中,直到communicate()方法從PIPE中讀取出PIPE中的文本。
dh這裏,我以爲PIPE就是鏈接兩個進程之間的輸入輸出,命令是有效的,可是測試了linux環境,win7環境失效,還得再找找緣由。
要注意的是,communicate()是Popen對象的一個方法,該方法會阻塞父進程,直到子進程完成。
咱們還能夠利用communicate()方法來使用PIPE給子進程輸入:
import subprocess
"cat"stdinchild = subprocess.Popen([],=subprocess.PIPE)
"vamei"child.communicate()
咱們啓動子進程以後,cat會等待輸入,直到咱們用communicate()輸入"vamei"。
dh我以爲管道內外交互、管道之間交互,不只僅是文字流還有命令,這個還要再好好學一學
經過使用subprocess包,咱們能夠運行外部程序。這極大的拓展了Python的功能。若是你已經瞭解了操做系統的某些應用,你能夠從Python中直接調用該應用(而不是徹底依賴Python),並將應用的結果輸出給Python,並讓Python繼續處理。shell的功能(好比利用文本流鏈接各個應用),就能夠在Python中實現。
signal包負責在Python程序內部處理信號,典型的操做包括預設信號處理函數,暫停並等待信號,以及定時發出SIGALRM等。要注意,signal包主要是針對UNIX平臺(好比Linux, MAC OS),而Windows內核中因爲對信號機制的支持不充分,因此在Windows上的Python不能發揮信號系統的功能。
signal包定義了各個信號名及其對應的整數,好比:
importsignal
printsignal.SIGALRM
printsignal.SIGCONT
Python所用的信號名和Linux一致。你能夠經過如下命令查詢:
$man7 signal
signal包的核心是使用signal.signal()函數來預設(register)信號處理函數,以下所示:
singnal.signalsignalnumhandler(,)
signalnum爲某個信號,handler爲該信號的處理函數。咱們在信號基礎裏提到,進程能夠無視信號,能夠採起默認操做,還能夠自定義操做。當handler爲signal.SIG_IGN時,信號被無視(ignore)。當handler爲singal.SIG_DFL,進程採起默認操做(default)。當handler爲一個函數名時,進程採起函數中定義的操做。
importsignal
# Define signal handler function
def myHandler(signum, frame):
'I received: ' print(, signum)
# register signal.SIGTSTP's handler
signal.signal(signal.SIGTSTP, myHandler)
signal.pause()
'End of Signal Demo'print()
在主程序中,咱們首先使用signal.signal()函數來預設信號處理函數。而後咱們執行signal.pause()來讓該進程暫停以等待信號,以等待信號。當信號SIGUSR1被傳遞給該進程時,進程從暫停中恢復,並根據預設,執行SIGTSTP的信號處理函數myHandler()。myHandler的兩個參數一個用來識別信號(signum),另外一個用來得到信號發生時,進程棧的情況(stack frame)。這兩個參數都是由signal.singnal()函數來傳遞的。
上面的程序能夠保存在一個文件中(好比test.py)。咱們使用以下方法運行:
$pythontest.py
以便讓進程運行。當程序運行到signal.pause()的時候,進程暫停並等待信號。此時,經過按下CTRL+Z向該進程發送SIGTSTP信號。咱們能夠看到,進程執行了myHandle()函數, 隨後返回主程序,繼續執行。(固然,也能夠用\$ps查詢process ID, 再使用$kill來發出信號。)
(進程並不必定要使用signal.pause()暫停以等待信號,它也能夠在進行工做中接受信號,好比將上面的signal.pause()改成一個須要長時間工做的循環。)
咱們能夠根據本身的須要更改myHandler()中的操做,以針對不一樣的信號實現個性化的處理。
一個有用的函數是signal.alarm(),它被用於在必定時間以後,向進程自身發送SIGALRM信號:
importsignal
# Define signal handler function
def myHandler(signum, frame):
"Now, it's the time" print()
exit()
# register signal.SIGALRM's handler
signal.signal(signal.SIGALRM, myHandler)
5signal.alarm()
whileTrue:
'not yet' print()
咱們這裏用了一個無限循環以便讓進程持續運行。在signal.alarm()執行5秒以後,進程將向本身發出SIGALRM信號,隨後,信號處理函數myHandler開始執行。
signal包的核心是設置信號處理函數。除了signal.alarm()向自身發送信號以外,並無其餘發送信號的功能。但在os包中,有相似於linux的kill命令的函數,分別爲:
os.killpidsid(,)
os.killpgpgidsid(,)
分別向進程和進程組發送信號。sid爲信號所對應的整數或者singal.SIG*。
實際上signal, pause,kill和alarm都是Linux應用編程中常見的C庫函數,在這裏,咱們只不過是用Python語言來實現了一下。實際上,Python 的解釋器是使用C語言來編寫的,因此有此類似性也並不意外。此外,在Python 3.4中,signal包被加強,信號阻塞等功能被加入到該包中。咱們暫時不深刻到該包中。
Python主要經過標準庫中的threading包來實現多線程。在當今網絡時代,每一個服務器都會接收到大量的請求。服務器能夠利用多線程的方式來處理這些請求,以提升對網絡端口的讀寫效率。Python是一種網絡服務器的後臺工做語言,因此多線程也就很天然被Python語言支持。
(關於多線程的原理和C實現方法,請參考Linux多線程與同步,要了解race condition, mutex和condition variable的概念)
咱們使用Python來實現Linux多線程與同步文中的售票程序。咱們使用mutex (也就是Python中的Lock類對象) 來實現線程的同步:
# A program to simulate selling tickets in multi-thread way
# Written by Vamei
importthreading
importtime
importos
# This function could be any function to do other chores.
def doChore():
0.5 time.sleep()
# Function for each thread
def booth(tid):
global i
global lock
whileTrue :
# Lock; or wait if other thread is holding the lock lock.acquire()
if0 i !=:
1# Sell tickets i = i -
':now left:'# Tickets left print(tid,,i)
# Other critical operations doChore()
else :
"Thread_id"" No more tickets" print(,tid,)
0# Exit the whole process immediately os._exit()
# Unblock lock.release()
# Non-critical operations doChore()
# Start of the main function
100# Available ticket number i =
# Lock (i.e., mutex)lock = threading.Lock()
# Start 10 threads
forin10krange():
# Set up thread; target: the callable (function) to be run, args: the argument for the callable new_thread = threading.Thread(target=booth,args=(k,))
# run the thread new_thread.start()
咱們使用了兩個全局變量,一個是i,用以儲存剩餘票數;一個是lock對象,用於同步線程對i的修改。此外,在最後的for循環中,咱們總共設置了10個線程。每一個線程都執行booth()函數。線程在調用start()方法的時候正式啓動 (實際上,計算機中最多會有11個線程,由於主程序自己也會佔用一個線程)。Python使用threading.Thread對象來表明線程,用threading.Lock對象來表明一個互斥鎖 (mutex)。
有兩點須要注意:
上面的Python程序很是相似於一個面向過程的C程序。咱們下面介紹如何經過面向對象 (OOP, object-oriented programming) 的方法實現多線程,其核心是繼承threading.Thread類。咱們上面的for循環中已經利用了threading.Thread()的方法來建立一個Thread對象,並將函數booth()以及其參數傳遞給改對象,並調用start()方法來運行線程。OOP的話,經過修改Thread類的run()方法來定義線程所要執行的命令。
# A program to simulate selling tickets in multi-thread way
# Written by Vamei
importthreading
importtime
importos
# This function could be any function to do other chores.
def doChore():
0.5 time.sleep()
# Function for each thread
class BoothThread(threading.Thread):
def __init__(self, tid, monitor):
self.tid = tid
self.monitor = monitor
threading.Thread.__init__(self)
def run(self):
whileTrue :
'lock'# Lock; or wait if other thread is holding the lock monitor[].acquire()
if'tick'0 monitor[] !=:
'tick''tick'1# Sell tickets monitor[] = monitor[] -
':now left:''tick'# Tickets left print(self.tid,,monitor[])
# Other critical operations doChore()
else :
"Thread_id"" No more tickets" print(,self.tid,)
0# Exit the whole process immediately os._exit()
'lock'# Unblock monitor[].release()
# Non-critical operations doChore()
# Start of the main function
'tick'100'lock'monitor = {:,:threading.Lock()}
# Start 10 threads
forin10krange():
new_thread = BoothThread(k, monitor)
new_thread.start()
咱們本身定義了一個類BoothThread, 這個類繼承自thread.Threading類。而後咱們把上面的booth()所進行的操做通通放入到BoothThread類的run()方法中。注意,咱們沒有使用全局變量聲明global,而是使用了一個詞典 monitor存放全局變量,而後把詞典做爲參數傳遞給線程函數。因爲詞典是可變數據對象,因此當它被傳遞給函數的時候,函數所使用的依然是同一個對象,至關於被多個線程所共享。這也是多線程乃至於多進程編程的一個技巧 (應儘可能避免上面的global聲明的用法,由於它並不適用於windows平臺)。
上面OOP編程方法與面向過程的編程方法相比,並無帶來太大實質性的差異。
threading.Thread對象: 咱們已經介紹了該對象的start()和run(), 此外:
下面的對象用於處理多線程同步。對象一旦被創建,能夠被多個線程共享,並根據狀況阻塞某些進程。
threading.Lock對象: mutex, 有acquire()和release()方法。
threading.Condition對象: condition variable,創建該對象時,會包含一個Lock對象 (由於condition variable老是和mutex一塊兒使用)。能夠對Condition對象調用acquire()和release()方法,以控制潛在的Lock對象。此外:
threading.Semaphore對象: semaphore,也就是計數鎖(semaphore傳統意義上是一種進程間同步工具)。建立對象的時候,能夠傳遞一個整數做爲計數上限 (sema = threading.Semaphore(5))。它與Lock相似,也有Lock的兩個方法。
threading.Event對象: 與threading.Condition相相似,至關於沒有潛在的Lock保護的condition variable。對象有True和False兩個狀態。能夠多個線程使用wait()等待,直到某個線程調用該對象的set()方法,將對象設置爲True。線程能夠調用對象的clear()方法來重置對象爲False狀態。
Python的os包中有查詢和修改進程信息的函數。學習Python的這些工具也有助於理解Linux體系。
os包中相關函數以下:
(1)、uname() 返回操做系統相關信息。相似於Linux上的uname命令。
(2)、umask() 設置該進程建立文件時的權限mask。相似於Linux上的umask命令
(3)、get() 查詢 (由如下代替)
UIDuid, euid, resuid, gid, egid, resgid:權限相關,其中resuid主要用來返回saved
pid, pgid, ppid, sid :進程相關
(4)、put*() 設置 (*由如下代替)
euid, egid:用於更改euid,egid。
superuid, gid :改變進程的uid, gid。只有user纔有權改變進程uid和gid (意味着要以$sudo python的方式運行Python)。
pgid, sid:改變進程所在的進程組(process group)和會話(session)。
(5)、getenviron():得到進程的環境變量
(6)、setenviron():更改進程的環境變量
例1,進程的real UID和real GID:
importos
print(os.getuid())
print(os.getgid())
將上面的程序保存爲py_id.py文件,分別用\$python py_id.py和\$sudo python py_id.py看一下運行結果。
咱們但願saved UID和saved GID如咱們在Linux用戶與「最小權限」原則中描述的那樣工做,但這很難。緣由在於,當咱們寫一個Python腳本後,咱們實際運行的是python這個解釋器,而不是Python腳本文件。對比C,C語言直接運行由C語言編譯成的執行文件。咱們必須更改python解釋器自己的權限來運用saved UID機制,然而這麼作又是異常危險的。
好比說,咱們的python執行文件爲/usr/bin/python (你能夠經過$which python獲知)
咱們先看一下:
$ls-l/usr/bin/python
的結果:
-rwxr-xr-x root root
咱們修改權限以設置set UID和set GID位 (參考Linux用戶與「最小權限」原則):
chmod6755$sudo/usr/bin/python
/usr/bin/python的權限成爲:
x-rwsr-sr-root root
隨後,咱們運行文件下面test.py文件,這個文件能夠是由普通用戶全部:
importos
print(os.getresuid())
咱們獲得結果:(1000, 0, 0)
上面分別是UID,EUID,saved UID。咱們只用執行一個由普通用戶擁有的python腳本,就能夠獲得super user的權限!因此,這樣作是極度危險的,咱們至關於交出了系統的保護系統。想像一下Python強大的功能,別人如今能夠用這些強大的功能做爲攻擊你的武器了!使用下面命令來恢復到從前:
chmod0755$sudo/usr/bin/python
關於腳本文件的saved UID/GID,更加詳細的討論見:http://www.faqs.org/faqs/unix-faq/faq/part4/section-7.html
咱們已經見過了使用subprocess包來建立子進程,但這個包有兩個很大的侷限性:
(1) 咱們老是讓subprocess運行外部的程序,而不是運行一個Python腳本內部編寫的函數。
(2) 進程間只經過管道進行文本交流。以上限制了咱們將subprocess包應用到更普遍的多進程任務。(這樣的比較實際是不公平的,由於subprocessing自己就是設計成爲一個shell,而不是一個多進程管理包)。
multiprocessing包是Python中的多進程管理包。與threading.Thread相似,它能夠利用multiprocessing.Process對象來建立一個進程。該進程能夠運行在Python程序內部編寫的函數。該Process對象與Thread對象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition類 (這些對象能夠像多線程那樣,經過參數傳遞給各個進程),用以同步進程,其用法與threading包中的同名類一致。因此,multiprocessing的很大一部份與threading使用同一套API,只不過換到了多進程的情境。
但在使用這些共享API的時候,咱們要注意如下幾點:
Process.PID中保存有PID,若是進程尚未start(),則PID爲None。
咱們能夠從下面的程序中看到Thread對象和Process對象在使用上的類似性與結果上的不一樣。各個線程和進程都作一件事:打印PID。但問題是,全部的任務在打印的時候都會向同一個標準輸出(stdout)輸出。這樣輸出的字符會混合在一塊兒,沒法閱讀。使用Lock同步,在一個任務輸出完成以後,再容許另外一個任務輸出,能夠避免多個任務同時向終端輸出。
# Similarity and difference of multi thread vs. multi process
# Written by Vamei
importos
importthreading
importmultiprocessing
# worker function
def worker(sign, lock):
lock.acquire()
print(sign, os.getpid())
lock.release()
# Main
'Main:'print(,os.getpid())
# Multi-thread
record = []
lock = threading.Lock()
forin5irange():
'thread' thread = threading.Thread(target=worker,args=(,lock))
thread.start()
record.append(thread)
forinthreadrecord:
thread.join()
# Multi-process
record = []
lock = multiprocessing.Lock()
forin5irange():
'process' process = multiprocessing.Process(target=worker,args=(,lock))
process.start()
record.append(process)
forinprocessrecord:
process.join()
全部Thread的PID都與主程序相同,而每一個Process都有一個不一樣的PID。
管道PIPE和消息隊列message queue,multiprocessing包中有Pipe類和Queue類來分別支持這兩種IPC機制。Pipe和Queue能夠用來傳送常見的對象。
下面的程序展現了Pipe的使用:
# Multiprocessing with Pipe
# Written by Vamei
importasmultiprocessingmul
def proc1(pipe):
'hello' pipe.send()
'proc1 rec:' print(,pipe.recv())
def proc2(pipe):
'proc2 rec:' print(,pipe.recv())
'hello, too' pipe.send()
# Build a pipe
pipe = mul.Pipe()
# Pass an end of the pipe to process 1
0p1 = mul.Process(target=proc1, args=(pipe[],))
# Pass the other end of the pipe to process 2
1p2 = mul.Process(target=proc2, args=(pipe[],))
p1.start()
p2.start()
p1.join()
p2.join()
這裏的Pipe是雙向的。
Pipe對象創建的時候,返回一個含有兩個元素的表,每一個元素表明Pipe的一端(Connection對象)。咱們對Pipe的某一端調用send()方法來傳送對象,在另外一端使用recv()來接收。
下面的程序展現了Queue的使用:
# Written by Vamei
importos
importmultiprocessing
importtime
#==================
# input worker
def inputQ(queue):
'(put):' info = str(os.getpid()) ++ str(time.time())
queue.put(info)
# output worker
def outputQ(queue,lock):
info = queue.get()
lock.acquire()
print'(get):' (str(os.getpid()) ++ info)
lock.release()
#===================
# Main
# store input processesrecord1 = []
# store output processesrecord2 = []
# To prevent messy printlock = multiprocessing.Lock()
3queue = multiprocessing.Queue()
# input processes
forin10irange():
process = multiprocessing.Process(target=inputQ,args=(queue,))
process.start()
record1.append(process)
# output processes
forin10irange():
process = multiprocessing.Process(target=outputQ,args=(queue,lock))
process.start()
record2.append(process)
forinprecord1:
p.join()
# No more object will come, close the queuequeue.close()
forinprecord2:
p.join()
一些進程使用put()在Queue中放入字符串,這個字符串中包含PID和時間。另外一些進程從Queue中取出,並打印本身的PID以及get()的字符串。
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
上一節的最後,初步瞭解Python多進程,如今咱們能夠繼續探索multiprocessing包中更加高級的工具。這些工具可讓咱們更加便利地實現多進程。
進程池 (Process Pool)能夠建立多個進程。這些進程就像是隨時待命的士兵,準備執行任務(程序)。一個進程池中能夠容納多個待命的士兵:
好比下面的程序:
importasmultiprocessingmul
def f(x):
return2 x**
5pool = mul.Pool()
12345678910rel = pool.map(f,[,,,,,,,,,])
print(rel)
咱們建立了一個允許5個進程的進程池 (Process Pool) 。Pool運行的每一個進程都執行f()函數。咱們利用map()方法,將f()函數做用到表的每一個元素上。這與built-in的map()函數相似,只是這裏用5個進程並行處理。若是進程運行結束後,還有須要處理的元素,那麼的進程會被用於從新運行f()函數。除了map()方法外,Pool還有下面的經常使用方法。
咱們在Python多進程初步已經提到,咱們應該儘可能避免多進程共享資源。多進程共享資源必然會帶來進程間相互競爭。而這種競爭又會形成race condition,咱們的結果有可能被競爭的不肯定性所影響。但若是須要,咱們依然能夠經過共享內存和Manager對象這麼作。
咱們已經講述了共享內存(shared memory)的原理,這裏給出用Python實現的例子:
# modified from official documentation
importmultiprocessing
def f(n, a):
3.14 n.value =
05 a[] =
'd'0.0num = multiprocessing.Value(,)
'i'10arr = multiprocessing.Array(, range())
p = multiprocessing.Process(target=f, args=(num, arr))
p.start()
p.join()
printnum.value
printarr[:]
這裏咱們實際上只有主進程和Process對象表明的進程。咱們在主進程的內存空間中建立共享的內存,也就是Value和Array兩個對象。對象Value被設置成爲雙精度數(d), 並初始化爲0.0。而Array則相似於C中的數組,有固定的類型(i, 也就是整數)。在Process進程中,咱們修改了Value和Array對象。回到主程序,打印出結果,主程序也看到了兩個對象的改變,說明資源確實在兩個進程之間共享。
Manager對象相似於服務器與客戶之間的通訊 (server-client),與咱們在Internet上的活動很相似。咱們用一個進程做爲服務器,創建Manager來真正存放資源。其它的進程能夠經過參數傳遞或者根據地址來訪問Manager,創建鏈接後,操做服務器上的資源。在防火牆容許的狀況下,咱們徹底能夠將Manager運用於多計算機,從而模仿了一個真實的網絡情境。下面的例子中,咱們對Manager的使用相似於shared memory,但能夠共享更豐富的對象類型。
importmultiprocessing
def f(x, arr, l):
3.14 x.value =
05 arr[] =
'Hello' l.append()
server = multiprocessing.Manager()
'd'0.0x = server.Value(,)
'i'10arr = server.Array(, range())
l = server.list()
proc = multiprocessing.Process(target=f, args=(x, arr, l))
proc.start()
proc.join()
print(x.value)
print(arr)
print(l)
Manager利用list()方法提供了表的共享方式。實際上你能夠利用dict()來共享詞典,Lock()來共享threading.Lock(注意,咱們共享的是threading.Lock,而不是進程的mutiprocessing.Lock。後者自己已經實現了進程共享)等。 這樣Manager就容許咱們共享更多樣的對象。
咱們已經在Python運算中看到Python最基本的數學運算功能。此外,math包補充了更多的函數。固然,若是想要更加高級的數學功能,能夠考慮選擇標準庫以外的numpy和scipy項目,它們不但支持數組和矩陣運算,還有豐富的數學和物理方程可供使用。
此外,random包能夠用來生成隨機數。隨機數不只能夠用於數學用途,還常常被嵌入到算法中,用以提升算法效率,並提升程序的安全性。
math包主要處理數學相關的運算。math包定義了兩個常數:
# 天然常數emath.e
# 圓周率pimath.pi
此外,math包還有各類運算函數 (下面函數的功能能夠參考數學手冊):
ceil1.22math.(x) #對x向上取整,好比x=,返回
floor1.21math.(x) #對x向下取整,好比x=,返回
powmath.(x,y) #指數運算,獲得x的y次方
loglog10010math.(x) #對數,默認基底爲e。能夠使用base參數,來改變對數的基地。好比math.(,base=)
sqrtmath.(x) #平方根
三角函數: math.sin(x), math.cos(x), math.tan(x), math.asin(x), math.acos(x), math.atan(x)
這些函數都接收一個弧度(radian)爲單位的x做爲參數。
角度和弧度互換: math.degrees(x), math.radians(x)
雙曲函數: math.sinh(x), math.cosh(x), math.tanh(x), math.asinh(x), math.acosh(x), math.atanh(x)
特殊函數: math.erf(x), math.gamma(x)
若是你已經瞭解僞隨機數(psudo-random number)的原理,那麼你能夠使用以下:
random.seedx()
來改變隨機數生成器的種子seed。若是你不瞭解其原理,你沒必要特別去設定seed,Python會幫你選擇seed。
random.choiceseqrandom.choicerange() #從序列的元素中隨機挑選一個元素,好比((10)),從0到9中隨機挑選一個整數。
random.sampleseqkk(,) #從序列中隨機挑選個元素
random.shuffleseq() #將序列的全部元素隨機排序
下面生成的實數符合均勻分佈(uniform distribution),意味着某個範圍內的每一個數字出現的機率相等:
# 隨機生成下一個實數,它在[0,1)範圍內。random.random()
# 隨機生成下一個實數,它在[a,b]範圍內。random.uniform(a,b)
下面生成的實數符合其它的分佈 (你能夠參考一些統計方面的書籍來了解這些分佈):
# 隨機生成符合高斯分佈的隨機數,mu,sigma爲高斯分佈的兩個參數。 random.gauss(mu,sigma)
# 隨機生成符合指數分佈的隨機數,lambd爲指數分佈的參數。random.expovariate(lambd)
此外還有對數分佈,正態分佈,Pareto分佈,Weibull分佈,可參考下面連接:
http://docs.python.org/library/random.html
假設咱們有一羣人蔘加舞蹈比賽,爲了公平起見,咱們要隨機排列他們的出場順序。咱們下面利用random包實現:
importrandom
'Tom''Vivian''Paul''Liya''Manu''Daniel''Shawn'all_people = [,,,,,,]
random.shuffle(all_people)
forini,nameenumerate(all_people):
':' print(i,+name)
在循環對象和函數對象中,咱們瞭解了循環器(iterator)的功能。循環器是對象的容器,包含有多個對象。經過調用循環器的next()方法 (__next__()方法,在Python 3.x中),循環器將依次返回一個對象。直到全部的對象遍歷窮盡,循環器將舉出StopIteration錯誤。
itertools的工具均可以自行實現。itertools只是提供了更加成形的解決方案。
在for i in iterator結構中,循環器每次返回的對象將賦予給i,直到循環結束。使用iter()內置函數,咱們能夠將諸如表、字典等容器變爲循環器。好比:
foriniiter([2, 4, 5, 6]):
print (i)
標準庫中的itertools包提供了更加靈活的生成循環器的工具。這些工具的輸入大都是已有的循環器。另外一方面,這些工具徹底能夠自行使用Python實現,該包只是提供了一種比較標準、高效的實現方式。這也符合Python「只有且最好只有解決方案」的理念。
# import the tools
fromimportitertools*
#從5開始的整數循環器,每次增長2,即5, 7, 9, 11, 13, 15 ...count(5, 2)
'abc'#重複序列的元素,既a, b, c, a, b, c ...cycle()
#重複1.2,構成無窮循環器,即1.2, 1.2, 1.2, ...repeat(1.2)
repeat也能夠有一個次數限制:
#重複10,共重複5次repeat(10, 5)
函數式編程是將函數自己做爲處理對象的編程範式。在Python中,函數也是對象,所以能夠輕鬆的進行一些函數式的處理,好比map(), filter(), reduce()函數。
itertools包含相似的工具。這些函數接收函數做爲參數,並將結果返回爲一個循環器。
好比:
fromimportitertools*
123123rlt = imap(pow, [,,], [,,])
forinnumrlt:
print(num)
上面顯示了imap函數。該函數與map()函數功能類似,只不過返回的不是序列,而是一個循環器。包含元素1, 4, 27,即1**1, 2**2, 3**3的結果。函數pow(內置的乘方函數)做爲第一個參數。pow()依次做用於後面兩個列表的每一個元素,並收集函數結果,組成返回的循環器。
此外,還能夠用下面的函數:
pow112233#pow將依次做用於表的每一個tuple。starmap(, [(,), (,), (,)])
ifilter函數與filter()函數相似,只是返回的是一個循環器。
lambda523567#將lambda函數依次做用於每一個元素,若是函數返回True,則收集原來的元素。6, 7。ifilter(x: x >, [,,,,])
此外:
lambda523567#與上面相似,但收集返回False的元素。2, 3, 5ifilterfalse(x: x >, [,,,,])
lambda513671#當函數返回True時,收集元素到循環器。一旦函數返回False,則中止。1, 3takewhile(x: x <, [,,,,])
lambda513671#當函數返回False時,跳過元素。一旦函數返回True,則開始收集剩下的全部元素到循環器。6, 7, 1dropwhile(x: x <, [,,,,])
咱們能夠經過組合原有循環器,來得到新的循環器。
chain[1, 2, 3][4, 5, 7](,) #鏈接兩個循環器成爲一個。1, 2, 3, 4, 5, 7
'abc'# 多個循環器集合的笛卡爾積。至關於嵌套循環product(, [1, 2])
forin'abc'm, nproduct(, [1, 2]):
print m, n
'abc'# 從'abc'中挑選兩個元素,好比ab, bc, ... 將全部結果排序,返回爲新的循環器。permutations(, 2)
注意,上面的組合分順序,即ab, ba都返回。
'abc'# 從'abcd'中挑選兩個元素,好比ab, bc, ... 將全部結果排序,返回爲新的循環器。combinations(, 2)
注意,上面的組合不分順序,即ab, ba的話,只返回一個ab。
'abc'# 與上面相似,但容許兩次選出的元素重複。即多了aa, bb, cccombinations_with_replacement(, 2)
將key函數做用於原循環器的各個元素。根據key函數結果,將擁有相同函數結果的元素分到一個新的循環器。每一個新的循環器以函數返回結果爲標籤。
這就好像一羣人的身高做爲循環器。咱們能夠使用這樣一個key函數: 若是身高大於180,返回"tall";若是身高底於160,返回"short";中間的返回"middle"。最終,全部身高將分爲三個循環器,即"tall", "short", "middle"。
def height_class(h):
if180 h >:
return"tall"
elif160 h <:
return"short"
else :
return"middle"
191158159165170177181182190friends = [,,,,,,,,]
friends = sorted(friends, key = height_class)
forinm, ngroupby(friends, key = height_class):
print(m)
print(list(n))
注意,groupby的功能相似於UNIX中的uniq命令。分組以前須要使用sorted()對原循環器的元素,根據key函數進行排序,讓同組元素先在位置上靠攏。
'ABCD'# 根據[1, 1, 1, 0]的真假值狀況,選擇第一個參數'ABCD'中的元素。A, B, Ccompress(, [1, 1, 1, 0])
# 相似於slice()函數,只是返回的是一個循環器islice()
# 相似於zip()函數,只是返回的是一個循環器。izip()
Python自帶一個輕量級的關係型數據庫SQLite。這一數據庫使用SQL語言。SQLite做爲後端數據庫,能夠搭配Python建網站,或者製做有數據存儲需求的工具。SQLite還在其它領域有普遍的應用,好比HTML5和移動端。Python標準庫中的sqlite3提供該數據庫的接口。
我將建立一個簡單的關係型數據庫,爲一個書店存儲書的分類和價格。數據庫中包含兩個表:category用於記錄分類,book用於記錄某個書的信息。一本書歸屬於某一個分類,所以book有一個外鍵(foreign key),指向catogory表的主鍵id。
sqlite3只是一個SQLite的接口。想要熟練的使用SQLite數據庫,還須要學習更多的關係型數據庫的知識。
我首先來建立數據庫,以及數據庫中的表。在使用connect()鏈接數據庫後,我就能夠經過定位指針cursor,來執行SQL命令:
importsqlite3
# test.db is a file in the working directory.
"test.db"conn = sqlite3.connect()
c = conn.cursor()
# create tables
'''CREATE TABLE categoryc.execute(
(id int primary key, sort int, name text)''')
'''CREATE TABLE bookc.execute(
(id int primary key,
sort int,
name text,
price real,
category int,
FOREIGN KEY (category) REFERENCES category(id))''')
# save the changes
conn.commit()
# close the connection with the database
conn.close()
SQLite的數據庫是一個磁盤上的文件,如上面的test.db,所以整個數據庫能夠方便的移動或複製。test.db一開始不存在,因此SQLite將自動建立一個新文件。
利用execute()命令,我執行了兩個SQL命令,建立數據庫中的兩個表。建立完成後,保存並斷開數據庫鏈接。
上面建立了數據庫和表,確立了數據庫的抽象結構。下面將在同一數據庫中插入數據:
importsqlite3
"test.db"conn = sqlite3.connect()
c = conn.cursor()
11'Cook Recipe'3121books = [(,,,.,),
23'Python Intro'1752 (,,,.,),
32'OS Intro'1362 (,,,.,),
]
# execute "INSERT"
"INSERT INTO category VALUES (1, 1, 'kitchen')"c.execute()
# using the placeholder
"INSERT INTO category VALUES (?, ?, ?)"22'computer'c.execute(, [(,,)])
# execute multiple commands
'INSERT INTO book VALUES (?, ?, ?, ?, ?)'c.executemany(, books)
conn.commit()
conn.close()
插入數據一樣能夠使用execute()來執行完整的SQL語句。SQL語句中的參數,使用"?"做爲替代符號,並在後面的參數中給出具體值。這裏不能用Python的格式化字符串,如"%s",由於這一用法容易受到SQL注入攻擊。
我也能夠用executemany()的方法來執行屢次插入,增長多個記錄。每一個記錄是表中的一個元素,如上面的books表中的元素。
在執行查詢語句後,Python將返回一個循環器,包含有查詢得到的多個記錄。你循環讀取,也能夠使用sqlite3提供的fetchone()和fetchall()方法讀取記錄:
importsqlite3
'test.db'conn = sqlite3.connect()
c = conn.cursor()
# retrieve one record
'SELECT name FROM category ORDER BY sort'c.execute()
print(c.fetchone())
print(c.fetchone())
# retrieve all records as a list
'SELECT * FROM book WHERE book.category=1'c.execute()
print(c.fetchall())
# iterate through the records
forin'SELECT name, price FROM book ORDER BY sort'rowc.execute():
print(row)
你能夠更新某個記錄,或者刪除記錄:
conn = sqlite3.connect("test.db")
c = conn.cursor()
UPDATESETWHEREid',(1000, 1))c.execute('bookprice=?=?
c.execute('DELETEFROMWHEREid2')book=
conn.commit()
conn.close()
你也能夠直接刪除整張表:
DROPTABLE')c.execute('book
若是刪除test.db,那麼整個數據庫會被刪除。
www.shiyanlou.com
www.163.com
www.baidu.com
www.36kr.com
www.cnblogs.com
www.douban.com
使用包含3個進程的進程池下載文件中網站的首頁。(你能夠使用subprocess調用wget或者curl等下載工具執行具體的下載任務)
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
有人表示,只學Python沒有用,必須學會一個框架(好比Django和web.py)才能找到工做。而個人想法是,掌握一個相似於框架的高級工具是有用的,可是基礎的東西可讓你永遠不被淘汰。不要被工具限制了本身的發展。今天,我在這裏想要展現的,就是不使用框架,甚至不使用Python標準庫中的高級包,只使用標準庫中的socket接口,寫一個Python服務器。
在當今Python服務器框架 (framework, 好比Django, Twisted, web.py等等) 橫行的時代,從底層的socket開始寫服務器彷佛是一個出力不討好的笨方法。框架的意義在於掩蓋底層的細節,提供一套對於開發人員更加友好的API,並處理諸如MVC的佈局問題。框架容許咱們快速的構建一個成型並且成熟的Python服務器。然而,框架自己也是依賴於底層(好比socket)。對於底層socket的瞭解,不只能夠幫助咱們更好的使用框架,更可讓咱們明白框架是如何設計的。更進一步,若是擁有良好的底層socket編程知識和其餘系統編程知識,你徹底能夠設計並開發一款本身的框架。若是你能夠從底層socket開始,實現一個完整的Python服務器,支持用戶層的協議,並處理好諸如MVC(Model-View-Control)、多線程(threading)等問題,並整理出一套清晰的函數或者類,做爲接口(API)呈現給用戶,你就至關於設計了一個框架。
socket接口是其實是操做系統提供的系統調用。socket的使用並不侷限於Python語言,你能夠用C或者JAVA來寫出一樣的socket服務器,而全部語言使用socket的方式都相似(Apache就是使用C實現的服務器)。而你不能跨語言的使用框架。框架的好處在於幫你處理了一些細節,從而實現快速開發,但同時受到Python自己性能的限制。咱們已經看到,許多成功的網站都是利用動態語言(好比Python, Ruby或者PHP,好比twitter和facebook)快速開發,在網站成功以後,將代碼轉換成諸如C和JAVA這樣一些效率比較高的語言,從而讓服務器能更有效率的面對天天億萬次的請求。在這樣一些時間,底層的重要性,就遠遠超過了框架。
下面的一篇文章雖然是在談JAVA,但我以爲也適用於Python的框架之爭:http://yakovfain.com/2012/10/11/the-degradation-of-java-developers/
咱們須要對網絡傳輸,特別是TCP/IP協議和socket有必定的瞭解。socket是進程間通訊的一種方法,它是基於網絡傳輸協議的上層接口。socket有許多種類型,好比基於TCP協議或者UDP協議(兩種網絡傳輸協議)。其中又以TCP socket最爲經常使用。TCP socket與雙向管道(duplex PIPE)有些相似,一個進程向socket的一端寫入或讀取文本流,而另外一個進程能夠從socket的另外一端讀取或寫入,比較特別是,這兩個創建socket通訊的進程能夠分別屬於兩臺不一樣的計算機。所謂的TCP協議,就是規定了一些通訊的守則,以便在網絡環境下可以有效實現上述進程間通訊過程。雙向管道(duplex PIPE)存活於同一臺電腦中,因此沒必要區分兩個進程的所在計算機的地址,而socket必須包含有地址信息,以便實現網絡通訊。一個socket包含四個地址信息: 兩臺計算機的IP地址和兩個進程所使用的端口(port)。IP地址用於定位計算機,而port用於定位進程 (一臺計算機上能夠有多個進程分別使用不一樣的端口)。
一個TCP socket鏈接的網絡:
在互聯網上,咱們可讓某臺計算機做爲服務器。服務器開放本身的端口,被動等待其餘計算機鏈接。當其餘計算機做爲客戶,主動使用socket鏈接到服務器的時候,服務器就開始爲客戶提供服務。
在Python中,咱們使用標準庫中的socket包來進行底層的socket編程。
首先是服務器端,咱們使用bind()方法來賦予socket以固定的地址和端口,並使用listen()方法來被動的監聽該端口。當有客戶嘗試用connect()方法鏈接的時候,服務器使用accept()接受鏈接,從而創建一個鏈接的socket:
# Written by Vamei
# Server side
socketimport
# Address
''HOST =
8000PORT =
'Yes'reply =
# Configure socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
# passively wait, 3: maximum number of connections in the queue
3s.listen()
# accept and establish connection
conn, addr = s.accept()
# receive message
1024request = conn.recv()
print'request is: ',request
print'Connected by', addr
# send message
conn.sendall(reply)
# close connection
conn.close()
socket.socket()建立一個socket對象,並說明socket使用的是IPv4(AF_INET,IP version 4)和TCP協議(SOCK_STREAM)。
而後用另外一臺電腦做爲客戶,咱們主動使用connect()方法來搜索服務器端的IP地址(在Linux中,你能夠用$ifconfig來查詢本身的IP地址)和端口,以便客戶能夠找到服務器,並創建鏈接:
# Written by Vamei
# Client side
import socket
# Address
HOST = '172.20.202.155'
PORT = 8000
request = 'can you hear me?'
# configure socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
# send message
s.sendall(request)
# receive message
reply = s.recv(1024)
print 'reply is: ',reply
# close connection
s.close()
在上面的例子中,咱們對socket的兩端均可以調用recv()方法來接收信息,調用sendall()方法來發送信息。這樣,咱們就能夠在分處於兩臺計算機的兩個進程間進行通訊了。當通訊結束的時候,咱們使用close()方法來關閉socket鏈接。
(若是沒有兩臺計算機作實驗,也能夠將客戶端IP想要connect的IP改成"127.0.0.1",這是個特殊的IP地址,用來鏈接當地主機。)
上面的例子中,咱們已經能夠使用TCP socket來爲兩臺遠程計算機創建鏈接。然而,socket傳輸自由度過高,從而帶來不少安全和兼容的問題。咱們每每利用一些應用層的協議(好比HTTP協議)來規定socket 使用規則,以及所傳輸信息的格式。
HTTP協議利用請求-迴應(request-response)的方式來使用TCP socket。客戶端向服務器發一段文本做爲request,服務器端在接收到request以後,向客戶端發送一段文本做爲response。在完成了這樣一次request-response交易以後,TCP socket被廢棄。下次的request將創建新的socket。request和response本質上說是兩個文本,只是HTTP協議對這兩個文本都有必定的格式要求。
request-response cycle:
如今,咱們寫出一個HTTP服務器端:
importsocket
# Address
''HOST =
8000PORT =
# Prepare HTTP response
'''HTTP/1.x 200 OK text_content =
Content-Type: text/html
<head>
<title>WOW</title>
</head>
<html>
<p>Wow, Python Server</p>
<IMG src="test.jpg"/>
</html>
'''
# Read picture, put into HTTP format
'test.jpg''rb'f = open(,)
'''pic_content =
HTTP/1.x 200 OK
Content-Type: image/jpg
'''
pic_content = pic_content + f.read()
f.close()
# Configure socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
# infinite loop, server forever
whileTrue:
# 3: maximum number of requests waiting
3 s.listen()
conn, addr = s.accept()
1024 request = conn.recv()
' '0 method = request.split()[]
' '1 src = request.split()[]
# deal with GET method
if'GET' method ==:
# ULR
if'/test.jpg' src ==:
content = pic_content
else : content = text_content
print'Connected by' , addr
print'Request is:' , request
conn.sendall(content)
# close connection
conn.close()
如咱們上面所看到的,服務器會根據request向客戶傳輸的兩條信息text_content和pic_content中的一條,做爲response文本。整個response分爲起始行(start line), 頭信息(head)和主體(body)三部分。起始行就是第一行:
200HTTP/1.xOK
它實際上又由空格分爲三個片斷,HTTP/1.x表示所使用的HTTP版本,200表示狀態(status code),200是HTTP協議規定的,表示服務器正常接收並處理請求,OK是供人來閱讀的status code。
頭信息跟隨起始行,它和主體之間有一個空行。這裏的text_content或者pic_content都只有一行的頭信息,text_content用來表示主體信息的類型爲html文本:
Content-Type: text/html
而pic_content的頭信息(Content-Type: image/jpg)說明主體的類型爲jpg圖片(image/jpg)。
主體信息爲html或者jpg文件的內容。
(注意,對於jpg文件,咱們使用'rb'模式打開,是爲了與windows兼容。由於在windows下,jpg被認爲是二進制(binary)文件,在UNIX系統下,則不須要區分文本文件和二進制文件。)
咱們並無寫客戶端程序,後面咱們會用瀏覽器做爲客戶端。request由客戶端程序發給服務器。儘管request也能夠像response那樣分爲三部分,request的格式與response的格式並不相同。request由客戶發送給服務器,好比下面是一個request:
GET1/test.jpg HTTP/.x
Accept: text/*
起始行能夠分爲三部分,第一部分爲請求方法(request method),第二部分是URL,第三部分爲HTTP版本。request method能夠有GET, PUT, POST, DELETE, HEAD。最經常使用的爲GET和POST。GET是請求服務器發送資源給客戶,POST是請求服務器接收客戶送來的數據。當咱們打開一個網頁時,咱們一般是使用GET方法;當咱們填寫表格並提交時,咱們一般使用POST方法。第二部分爲URL,它一般指向一個資源(服務器上的資源或者其它地方的資源)。像如今這樣,就是指向當前服務器的當前目錄的test.jpg。
按照HTTP協議的規定,服務器須要根據請求執行必定的操做。正如咱們在服務器程序中看到的,咱們的Python程序先檢查了request的方法,隨後根據URL的不一樣,來生成不一樣的response(text_content或者pic_content)。隨後,這個response被髮送回給客戶端。
爲了配合上面的服務器程序,我已經在放置Python程序的文件夾裏,保存了一個test.jpg圖片文件。咱們在終端運行上面的Python程序,做爲服務器端,再打開一個瀏覽器做爲客戶端。(若是有時間,你也徹底能夠用Python寫一個客戶端。原理與上面的TCP socket的客戶端程序相相似。)
在瀏覽器的地址欄輸入:127.0.0.1:8000
(固然,你也能夠用另外一臺電腦,並輸入服務器的IP地址。) 我獲得下面的結果:
OK,我已經有了一個用Python實現的,並從socket寫起的服務器了。
從終端,咱們能夠看到,瀏覽器實際上發出了兩個請求。第一個請求爲 (關鍵信息在起始行,這一個請求的主體爲空):
GET/HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
咱們的Python程序根據這個請求,發送給服務器text_content的內容。
瀏覽器接收到text_content以後,發現正文的html文本中有,知道須要得到text.jpg文件來補充爲圖片,當即發出了第二個請求:
GET/test.jpgHTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1
Accept: image/png,image/*;q=0.8,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://127.0.0.1:8000/
咱們的Python程序分析過起始行以後,發現/test.jpg符合if條件,因此將pic_content發送給客戶。
最後,瀏覽器根據html語言的語法,將html文本和圖畫以適當的方式顯示出來。(html可參考http://www.w3schools.com/html/default.asp)
注意,在Python 3.x中,BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer整合到http.server包,SocketServer更名爲socketserver,請注意查閱官方文檔。
剛纔,咱們使用socket接口,製做了一個處理HTTP請求的Python服務器。任何一臺裝有操做系統和Python解釋器的計算機,均可以做爲HTTP服務器使用。我將在這裏不斷改寫上一篇文章中的程序,引入更高級的Python包,以寫出更成熟的Python服務器。
我首先增長該服務器的功能。這裏增添了表格,以及處理表格提交數據的"POST"方法。你會發現這裏只是比剛纔用socket寫的Python服務器增長不多的一點內容。
原始程序:
# A messy HTTP server based on TCP socket
importsocket
# Address
''HOST =
8000PORT =
'''text_content =
HTTP/1.x 200 OK
Content-Type: text/html
<head>
<title>WOW</title>
</head>
<html>
<p>Wow, Python Server</p>
<IMG src="test.jpg"/>
<form name="input" action="/" method="post">
First name:<input type="text" name="firstname"><br>
<input type="submit" value="Submit">
</form>
</html>
'''
'test.jpg''rb'f = open(,)
'''pic_content =
HTTP/1.x 200 OK
Content-Type: image/jpg
'''
pic_content = pic_content + f.read()
# Configure socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
# Serve forever
whileTrue:
3 s.listen()
conn, addr = s.accept()
1024# 1024 is the receiving buffer size request = conn.recv()
' '0 method = request.split()[]
' '1 src = request.split()[]
print'Connected by' , addr
print'Request is:' , request
# if GET method request
if'GET' method ==:
# if ULR is /test.jpg
if'/test.jpg' src ==:
content = pic_content
else : content = text_content
# send message
conn.sendall(content)
# if POST method request
if'POST' method ==:
'\r\n' form = request.split()
''# Find the empty line idx = form.index()
# Main content of the request entry = form[idx:]
-1'='-1 value = entry[].split()[]
'\n <p>''</p>' conn.sendall(text_content ++ value +)
######
# More operations, such as put the form into database
# ...
######
# close connection
conn.close()
服務器進行的操做很簡單,即從POST請求中提取數據,再顯示在屏幕上。
運行上面Python服務器,使用一個瀏覽器打開:
頁面新增了表格和提交(submit)按鈕。在表格中輸入aa並提交,頁面顯示出aa。
下一步要用一些高級包,來簡化以前的代碼。
首先使用SocketServer包來方便的架設服務器。在上面使用socket的過程當中,咱們先設置了socket的類型,而後依次調用bind(),listen(),accept(),最後使用while循環來讓服務器不斷的接受請求。上面的這些步驟能夠經過SocketServer包來簡化。
SocketServer:
# Written by Vamei
# use TCPServer
importSocketServer
''HOST =
8000PORT =
'''text_content =
HTTP/1.x 200 OK
Content-Type: text/html
<head>
<title>WOW</title>
</head>
<html>
<p>Wow, Python Server</p>
<IMG src="test.jpg"/>
<form name="input" action="/" method="post">
First name:<input type="text" name="firstname"><br>
<input type="submit" value="Submit">
</form>
</html>
'''
'test.jpg''rb'f = open(,)
'''pic_content =
HTTP/1.x 200 OK
Content-Type: image/jpg
'''
pic_content = pic_content + f.read()
# This class defines response to each request
class MyTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
# self.request is the TCP socket connected to the client
1024 request = self.request.recv()
print'Connected by'0 ,self.client_address[]
print'Request is' , request
' '0 method = request.split()[]
' '1 src = request.split()[]
if'GET' method ==:
if'/test.jpg' src ==:
content = pic_content
else : content = text_content
self.request.sendall(content)
if'POST' method ==:
'\r\n' form = request.split()
''# Find the empty line idx = form.index()
# Main content of the request entry = form[idx:]
-1'='-1 value = entry[].split()[]
'\n <p>''</p>' self.request.sendall(text_content ++ value +)
######
# More operations, such as put the form into database
# ...
######
# Create the server
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Start the server, and work forever
server.serve_forever()
我創建了一個TCPServer對象,即一個使用TCP socket的服務器。在創建TCPServe的同時,設置該服務器的IP地址和端口。使用server_forever()方法來讓服務器不斷工做(就像原始程序中的while循環同樣)。
咱們傳遞給TCPServer一個MyTCPHandler類。這個類定義瞭如何操做socket。MyTCPHandler繼承自BaseRequestHandler。改寫handler()方法,來具體規定不一樣狀況下服務器的操做。
在handler()中,經過self.request來查詢經過socket進入服務器的請求 (正如咱們在handler()中對socket進行recv()和sendall()操做),還使用self.address來引用socket的客戶端地址。
通過SocketServer的改造以後,代碼仍是不夠簡單。 咱們上面的通訊基於TCP協議,而不是HTTP協議。所以,咱們必須手動的解析HTTP協議。咱們將創建基於HTTP協議的服務器。
HTTP協議基於TCP協議,但增長了更多的規範。這些規範,雖然限制了TCP協議的功能,但大大提升了信息封裝和提取的方便程度。
對於一個HTTP請求(request)來講,它包含有兩個重要信息:請求方法和URL。
請求方法(request method) |
URL |
操做 |
GET |
/ |
發送text_content |
GET |
/text.jpg |
發送pic_content |
POST |
/ |
分析request主體中包含的value(其實是咱們填入表格的內容); 發送text_content和value |
根據請求方法和URL的不一樣,一個大型的HTTP服務器能夠應付成千上萬種不一樣的請求。在Python中,咱們能夠使用SimpleHTTPServer包和CGIHTTPServer包來規定針對不一樣請求的操做。其中,SimpleHTTPServer能夠用於處理GET方法和HEAD方法的請求。它讀取request中的URL地址,找到對應的靜態文件,分析文件類型,用HTTP協議將文件發送給客戶。
SimpleHTTPServer:
咱們將text_content放置在index.html中,並單獨存儲text.jpg文件。若是URL指向index_html的母文件夾時,SimpleHTTPServer會讀取該文件夾下的index.html文件。
我在當前目錄下生成index.html文件:
<head>
<title></title>WOW
</head>
<html>
<p></p>Wow, Python Server
<IMG src="test.jpg"/>
<form name="input" action="/" method="post">
<input type="text" name="firstname"><br>First name:
<input type="submit" value="Submit">
</form>
</html>
改寫Python服務器程序。使用SimpleHTTPServer包中惟一的類SimpleHTTPRequestHandler:
# Written by Vamei
# Simple HTTPsERVER
import SocketServer
import SimpleHTTPServer
HOST = ''
PORT = 8000
# Create the server, SimpleHTTPRequestHander is pre-defined handler in SimpleHTTPServer package
server = SocketServer.TCPServer((HOST, PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
# Start the server
server.serve_forever()
這裏的程序不能處理POST請求。我會在後面使用CGI來彌補這個缺陷。值得注意的是,Python服務器程序變得很是簡單。將內容存放於靜態文件,並根據URL爲客戶端提供內容,這讓內容和服務器邏輯分離。每次更新內容時,我能夠只修改靜態文件,而不用中止整個Python服務器。
這些改進也付出代價。在原始程序中,request中的URL只具備指導意義,我能夠規定任意的操做。在SimpleHTTPServer中,操做與URL的指向密切相關。我用自由度,換來了更加簡潔的程序。
CGIHTTPServer包中的CGIHTTPRequestHandler類繼承自SimpleHTTPRequestHandler類,因此能夠用來代替上面的例子,來提供靜態文件的服務。此外,CGIHTTPRequestHandler類還能夠用來運行CGI腳本。
CGIHTTPServer:
先看看什麼是CGI (Common Gateway Interface)。CGI是服務器和應用腳本之間的一套接口標準。它的功能是讓服務器程序運行腳本程序,將程序的輸出做爲response發送給客戶。整體的效果,是容許服務器動態的生成回覆內容,而沒必要侷限於靜態文件。
支持CGI的服務器程接收到客戶的請求,根據請求中的URL,運行對應的腳本文件。服務器會將HTTP請求的信息和socket信息傳遞給腳本文件,並等待腳本的輸出。腳本的輸出封裝成合法的HTTP回覆,發送給客戶。CGI能夠充分發揮服務器的可編程性,讓服務器變得「更聰明」。
服務器和CGI腳本之間的通訊要符合CGI標準。CGI的實現方式有不少,好比說使用Apache服務器與Perl寫的CGI腳本,或者Python服務器與shell寫的CGI腳本。
爲了使用CGI,咱們須要使用BaseHTTPServer包中的HTTPServer類來構建服務器。Python服務器的改動很簡單。
CGIHTTPServer:
# Written by Vamei
# A messy HTTP server based on TCP socket
import BaseHTTPServer
import CGIHTTPServer
HOST = ''
PORT = 8000
# Create the server, CGIHTTPRequestHandler is pre-defined handler
server = BaseHTTPServer.HTTPServer((HOST, PORT), CGIHTTPServer.CGIHTTPRequestHandler)
# Start the server
server.serve_forever()
CGIHTTPRequestHandler默認當前目錄下的cgi-bin和ht-bin文件夾中的文件爲CGI腳本,而存放於其餘地方的文件被認爲是靜態文件。所以,咱們須要修改一下index.html,將其中form元素指向的action改成cgi-bin/post.py。
<head>
<title></title>WOW
</head>
<html>
<p></p>Wow, Python Server
<IMG src="test.jpg"/>
<form name="input" action="cgi-bin/post.py" method="post">
<input type="text" name="firstname"><br>First name:
<input type="submit" value="Submit">
</form>
</html>
我建立一個cgi-bin的文件夾,並在cgi-bin中放入以下post.py文件,也就是咱們的CGI腳本:
#!/usr/bin/env python
# Written by Vamei
importcgi
form = cgi.FieldStorage()
# Output to stdout, CGIHttpServer will take this as response to the client
print"Content-Type: text/html"# HTML is following
print# blank line, end of headers
print"<p>Hello world!</p>"# Start of content
print"<p>"'firstname'"</p>"+ repr(form[]) +
(post.py須要有執行權限,chmod +x cgi-bin/post.py)
第一行說明了腳本所使用的語言,即Python。 cgi包用於提取請求中包含的表格信息。腳本只負責將全部的結果輸出到標準輸出(使用print)。CGIHTTPRequestHandler會收集這些輸出,封裝成HTTP回覆,傳送給客戶端。
對於POST方法的請求,它的URL須要指向一個CGI腳本(也就是在cgi-bin或者ht-bin中的文件)。CGIHTTPRequestHandler繼承自SimpleHTTPRequestHandler,因此也能夠處理GET方法和HEAD方法的請求。此時,若是URL指向CGI腳本時,服務器將腳本的運行結果傳送到客戶端;當此時URL指向靜態文件時,服務器將文件的內容傳送到客戶端。
更進一步,我可讓CGI腳本執行數據庫操做,好比將接收到的數據放入到數據庫中,以及更豐富的程序操做。相關內容從略。
我使用了Python標準庫中的一些高級包簡化了Python服務器。最終的效果分離靜態內容、CGI應用和服務器,下降三者之間的耦合,讓代碼變得簡單而容易維護。
但願你享受在本身的電腦上架設服務器的過程。
將本節內容裏的兩種Python服務器在實驗樓環境中運行實現,並截圖。
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
Django是Python下的一款網絡服務器框架。Python下有許多款不一樣的框架。Django是重量級選手中最有表明性的一位。許多成功的網站和APP都基於Django。雖然Django之於Python,達不到Rail之於Ruby的一統江湖的地位,但Django無疑也是Python在網絡應用方面的一位主將。使用Django,能夠方便的實現一個功能全面、管理簡便的網站或App後端。這裏從實用的角度出發,介紹如何使用Django框架。
啓動計算機中的Python,嘗試載入Django模塊。若是能夠成功載入,那麼說明Django已經安裝好:
importdjango
print(django.VERSION)
可是實驗樓環境裏Django尚未安裝,能夠在命令行,嘗試使用pip安裝:
sudopip install -i http://mirrors.aliyuncs.com/pypi/simple django
或者使用easy_install:
sudoeasy_install django
在命令行使用下面的命令建立項目:
django-admin.pystartprojectmysite
在當前目錄下,將生成mysite文件夾。其文件樹結構以下:
mysite
manage.py├──
mysite└──
init__.py ├── __
settings.py ├──
urls.py ├──
wsgi.py └──
directoryfiles1, 5
進入mysite目錄,啓動服務器:
cdmysite
python manage.py runserver 8000
上面的8000爲端口號。若是不說明,那麼端口號默認爲8000。
打開瀏覽器,訪問http://127.0.0.1:8000,能夠看到服務器已經在運行:
雖然有一個能跑的服務器,但什麼內容都沒有。
在http協議中能夠看到,網絡服務器是「請求-迴應」的工做模式。客戶向URL發送請求,服務器根據請求,開動後廚,並最終爲客人上菜。Django採用的MVC結構,即點單、廚房、儲藏室分離。
咱們須要一個指揮員,將URL對應分配給某個對象處理,這須要在mysite/mysite下的urls.py設定。Python會根據該程序,將URL請求分給某個廚師。
mysite
manage.py├──
mysite└──
init__.py ├── __
settings.py ├──
urls.py ├──
wsgi.py └──
directoryfiles1, 5
將urls.py修改成:
fromimportdjango.conf.urlspatterns, include, url
fromimportdjango.contribadmin
admin.autodiscover()
''urlpatterns = patterns(,
# Examples:
# url(r'^$', 'mysite.views.home', name='home'),
# url(r'^blog/', include('blog.urls')),
r'^admin/' url(, include(admin.site.urls)),
r'^$''mysite.views.first_page' url(,),
)
咱們添加了最後一行。它將根目錄的URL分配給一個對象進行處理,這個對象是mysite.views.first_page。
用以處理HTTP請求的這一對象還不存在,咱們在mysite/mysite下建立views.py,並在其中定義first_page函數:
# -*- coding: utf-8 -*-
fromimportdjango.httpHttpResponse
def first_page(request):
return"<p>世界好</p>" HttpResponse()
第一行說明字符編碼爲utf-8,爲下面使用中文作準備。first_page函數的功能,是返回http回覆,即這裏的
世界好
。first_page有一個參數request,該參數包含有請求的具體信息,好比請求的類型等,這裏並無用到。
頁面效果以下:
一個網站可能有多個功能。咱們能夠在Django下,以app爲單位,模塊化的管理,而不是將全部的東西都丟到一個文件夾中。在mysite目錄下,運行manange.py,建立新的app:
$pythonmanage.py startapp west
這個新的app叫作west,用來處理西餐。
咱們的根目錄下,出現了一個新的叫作west的文件夾。
mysite/
├── manage.py
├── mysite
__init__│ ├──.py
__init__│ ├──.pyc
│ ├── settings.py
│ ├── settings.pyc
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
└── west
├── admin.py
├── __init__.py
├── models.py
├── tests.py
└── views.py
咱們還須要修改項目設置,說明咱們要使用west。在mysite/mysite/settings.py中,在INSTALLED_APPS中,增長"west":
INSTALLED_APPS= (
'django.contrib.admin' ,
'django.contrib.auth' ,
'django.contrib.contenttypes' ,
'django.contrib.sessions' ,
'django.contrib.messages' ,
'django.contrib.staticfiles' ,
'west' ,
)
能夠看到,除了新增長的west,Django已經默認加載了一些功能性的app,好比用戶驗證、會話管理、顯示靜態文件等。咱們將在之後講解它們的用途。
咱們下面爲APP增長首頁。咱們以前是在mysite/urls.py中設置的URL訪問對象。依然採用相似的方式設置。
另外一方面,爲了去耦合,實現模塊化,咱們應該在west/urls.py中設置URL訪問對象。具體以下:
首先,修改mysite/urls.py:
fromimportdjango.conf.urlspatterns, include, url
fromimportdjango.contribadmin
admin.autodiscover()
''urlpatterns = patterns(,
# Examples:
# url(r'^$', 'mysite.views.home', name='home'),
# url(r'^blog/', include('blog.urls')),
r'^admin/' url(, include(admin.site.urls)),
r'^$''mysite.views.first_page' url(,),
r'^west/''west.urls' url(, include()),
)
注意新增長的最後一行。這裏,咱們提醒指揮員,對於west/的訪問,要參考west/urls.py。
隨後,咱們建立west/urls.py,添加內容:
fromimportdjango.conf.urlspatterns, include, url
''urlpatterns = patterns(,
r'^$''west.views.first_page' url(,),
)
將URL對應west下,views.py中的first_page函數。
最後,在west下,修改views.py爲:
# -*- coding: utf-8 -*-
fromimportdjango.httpHttpResponse
def first_page(request):
return"<p>西餐</p>" HttpResponse()
訪問http://127.0.0.1:8000/west,查看效果。
能夠看到,Django的建立過程很是簡單。但這只是初次嘗試Django。爲了建立一個完整功能的網站,還須要調用Django許多其它的功能。
Django爲多種數據庫後臺提供了統一的調用API。根據需求不一樣,Django能夠選擇不一樣的數據庫後臺。MySQL算是最經常使用的數據庫。咱們這裏將Django和MySQL鏈接。
安裝MYSQLdb:
getsudo apt-install python-mysqldb
在Linux終端下啓動mysql:
sudoservice mysql start
mysql -u root
在MySQL中創立Django項目的數據庫:
DEFAULTmysql> CREATE DATABASE villaCHARSET=utf8;
這裏使用utf8做爲默認字符集,以便支持中文。
在MySQL中創立用戶,並授予相關權限:
'vamei''localhost''shiyanlou'mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON villa.* TO@IDENTIFIED BY;
在settings.py中,將DATABASES對象更改成:
DATABASES = {
'default' : {
'ENGINE''django.db.backends.mysql' :,
'NAME''villa' :,
'USER''root' :,
'HOST''localhost' :,
'PORT''3306' :,
}
}
後臺類型爲mysql。上面包含數據庫名稱和用戶的信息,它們與MySQL中對應數據庫和用戶的設置相同。Django根據這一設置,與MySQL中相應的數據庫和用戶鏈接起來。此後,Django就能夠在數據庫中讀寫了。
MySQL是關係型數據庫。但在Django的幫助下,咱們不用直接編寫SQL語句。Django將關係型的表(table)轉換成爲一個類(class)。而每一個記錄(record)是該類下的一個對象(object)。咱們能夠使用基於對象的方法,來操縱關係型的MySQL數據庫。
在傳統的MySQL中,數據模型是表。在Django下,一個表爲一個類。表的每一列是該類的一個屬性。在models.py中,咱們建立一個只有一列的表,即只有一個屬性的類:
fromimportdjango.dbmodels
class Character(models.Model):
200 name = models.CharField(max_length=)
def __unicode__(self):
return self.name
類Character定義了數據模型,它須要繼承自models.Model。在MySQL中,這個類其實是一個表。表只有一列,爲name。能夠看到,name屬性是字符類型,最大長度爲200。
類Character有一個__unicode__()方法,用來講明對象的字符表達方式。若是是Python 3,定義__str__()方法,實現相同的功能。
命令Django同步數據庫。Django根據west/models.py中描述的數據模型,在MySQL中真正的建立各個關係表:
pythonmanage.pysyncdb
pythonmanage.pymakemigrationswest
pythonmanage.pymigrate
同步數據庫後,Django將創建相關的MySQL表格,並要求你建立一個超級用戶:
Creating tables ...
Creating table django_admin_log
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table west_character
You just installed Django's auth system, which means you don't have any superusers defined.
createnownoWould you like toone? (yes/): yes
touse'tommy'Username (leave blank): root
Email address: xxxxx@xmail.com
Password:
Password(again):
Superuser created successfully.
SQLInstalling custom...
indexesInstalling...
0objectfrom0Installed(s)fixture(s)
數據模型創建了。打開MySQL命令行(提示輸入密碼:shiyanlou):
$mysql-u root
查看數據模型:
USEvilla;
SHOWTABLES;
SHOWCOLUMNSFROMwest_character;
最後一個命令返回Character類的對應表格:
+-------+--------------+------+-----+---------+----------------+
| Field || Null || Default ||Type KeyExtra
+-------+--------------+------+-----+---------+----------------+
|| int(11) || PRI || auto_increment |id NO NULL
| name |200| NO || NULL ||varchar()
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
能夠看到,Django還自動增長了一個id列,做爲記錄的主鍵(Primary Key)。
數據模型雖然創建了,但尚未數據輸入。爲了簡便,咱們手動添加記錄。打開MySQL命令行,並切換到相應數據庫。添加記錄:
INSERTINTOnameValues'Vamei'west_character ()();
INSERTINTOnameValues'Django'west_character ()();
INSERTINTOnameValues'John'west_character ()();
查看記錄:
SELECTFROM*west_character;
能夠看到,三個名字已經錄入數據庫。
下面咱們從數據庫中取出數據,並返回給http請求。在west/views.py中,添加視圖。對於對應的請求,咱們將從數據庫中讀取全部的記錄,而後返回給客戶端:
# -*- coding: utf-8 -*-
fromimportdjango.httpHttpResponse
fromimportwest.modelsCharacter
def staff(request):
staff_list = Character.objects.all()
staff_str = map(str, staff_list)
return"<p>"' '"</p>" HttpResponse(+.join(staff_str) +)
能夠看到,咱們從west.models中引入了Character類。經過操做該類,咱們能夠讀取表格中的記錄。
爲了讓http請求能找到上面的程序,在west/urls.py增長url導航:
fromimportdjango.conf.urlspatterns, include, url
''urlpatterns = patterns(,
r'^staff/''west.views.staff' url(,),
)
運行服務器。在瀏覽器中輸入URL:
127.0.0.1:8000/west/staff
查看效果,從數據庫讀出數據,顯示在頁面:
Django使用類和對象接口,來操縱底層的數據庫。有了數據庫,就有了站點內容的大本營。
按照實驗過程操做一遍並截圖。
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
在以前的程序中,咱們直接生成一個字符串,做爲http回覆,返回給客戶端。這一過程當中使用了django.http.HttpResponse()。
在這樣的一種回覆生成過程當中,咱們實際上將數據和視圖的格式混合了到上面的字符串中。看似方便,卻爲咱們的管理帶來困難。想像一個成熟的網站,其顯示格式會有許多重複的地方。若是能夠把數據和視圖格式分離,就能夠重複使用同一視圖格式了。
Django中自帶的模板系統,能夠將視圖格式分離出來,做爲模板使用。這樣,不但視圖能夠容易修改,程序也會顯得美觀大方。
咱們拿一個獨立的templay.html文件做爲模板。它放在templates/west/文件夾下。文件系統的結構如今是:
mysite/
├── mysite
├── templates
│ └── west
└── west
templay.html文件的內容是:
<h1></h1>{{ label }}
能夠看到,這個文件中,有一個奇怪的雙括號包起來的陌生人。這就是咱們將來數據要出現的地方。而相關的格式控制,即
標籤,則已經標明在該模板文件中。
咱們須要向Django說明模板文件的搜索路徑,修改mysite/settings.py,添加:
# Template dir
# This is for django 1.8
TEMPLATES = [
{
'BACKEND':
'django.template.backends.django.DjangoTemplates',
'DIRS': (os.path.
join(BASE_DIR,
'templates/west'),),
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.tz',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
若是還有其它的路徑用於存放模板,能夠增長該元組中的元素,以便Django能夠找到須要的模板。
咱們如今修改west/views.py,增長一個新的對象,用於向模板提交數據:
# -*- coding: utf-8 -*-
#from django.http import HttpResponse
from django.shortcuts
import render
def templay(request):
context = {}
context[
'label'] =
'Hello World!'
return render(request,
'templay.html', context)
能夠看到,咱們這裏使用render來替代以前使用的HttpResponse。render還使用了一個詞典context做爲參數。這就是咱們的數據。
context中元素的鍵值爲'label',正對應剛纔的「陌生人」的名字。這樣,該context中的‘label’元素值,就會填上模板裏的坑,構成一個完整的http回覆。
做爲上節內容的一個小練習,自行修改west/urls.py,讓http://127.0.0.1:8000/west/templay的URL請求能夠找到相應的view對象。
訪問http://127.0.0.1:8000/west/templay,能夠看到頁面:
再來回顧一下整個流程。west/views.py中的templay()在返回時,將環境數據context傳遞給模板templay.html。Django根據context元素中的鍵值,將相應數據放入到模板中的對應位置,生成最終的http回覆。
這一模板系統能夠與Django的其它功能相互合做。上一回,咱們從數據庫中提取出了數據。若是將數據庫中的數據放入到context中,那麼就能夠將數據庫中的數據傳送到模板。
修改上次的west/views.py中的staff:
def staff(request):
staff_list = Character.objects.all()
staff_str = map(str, staff_list)
'label'' ' context = {:.join(staff_str)}
return'templay.html' render(request,, context)
Django實際上提供了豐富的模板語言,能夠在模板內部有限度的編程,從而更方便的編寫視圖和傳送數據。
咱們下面體驗一下最多見的循環與選擇。
上面的staff中的數據其實是一個數據容器,有三個元素。剛纔咱們將三個元素鏈接成一個字符串傳送。
實際上,利用模板語言,咱們能夠直接傳送數據容器自己,再循環顯示。修改staff()爲:
def staff(request):
staff_list = Character.objects.all()
return'templay.html''staffs' render(request,, {: staff_list})
從數據庫中查詢到的三個對象都在staff_list中。咱們直接將staff_list傳送給模板。
將模板templay.html修改成:
forin{%itemstaffs %}
<p>{{ item.id }}, {{item}}</p>
{% endfor %}
咱們以相似於Python中for循環的方式來定義模板中的for,以顯示staffs中的每一個元素。
還能夠看到,對象.屬性名的引用方式能夠直接用於模板中。
選擇結構也與Python相似。根據傳送來的數據是否爲True,Django選擇是否顯示。使用方式以下:
if{%condition1 %}
... display 1
elif{%condiiton2 %}
... display 2
else{%%}
... display 3
{% endif %}
其中的elif和else和Python中同樣,是能夠省略的。
模板能夠用繼承的方式來實現複用。咱們下面用templay.html來繼承base.html。這樣,咱們能夠使用base.html的主體,只替換掉特定的部分。
新建templates/west/base.html:
<html>
<head>
<title></title> templay
</head>
<body>
<h1></h1> come from base.html
{% block mainbody %}
<p></p> original
{% endblock %}
</body>
</html>
該頁面中,名爲mainbody的block標籤是能夠被繼承者們替換掉的部分。
咱們在下面的templay.html中繼承base.html,並替換特定block:
"base.html"{% extends%}
{% block mainbody %}
forin{%itemstaffs %}
<p>{{ item.id }},{{ item.name }}</p>
{% endfor %}
{% endblock %}
第一句說明templay.html繼承自base.html。能夠看到,這裏相同名字的block標籤用以替換base.html的相應block。
HTTP協議以「請求-回覆」的方式工做。客戶發送請求時,能夠在請求中附加數據。服務器經過解析請求,就能夠得到客戶傳來的數據,並根據URL來提供特定的服務。
HTML文件中能夠包含表格標籤。HTML表格的目的是幫助用戶構成HTTP請求,把數據用GET或者POST的方法,傳遞給某一URL地址。下面是一個表格的例子:
<form action="/west/investigate/" method="get">
<input type="text" name="staff">
<input type="submit" value="Submit">
</form>
這裏的form標籤有兩個屬性。action用於說明URL地址,method說明請求的方法。
表格中還包含有兩個input標籤,即兩個輸入欄目。根據type的不一樣,第一個爲一個文本框,第二個爲一個提交按鈕。name爲輸入欄的名字。服務器在解析數據時,將以name爲索引。
咱們能夠將上面的表格直接存入模板form.html,並在west/views.py中定義一個視圖form()來顯示錶格:
fromimportdjango.shortcutsrender
def form(request):
return'form.html' render(request,)
設置urls.py,讓對[site]/west/form/的訪問,指向該視圖。
最後,咱們在west/views.py中定義investigate()來處理該表格提交的數據:
fromimportdjango.shortcutsrender
def investigate(request):
'staff' rlt = request.GET[]
return HttpResponse(rlt)
能夠看到,HTTP請求的相關信息,包括請求的方法,提交的數據,都包含在request參數中。
表格是經過GET方法提交的。咱們能夠經過request.GET['staff'],來得到name爲staff的輸入欄的數據。該數據是一個字符串。investigate()將直接顯示該字符串。
設置urls.py,讓該處理函數對應action的URL([site]/west/investigate/)。
當咱們訪問http://127.0.0.1:8000/west/form時,將顯示:
提交表格後,頁面將轉到[site]/west/investigate。investigate()讀取字符串後,在頁面上顯示出來。
上面咱們使用了GET方法。視圖顯示和請求處理分紅兩個函數處理。
提交數據時更經常使用POST方法。咱們下面使用該方法,並用一個URL和處理函數,同時顯示視圖和處理請求。
先建立模板investigate.html
<form action="/west/investigate/" method="post">
{% csrf_token %}
<input type="text" name="staff">
<input type="submit" value="Submit">
</form>
<p></p>{{ rlt }}
咱們修改提交表格的方法爲post。在模板的末尾,咱們增長一個rlt記號,爲表格處理結果預留位置。
表格後面還有一個{% csrf_token %}的標籤。csrf全稱是Cross Site Request Forgery。這是Django提供的防止假裝提交請求的功能。POST方法提交的表格,必須有此標籤。
在west/views.py中,用investigate()來處理表格:
fromimportdjango.shortcutsrender
fromimportdjango.core.context_processorscsrf
def investigate(request):
ctx ={}
ctx.update(csrf(request))
if request.POST:
'rlt''staff' ctx[] = request.POST[]
return"investigate.html" render(request,, ctx)
這裏的csrf()是和上面的{% csrf_token %}對應。咱們在這裏不深究。
看程序的其它部分。對於該URL,可能有GET或者POST方法。if的語句有POST方法時,額外的處理,即提取表格中的數據到環境變量。
最終效果以下:
咱們還可讓客戶提交的數據存入數據庫。使用莊園疑雲中建立的模型。咱們將客戶提交的字符串存入模型Character。
修改west/views.py的investigate():
fromimportdjango.shortcutsrender
fromimportdjango.core.context_processorscsrf
fromimportwest.modelsCharacter
def investigate(request):
if request.POST:
'staff' submitted = request.POST[]
new_record = Character(name = submitted)
new_record.save()
ctx ={}
ctx.update(csrf(request))
all_records = Character.objects.all()
'staff' ctx[] = all_records
return"investigate.html" render(request,, ctx)
在POST的處理部分,咱們調用Character類建立新的對象,並讓該對象的屬性name等於用戶提交的字符串。經過save()方法,咱們讓該記錄入庫。
隨後,咱們從數據庫中讀出全部的對象,並傳遞給模板。
咱們還須要修改模板investigate.html,以更好的顯示:
<form action="/west/investigate/" method="post">
{% csrf_token %}
<input type="text" name="staff">
<input type="submit" value="Submit">
</form>
{% for person in staff %}
<p></p>{{ person }}
{% endfor %}
咱們使用模板語言的for,來顯示全部的記錄。
效果以下:
客戶提交數據後,服務器每每須要對數據作一些處理。好比檢驗數據,看是否符合預期的長度和數據類型。在必要的時候,還須要對數據進行轉換,好比從字符串轉換成整數。這些過程一般都至關的繁瑣。
Django提供的數據對象能夠大大簡化這一過程。該對象用於說明表格所預期的數據類型和其它的一些要求。這樣Django在得到數據後,能夠自動根據該表格對象的要求,對數據進行處理。
修改west/views.py:
fromimportdjango.shortcutsrender
fromimportdjango.core.context_processorscsrf
fromimportwest.modelsCharacter
fromimportdjangoforms
class CharacterForm(forms.Form):
200 name = forms.CharField(max_length =)
def investigate(request):
if request.POST:
form = CharacterForm(request.POST)
if form.is_valid():
'name' submitted = form.cleaned_data[]
new_record = Character(name = submitted)
new_record.save()
form = CharacterForm()
ctx ={}
ctx.update(csrf(request))
all_records = Character.objects.all()
'staff' ctx[] = all_records
'form' ctx[] = form
return"investigate.html" render(request,, ctx)
上面定義了CharacterForm類,並經過屬性name,說明了輸入欄name的類型爲字符串,最大長度爲200。
在investigate()函數中,咱們根據POST,直接創立form對象。該對象能夠直接判斷輸入是否有效,並對輸入進行預處理。空白輸入被視爲無效。
後面,咱們再次建立一個空的form對象,並將它交給模板顯示。
在模板investigate.html中,咱們能夠直接顯示form對象:
<form action="/west/investigate/" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
{% for person in staff %}
<p></p>{{ person }}
{% endfor %}
若是有多個輸入欄,咱們能夠用相同的方式直接顯示整個form,而不是加入許多個標籤。
效果以下:
一、顯示上面「流程」部分的「staff」頁面。
二、按照實驗過程操做一遍並截圖。
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
Django提供一個管理數據庫的app,即django.contrib.admin。這是Django最方便的功能之一。經過該app,咱們能夠直接經由web頁面,來管理咱們的數據庫。這一工具,主要是爲網站管理人員使用。
這個app一般已經預裝好,你能夠在mysite/settings.py中的INSTALLED_APPS看到它。
admin界面位於[site]/admin這個URL。這一般在mysite/urls.py中已經設置好。好比,下面是個人urls.py:
fromimportdjango.conf.urlspatterns, include, url
fromimportdjango.contribadmin
# adminadmin.autodiscover()
''urlpatterns = patterns(,
r'^admin/'# admin url(, include(admin.site.urls)),
r'^west/''west.urls' url(, include()),
)
爲了讓admin界面管理某個數據模型,咱們須要先註冊該數據模型到admin。好比,咱們以前在west中建立的模型Character。修改west/admin.py:
fromimportdjango.contribadmin
fromimportwest.modelsCharacter
# Register your models here.
admin.site.register(Character)
訪問http://127.0.0.1:8000/admin,登陸後,能夠看到管理界面:
這個頁面除了west.characters外,還有用戶和組信息。它們來自Django預裝的Auth模塊。咱們將在之後處理用戶管理的問題。
管理頁面的功能強大,徹底有能力處理更加複雜的數據模型。
先在west/models.py中增長一個更復雜的數據模型:
fromimportdjango.dbmodels
# Create your models here.
class Contact(models.Model):
200 name = models.CharField(max_length=)
0 age = models.IntegerField(default=)
email = models.EmailField()
def __unicode__(self):
return self.name
class Tag(models.Model):
contact = models.ForeignKey(Contact)
50 name = models.CharField(max_length=)
def __unicode__(self):
return self.name
這裏有兩個表。Tag以Contact爲外部鍵。一個Contact能夠對應多個Tag。
咱們還能夠看到許多在以前沒有見過的屬性類型,好比IntegerField用於存儲整數。
同步數據庫:
$pythonmanage.py syncdb
在west/admin.py註冊多個模型並顯示:
fromimportdjango.contribadmin
fromimportwest.modelsCharacter,Contact,Tag
# Register your models here.
admin.site.register([Character, Contact, Tag])
模型將在管理頁面顯示。好比Contact的添加條目的頁面以下:
咱們能夠自定義管理頁面,來取代默認的頁面。好比上面的"add"頁面。咱們想只顯示name和email部分。修改west/admin.py:
fromimportdjango.contribadmin
fromimportwest.modelsCharacter,Contact,Tag
# Register your models here.
class ContactAdmin(admin.ModelAdmin):
'name''email' fields = (,)
admin.site.register(Contact, ContactAdmin)
admin.site.register([Character, Tag])
上面定義了一個ContactAdmin類,用以說明管理頁面的顯示格式。裏面的fields屬性,用以說明要顯示的輸入欄。咱們沒有讓"age"顯示。因爲該類對應的是Contact數據模型,咱們在註冊的時候,須要將它們一塊兒註冊。顯示效果以下:
咱們還能夠將輸入欄分塊,給每一塊輸入欄以本身的顯示格式。修改west/admin.py爲:
fromimportdjango.contribadmin
fromimportwest.modelsCharacter,Contact,Tag
# Register your models here.
class ContactAdmin(admin.ModelAdmin):
fieldsets = (
'Main' [,{
'fields''name''email' :(,),
}],
'Advance' [,{
'classes''collapse'# CSS : (,),
'fields''age' : (,),
}]
)
admin.site.register(Contact, ContactAdmin)
admin.site.register([Character, Tag])
上面的欄目分爲了Main和Advance兩部分。classes說明它所在的部分的CSS格式。這裏讓Advance部分收斂起來:
Advance部分旁邊有一個Show按鈕,用於展開。
上面的Contact是Tag的外部鍵,因此有外部參考的關係。而在默認的頁面顯示中,將二者分離開來,沒法體現出二者的從屬關係。咱們能夠使用Inline顯示,讓Tag附加在Contact的編輯頁面上顯示。
修改west/admin.py:
fromimportdjango.contribadmin
fromimportwest.modelsCharacter,Contact,Tag
# Register your models here.
class TagInline(admin.TabularInline):
model = Tag
class ContactAdmin(admin.ModelAdmin):
# Inline inlines = [TagInline]
fieldsets = (
'Main' [,{
'fields''name''email' :(,),
}],
'Advance' [,{
'classes''collapse' : (,),
'fields''age' : (,),
}]
)
admin.site.register(Contact, ContactAdmin)
admin.site.register([Character])
效果以下:
在Contact輸入數條記錄後,Contact的列表頁看起來以下:
咱們也能夠自定義該頁面的顯示,好比在列表中顯示更多的欄目,只須要在ContactAdmin中增長list_display屬性:
fromimportdjango.contribadmin
fromimportwest.modelsCharacter,Contact,Tag
# Register your models here.
class ContactAdmin(admin.ModelAdmin):
'name''age''email'# list list_display = (,,)
admin.site.register(Contact, ContactAdmin)
admin.site.register([Character, Tag])
列表頁新的顯示效果以下:
咱們還能夠爲該列表頁增長搜索欄。搜索功能在管理大量記錄時很是有用。使用search_fields說明要搜索的屬性:
fromimportdjango.contribadmin
fromimportwest.modelsCharacter,Contact,Tag
# Register your models here.
class ContactAdmin(admin.ModelAdmin):
'name''age''email' list_display = (,,)
'name' search_fields = (,)
admin.site.register(Contact, ContactAdmin)
admin.site.register([Character])
效果以下:
以前咱們瞭解了:
上面的功能模塊容許咱們作出一個具備互動性的站點,但沒法驗證用戶的身份。咱們此次瞭解用戶驗證部分。經過用戶驗證,咱們能夠根據用戶的身份,提供不一樣的服務。
一個Web應用的用戶驗證是它的基本組成部分。咱們在使用一個應用時,老是從「登陸」開始,到「登出」結束。另外一方面,用戶驗證又和網站安全、數據庫安全息息相關。HTTP協議是無狀態的,但咱們能夠利用儲存在客戶端的cookie或者儲存在服務器的session來記錄用戶的訪問。
Django有管理用戶的模塊,即django.contrib.auth。你能夠在mysite/settings.py裏看到,這個功能模塊已經註冊在INSTALLED_APPS中。利用該模塊,你能夠直接在邏輯層面管理用戶,不須要爲用戶創建模型,也不須要手工去實現會話。
你能夠在admin頁面直接看到用戶管理的對話框,即Users。從這裏,你能夠在這裏建立、刪除和修改用戶。點擊Add增長用戶daddy,密碼爲daddyiscool。
在admin頁面下,咱們還能夠控制不一樣用戶組對數據庫的訪問權限。咱們能夠在Groups中增長用戶組,設置用戶組對數據庫的訪問權限,並將用戶加入到某個用戶組中。
在這一章節中,咱們創立一個新的app,即users。下文的模板和views.py,都針對該app。
咱們創建一個簡單的表格。用戶經過該表格來提交登錄信息,並在Django服務器上驗證。若是用戶名和密碼正確,那麼登入用戶。
咱們首先增長一個登陸表格:
"form""/login""post"<form role=action=method=>
<label>Username</label>
"text"'username' <input type=name=>
<label>Password</label>
"password""password" <input name=type=>
<input type="submit" value="Submit">
</form>
咱們在views.py中,定義處理函數user_login(),來登入用戶:
# -*- coding: utf-8 -*-
fromimportdjango.shortcutsrender, redirect
fromimportdjango.core.context_processorscsrf
fromimportdjango.contrib.auth*
def user_login(request):
'''
login
'''
if request.POST:
'' username = password =
'username' username = request.POST.get()
'password' password = request.POST.get()
user = authenticate(username=username, password=password)
ifisnotNoneand useruser.is_active:
login(request, user)
return'/' redirect()
ctx = {}
ctx.update(csrf(request))
return'login.html' render(request,,ctx)
上面的authenticate()函數,能夠根據用戶名和密碼,驗證用戶信息。而login()函數則將用戶登入。它們來自於django.contrib.auth。
做爲替換,咱們能夠使用特別的form對象,而不自行定義表格。這將讓代碼更簡單,並且提供必定的完整性檢驗。
有時用戶但願能銷燬會話。咱們能夠提供一個登出的URL,即/users/logout。登入用戶訪問該URL,便可登出。在views.py中,增長該URL的處理函數:
# -*- coding: utf-8 -*-
fromimportdjango.shortcutsredirect
def user_logout(request):
'''
logout
URL: /users/logout
'''
logout(request)
return'/' redirect()
咱們修改urls.py,讓url對應user_logout()。訪問http://127.0.0.1/users/logout,就能夠登出用戶。
上面說明了如何登入和登出用戶,但尚未真正開始享受用戶驗證帶來的好處。用戶登錄的最終目的,就是爲了讓服務器能夠區別對待不一樣的用戶。好比說,有些內容只能讓登錄用戶看到,有些內容則只能讓特定登錄用戶看到。咱們下面將探索如何實現這些效果。
在Django中,對用戶身份的檢驗,主要是在views.py中進行。views.py是鏈接模型和視圖的中間層。HTTP請求會轉給views.py中的對應處理函數處理,併發回回復。在views.py的某個處理函數準備HTTP回覆的過程當中,咱們能夠檢驗用戶是否登錄。根據用戶是否登錄,咱們能夠給出不一樣的回覆。最原始的方式,是使用if式的選擇結構:
# -*- coding: utf-8 -*-
fromimportdjango.httpHttpResponse
def diff_response(request):
if request.user.is_authenticated():
"<p>my dear user</p>" content =
else :
"<p>you wired stranger</p>" content =
return HttpResponse(content)
能夠看到,用戶的登陸信息包含在request.user中,is_authenticated()方法用於判斷用戶是否登陸,若是用戶沒有登陸,那麼該方法將返回false。該user對象屬於contrib.auth.user類型,還有其它屬性可供使用,好比:
屬性 |
功能 |
get_username() |
返回用戶名 |
set_password() |
設置密碼 |
get_fullname() |
返回姓名 |
last_login |
上次登陸時間 |
date_joined |
帳戶建立時間 |
在Django中,咱們還能夠利用裝飾器,根據用戶的登陸情況,來決定views.py中處理函數的顯示效果。相對於上面的if結構,裝飾器使用起來更加方便。下面的user_only()是views.py中的一個處理函數。
fromimportdjango.contrib.auth.decoratorslogin_required
fromimportdjango.httpHttpResponse
@login_required
def user_only(request):
return"<p>This message is for logged in user only.</p>" HttpResponse()
注意上面的裝飾器login_required,它是Django預設的裝飾器。user_only()的回覆結果只能被登陸用戶看到,而未登陸用戶將被引導到其餘頁面。
Django中還有其它的裝飾器,用於修飾處理函數。相應的http回覆,只能被特殊的用戶看到。好比user_passes_test,容許的用戶必須知足特定標準,而這一標準是能夠用戶自定義的。好比下面,在views.py中增添:
fromimportdjango.contrib.auth.decoratorsuser_passes_test
fromimportdjango.httpHttpResponse
def name_check(user):
return'vamei' user.get_username() ==
@user_passes_test(name_check)
def specific_user(request):
return"<p>for Vamei only</p>" HttpResponse()
裝飾器帶有一個參數,該參數是一個函數對象name_check。當name_check返回真值,即用戶名爲vamei時,specific_user的結果才能被用戶看到。
進一步,用戶是否登錄這一信息,也能夠直接用於模板。比較原始的方式是把用戶信息直接做爲環境數據,提交給模板。然而,這並非必須的。事實上,Django爲此提供了捷徑:咱們能夠直接在模板中調用用戶信息。好比下面的模板:
if{%user.is_authenticated %}
true <p>Welcome, my genuine user, mylove.</p>
else{%%}
notnot <p>Sorry,login, you areyet my sweetheart. </p>
{% endif %}
不須要環境變量中定義,咱們就能夠直接在模板中引用user。這裏,模板中調用了user的一個方法,is_authenticated,將根據用戶的登陸狀況,返回真假值。須要注意,和正常的Python程序不一樣,在Django模板中調用方法並不須要後面的括號。
咱們上面利用了admin管理頁面來增長和刪除用戶。這是一種簡便的方法,但並不能用於通常的用戶註冊的情境。咱們須要提供讓用戶自主註冊的功能。這可讓站外用戶提交本身的信息,生成本身的帳戶,並開始做爲登錄用戶使用網站。
用戶註冊的基本原理很是簡單,即創建一個提交用戶信息的表格。表格中至少包括用戶名和密碼。相應的處理函數提取到這些信息後,創建User對象,並存入到數據庫中。
咱們能夠利用Django中的UserCreationForm,比較簡潔的生成表格,並在views.py中處理表格:
fromimportdjango.contrib.auth.formsUserCreationForm
fromimportdjango.shortcutsrender, redirect
fromimportdjango.core.context_processorscsrf
def register(request):
if'POST' request.method ==:
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
return"/" redirect()
else :
form = UserCreationForm()
'form' ctx = {: form}
ctx.update(csrf(request))
return"register.html" render(request,, ctx)
相應的模板register.html以下:
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Register">
</form>
前面介紹了Django最主要的幾個方面:數據庫,模板,動態生成頁面等。但都是使用python manage.py runserver來運行服務器。這是一個實驗性的web服務器,不適用於正常的站點運行。咱們須要一個能夠穩定而持續的服務器。這個服務器負責監聽http端口,將收到的請求交給Django處理,將Django的回覆發還給客戶端。
這樣的持續性服務器能夠有不少選擇,好比apache, Nginx, lighttpd等。這裏將使用最多見的apache服務器。服務器和Django之間經過Python的web服務接口WSGI鏈接,所以咱們一樣須要apache下的mod_wsgi模塊。
首先須要安裝apache2和mod_wsgi。咱們能夠使用apt-get安裝:
installsudo apt-getapache2
getinstallmodsudo apt-libapache2--wsgi
在apache的配置文件/etc/apache2/apache2.conf中增長下面的配置:
# Django
WSGIScriptAlias/ /home/shiyanlou/mysite/mysite/wsgi.py
WSGIPythonPath/home/shiyanlou/mysite
<Directory /home/shiyanlou/mysite/mysite>
<Files wsgi.py>
Order deny,allow
Requireall granted
</Files>
</Directory>
上面的配置中/home/shiyanlou/mysite是Django項目所在的位置。而/home/shiyanlou/mysite/mysite/wsgi.py是Django項目中z自動建立的文件。
能夠看到,利用WSGIScriptAlias,咱們實際上將URL /對應了wsgi接口程序。這樣,當咱們訪問根URL時,訪問請求會經由WSGI接口,傳遞給Django項目mysite。
配置好後,重啓apache2:
sudo/etc/init.d/apache2 restart
使用瀏覽器,能夠檢查效果:
Django的主要功能是動態的生成HTTP回覆。不少媒體文件是靜態存儲的,如.js文件,.css文件和圖片文件。這些文件變更的頻率較小。咱們但願靜態的提供這些文件,而不是動態的生成。這樣既能夠減少服務器的負擔,也便於在瀏覽器緩存,提升用戶體驗。
咱們能夠在apache2.conf中添加以下配置:
Alias/media/ /home/shiyanlou/media/
Alias/static/ /home/shiyanlou/static/
<Directory /home/shiyanlou/static/>
Orderdeny,allow
Requireallgranted
</Directory>
<Directory /home/shiyanlou/media/>
Orderdeny,allow
Requireallgranted
</Directory>
# Django
WSGIScriptAlias/ /home/shiyanlou/mysite/mysite/wsgi.py
WSGIPythonPath/home/shiyanlou/mysite
<Directory /home/shiyanlou/mysite/mysite/ >
<Files wsgi.py>
Order deny,allow
Requireall granted
</Files>
</Directory>
這樣,/static/和/media/這兩個URL的訪問將引導向存有靜態文件的/home/shiyanlou/static/和/home/shiyanlou/media/,apache將直接向客戶提供這兩個文件夾中的靜態文件。而剩下的URL訪問,將導向WSGI接口,由Django動態處理。
在/home/shiyanlou/static/中放入文件revenge.jpg,訪問http://localhost/static/revenge:
雲平臺或者服務器的部署是一個大的課題,這裏沒法深刻到全部的細節。幸運的是,在網上有豐富的資料。你能夠根據本身的平臺和問題,搜索相應的資料。
在Django的debug模式下,咱們能夠在app文件夾中創建static目錄,放入靜態文件。Django將自動搜索到其中的靜態文件。但這一方法有很大的安全隱患,只適用於開發。