本篇基於Python 2.7.9
根據廖雪峯Python教程整理
- URL:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000
- 安裝及IDE
上Python官網下載安裝包。
配置環境變量:C:\Python27。
cmd中輸入 python 出現信息則爲安裝正確。
Minor Problem:
用eclipse IDE 寫Python時可能會出現輸出中文(print 「中文」)亂碼的狀況,這時候要在文件頭加入
若是不想每次都更改,能夠再Preferences-PyDev-Editor-Templates中的Empty中加入:
#-*- encoding: utf-8 -*-'''Created on ${date}@author: ${user}'''${cursor}
python做爲更高級的語言,解釋器是由C或者Java等其餘語言實現的,因此在速度上要比較慢。
python中換行和縮進對齊是劃分代碼塊一種方法,結尾沒有分號。結尾若有冒號,則下面縮進的語句視爲代碼塊。(縮進通常4個空格)
python源代碼不能加密,發佈既要發佈源代碼。
在CMD中也能夠寫Python,使用exit()退出。
註釋使用#標記一行。使用'''@Notes'''能夠標記多行。
大小寫敏感,如bool型的True和False開頭大寫。
輸出print:
加字符便可輸出指定文字:
不一樣字符能夠用,鏈接。可是用,鏈接至關於中間增長一個空格:
不想增長空格,可使用:
-
print 'hello''world'print 'hello'+'world'
print自帶換行,print就換一行,也可在字符中使用
\n。
若是不想換行,能夠用:
print "hello",print "world"
可是這種狀況自帶空格了(+顯然不能夠),若是要求更高的自由度,可使用:(須要import sys)
sys.stdout.write("Hello World")
相似於java的system.out.print();
print中‘’和「」都是能夠的。
固然Python也支持格式化輸出:
print "Hi %s, your score is %d." %('Aran',99)
輸入
raw_input():
調用raw_input()函數就至關於等待用戶輸入,能夠用:
把結果存入name中。
其中raw_input()能夠帶參數,參數是字符串,至關於輸入提示。返回的結構都是字符串類型。
順便提下input()函數,它接受的對象是一個Python表達式,返回的是表達式的結果。
在name = input()後輸入:
4==5
name的值是是bool型的False
python中可使用type(para)來獲得para的數據類型。也能夠用isinstance(para, int)來判斷。
整型int
浮點型float
字符串型str:
是str不是String。
「」‘’嵌套是可使用轉義符\。
使用r''表示''內字符不轉義。
布爾型bool:
True、False
bool值得運算符有and、or、not三種運算
判斷使用 ==
空值None:
內建類型None表示一個空對象,沒有方法和屬性。
None是一個特殊的常量。
None不是0。
None不是空字符串。
None和任何其餘的數據類型比較永遠返回False。
None有本身的數據類型NoneType。
你能夠將None複製給任何變量,可是你不能建立其餘NoneType對象。
變量:
大小寫英文、數字和_的組合,且不能用數字開頭。
深刻理解Python變量在內存中的實現(和Java相似):
當咱們寫
a = 'ABC'
時,Python解釋器是這麼執行的:
1. 在內存中建立一個‘ABC’的字符串
2. 在內存中穿件一個名爲a的變量,並指向‘ABC’
由此當咱們寫
a = 'ABC'
b = a
a = 'XYZ'
時,系統會這樣執行:
第一個賦值是指向。第二個賦值是將a的指向給予b。
理解這一點,對後面複雜函數對象的理解頗有意義。
ord()
函數將字符轉換爲ASCII碼輸出。
chr()
函數將ASCII碼轉換爲字符輸出。
list是一個可變的有序數據集。首元素從0開始。
name = ['Mike','Bob','Tracy']
python支持倒序。能夠是從0~2,也能夠是從-1~-3。超出範圍後python會報IndexError錯誤。
list中數據類型不必定要相同。裏面也能夠繼續使用list:
p = ['asp', 'php']s = ['python', 'java', p, 'scheme']print s[2][1]
固然也能夠爲空:L = []
經常使用函數有:
len():
能夠獲得list的元素個數:len(name)
append():
追加到list末尾:append('Adam')
insert(): 插入到某一索引:insert(1,'Jack')
pop():
刪除某一位置的元素,沒有參數則爲最後一個元素:
pop() pop(1)
tuple:
tuple是一個不可變的有序數據集。
name = (
'Mike','Bob','Tracy'
)
其它的操做和list相似。只是定義一個元素的tuple時,爲了和()區分開來,須要加逗號:name = ('Mike',)
重點解釋下不可變:
是指tuple中元素的指向不會變化,
即指向'a',就不能改爲指向'b',指向一個list,就不能改爲指向其餘對象,但指向的這個list自己是可變的!
看下面一段代碼:
a = ['a1','a2']b = ['b1','b2']name = (a,b)a = ['A1','A2']b[0] = 'B1'b[1] = 'B2'print name
輸出的結果爲:
(['a1', 'a2'], ['B1', 'B2'])
這樣理解:name中首元素指向了['a1','a2']的內存地址,雖而後來變量a指向了別處,但name中首元素所指向的內存地址中數據沒有變化的。name中第二個元素指向了['b1','b2']的內存地址,可是後來該地址中的數據發生了變化,變成了['B1','B2']。全部name中就也有了變化。
相似於Java語言中的Map,使用key-value進行存儲,是可變的無序不重複數據集。
舉個栗子(名字—成績):
dd = {'Mike':95, 'Bob':96, 'Jenny':92, 'Aran':99}
對於重複的key,後寫入的key-value會覆蓋前面的。
基於key來獲得value:dd['Mike']。若是不存在該key則會報
KeyError的錯誤。
其餘函數:
=:
賦值會覆蓋掉原來key的value,若是key不存在,則至關於新增。
in:
判斷一個元素是否在一個容器中,在後面的循環中會介紹。
get():
也是基於key來獲得value的方法,若是不存在能夠返回默認的None:dd.get('Thomas') 或本身定義的值: dd.get('Thomas','不存在')
BTW:
dict是根據key來計算value存儲位置的,因此要求key不可變且不能相同。
與list相比,dict具備插入查找速度快的特色,相應的佔用內存會多一些。
dict也有比較抽象的表達形式:{'Aran':90,'Bon':100,'Lee':98}['Aran'],這也是一個dict,'Aran'是索引,結果是90。
set:
set能夠看做dict的簡化版:只存儲key而沒有value。也能夠看做list的嚴格版:只能存儲無序的無重複的數據集。相似數學意義上的集合。
set是經過一個list進行構建的。
ss = set(['Allen','Bob','Crystal','David','Eclipse'])
add(key):
添加元素
remove(key): 刪除元素
再議不可變之Hash:
dict和set容器都要求key值不可變不重複無序的,是由於他們的儲存是要根據key值進行Hash的。因此它們要求元素或者其指向必定可以Hash,且容器中記錄的永遠是內存中的具體值。
栗子1:
a = ['a1','a2']b = ['b1','b2']ss = set([a,b])
會提示
TypeError,由於a、b是沒法hash的。
栗子2:
b = ['b1','b2']ss = set(b) b[0] = 'B1'b[1] = 'B2'print ss
跟上面tuple的栗子同樣,可是結果卻不同:
set(['b1', 'b2'])
由於set直接記錄的是具體的值,而tuple記錄了指向。
if A: print Aelif B: print Belse: print 'others'
判斷條件:
非零數值、非空字符串、非空list等,就判斷爲True,不然爲False
BTW:
Python沒有相似java的a>b?a:b三目運算符,可是它提供了xx if xx else xx.舉例:
a if a>b else b 和java那個三目運算符同樣。
循環:
for x in list/tuple/dict/set:
print x
把每一個元素(dict是key值)代入變量x,而後執行縮進塊的語句。
函數range():
range(100):生成[0,100)這100個元素的list
range(5,99):生成[5,99)的list
舉例數據類型轉換函數:int(), float(), str(), unicode(), bool()
bool(2231) --> True
引用:
函數名其實就是一個函數對象的引用,
徹底能夠把函數名賦給一個變量,至關於給這個函數起了一個「別名」。
xxx = bool
print xxx(214312) --> True
定義:
舉絕對值函數例子:
def my_abs(x): if x >= 0: return x else: return -x
return後函數就結束了(廢話)。若是沒有寫return,或者只寫return,默認返回值是None
Python中能夠return多個值:
def test(nx,ny):
return nx,ny
接受時:
x,y = text(1,2)
其實就是返回個tuple,python簡化了。
BTW*2:
有些語句函數不知道寫什麼,能夠先寫個pass。
isinstance函數能夠進行數據類型檢查。
參數:
默認參數:
相似C++的默認參數(Java不支持,經過重載也能夠達到相似效果):
def power(x,n=2):
若是隻用一個參數,則n默認爲2,若是有兩個參數則n爲第二個參數。
那麼問題來了,多個默認參數,只想指定其中一個該怎麼作:
def power(x,y,n=2,m=3):
調用時能夠:power(1,2,m=9),從而n就是默認的2,m是9。
Att:
默認參數必須指向不變對象
,否則能夠看下面的例子:
def add_end(L=[]): L.append('END') return L
至關於默認參數就是空List,在空List後面追加一個‘END’
執行print add_end() -->
['END']
結果對的。
再使用一次:print add_end() -->
['END', 'END'] 納尼??
解釋:
Python函數在定義的時候,默認參數L的值就被計算出來了,即[],由於默認參數L也是一個變量,它指向對象[],每次調用該函數,若是改變了L的內容,則下次調用時,默認參數的內容就變了,再也不是函數定義時的[]了。
至關於:
kk = []def add_end4(L = kk): L.append('END') return Lprint kkprint add_end4()print kkprint add_end4()print kk
kk的值一直在改變。
Ps~經測試當咱們的默認參數是固定字符串,set數據時,沒有上述問題的。
可變參數:
可變參數容許你傳入0個或任意個參數,這些可變參數在函數調用時自動組裝爲一個tuple
:
def calc(*numbers):
能夠採用calc(1,2,3,4)的方式。
若是有一個list,也能夠直接傳list:
L = [1,2,3,4] calc(*L)
關鍵字參數:
而關鍵字參數容許你傳入0個或任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝爲一個dict。
def person(name, age, **kw):
能夠傳入任意個數的關鍵字參數:
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
參數的組合運用:
上面的四種參數能夠混合使用,注意參數定義的順序是:
必選參數、默認參數、可變參數和關鍵字參數
。
看栗子:
def func(a, b, c=0, *args, **kw): print 'a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw
結果:
>>> func(1, 2)a = 1 b = 2 c = 0 args = () kw = {}>>> func(1, 2, c=3)a = 1 b = 2 c = 3 args = () kw = {}>>> func(1, 2, 3, 'a', 'b')a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}>>> func(1, 2, 3, 'a', 'b', x=99)a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
全部的參數均可以經過一個tuple和dict來傳遞:
>>> args = (1, 2, 3, 4)>>> kw = {'x': 99}>>> func(*args, **kw)a = 1 b = 2 c = 3 args = (4,) kw = {'x': 99}
參數總結:
*args是可變參數,args接收的是一個tuple;
**kw是關鍵字參數,kw接收的是一個dict。
可變參數既能夠直接傳入:func(1, 2, 3),又能夠先組裝list或tuple,再經過*args傳入:func(*(1, 2, 3));
關鍵字參數既能夠直接傳入:func(a=1, b=2),又能夠先組裝dict,再經過**kw傳入:func(**{'a': 1, 'b': 2})。
def func14(name, sex, age, job ='student', *args, **kwargs): return name,sex,age,job,args,kwargsfunc14('Aran','Male','23','studnet',*('java','C++'),**{'college':'USTC','major':'Software'})func14('Aran','Male','23','student','java','C++',college='USTC',major='Software')
上面代碼中二者是同樣的。
使用*args和**kw是Python的習慣寫法,固然也能夠用其餘參數名,但最好使用習慣用法。
遞歸函數:
不要再return中才調用遞歸,這樣會致使棧溢出。
切片:
能夠很是方便的取得list和tuple的部分元素。
L是一個list,L[m:n]就是取得[m,n)位置的元素。(Python中常常前閉後開。)L[:]就是所有取。
Python支持倒序,L[-10:0]就是最後十個元素,並且0和最大位均可以省略。
Interesting的是Python支持間隔取數:L[::5]就是所有元素間隔5個取一個數。
迭代:
Python中for循環抽象程度要高於Java的for循環,它能夠做用在任何能夠迭代的對象上。
判斷一個對象是否能夠迭代,可使用:
isinstance(Object,Iterable) #返回bool
傳統的List&Tuple遍歷語法很簡單:
def func18(): dd = {'a':1,'b':2,'c':3,'d':4,'e':5} for kk in dd: print kk, dd[kk] for vv in dd.itervalues(): print 'value: ',vv for kk,vv in dd.iteritems(): print kk,vv
其中直接遍歷dict是獲得key,遍歷dict.itervalues()是獲得value,遍歷dict.iteritems()是獲得一個tuple條目。
enumerate函數能夠把list和tuple對象變成位置索引-數值對:
def func19(): tt = ('b','a','c','e','g') for i in enumerate(tt): print i
結果是:
(0, 'b')
(1, 'a')
(2, 'c')
(3, 'e')
(4, 'g')
上文中咱們也知曉,能夠用兩個變量來直接獲得tuple的值。
列表生成式:
直接看例子:
- #2 & 3 Loop
def func20(): print [x*x for x in range(1,11)] print [x*x for x in range(1,11) if x%2 == 0] print [s.lower() for s in ['Hello','World','IBM','Apple']] print [m+n for m in 'ABC' for n in 'XYZ'] print [m+n+z for m in 'AB' for n in 'XY' for z in 'PQ']
結果:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[4, 16, 36, 64, 100]
['hello', 'world', 'ibm', 'apple']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
['AXP', 'AXQ', 'AYP', 'AYQ', 'BXP', 'BXQ', 'BYP', 'BYQ']
以上例子在位於PythonLearn的2015-4-7.py。
生成器:
列表生成式在方便之餘,還有一點小問題:若是建立一個幾百萬的大列表,而咱們只須要訪問其中的幾個元素,那就太浪費空間了。這時候咱們須要這種一邊循環一邊計算的機制,稱爲生成器(Generator)
g = (m+n+z for m in 'ABC' for n in 'XYZ' for z in '123')
與列表生成式相似,這樣咱們定義了一個生成器。
獲取裏面的數值須要使用g.next()來獲得下一個值。並且生成器會本身記錄上次next到什麼位置:(生成器也是能夠迭代的)
print g.next() for n in g: print n
自定義生成器函數 - yield:
在函數中,函數是調用就順序執行遇到return或者最後一行函數語句就返回。而在生成器函數中,yield代替了return,並且是在每次調用next()的時候執行函數,遇到yield語句返回,再次執行時是從上次返回的yield處繼續執行。
舉個栗子:
#Yield for Generatordef func5(): print 'Step 1' yield 1 print 'Step 2' yield 2 print 'Step 3' yield 3
直接調用函數是沒有效果的。採用:
print func5().next()print func5().next()print func5().next()
輸出結果:
Step 1
1
Step 1
1
Step 1
1
說明直接調用函數next()是沒法保留yield記錄的,須要將這個函數交給一個變量。
g= func5()print g.next()print g.next()print g.next()
這樣就輸出正確的結果了:
注意!不是g=func5,這個是指向這個函數的變量。
- 高階函數(map/reduce, filter, sorted)
經過上文咱們已經明白變量能夠指向函數(f=func),函數名也是變量在Python中的意義。這一意義使得回調函數變得異常簡單。
#callback funcdef func6(x, y, f): return f(x)+f(y)
調用的話直接print func6(-10,-20,abs)。結果就是30。
map/reduce :
map函數接受兩個參數,一個是函數,一個是序列,map將傳入的函數依次做用到序列(不必定是list,只要可迭代)的每個元素,並把結果做爲新的list返回。(返回必定是list)
舉例將list中的每一個數字當作字符串返回:
print map(str,[11,22,33,44,55,66,77,88,99,100])
結果爲:['11', '22', '33', '44', '55', '66', '77', '88', '99', '100']
reduce函數接受兩個參數,一個是函數,一個是序列,reduce把函數做用在序列上的元素上,並和下一個元素作累積計算。其效果就至關於:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
舉個栗子:
def fun8(x,y): return x*10+yprint reduce(fun8,[3,5,7,1,6,0,1,0])
結果爲:35716010
上面咱們實現了intTostr,下面咱們實現strToint:(雖然這兩個函數均可以用自帶函數str()和int()直接實現)
def fun8(x,y): return x*10+ydef fun9(s): return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]def fun10(str): return reduce(fun8,map(fun9,str))print fun10('9231297'),type(fun10('9231297'))
結果爲: 9231297 <type 'int'>
filter:
filter函數用於過濾序列。和map()相似,filter()也接受一個函數和一個序列。不過fliter()把傳入的函數依次做用於每一個元素,而後根據返回值是True或False決定保留仍是丟棄該元素。
舉個栗子:
def func11(n): return n%2 == 0print filter(func11,range(1,101))
就把[1,101)的偶數保留了下來。
完成了個素數保留的練習:
#Excerise Timedef primeJudege(n): if n == 1: return False elif n == 2: return True else: p = n/2 for i in range(2,p+1): if n%i == 0: return False return Trueprint filter(primeJudege,range(1,101))
sorted:
排序函數在python中也是抽象的高階函數。
sorted函數默認從小到大排序:
print sorted([36, 5, 12, 9, 21]) ————> [5, 9, 12, 21, 36]
它能夠接受一個函數,做爲排序的依據:f(x,y)。函數必須返回-1做爲依據:必需要返回-1來指明不調換的狀況。(調換的狀況能夠不指明)若是沒有返回-1,則不會對序列作操做。
如我重寫教程中的例子:根據名字首字母從小到大排序,忽略大小寫:
def func13(s1,s2): ss1 = s1.lower(); ss2 = s2.lower(); if ss1<ss2: return -1 else: return 1print sorted(['bob', 'about', 'Zoo', 'Credit'], func13)
結果:['about', 'bob', 'Credit', 'Zoo']
以上例子在位於PythonLearn的2015-4-8.py。
返回函數:
當咱們調用一個函數,但並不須要馬上獲得結果時,咱們能夠把函數當作一個變量返回。在須要時再計算結果。
def func2(*args): def func2i1(): ans = 0 for n in args: ans = ans + n return ans return func2i1
在func2中定義了一個函數func2i1,它能夠對func2函數的可變參數進行求和並返回結果。其實func2i1至關於一個閉包函數了(參見:
JS函數&閉包)。
而在fun2中,直接返回了函數。
f = func2(1,2,3,4,5)時,f還只是一個函數,令f()能夠獲得計算結果。
BTW:令f1 =
func2(1,2,3,4,5)且f2 = func2(1,2,3,4,5)。f1和f2並不等。
閉包:
閉包的屬性咱們已經很清晰了:
def func3(): print 'inFunc3' def func3i1(): print 'InFunc3i1' print 'outFunc3'
在調用func3()時,閉包是不會執行了。除非func3()中再調用func3i1():
def func3(): print 'inFunc3' def func3i1(): print 'InFunc3i1' func3i1() print 'outFunc3'
這樣就能夠打印'InFunc3i1'了。理解了閉包是不會本身執行的,咱們看這個例子:
def func4(): l=[] for i in range(0,100): def func4i1(): l.append(i) func4i1() return l
func4i1雖然在循環中,可是能夠理解爲pass。循環結束後i=99,這時執行了一次func4i1(),因此l最後結果就是[99]
若是,把func4i1()加入循環中,就是[0,1,2,3.....99]。
理解了上述例子,咱們看教程中的例子:
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fsprint [x() for x in count()]
append雖然在循環內,可是並無執行函數f(不是f()),因此理解成pass。當函數指向都添加(append)完畢了,x()執行函數時,i爲3,返回9。因此答案是:
[9, 9, 9]
若是改爲以下代碼:
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f()) return fsprint [x for x in count()]
答案就是:[1,4,9]
教程上採用了更牛逼的方法:再建立一個函數,用該函數的參數綁定循環變量當前的值,不管該循環變量後續如何更改,已綁定到函數參數的值不變。
def count(): fs = [] for i in range(1,4): def f(j): def g(): return j*j return g fs.append(f(i)) return fsprint [x() for x in count()]
至關於把f(1)、f(2)、f(3)放入list中。這樣結果就沒有什麼異議了:
[1,4,9]
採用lambda關鍵字。冒號前面的x表示函數參數。匿名函數只能有一個表達式,不用寫return,返回值就是該表達式的結果。
lambda x:x*x 就至關於 def f(x): return x*x
匿名函數能夠沒有參數,也能夠當作返回值返回。
def build(x): return lambda: x
以上例子在位於PythonLearn的2015-4-9.py。
裝飾器,簡而言之就是給函數增長一些功能而不影響函數自己。
在Python能夠方便的返回函數,裝飾器的實現也比java簡單不少。
假設有下面兩個函數:
import datetimedef func1(mood): print mood , print datetime.date.today()def func2(name,score): print name,scorefunc1('happy')func2('Aran',99)
func1打印心情和日期。func2打印姓名和分數。
如今咱們想讓調用函數的時候,打印出執行這個函數的名稱,能夠採用裝飾器模式:
def func2c1(func): def wrapper(*args): print 'execute %s()....' %func.__name__ return func(*args) return wrapper
這是python中裝飾器的格式,教程上的更加標準。
執行一下:
func2c1(func1)('happy')func2c1(func2)('Aran',99)
結果:
execute func1()....
happy 2015-04-10
execute func2()....
Aran 99
與此同時python還提供了一種更簡單的方式:在函數定義前@裝飾器,之後執行函數,至關於執行裝飾器:
import datetimedef func2c1(func): def wrapper(*args): print 'execute %s()....' % func.__name__ return func(*args) return wrapper@func2c1def func1(mood): print mood, print datetime.date.today()@func2c1def func2(name, score): print name, scorefunc1('happy')func2('Aran', 99)
結果跟上面是同樣的。(ps,函數定義上面能夠加多個裝飾器,多行@)
若是要讓裝飾器支持參數,須要將兩重函數嵌套增長爲三重:
def func2cc1(text): def decorator(func): def wrapper(*args): print '%s %s()...' %(text,func.__name__) func(*args) return wrapper return decorator@func2cc1('call')def func1(mood): print mood, print datetime.date.today()@func2cc1('call')def func2(name, score): print name, scorefunc1('happy')func2('Aran', 99)
把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單。
int()函數能夠帶一個參數,決定參數是多少進制來返回十進制。int('111',2)的結果是7。
若是咱們常用2進制,不想每次都輸入參數,能夠定義一個函數:
def int2(x, base=2): return int(x, base)
python也提供了更簡單的偏函數方法:int2 = functools.partial(int, base=2)。和上面的函數是一個意思。
每個python文件都是一個模塊。python的模塊搜索路徑包含當前目錄,能夠直接import。
(注意import不帶.py)
Python中還有Java中包的概念,當前目錄下須要import 包名.模塊名。調用的時候也須要包名.模塊名。(能夠用別名來免去包名)
注意的是每個包目錄下面都會有一個__init__.py的文件,這個文件是必須存在的,不然,Python就把這個目錄當成普通目錄,而不是一個包。__init__.py能夠是空文件,也能夠有Python代碼,由於__init__.py自己就是一個模塊,而它的模塊名就是包名。對於這個模塊,導入的時候只須要有import 包名
import sysdef func3(): args = sys.argv print 'Hello, your argv is:', print args[1:]
上面例子是使用命令行運行Python時,就能夠看到帶參數符了。
別名:
導入模塊時還可使用別名。import aa as bb: 這樣之後就要使用別名bb。
Tips:
Python標準庫通常會提供StringIO和cStringIO兩個庫,這兩個庫的接口和功能是同樣的,可是cStringIO是C寫的,速度更快。因此常常會有這樣的寫法:
try: import cStringIO as StringIOexcept ImportError: # 導入失敗會捕獲到ImportError import StringIO
做用域:
正常的函數和變量名都是public的,能夠直接引用。
相似__author__的是特殊變量(表示這個py的做者),能夠被直接引用,但通常都是特殊用途。不要使用這種變量名。
相似_xxx或者__xxx的函數和變量是非公開的,不該該被直接引用。注意:不該該不是不能,python並無一種方法能夠徹底限制訪問private函數或變量,只是從編程習慣上不該該這麼作。
安裝第三方模塊:
java中第三方依賴包管理能夠用maven,而在python中封裝了包管理工具:pip。(安裝python時,確保勾選了pip和Add python.exe to Path。若是在cmd中輸入pip沒反應,就要從新運行安裝程序添加pip)
第三方庫的名稱能夠在官網的pypi上搜索,安裝只須要在cmd中輸入pip install xxx
模塊搜索路徑:
默認狀況下,python解釋器會搜索當前目錄、全部已安裝的內置模塊和第三方模塊,搜索路徑存放在sys模塊的path變量中。能夠把它打印出來查看。
咱們要添加本身的所搜目錄,有兩種方法:
一.直接append變量sys.path,運行時修改,運行結束後失效。
二.設置環境變量pythonpath,該環境變量的內容會被自動添加到模塊搜索路徑中。
__future__:
python的版本更迭速度比較快,要想在當前版本中使用新版的特性,須要使用__future__模塊:
from __future__ import division
這樣把新版本的除法特性移植過來了。
試一試不一樣版本中除法:10/3, 10.0/3, 10//3
import module 和 from module import * :
後者能夠在調用時省略
類與實例:
class Student(object): pass
class後面緊接着是類名,即Student,類名一般是大寫開頭的單詞,緊接着是(object),表示該類是從哪一個類繼承下來的,繼承的概念咱們後面再講,一般,若是沒有合適的繼承類,就使用object類,這是全部類最終都會繼承的類。
定義好了Student類,就能夠根據Student類建立出Student的實例,建立實例是經過類名+()實現的:
aran = Student()print aran
結果:<__main__.Student object at 0x10a67a590>。能夠看到,變量bart指向的就是一個Student的object,後面的0x10a67a590是內存地址。
能夠自由地給一個實例變量綁定屬性:aran.name = 'aran'
因爲類能夠起到模板的做用,所以,能夠在建立實例的時候,把一些咱們認爲必須綁定的屬性強制填寫進去。經過定義一個特殊的__init__方法,在建立實例的時候,就把name,score等屬性綁上去:
class Student(object): def __init__(self,name,score): self.name = name self.score = scorearan = Student('test',90)aran.name = 'aran'
注意到__init__方法的第一個參數永遠是self,表示建立的實例自己,所以,在__init__方法內部,就能夠把各類屬性綁定到self,由於self就指向建立的實例自己。
ps:python類中的成員函數的第一個參數通常都是self。
以上例子在位於PythonLearn的2015-4-10.py。
數據封裝:
類內能夠直接定義函數,函數要包含一個參數就是self,相似初始化函數,這個參數在函數調用的時候不須要寫。
前面的例子中,類的變量能夠直接在外部進行訪問賦值。咱們要想把它封裝起來須要私有變量:__name,__score。這樣就和java相似了,須要get/set方法,外部沒法訪問。
嚴格意義上,即便是私有變量python在外部也能夠訪問。python解釋器只是替換了變量的名稱,不過不要這麼作。
繼承:
跟Java繼承差很少,定義類的時候()裏面的是父類。
python中也有super()方法,不過和java不同,使用super(class,self)來得到class的父類。
多態:
子類能夠覆蓋父類的同名函數,和Java的多態差很少。
ps:採用instance函數判斷一個子類的實例和父類,返回的結果是True。判斷一個父類實例和子類,結果確定是False。
過於繼承多態帶來的的‘對擴展開放,對修改封閉’參見教程。
對象類型的判斷:
type和isinstance前面已經講過。其中isinstance參數支持tuple來判斷是否是其中一個:
isinstance(u'a', (str, unicode))
dir(object):
返回object的全部屬性和方法的list。
注意到字符串變量含有方法__len__。在Python中,調用len()其實是自動調用字符串的__len__()方法。
僅僅把屬性方法列出來仍是不夠的,python提供了getattr()、setattr()、hasattr()方法讓咱們能夠直接的操做一個對象。
getattr()是獲得屬性,getattr(obj, 'z'):獲得obj對象名稱爲z的屬性或者方法。
若是不存在會報
AttributeError的錯誤,
也能夠設置成不存在返回默認值:getattr(obj,'z',404)
setattr須要三個參數:對象,屬性,值。hasattr只須要兩個,返回True或者False。
MethodTypes方法:動態增長成員函數
前面介紹瞭如何動態給類綁定屬性(直接調用賦值)。位於types模塊中的MethodTypes函數能夠給類動態的增長成員函數。
給類的實例增長成員函數:
from types import MethodTypeclass Cla2(object): passdef set_name(self,name): self.name = namec = Cla2()c.ssname = MethodType(set_name,c,Cla2)c.ssname('Bom')print c.nameprint hasattr(c,'ssname')
三個參數分別是待綁定函數,實例名,類名。注意綁定到實例的成員函數是ssname而不是set_name。結果是:
Bom
True
給類增長成員函數:
from types import MethodTypeclass Cla2(object): passdef set_name(self,name): self.name = nameCla2.ssname = MethodType(set_name,None,Cla2)c = Cla2()c.ssname('Bom')print c.nameprint hasattr(c,'ssname')
結果和上面同樣,注意中間的參數是None,也能夠不寫。
type():動態建立類
動態語言和靜態語言最大的不一樣,就是函數和類的定義,不是編譯時定義的,而是運行時動態建立的。clas的定義是運行時動態建立的,而建立class的方法就是使用type()函數。咱們也能夠經過type()函數建立類:
Cla4c = type('Cla4',(object,),dict(hello=func1))h =Cla4c()h.hello('aran')
以上代碼等同於:
class Cla4(object): hello = func1Cla4c = Cla4h = Cla4c()h.hello('aran')
其中type傳入的3個參數分別是:
class的名稱;
繼承的父類集合,注意Python支持多重繼承,若是隻有一個父類,別忘了tuple的單元素寫法;
class的方法名稱與函數綁定,這裏咱們把函數fn綁定到方法名hello上。
ps, 經過type()函數建立的類和直接寫class是徹底同樣的,由於Python解釋器遇到class定義時,僅僅是掃描一下class定義的語法,而後調用type()函數建立出class。
metaclass:
直譯爲元類,咱們能夠把類當作metaclass的‘實例’。有關metaclass的代碼較爲複雜且不太經常使用,在此忽略。
多重繼承及Mixin:
多重繼承就是在繼承的基礎上,多加一個類:Class Cla3(Cla1,Cla2):。(PS,Java是單一繼承)
在設計類的繼承關係時,一般,主線都是單一繼承下來的,例如,Ostrich繼承自Bird。可是,若是須要「混入」額外的功能,經過多重繼承就能夠實現,好比,讓Ostrich除了繼承自Bird外,再同時繼承Runnable。這種設計一般稱之爲Mixin(摻和模式)。
爲了更好地看出繼承關係,咱們把Runnable和Flyable改成RunnableMixin和FlyableMixin。相似的,你還能夠定義出肉食動物CarnivorousMixin和植食動物HerbivoresMixin,讓某個動物同時擁有好幾個Mixin。
Mixin的目的就是給一個類增長多個功能,這樣,在設計類的時候,咱們優先考慮經過多重繼承來組合多個Mixin的功能,而不是設計多層次的複雜的繼承關係。
在上文中咱們看到了類中__len__有着特殊用途。python中,還有一些特殊用途的屬性函數和裝飾器:
__slots__屬性:
在class中,咱們能夠經過__slots__來限制class可以添加的屬性:
class Cla3(object): __slots__ = ('name','age')
這樣這個類只能有這兩個屬性,不然會報AttributeError的錯誤。
ps,父類的__slot__屬性子類默認不會繼承。若是子類中也定義了__slots__,那麼子
類容許定義的屬性就是自身的__slots__加上父類的__slots__。
__str__:函數,返回類自己的print的結果。
__iter__:函數。若是一個類想要實現for...in的循環,就須要實現__iter__()方法。該方法返回一個迭代對象,而後Python的for循環就會不斷地調用該迭代對象的next()方法拿到循環的下一個值,知道遇到StopIteration錯誤時退出循環。
舉個例子,定製__iter__實現斐波那契數列迭代:
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化兩個計數器a,b def __iter__(self): return self # 實例自己就是迭代對象,故返回本身 def next(self): self.a, self.b = self.b, self.a + self.b # 計算下一個值 if self.a > 100000: # 退出循環的條件 raise StopIteration(); return self.a # 返回下一個值
__getitem__:函數。若是一個類想要按照下標取出元素,須要__getitem__實現,在上面類中加入以下函數:
def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a
可是此時Fib還不支持切片,由於__getitem__()傳入的參數多是一個int,也多是一個切片對象slice,改進:
def __getitem__(self, n): if isinstance(n,int): a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n,slice): start = n.start stop = n.stop a,b = 1,1 L =[] for x in range(stop): if x >= start: L.append(a) a,b = b,a+b return L
可是這樣的切片還不支持省略,可見實現一些基礎功能並非so easy,利用三目運算符改進一下:
class Fib(object): def __getitem__(self, n): if isinstance(n,int): a,b = 1,1 for x in range(n): a,b = b,a+b return a if isinstance(n,slice): start = 0 if n.start == None else n.start stop = 100 if n.stop == None else n.stop a,b =1,1 L=[] for x in range(stop): if x>= start: L.append(a) a,b = b,a+b return Lff = Fib()print ff[:]
除了__getitem__外還有__setitem__():把對象視做list或者dict來對集合進行賦值。__delitem__():刪除某個元素。在此再也不詳述。
__getattr__:在調用類的成員變量函數中,若是類中沒有這個屬性,python默認就會再調用__getattr__()函數。(若是有的話不會)
class Cla1(object): @property def score(self): return self._score def __getattr__(self, item): if item == 'score': return 100 raise AttributeError("\'Cla1\' has no attribute: \'%s\'" %item)cc = Cla1()print cc.score
print的時候執行的就是__getattr__('score')
__call__:定義類的__call__方法能夠直接執行實例自己的調用:
class Cla3(object): def __call__(self): print '__call__'dd = Cla3()dd()
執行的就是__call__函數。判斷一個對象是否能夠被調用,可使用callable(object)來判斷。如上文中的cc返回False,dd返回True。
@property:設置默認賦值時調用函數的裝飾器
在上面類的變量屬性中,直接賦值而不檢查參數,是不符合邏輯的。咱們採用get/set方法改進:
class Cla4(object): def get_score(self): return self._score def set_score(self,value): if not isinstance(value,int): raise ValueError('score must be an integer!') elif value <0 or value >100: raise ValueError('score must between 0 ~ 100!') else: self._score = value
參數不符合格式則拋出錯誤。(注意此時score是private,不然會出問題)
可是,咱們每次都調用get/set略顯麻煩,此時須要python內置的@property裝飾器來把一個方法變成屬性調用直接賦值。如咱們想針對類中屬性變量score進行裝飾:
class Cla5(object): @property def score(self): return self._score @score.setter def score(self,value): if not isinstance(value,int): raise ValueError('score must be an integer!') elif value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') else: self._score = value
其中屬性的get方法只用@property裝飾,set方法須要score.setter裝飾。
此後能夠直接給調用獲得score的值仍是賦值給score都是默認調用上面的方法。
以上例子在位於PythonLearn的2015-4-13.py。
python支持try...except...finally...,舉例:
try: print 'try...' r = 10/0 print 'result:',rexcept ZeroDivisionError,e: print 'except:',efinally: print 'finally...'
當錯誤發生時,後續語句print 'result:', r不會被執行,except因爲捕獲到ZeroDivisionError,所以被執行。最後,finally語句被執行。而後,程序繼續按照流程往下走。
python也支持多個except和else:
try: print 'try...' r = 10/int('a') print 'result:',rexcept ValueError,e: print 'ValueError',eexcept ZeroDivisionError,e: print 'ZeroDivisionError:',eelse: print 'no error!'finally: print 'finally...'
須要注意的是python的錯誤其實也是class,全部的錯誤類型都繼承自BaseException,因此使用except時須要注意,它們不但捕獲該類型的錯誤,還把其子類「一網打盡」。會致使子類永遠捕獲不到錯誤。
記錄錯誤:
python運行出錯後,咱們會在console中看到錯誤的層層堆棧信息,程序也終止運行。若是想讓程序繼續運行,可使用python內置的logging模塊把錯誤堆棧打印出來,並讓程序繼續執行直至正常退出。看例子:
def foo(s): return 10 / int(s)def bar(s): return foo(s) * 2def main(): try: bar('0') except StandardError, e: logging.exception(e)main()print 'END'
console輸出的結果:
C:\Python27\python.exe C:/Aran_Files/Git/PythonLearn/2015-04-14.pyERROR:root:integer division or modulo by zeroaranTraceback (most recent call last):aranEND File "C:/Aran_Files/Git/PythonLearn/2015-04-14.py", line 87, in main bar('0') File "C:/Aran_Files/Git/PythonLearn/2015-04-14.py", line 83, in bar return foo(s) * 2 File "C:/Aran_Files/Git/PythonLearn/2015-04-14.py", line 80, in foo return 10 / int(s)ZeroDivisionError: integer division or modulo by zeroProcess finished with exit code 0
以上例子在位於PythonLearn的2015-4-14.py。
拋出錯誤:
在實際代碼編寫中,咱們except到一個錯誤,打印錯誤信息後,能夠直接raise(raise不帶參數就把當前錯誤原樣拋出)。由於捕獲錯誤的目的只是記錄一下,因爲當前函數不知道應該怎麼處理,最恰當的方式就是繼續往上拋,以便頂層調用者去處理。
btw,咱們還能夠except到一個錯誤類型,拋出另一個錯誤類型,只要是合理的轉換邏輯便可。
print:
打印出來錯誤,簡單粗暴有效。
assert:
能夠用print的地方均可以用assert代替,assert後接一個表達式和一個字符串。若是表達式爲假,則拋出
AssertionError並打印字符串,爲真則沒有異常。
def func2(s): assert s!=0,'n is zero' return sfunc2(0)
assert能夠設置忽略,運行py時添加參數-O便可。
logging:
logging能夠指定輸出文件的位置,並且能夠設置等級,根據等級來肯定哪些起做用。
logging的等級從低到高有:debug, info, warning, error。採用logging.info('')的方式。
設置級別的方式是:logging.basicConfig(level = logging.INFO)
ps,須要import logging
pdb.set_trace():設置斷點
須要import pdb。程序會在pdb.set_trace()處暫停並進入pdb調試環境,使用‘p 變量名‘ 查看變量值,使用命令c繼續運行。
IDE調試工具:
以上例子在位於PythonLearn的2015-4-20.py。
IO在計算機中值Input和Output。因爲程序和運行時的數據都是在內存中由CPU來執行,涉及到數據交換的地方也就是磁盤、網絡等,須要IO接口。
IO編程中,Stream(流)的概念很重要,Input Stream就是數據從外面(磁盤、網絡)流進內存,Output Stream就是流出。每一個只能單向流動。
因爲CPU和內存的速度遠遠高於外設,因此在IO編程中就有速度不匹配的問題,解決辦法兩個:
第一種是CPU等着,也就是程序暫停執行後續代碼,等100M的數據在10秒後寫入磁盤,再接着往下執行,這種模式稱爲同步IO;
另外一種方法是CPU不等待,只是告訴磁盤,「您老慢慢寫,不着急,我接着幹別的事去了」,因而,後續代碼能夠馬上接着執行,這種模式稱爲異步IO。
同步IO和異步IO的區別在因而否等待IO執行結果,異步IO要比同步IO的效率更高,可是編程複雜度也會更高。由於,磁盤寫入數據完畢時,如何通知CPU會有不一樣的設計模式。
每一種編程語言都會把操做系統提供的低級C接口封裝起來方便使用,Python也不例外,本章主要討論同步IO。
讀文件:
對於ASCII編碼文件能夠直接讀:
f = open('Stream.txt','r')print f.read()f.close()
open的第一個參數是路徑,默認是py當前文件夾,'r'表示只讀。read()函數是一次性讀取所有內容。最後要關閉stream。
按着java的邏輯,打開文件要trycatch確保文件不存在也不影響後續的操做:
try: f = open('Streasm.txt','r') print f.read()except IOError,e: passfinally: if f: f.close()
這樣代碼顯得比較臃腫,python給咱們提供了with方法來替代上述代碼:
with open('Stream.txt','r') as f: print f.read()
不用本身手動關閉Stream了。此外read()函數能夠加參數肯定每次最多讀取的字節數。readline()能夠每次讀取一行。readlines()能夠一次性讀取全部內容並按行返回list。
ps,文本中每一行都有'\n',能夠經過line.strip()刪掉。
對於非ASCII編碼文件,須要以二進制模式打開,再編碼:
with open('text.txt','rb') as f: print f.read().decode('gbk')
參數rb是打開二進制的方式,decode是按照編碼格式解碼。python以爲每次read都要解碼仍是很麻煩,提供了codecs快捷方法:
import codecswith codecs.open('text.txt','r','gbk') as f: print f.read()
寫文件:
瞭解了讀文件,寫文件就比較簡單了:
with open('Stream.txt','w') as f: f.write('Hello,world')
其中參數w、wb表示刪除原文本寫入(再次write時不會覆蓋,由於是同一個f)。a表示在原文本上追加。
btw,寫文件時操做系統都是先緩存數據,再寫入磁盤,只有當stream close時才所有寫入。so~最好使用with語句。
查看系統:
python內置的os模塊能夠直接調用系統提供的接口函數:
os.name:操做系統,若是是windows是nt
os.environ:環境變量
os.getenv():獲得某個環境變量
操做文件和目錄:
os.path.abspath('.'):獲得當前目錄下的絕對路徑(也可使用sys.argv[0])
查看當前目錄下的全部文件:(參數也可使用上面的那個絕對路徑,而非'.')
for file in os.listdir('.'):
print file
過濾查看當前目錄下文件:
for file in glob.glob('*.py'):
print file
路徑拼接:(因爲不一樣操做系統路徑表示不同,python提供方法省去操做麻煩)
os.path.join(os.path.abspath('.'),'testdir','test.txt')
結果:C:\Aran_Files\Git\PythonLearn\testdir\test.txt
建立刪除路徑:
os.mkdir()
os.rmdir()
拆分路徑和文件:(把一個路徑拆分爲兩部分,後一部分老是最後級別的目錄或文件名):
os.path.split(sys.argv[0])
拆分文件拓展名:
os.path.spilitext(sys.argv[0])
重命名和刪除文件:
os.rename()和os.remove()
拷貝文件的功能在os模塊中並無提供,可使用shutil模塊中額copyfile函數。
綜上咱們能夠用上面的組合實現一些快捷功能:
當前目錄下全部子目錄:[x for x in os.listdir('.') if os.path.isdir(x)]
當前目錄下全部py文件:[x for x in os.listdir('.') if os.path.splitext(x)[1]=='.py']
練習:編寫一個能在當前目錄以及當前目錄的全部子目錄下查找文件名包含指定字符串的文件的函數:
def search(str,path=os.path.abspath('.')): for x in os.listdir(path): if os.path.isdir(x): search(str,os.path.join(path,x)) else: if str in x: print os.path.join(path,x)
變量從內存中變成可存儲或傳輸的過程稱之爲序列化,在Python中叫pickling。把變量內容從序列化的對象從新讀到內存裏稱之爲反序列化,即unpickling。
Python提供cPickle模塊和pickle模塊來實現序列化pickle.dumps()和反序列化pickle.loads():
try: import CPickle as pickleexcept ImportError: import pickled = dict(name='Bob',age=20,score=88)dp = pickle.dumps(d)print dpd = pickle.loads(dp)print d
也有針對文件的pickle.dump()和pickle.load():
d = dict(name='Bob',age=20,score=88)with open('dump.txt','wb') as f: pickle.dump(d,f)with open('dump.txt','rb') as f: d = pickle.load(f)print d
上面序列化的格式只可以在python中使用顯然不夠通用,python內置了json模塊來提供轉換:
J
SON類型
Python類型
{}
dict
[]
list
"string"
'str'或u'unicode'
1234.56
int或float
true/false
True/False
null
None
採用json.dumps()和json.loads()來實現。
在序列化當中,默認形式都是以dict傳輸的。若是不是dict對象(好比類),咱們須要手動的將其轉換爲dict再進行序列化。好在一般類的實例中都有一個__dict__屬性,它就是該類的一個dict:
json.dumps(s.__dict__)
有少數類除外,好比定義了__slots__的類,這種狀況下只有本身手寫轉換函數了。dumps()支持不少參數,其中關鍵字參數
default
就是一個轉換函數。
在反序列化中,咱們必須手動實現DictToClass的函數了,用於替代loads裏面的object_hook參數:
import jsonclass Student(object): def __init__(self, name, score): self.name = name self.score = scores = Student('Bob', 88)#序列化print(json.dumps(s.__dict__))#反序列化print(json.loads(json.dumps(s.__dict__),object_hook=lambda d:Student(d['name'],d['score'])))
python既支持多進程又支持多線程。
多進程:
Linux下能夠經過fork()調用實現多進程,詳見教程。跨平臺使用multiprocessing:
- 運行結果: Parent process 9220. Process will start. Run child process test (99)... Process end.
from multiprocessing import Processimport os
def run_proc(name): print 'Run child process %s (%s)...' % (name, os.getpid())
if __name__=='__main__': print 'Parent process %s.' % os.getpid() p = Process(target=run_proc, args=('test',)) print 'Process will start.' p.start() p.join() print 'Process end.'
run_proc就是一個打印函數,os.getpid是進程的pid。
if __name__ == '__main__'這個語句很常見,用來判斷當前模塊是直接運行的仍是import的。import進來的模塊的__name__是模塊名,缺省名稱的當前模塊下__name__爲__main__。
Pro
cess()函數就是用來建立進程的構造函數,支持不少參數,能夠查看Declaration。target傳入的函數會在進程啓動時執行,該函數的參數就是args傳入的tuple。
start時才啓動進程,join用於等待子進程結束後再繼續往下運行。
要啓動大量進程的話就考慮進程池:
from multiprocessing import Poolimport os,time,randomdef time_task(name): print 'Run task %s(%s)...' %(name,os.getpid()) start = time.time() time.sleep(random.random()*10) end = time.time() print 'Task %s runs %0.2f seconds.'%(name,(end-start))if __name__ == '__main__': print 'Parent process %s.' % os.getpid() p = Pool(4) for i in range(5): p.apply_async(time_task,args=(i,)) print 'Waiting for all subprocesses done...' p.close() p.join() print 'All subprocesses done.'運行結果: Parent process 11232. Waiting for all subprocesses done... Run task 0(10824)... Run task 1(8960)... Run task 2(8216)... Run task 3(7340)... Task 2 runs 0.24 seconds. Run task 4(8216)... Task 3 runs 2.15 seconds. Task 1 runs 5.85 seconds. Task 0 runs 8.99 seconds. Task 4 runs 9.88 seconds. All subprocesses done.
使用方法和上面的差很少。注意import的是Pool,並且Pool()中參數表示同時執行的進程數。Pool對象在調用join()後就開始執行並等待全部子進程執行完畢,調用join()前必須先調用close(),來讓進程池不能添加新的進程了。
多進程就要考慮進程之間的通訊:Queue()類
from multiprocessing import Process, Queueimport os, time, random# 寫數據進程執行的代碼:def write(q): for value in ['A', 'B', 'C']: print 'Put %s to queue...' % value q.put(value) time.sleep(random.random())# 讀數據進程執行的代碼:def read(q): while True: value = q.get(True) print 'Get %s from queue.' % valueif __name__=='__main__': # 父進程建立Queue,並傳給各個子進程: q = Queue() pw = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) # 啓動子進程pw,寫入: pw.start() # 啓動子進程pr,讀取: pr.start() # 等待pw結束: pw.join() # pr進程裏是死循環,沒法等待其結束,只能強行終止: pr.terminate()運行結果: Put A to queue... Get A from queue. Put B to queue... Get B from queue. Put C to queue... Get C from queue.
以上例子在位於PythonLearn的2015-4-23.py和2015-04-23_process.py。
多線程:
python提供了封裝好的threading高級模塊進行多線程開發。
每一個進程都有一個主線程:MainThread。建立線程方法:
print threading.current_thread().namedef namePrint(ID): print threading.current_thread().name , IDt = threading.Thread(target=namePrint, name = 'Threadtest',args=(1,))t.start()t.join()
結果: MainThread Threadtest 1
使用threading.current_thread().name獲得線程名,若是本身沒有指定線程名,python會以Thread-1方式命名。建立線程的參數和進程相同,在start()時執行線程。
線程鎖:
多線程和多進程最大的不一樣在於,多進程中,同一變量各自有一份備份在於每一個進程中,互相不影響。而多線程中,全部變量由全部線程共享,因此沒有線程鎖的機制內容很容易被改亂。(緣由詳見教程)
使用鎖的方法就是先定義鎖,再得到鎖,在try中執行修改,在finally中釋放掉鎖:
balance = 0lock = threading.Lock()def changeBalance(n): global balance balance = ndef run_thread(n): lock.acquire() try: changeBalance(n) finally: lock.release()t1 = threading.Thread(target=run_thread,args=(100,))t2 = threading.Thread(target=run_thread,args=(20,))t1.start()t2.start()t1.join()t2.join()print balance
第2句定義了鎖,第5句表示變量是全局的那個變量,第9句得到鎖而後執行變量改變再釋放掉。
ps,因爲python解釋器有GIL全局鎖的問題,在python的多線程中沒法充分利用cpu的每一個核,多進程能夠。
ThreadLocal:
進程本身的數據該如何傳輸存放呢?
一種方式是線程的數據就在線程中,不對外共享可見:
- 結果: Hello, Alice (in Thread-1) Hello, Bob (in Thread-2)
import threadingdef process_print(name): print 'Hello, %s (in %s)' % (name, threading.current_thread().name)def process_thread(name): process_print(name)t1 = threading.Thread(target= process_thread, args=('Alice',))t2 = threading.Thread(target= process_thread, args=('Bob',))t1.start()t2.start()t1.join()t2.join()
這樣的想法最天然,可是實現起來有些不簡練:線程的數據要看成參數在不一樣的函數之間傳輸。(由於函數是共享的)
不經過傳參的方法,就是定義全局的dict,將線程數據所有放入其中,這樣的話數據至關於透明並且共享:
import threadingdict = {}def process_print(): print 'Hello, %s (in %s)' % (dict[threading.current_thread().name], threading.current_thread().name)def process_thread(name): dict[threading.current_thread().name] = name process_print()t1 = threading.Thread(target= process_thread, args=('Alice',))t2 = threading.Thread(target= process_thread, args=('Bob',))t1.start()t2.start()t1.join()t2.join()
python給咱們提供了另外一種辦法,既能夠簡練又能夠數據隱藏- ThreadLocal:
import threadinglocal = threading.local()def process_print(): print 'Hello, %s (in %s)' % (local.name, threading.current_thread().name)def process_thread(name,): local.name = name process_print()t1 = threading.Thread(target= process_thread, args=('Alice',))t2 = threading.Thread(target= process_thread, args=('Bob',))t1.start()t2.start()t1.join()t2.join()
結果都和第一個結果同樣,這樣的處理須要考慮不一樣線程args和函數參數匹配的問題。
以上例子在位於PythonLearn的2015-4-27.py。
分佈式進程:
教程講的略粗糙,在這裏略過,有空再深究。
\d
表示一個數字
\w
表示一個字母或者數字
\s
表示一個空格或者Tab符
*
表示任意個字符(包括0個)
+
表示至少一個字符
?
表示0個或者1個字符
{n}
表示n個字符
{n,m}
表示n-m個字符
舉個栗子:
\d{3}\s+\d{3,8}
3個數字 空格
3-8個數字,表示任意個空格分隔開的帶區號的電話號碼
ps,特殊字符可使用 \特殊字符 進行轉義。
更精準的匹配須要使用[]和-表示範圍:
[0-9]
能夠匹配一個0-9的數字
[0-9]+
能夠匹配至少一個數字
[0-9a-zA-Z]
能夠匹配一個數字或者字母
A|B
能夠匹配A或者B
^
表示行的開頭,^\d表示以數字爲開頭
$
表示行的結束,\d$表示以數字結束
正則表達式還能夠設置分組,用()表示一個分組。
貪婪匹配:
正則分組默認的是前面貪婪匹配,若是咱們用(^\d+)(0*$)去匹配2312020000則第二個分組就匹配不到,咱們必須讓第一個不進行貪婪匹配,加一個?就能夠了:(^\d+?)(0*$)匹配獲得的分組是231202,0000。
python中的正則表達式:
因爲python字符串也須要\進行轉義,因此特別注意\\: print 'ABC\\-001' 結果就是ABC\-001。加上r能夠防止轉義,詳見下文。
re模塊:
re模塊提供match()函數,匹配則返回一個Match對象,不然返回None:(加r防止字符轉義)
import retest = 'QQ1041149156's = r'^[Q|q]{2}\d*'if re.match(s,test): print Trueelse: print False
字符串切分:
普通的字符切分很簡單:'a b c'.split(' ')輸出切分後的list。若是複雜一點,a b c之間空格不定,則須要使用正則表達式進行切分:
test = 'a b c d's = r'\s+'print re.split(s,test)
分組:
正則匹配有分組的話,在python中也能夠實現:
test = '091-23123123's = r'(^\d{3})-(\d{3,8}$)'m = re.match(s,test)print m.groups() 結果: ('091', '23123123')
編譯:
python中使用正則表達式,re模塊的執行順序是:先編譯正則表達式(若是不合法會報錯),再去匹配。若是一個正則要重複使用,處於效率的考慮,能夠預先編譯,省去重複編譯的環節:
test = 'qq1041149156's = r'^[Q|q]{2}\d*'re_s = re.compile(s)if re_s.match(test): print Trueelse: print False
namedtuple:能夠給tuple添加屬性名稱,便於方便的引用:
from collections import namedtuplePoint = namedtuple('Point',['x','y'])p = Point(1,2)print p.x,p.y
deque:因爲list是線性存儲的,因此插入和刪除顯得比較麻煩,deque就是爲了高效實現插入和刪除操做的雙向列表:append,appendleft,pop,popleft
from collections import dequeL = ['a','b','c']q = deque(L)q.append('y')q.appendleft('x')print q結果:deque(['x', 'a', 'b', 'c', 'y'])
defaultdict:使用dict時,若是引用的key不存在就會keyError,使用defaultdict則會返回一個默認值。
OrderedDict:實現dict按照key值插入的順序排序
Counter:是一個簡單的計數器,統計字符出現的個數:
from collections import Counterc= Counter()for ch in 'Programmer': c[ch] = c[ch] + 1print c結果:Counter({'r': 3, 'm': 2, 'a': 1, 'e': 1, 'g': 1, 'o': 1, 'P': 1})
Base64:
一種任意二進制到文本字符串的編碼方式。在此略過。
struct:
pyhton中用來解決str和其它二進制數據類型的轉換。在此略過。
hashlib:
python中的hashlib提供了常見的摘要算法,MD5和SHA1等等:
import hashlibmd5 = hashlib.md5()md5.update("My name is AranLiu.")print md5.hexdigest()
算法主要用於驗證和防篡改,詳情參見教程。
itertools:
python的內建模塊提供了很是有用的用於操做迭代對象的函數。在此略過。
以上例子在位於PythonLearn的2015-4-28.py。
XML、HTMLParser、PIL和圖形界面編程在此略過。
python基礎知識點到此結束。
教程隨後的項目及實戰模塊,會以單獨筆記的形式進行一週一更。