基本語(yǔ)法
輸入輸出
print / input
如果字符串里面有很多字符都需要轉(zhuǎn)義储笑,就需要加很多\,為了簡(jiǎn)化圆恤,Python還允許用r''表示''內(nèi)部的字符串默認(rèn)不轉(zhuǎn)義
>>> print('\\\t\\')
\????????\
>>> print(r'\\\t\\')
\\\t\\
如果字符串內(nèi)部有很多換行突倍,用\n寫(xiě)在一行里不好閱讀,為了簡(jiǎn)化盆昙,Python允許用 '''...''' 的格式表示多行內(nèi)容羽历,注意...是提示符,不是代碼的一部分
>>> print('''line1
... ????line2
... ????line3''')
?line1
?line2
?line3
數(shù)據(jù)類型弱左,變量和常量
布爾值 True, False, and, or , not
空值 None窄陡,None不能理解為0,因?yàn)?是有意義的拆火,而None是一個(gè)特殊的空值
變量名必須是大小寫(xiě)英文跳夭、數(shù)字和_的組合,且不能用數(shù)字開(kāi)頭们镜,同一個(gè)變量可以反復(fù)賦值币叹,而且可以是不同類型的變量
通常用全部大寫(xiě)的變量名表示常量
有兩種除法,一種除法是 /模狭,/ 除法計(jì)算結(jié)果是浮點(diǎn)數(shù)颈抚,即使是兩個(gè)整數(shù)恰好整除,結(jié)果也是浮點(diǎn)數(shù)嚼鹉,還有一種除法是 //贩汉,稱為地板除,兩個(gè)整數(shù)的除法仍然是整數(shù)锚赤,取余 % 輸出仍然是整數(shù)
字符編碼和字符串
Unicode把所有語(yǔ)言都統(tǒng)一到一套編碼里匹舞,這樣就不會(huì)再有亂碼問(wèn)題了。Unicode標(biāo)準(zhǔn)也在不斷發(fā)展线脚,但最常用的是用兩個(gè)字節(jié)表示一個(gè)字符(如果要用到非常偏僻的字符赐稽,就需要4個(gè)字節(jié))叫榕。
UTF-8編碼把一個(gè)Unicode字符根據(jù)不同的數(shù)字大小編碼成1-6個(gè)字節(jié),常用的英文字母被編碼成1個(gè)字節(jié)姊舵,漢字通常是3個(gè)字節(jié)晰绎,只有很生僻的字符才會(huì)被編碼成4-6個(gè)字節(jié)。如果你要傳輸?shù)奈谋景罅坑⑽淖址ǘ。肬TF-8編碼就能節(jié)省空間荞下。
在計(jì)算機(jī)內(nèi)存中,統(tǒng)一使用Unicode編碼躏将,當(dāng)需要保存到硬盤(pán)或者需要傳輸?shù)臅r(shí)候锄弱,就轉(zhuǎn)換為UTF-8編碼考蕾。
ord() 函數(shù)獲取字符的整數(shù)表示祸憋,chr() 函數(shù)把編碼轉(zhuǎn)換為對(duì)應(yīng)的字符,如果知道字符的整數(shù)編碼肖卧,還可以用十六進(jìn)制這么寫(xiě)str
>>> '\u4e2d\u6587'
????????'中文'
如果要在網(wǎng)絡(luò)上傳輸蚯窥,或者保存到磁盤(pán)上,就需要把str變?yōu)橐宰止?jié)為單位的bytes塞帐。Python對(duì)bytes類型的數(shù)據(jù)用帶b前綴的單引號(hào)或雙引號(hào)表示拦赠。要注意區(qū)分'ABC'和b'ABC',前者是str葵姥,后者雖然內(nèi)容顯示得和前者一樣荷鼠,但bytes的每個(gè)字符都只占用一個(gè)字節(jié)。以Unicode表示的str通過(guò)encode()方法可以編碼為指定的bytes榔幸,
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last): File "<stdin>", line 1, in <module>UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
反過(guò)來(lái)允乐,如果我們從網(wǎng)絡(luò)或磁盤(pán)上讀取了字節(jié)流,那么讀到的數(shù)據(jù)就是bytes削咆。要把bytes變?yōu)閟tr牍疏,就需要用decode()方法,如果bytes中包含無(wú)法解碼的字節(jié)拨齐,decode()方法會(huì)報(bào)錯(cuò)鳞陨,如果bytes中只有一小部分無(wú)效的字節(jié),可以傳入errors='ignore'忽略錯(cuò)誤的字節(jié)瞻惋。
要計(jì)算str包含多少個(gè)字符厦滤,可以用len()函數(shù),記住不是包含多少byte歼狼,如果換成bytes掏导,len()函數(shù)就計(jì)算字節(jié)數(shù)。
由于Python源代碼也是一個(gè)文本文件蹂匹,所以碘菜,當(dāng)你的源代碼中包含中文的時(shí)候,在保存源代碼時(shí),就需要?jiǎng)?wù)必指定保存為UTF-8編碼忍啸。當(dāng)Python解釋器讀取源代碼時(shí)仰坦,為了讓它按UTF-8編碼讀取,我們通常在文件開(kāi)頭寫(xiě)上這兩行:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
格式化
'Hi, %s, you have $%d.' % ('Michael',1000000)
有些時(shí)候计雌,字符串里面的%是一個(gè)普通字符就需要轉(zhuǎn)義悄晃,用%%來(lái)表示一個(gè)%
如果不太確定應(yīng)該用什么,%s永遠(yuǎn)起作用凿滤,它會(huì)把任何數(shù)據(jù)類型轉(zhuǎn)換為字符串
format()妈橄,另一種格式化字符串的方法
list
list是一種有序的集合,可以隨時(shí)添加和刪除其中的元素翁脆,用len()函數(shù)可以獲得list元素的個(gè)數(shù)眷蚓。
>>> class = ['aa', 'bb', 'cc']
如果要取最后一個(gè)元素,除了計(jì)算索引位置外反番,還可以用-1做索引沙热,直接獲取最后一個(gè)元素。
追加元素 .append(xx)罢缸,插入元素 .insert(idx, xx)篙贸,刪除末尾的元素 .pop(),刪除指定位置元素 .pop(i)
list里面的元素的數(shù)據(jù)類型也可以不同枫疆,list元素也可以是另一個(gè)list
多維list訪問(wèn)可以通過(guò) A[x][y]或A[x, y]
tuple
tuple和list非常類似爵川,但是tuple一旦初始化就不能修改,它也沒(méi)有append()息楔,insert()這樣的方法
只有1個(gè)元素的tuple定義時(shí)必須加一個(gè)逗號(hào) , 來(lái)消除歧義
>>> class = ('aa', 'bb', 'cc')
>>> class = (1, )
tuple所謂的“不變”是說(shuō)寝贡,tuple的每個(gè)元素,指向永遠(yuǎn)不變钞螟,指向一個(gè)list兔甘,就不能改成指向其他對(duì)象,但指向的這個(gè)list本身是可變的
條件判斷
if xxx:
elif yyy:
else:
循環(huán)
Python的循環(huán)有兩種鳞滨,一種是for...in循環(huán)洞焙,依次把list或tuple中的每個(gè)元素迭代出來(lái):
for x in y:
range()函數(shù),可以生成一個(gè)整數(shù)序列
第二種循環(huán)是while循環(huán)拯啦,只要條件滿足澡匪,就不斷循環(huán),條件不滿足時(shí)退出循環(huán):
while xxx:
break 提前結(jié)束循環(huán)褒链,continue跳過(guò)當(dāng)前循環(huán)
dict
>>> d = {'Michael' : 95, 'Bob' : 75, 'Tracy' : 85}
這是一種key-value存儲(chǔ)方式唁情,如果key不存在,dict就會(huì)報(bào)錯(cuò)甫匹,要避免key不存在的錯(cuò)誤甸鸟,有兩種辦法惦费,一是通過(guò)in判斷key是否存在
>>> 'Thomas' in d
False
二是通過(guò)dict提供的get()方法,如果key不存在抢韭,可以返回None薪贫,或者自己指定的value:
>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1
要?jiǎng)h除一個(gè)key,用pop(key)方法刻恭,對(duì)應(yīng)的value也會(huì)從dict中刪除瞧省,請(qǐng)務(wù)必注意,dict內(nèi)部存放的順序和key放入的順序是沒(méi)有關(guān)系的鳍贾。
和list比較鞍匾,dict有以下幾個(gè)特點(diǎn):
? ? ? ? 1. 查找和插入的速度極快,不會(huì)隨著key的增加而變慢骑科;
? ? ? ? 2. 需要占用大量的內(nèi)存蔓纠,內(nèi)存浪費(fèi)多祟剔。
而list相反:
? ? ? ? 1. 查找和插入的時(shí)間隨著元素的增加而增加位迂;
? ? ? ? 2. 占用空間小锄贷,浪費(fèi)內(nèi)存很少构眯。
所以橄镜,dict是用空間來(lái)?yè)Q取時(shí)間的一種方法
set
set和dict類似蛀缝,也是一組key的集合萤厅,但不存儲(chǔ)value暇藏。由于key不能重復(fù)蜜笤,所以,在set中盐碱,沒(méi)有重復(fù)的key把兔,要?jiǎng)?chuàng)建一個(gè)set,需要提供一個(gè)list作為輸入集合瓮顽,重復(fù)元素在set中自動(dòng)被過(guò)濾:
>>> s = set([1,1,2,2,3,3])
>>> s
{1,2,3}
通過(guò)add(key)方法可以添加元素到set中县好,可以重復(fù)添加,但不會(huì)有效果暖混;通過(guò)remove(key)方法可以刪除元素
set可以看成數(shù)學(xué)意義上的無(wú)序和無(wú)重復(fù)元素的集合缕贡,因此,兩個(gè)set可以做數(shù)學(xué)意義上的交集拣播、并集等操作
>>> s1 = set([1,2,3])
>>> s2 = set([2,3,4])
>>> s1 & s2
{2,3}
>>> s1 | s2
{1,2,3,4}
函數(shù)
def my_abs(x):
? ? xxxx
? ? return (y)
如果想定義一個(gè)什么事也不做的空函數(shù)晾咪,可以用pass語(yǔ)句,實(shí)際上pass可以用來(lái)作為占位符贮配,比如現(xiàn)在還沒(méi)想好怎么寫(xiě)函數(shù)的代碼谍倦,就可以先放一個(gè)pass,讓代碼能運(yùn)行起來(lái)
def nop():
? ? pass
參數(shù)類型做檢查泪勒,可以用內(nèi)置函數(shù)isinstance()實(shí)現(xiàn):
def my_abs(x):
? ? if not isinstance(x, (int, float)):
? ? ? ? raiseTypeError('bad operand type')
? ? if x >=0:
? ? ? ? return x
? ? else:
? ? ? ? return -x
函數(shù)可以返回多個(gè)值昼蛀,但其實(shí)這只是一種假象宴猾,Python函數(shù)返回的仍然是單一值,原來(lái)返回值是一個(gè)tuple叼旋。
默認(rèn)參數(shù)等同C
可變參數(shù)就是傳入的參數(shù)個(gè)數(shù)是可變的
def calc(*numbers):
? ? sum =0
? ? for n in numbers:
? ? ? ? sum = sum + n * n
? ? return sum
定義可變參數(shù)和定義一個(gè)list或tuple參數(shù)相比鳍置,僅僅在參數(shù)前面加了一個(gè)*號(hào)。在函數(shù)內(nèi)部送淆,參數(shù)numbers接收到的是一個(gè)tuple税产,因此,函數(shù)代碼完全不變偷崩。但是辟拷,調(diào)用該函數(shù)時(shí),可以傳入任意個(gè)參數(shù)阐斜,包括0個(gè)參數(shù)衫冻。Python允許你在list或tuple前面加一個(gè)*號(hào),把list或tuple的元素變成可變參數(shù)傳進(jìn)去
>>> nums = [1,2,3]
>>> calc(*nums)
14
關(guān)鍵字參數(shù)允許傳入0個(gè)或任意個(gè)含參數(shù)名的參數(shù)谒出,這些關(guān)鍵字參數(shù)在函數(shù)內(nèi)部自動(dòng)組裝為一個(gè)dict
>>> def person(name, age, **kw):
如果要限制關(guān)鍵字參數(shù)的名字隅俘,就可以用命名關(guān)鍵字參數(shù)
>>> def person(name, age, *, city, job):
和關(guān)鍵字參數(shù)**kw不同,命名關(guān)鍵字參數(shù)需要一個(gè)特殊分隔符*笤喳,*后面的參數(shù)被視為命名關(guān)鍵字參數(shù)
遞歸函數(shù)
遞歸函數(shù)需要注意防止棧溢出为居,解決遞歸調(diào)用棧溢出的方法是通過(guò)尾遞歸優(yōu)化,事實(shí)上尾遞歸和循環(huán)的效果是一樣的杀狡,所以蒙畴,把循環(huán)看成是一種特殊的尾遞歸函數(shù)也是可以的。尾遞歸調(diào)用時(shí)呜象,如果做了優(yōu)化膳凝,棧不會(huì)增長(zhǎng),因此恭陡,無(wú)論多少次調(diào)用也不會(huì)導(dǎo)致棧溢出蹬音。
deffact(n):
? ? return fact_iter(n,1)
deffact_iter(num, product):
? ? ifnum ==1:
? ? ? ? return product
? ? return fact_iter(num -1, num * product)
高級(jí)特性
切片(slice)
L[0:3]表示,從索引0開(kāi)始取休玩,直到索引3為止著淆,但不包括索引3,如果第一個(gè)索引是0哥捕,還可以省略牧抽,同樣支持倒數(shù)切片
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
>>> L[0:3]
['Michael', 'Sarah', 'Tracy']
>>> L = list(range(100))
>>> L
[0,1,2,3, ...,99]
>>> L[:10:2]? ? ? ? //前10個(gè)數(shù),每?jī)蓚€(gè)取一個(gè)
[0, 2, 4, 6, 8]
>>> L[::5]? ? ? ? ? ? //所有數(shù)遥赚,每5個(gè)取一個(gè):
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
>>> L[:]? ? ? ? ? ? ? //甚至什么都不寫(xiě)扬舒,只寫(xiě)[:]就可以原樣復(fù)制一個(gè)list
list和tuple都可以使用切片
迭代
python的for循環(huán)不僅可以用在list或tuple上,還可以作用在其他可迭代對(duì)象上凫佛。
list這種數(shù)據(jù)類型雖然有下標(biāo)讲坎,但很多其他數(shù)據(jù)類型是沒(méi)有下標(biāo)的孕惜,但是,只要是可迭代對(duì)象晨炕,無(wú)論有無(wú)下標(biāo)衫画,都可以迭代,比如dict就可以迭代瓮栗,默認(rèn)情況下削罩,dict迭代的是key。如果要迭代value费奸,可以用for value in d.values()弥激,如果要同時(shí)迭代key和value,可以用for k, v in d.items()愿阐,字符串也是可迭代對(duì)象微服。
如何判斷一個(gè)對(duì)象是可迭代對(duì)象呢?方法是通過(guò)collections模塊的Iterable類型判斷:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable)? # list是否可迭代
True
>>> isinstance(123, Iterable)? # 整數(shù)是否可迭代
False
如果要對(duì)list實(shí)現(xiàn)類似Java那樣的下標(biāo)循環(huán)怎么辦缨历?Python內(nèi)置的enumerate函數(shù)可以把一個(gè)list變成索引-元素對(duì)以蕴,這樣就可以在for循環(huán)中同時(shí)迭代索引和元素本身:
>>> for i, value in enumerate(['A','B','C']):
... ????????print(i, value)
...
0 A
1 B
2 C
列表生成式
for循環(huán)后面還可以加上if判斷,還可以使用兩層循環(huán)辛孵,可以生成全排列
>>> [x * x for x in range(1,11)]
[1,4,9,16,25,36,49,64,81,100]
>>> [x * x for x in range(1,11) if x %2==0]
[4,16,36,64,100]
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX','AY','AZ','BX','BY','BZ','CX','CY','CZ']
循環(huán)其實(shí)可以同時(shí)使用兩個(gè)甚至多個(gè)變量丛肮,列表生成式也可以使用兩個(gè)變量來(lái)生成list
>>> d = {'x':'A','y':'B','z':'C'}
>>> for k, v in d.items()
:... ????????print(k,'=', v)
...
y = B
x = A
z = C
>>> d = {'x':'A','y':'B','z':'C'}
>>> [k +'='+ v for k, v in d.items()]
['y=B','x=A','z=C']
>>> L = ['Hello','World','IBM','Apple']
>>> [s.lower() for s in L]
['hello','world','ibm','apple']
生成器(generator)
通過(guò)列表生成式,我們可以直接創(chuàng)建一個(gè)列表觉吭。但是腾供,受到內(nèi)存限制,列表容量肯定是有限的鲜滩。而且,創(chuàng)建一個(gè)包含100萬(wàn)個(gè)元素的列表节值,不僅占用很大的存儲(chǔ)空間徙硅,如果我們僅僅需要訪問(wèn)前面幾個(gè)元素,那后面絕大多數(shù)元素占用的空間都白白浪費(fèi)了搞疗。所以嗓蘑,如果列表元素可以按照某種算法推算出來(lái),那我們是否可以在循環(huán)的過(guò)程中不斷推算出后續(xù)的元素呢匿乃?這樣就不必創(chuàng)建完整的list桩皿,從而節(jié)省大量的空間。在Python中幢炸,這種一邊循環(huán)一邊計(jì)算的機(jī)制泄隔,稱為生成器:generator
要?jiǎng)?chuàng)建一個(gè)generator,有很多種方法宛徊。第一種方法很簡(jiǎn)單佛嬉,只要把一個(gè)列表生成式的[]改成()逻澳,就創(chuàng)建了一個(gè)generator,如果要一個(gè)一個(gè)打印出來(lái)暖呕,可以通過(guò)next()函數(shù)獲得generator的下一個(gè)返回值斜做,沒(méi)有更多的元素時(shí),拋出StopIteration的錯(cuò)誤
>>> L = [x * x for x in range(10)]
>>> L
[0,1,4,9,16,25,36,49,64,81]
>>> g = (x * x for x in range(10))
>>> g
?<generator object <genexpr> at 0x1022ef630>
>>>next(g)
0
>>>next(g)
1
正確的方法是使用for循環(huán)湾揽,因?yàn)間enerator也是可迭代對(duì)象
>>> g = (x * x for x in range(10))
>>> for n in g:
... ????????print(n)
generator和函數(shù)的執(zhí)行流程不一樣瓤逼。函數(shù)是順序執(zhí)行,遇到return語(yǔ)句或者最后一行函數(shù)語(yǔ)句就返回库物。而變成generator的函數(shù)抛姑,在每次調(diào)用next()的時(shí)候執(zhí)行,遇到y(tǒng)ield語(yǔ)句返回艳狐,再次執(zhí)行時(shí)從上次返回的yield語(yǔ)句處繼續(xù)執(zhí)行定硝。
>>>def odd():
...? ? print('step 1')
...? ? yield(1)
...? ? print('step 2')
...? ? yield(3)
...? ? print('step 3')
...? ? yield(5)
>>>o = odd()
>>>next(o)
step1
1
>>>next(o)
step2
3
>>>next(o)
step3
5
>>>next(o)
Traceback(most recent call last):
????File"<stdin>", line1, in <module>
StopIteration
同樣的,把函數(shù)改成generator后毫目,我們基本上從來(lái)不會(huì)用next()來(lái)獲取下一個(gè)返回值蔬啡,而是直接使用for循環(huán)來(lái)迭代。但是用for循環(huán)調(diào)用generator時(shí)镀虐,發(fā)現(xiàn)拿不到generator的return語(yǔ)句的返回值箱蟆。如果想要拿到返回值,必須捕獲StopIteration錯(cuò)誤刮便,返回值包含在StopIteration的value中空猜。
迭代器
可以直接作用于for循環(huán)的數(shù)據(jù)類型有以下幾種:
一類是集合數(shù)據(jù)類型,如list恨旱、tuple辈毯、dict、set搜贤、str等谆沃;
一類是generator,包括生成器和帶yield的generator function仪芒。
這些可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱為可迭代對(duì)象:Iterable唁影。可以使用isinstance()判斷一個(gè)對(duì)象是否是Iterable對(duì)象掂名。
而生成器不但可以作用于for循環(huán)据沈,還可以被next()函數(shù)不斷調(diào)用并返回下一個(gè)值,直到最后拋出StopIteration錯(cuò)誤表示無(wú)法繼續(xù)返回下一個(gè)值了饺蔑⌒拷椋可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱為迭代器:Iterator“蚰疲可以使用isinstance()判斷一個(gè)對(duì)象是否是Iterator對(duì)象掏湾。
生成器都是Iterator對(duì)象裹虫,但list、dict融击、str雖然是Iterable筑公,卻不是Iterator。把list尊浪、dict匣屡、str等Iterable變成Iterator可以使用iter()函數(shù)。
Python的Iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流拇涤,Iterator對(duì)象可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)數(shù)據(jù)捣作,直到?jīng)]有數(shù)據(jù)時(shí)拋出StopIteration錯(cuò)誤《焓浚可以把這個(gè)數(shù)據(jù)流看做是一個(gè)有序序列券躁,但我們卻不能提前知道序列的長(zhǎng)度,只能不斷通過(guò)next()函數(shù)實(shí)現(xiàn)按需計(jì)算下一個(gè)數(shù)據(jù)掉盅,所以Iterator的計(jì)算是惰性的也拜,只有在需要返回下一個(gè)數(shù)據(jù)時(shí)它才會(huì)計(jì)算。
map/reduce
map()函數(shù)接收兩個(gè)參數(shù)趾痘,一個(gè)是函數(shù)慢哈,一個(gè)是Iterable,map將傳入的函數(shù)依次作用到序列的每個(gè)元素永票,并把結(jié)果作為新的Iterator返回.
>>> def f(x):
... return x * x
...
>>> r = map(f, [1,2,3,4,5,6,7,8,9])
>>> list(r)
[1,4,9,16,25,36,49,64,81]
reduce()把一個(gè)函數(shù)作用在一個(gè)序列[x1, x2, x3, ...]上卵贱,這個(gè)函數(shù)必須接收兩個(gè)參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個(gè)元素做累積計(jì)算侣集,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
filter
Python內(nèi)建的filter()函數(shù)用于過(guò)濾序列键俱。和map()類似,filter()也接收一個(gè)函數(shù)和一個(gè)序列肚吏。和map()不同的是方妖,filter()把傳入的函數(shù)依次作用于每個(gè)元素,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素罚攀。
注意到filter()函數(shù)返回的是一個(gè)Iterator,也就是一個(gè)惰性序列雌澄,所以要強(qiáng)迫f(wàn)ilter()完成計(jì)算結(jié)果斋泄,需要用list()函數(shù)獲得所有結(jié)果并返回list。
sorted
Python內(nèi)置的sorted()函數(shù)就可以對(duì)list進(jìn)行排序镐牺,sorted()函數(shù)也是一個(gè)高階函數(shù)炫掐,它還可以接收一個(gè)key函數(shù)來(lái)實(shí)現(xiàn)自定義的排序,要進(jìn)行反向排序睬涧,不必改動(dòng)key函數(shù)募胃,可以傳入第三個(gè)參數(shù)reverse=True旗唁。
>>> sorted([36,5, -12,9, -21], key=abs)
[5,9, -12, -21,36]
>>> sorted(['bob','about','Zoo','Credit'], key=str.lower)
['about','bob','Credit','Zoo']
>>> sorted(['bob','about','Zoo','Credit'], key=str.lower, reverse=True)
['Zoo','Credit','bob','about']
函數(shù)作為返回值
相關(guān)參數(shù)和變量都保存在返回的函數(shù)中,這種稱為“閉包(Closure)”痹束,麻煩检疫,有用嗎?
匿名函數(shù)
關(guān)鍵字lambda表示匿名函數(shù)祷嘶,冒號(hào)前面的x表示函數(shù)參數(shù)屎媳。匿名函數(shù)有個(gè)限制,就是只能有一個(gè)表達(dá)式论巍,不用寫(xiě)return烛谊,返回值就是該表達(dá)式的結(jié)果。
用匿名函數(shù)有個(gè)好處嘉汰,因?yàn)楹瘮?shù)沒(méi)有名字丹禀,不必?fù)?dān)心函數(shù)名沖突。此外鞋怀,匿名函數(shù)也是一個(gè)函數(shù)對(duì)象双泪,也可以把匿名函數(shù)賦值給一個(gè)變量,再利用變量來(lái)調(diào)用該函數(shù):
裝飾器
__name__接箫,可以拿到函數(shù)的名字攒读,__doc__,__author__?這里略過(guò)
偏函數(shù)
簡(jiǎn)單總結(jié)functools.partial的作用就是辛友,把一個(gè)函數(shù)的某些參數(shù)給固定妆”狻(也就是設(shè)置默認(rèn)值),返回一個(gè)新的函數(shù)废累,調(diào)用這個(gè)新函數(shù)會(huì)更簡(jiǎn)單邓梅。
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
當(dāng)函數(shù)的參數(shù)個(gè)數(shù)太多,需要簡(jiǎn)化時(shí)邑滨,使用functools.partial可以創(chuàng)建一個(gè)新的函數(shù)日缨,這個(gè)新函數(shù)可以固定住原函數(shù)的部分參數(shù),從而在調(diào)用時(shí)更簡(jiǎn)單掖看。
模塊
在Python中匣距,一個(gè).py文件就稱之為一個(gè)模塊(Module)。使用模塊還可以避免函數(shù)名和變量名沖突哎壳。相同名字的函數(shù)和變量完全可以分別存在不同的模塊中毅待,為了避免模塊名沖突,Python又引入了按目錄來(lái)組織模塊的方法归榕,稱為包(Package)尸红。
引入了包以后,只要頂層的包名不與別人沖突,那所有模塊都不會(huì)與別人沖突⊥饫铮現(xiàn)在怎爵,abc.py模塊的名字就變成了mycompany.abc,類似的盅蝗,xyz.py的模塊名變成了mycompany.xyz鳖链。請(qǐng)注意,每一個(gè)包目錄下面都會(huì)有一個(gè)__init__.py的文件风科,這個(gè)文件是必須存在的撒轮,否則,Python就把這個(gè)目錄當(dāng)成普通目錄贼穆,而不是一個(gè)包题山。__init__.py可以是空文件,也可以有Python代碼故痊,因?yàn)開(kāi)_init__.py本身就是一個(gè)模塊顶瞳,而它的模塊名就是mycompany。
創(chuàng)建自己的模塊時(shí)愕秫,要注意:
1. 模塊名要遵循Python變量命名規(guī)范慨菱,不要使用中文、特殊字符戴甩;
2. 模塊名不要和系統(tǒng)模塊名沖突符喝,最好先查看系統(tǒng)是否已存在該模塊,檢查方法是在Python交互環(huán)境執(zhí)行import abc甜孤,若成功則說(shuō)明系統(tǒng)存在此模塊协饲。
使用模塊
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ ='Leo Zhao'
import sys
def test():
? ? args = sys.argv
if __name__=='__main__':
? ? test()
類似__xxx__這樣的變量是特殊變量,可以被直接引用缴川,但是有特殊用途茉稠,比如上面的__author__,__name__就是特殊變量把夸,hello模塊定義的文檔注釋也可以用特殊變量__doc__訪問(wèn)而线,我們自己的變量一般不要用這種變量名;
類似_xxx和__xxx這樣的函數(shù)或變量就是非公開(kāi)的(private)恋日,不應(yīng)該被直接引用膀篮,比如_abc,__abc等岂膳;
安裝第三方模塊
在Python中各拷,安裝第三方模塊,是通過(guò)包管理工具pip完成的闷营,一般來(lái)說(shuō),第三方庫(kù)都會(huì)在Python官方的pypi.python.org網(wǎng)站注冊(cè)。用pip一個(gè)一個(gè)安裝費(fèi)時(shí)費(fèi)力傻盟,還需要考慮兼容性速蕊。我們推薦直接使用Anaconda,這是一個(gè)基于Python的數(shù)據(jù)處理和科學(xué)計(jì)算平臺(tái)娘赴,它已經(jīng)內(nèi)置了許多非常有用的第三方庫(kù)规哲,我們裝上Anaconda,就相當(dāng)于把數(shù)十個(gè)第三方模塊自動(dòng)安裝好了诽表,非常簡(jiǎn)單易用唉锌。
默認(rèn)情況下,Python解釋器會(huì)搜索當(dāng)前目錄竿奏、所有已安裝的內(nèi)置模塊和第三方模塊袄简,搜索路徑存放在sys模塊的path變量中,如果我們要添加自己的搜索目錄泛啸,有兩種方法:一是直接修改sys.path绿语,添加要搜索的目錄,第二種方法是設(shè)置環(huán)境變量PYTHONPATH候址,該環(huán)境變量的內(nèi)容會(huì)被自動(dòng)添加到模塊搜索路徑中吕粹。設(shè)置方式與設(shè)置Path環(huán)境變量類似。
>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')
面向?qū)ο缶幊?/h1>class Student(object):
? ??def __init__(self, name, score):
? ? ? ? ?self.name = name
? ? ? ? ?self.score = score
class Student(object):
? ??def __init__(self, name, score):
? ? ? ? ?self.name = name
? ? ? ? ?self.score = score
class后面緊接著是類名岗仑,即Student匹耕,類名通常是大寫(xiě)開(kāi)頭的單詞,緊接著是(object)荠雕,表示該類是從哪個(gè)類繼承下來(lái)的稳其,如果沒(méi)有合適的繼承類,就使用object類舞虱,這是所有類最終都會(huì)繼承的類欢际。
__init__方法的第一個(gè)參數(shù)永遠(yuǎn)是self,表示創(chuàng)建的實(shí)例本身矾兜,因此损趋,在__init__方法內(nèi)部,就可以把各種屬性綁定到self椅寺,因?yàn)閟elf就指向創(chuàng)建的實(shí)例本身浑槽。有了__init__方法,在創(chuàng)建實(shí)例的時(shí)候返帕,就不能傳入空的參數(shù)了桐玻,必須傳入與__init__方法匹配的參數(shù),但self不需要傳荆萤,Python解釋器自己會(huì)把實(shí)例變量傳進(jìn)去镊靴。
和普通的函數(shù)相比铣卡,在類中定義的函數(shù)只有一點(diǎn)不同,就是第一個(gè)參數(shù)永遠(yuǎn)是實(shí)例變量self偏竟,并且煮落,調(diào)用時(shí),不用傳遞該參數(shù)踊谋。除此之外蝉仇,類的方法和普通函數(shù)沒(méi)有什么區(qū)別,所以殖蚕,你仍然可以用默認(rèn)參數(shù)轿衔、可變參數(shù)、關(guān)鍵字參數(shù)和命名關(guān)鍵字參數(shù)睦疫。
和靜態(tài)語(yǔ)言不同害驹,Python允許對(duì)實(shí)例變量綁定任何數(shù)據(jù),也就是說(shuō)笼痛,對(duì)于兩個(gè)實(shí)例變量裙秋,雖然它們都是同一個(gè)類的不同實(shí)例,但擁有的變量名稱都可能不同:
>>> bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8
>>> bart.age
8
>>> lisa.age
Traceback (most recent call last):
? ?File "", line 1, in <module>
AttributeError: 'Student' object has no attribute 'age'
訪問(wèn)限制
如果要讓內(nèi)部屬性不被外部訪問(wèn)缨伊,可以把屬性的名稱前加上兩個(gè)下劃線__摘刑,就變成了一個(gè)私有變量(private),只有內(nèi)部可以訪問(wèn)刻坊,外部不能訪問(wèn)枷恕。
class Student(object):
? ? def __init__(self, name, score):
? ? ? ? self.__name = name
? ? ? ? self.__score = score
雙下劃線開(kāi)頭的實(shí)例變量是不是一定不能從外部訪問(wèn)呢?其實(shí)也不是谭胚。不能直接訪問(wèn)__name是因?yàn)镻ython解釋器對(duì)外把__name變量改成了_Student__name徐块,所以,仍然可以通過(guò)_Student__name來(lái)訪問(wèn)__name變量
繼承和多態(tài)
同C++
靜態(tài)語(yǔ)言 vs 動(dòng)態(tài)語(yǔ)言
對(duì)于靜態(tài)語(yǔ)言(例如C++)來(lái)說(shuō)灾而,如果需要傳入Animal類型胡控,則傳入的對(duì)象必須是Animal類型或者它的子類,否則旁趟,將無(wú)法調(diào)用run()方法昼激。對(duì)于Python這樣的動(dòng)態(tài)語(yǔ)言來(lái)說(shuō),則不一定需要傳入Animal類型锡搜。我們只需要保證傳入的對(duì)象有一個(gè)run()方法就可以了.
獲取對(duì)象信息
type():
使用type()判斷對(duì)象類型橙困,判斷基本數(shù)據(jù)類型可以直接寫(xiě)int,str等耕餐,但如果要判斷一個(gè)對(duì)象是否是函數(shù)怎么辦凡傅?可以使用types模塊中定義的常量:
>>> import types
>>> def fn():
... pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
isinstance():
要判斷class的類型,可以使用isinstance()函數(shù)肠缔,能用type()判斷的基本類型也可以用isinstance()判斷夏跷,并且還可以判斷一個(gè)變量是否是某些類型中的一種哼转,比如:
>>> isinstance([1,2,3], (list, tuple))
True
>>> isinstance((1,2,3), (list, tuple))
True
dir():
如果要獲得一個(gè)對(duì)象的所有屬性和方法,可以使用dir()函數(shù)拓春,它返回一個(gè)包含字符串的list释簿,這個(gè)可用于查詢特定類型支持的函數(shù)操作:
>>> dir('ABC')['__add__','__class__',...,'__subclasshook__','capitalize','casefold',...,'zfill']
類似__xxx__的屬性和方法在Python中都是有特殊用途的,比如__len__方法返回長(zhǎng)度硼莽。在Python中,如果你調(diào)用len()函數(shù)試圖獲取一個(gè)對(duì)象的長(zhǎng)度煮纵,實(shí)際上懂鸵,在len()函數(shù)內(nèi)部,它自動(dòng)去調(diào)用該對(duì)象的__len__()方法行疏,所以匆光,下面的代碼是等價(jià)的:
>>> len('ABC')
3
>>> 'ABC'.__len__()
3
我們自己寫(xiě)的類,如果也想用len(myObj)的話酿联,就自己寫(xiě)一個(gè)__len__()方法:
>>> class MyDog(object):
... def __len__(self):
... return 100
...
>>> dog = MyDog()
>>> len(dog)
100
配合getattr()终息、setattr()以及hasattr(),我們可以直接操作一個(gè)對(duì)象的狀態(tài):
>>> hasattr(obj,'x')????# 有屬性'x'嗎贞让?
True
>>> obj.x
9
>>> hasattr(obj,'y')????# 有屬性'y'嗎周崭?
False
>>> setattr(obj,'y',19)????# 設(shè)置一個(gè)屬性'y'
>>> hasattr(obj,'y')????# 有屬性'y'嗎?
True
>>> getattr(obj,'y')????# 獲取屬性'y'
19
>>> obj.y????# 獲取屬性'y'
19
如果試圖獲取不存在的屬性喳张,會(huì)拋出AttributeError的錯(cuò)誤续镇,可以傳入一個(gè)default參數(shù),如果屬性不存在销部,就返回默認(rèn)值:
>>> getattr(obj, 'z') ????# 獲取屬性'z'
Traceback (most recent call last):? File "", line 1, in <module>
AttributeError: 'MyObject' object has no attribute 'z'
>>> getattr(obj,'z',404)????# 獲取屬性'z'摸航,如果不存在,返回默認(rèn)值404
404
也可以獲得對(duì)象的方法:
>>> hasattr(obj,'power')????# 有屬性'power'嗎舅桩?
True
>>> getattr(obj,'power')????# 獲取屬性'power'
<bound method MyObject.power of <__main__.MyObject object at0x10077a6a0>>
>>>> fn = getattr(obj,'power')????# 獲取屬性'power'并賦值到變量fn
>>> fn????# fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at0x10077a6a0>>
>>> fn()????# 調(diào)用fn()與調(diào)用obj.power()是一樣的
81
實(shí)例屬性和類屬性
由于Python是動(dòng)態(tài)語(yǔ)言酱虎,根據(jù)類創(chuàng)建的實(shí)例可以任意綁定屬性,在編寫(xiě)程序的時(shí)候擂涛,千萬(wàn)不要對(duì)實(shí)例屬性和類屬性使用相同的名字读串,因?yàn)橄嗤Q的實(shí)例屬性將屏蔽掉類屬性,但是當(dāng)你刪除實(shí)例屬性后歼指,再使用相同的名稱爹土,訪問(wèn)到的將是類屬性。實(shí)例屬性屬于各個(gè)實(shí)例所有踩身,互不干擾胀茵;類屬性屬于類所有,所有實(shí)例共享一個(gè)屬性挟阻,類似C++ static琼娘;
>>>class Student(object):
...? ? ?def __init__(self, name):
...? ? ?self.name = name
...
>>>s = Student('Bob')
>>>s.score =90? ? #實(shí)例屬性
>>>del s.score>>>classStudent(object):
...? ? ?name ='Student'? #類屬性
實(shí)例綁定方法
給一個(gè)實(shí)例綁定的方法峭弟,對(duì)另一個(gè)實(shí)例是不起作用:
>>> def set_age(self, age):????# 定義一個(gè)函數(shù)作為實(shí)例方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s)????# 給實(shí)例綁定一個(gè)方法
>>> s.set_age(25)????# 調(diào)用實(shí)例方法
>>> s.age????# 測(cè)試結(jié)果25
為了給所有實(shí)例都綁定方法,可以給class綁定方法:
>>> def set_score(self, score)
:... self.score = score
...
>>> Student.set_score = set_score
__slots__
使用__slots__限制實(shí)例能添加的屬性:
>>> class Student(object):
...????????__slots__ = ('name','age')????# 用tuple定義允許綁定的屬性名稱
...
>>> s = Student()????# 創(chuàng)建新的實(shí)例
>>> s.name ='Michael'????# 綁定屬性'name'
>>> s.age =25????# 綁定屬性'age'
>>> s.score =99????# 綁定屬性'score'
Traceback (most recent call last): File"<stdin>", line1, in <module>
AttributeError: 'Student' object has no attribute 'score'
使用__slots__要注意脱拼,__slots__定義的屬性僅對(duì)當(dāng)前類實(shí)例起作用瞒瘸,對(duì)繼承的子類是不起作用的。
@property
Python內(nèi)置的@property裝飾器就是負(fù)責(zé)把一個(gè)方法變成屬性調(diào)用的熄浓,這個(gè)神奇的@property情臭,我們?cè)趯?duì)實(shí)例屬性操作的時(shí)候,就知道該屬性很可能不是直接暴露的赌蔑,而是通過(guò)getter和setter方法來(lái)實(shí)現(xiàn)的俯在,還可以定義只讀屬性,只定義getter方法娃惯,不定義setter方法就是一個(gè)只讀屬性赠涮。用于對(duì)賦值操作進(jìn)行檢查限制槽畔。
>>>class Student(object):
...????????@property
...? ? ? ? def birth(self):
...????????????return self._birth
...
...????????@birth.setter
...????????def birth(self, value):
...????????????self._birth = value
...
...????????@property
...? ? ? ? def age(self):
...????????????return 2015 - self._birth
定制類
__str__() 烤芦,__repr__() 添加打印類信息涕蜂,
__iter__?如果一個(gè)類想被用于for ... in循環(huán),類似list或tuple那樣皿哨,就必須實(shí)現(xiàn)一個(gè)__iter__()方法浅侨,該方法返回一個(gè)迭代對(duì)象,然后往史,Python的for循環(huán)就會(huì)不斷調(diào)用該迭代對(duì)象的__next__()方法拿到循環(huán)的下一個(gè)值仗颈,直到遇到StopIteration錯(cuò)誤時(shí)退出循環(huán)。
__getitem__?表現(xiàn)得像list那樣按照下標(biāo)取出元素椎例,需要實(shí)現(xiàn)__getitem__()方法
__getattr__?獲得屬性挨决,可以把一個(gè)類的所有屬性和方法調(diào)用全部動(dòng)態(tài)化處理了
__call__?
枚舉類
Enum可以把一組相關(guān)常量定義在一個(gè)class中,且class不可變订歪,而且成員可以直接比較脖祈,@unique裝飾器可以幫助我們檢查保證沒(méi)有重復(fù)值。
from enum import Enum, unique
@unique
class Weekday(Enum):
????Sun =0????# Sun的value被設(shè)定為0
????Mon =1
????Tue =2
????Wed =3
????Thu =4
????Fri =5
????Sat =6
元類
type()
創(chuàng)建一個(gè)class對(duì)象刷晋,type()函數(shù)依次傳入3個(gè)參數(shù):
1. class的名稱盖高;
2. 繼承的父類集合,注意Python支持多重繼承眼虱,如果只有一個(gè)父類喻奥,別忘了tuple的單元素寫(xiě)法;
3. class的方法名稱與函數(shù)綁定捏悬,這里我們把函數(shù)fn綁定到方法名hello上撞蚕。
metaclass
除了使用type()動(dòng)態(tài)創(chuàng)建類以外,要控制類的創(chuàng)建行為过牙,還可以使用metaclass甥厦,暫且略過(guò)纺铭。
錯(cuò)誤、調(diào)試和測(cè)試
錯(cuò)誤處理
try:
...
except xxException as e:
...
else:
...
finally:
...
Python內(nèi)置的logging模塊可以非常容易地記錄錯(cuò)誤信息:
import logging
logging.exception(e)
logging.info(xxx)
logging.basicConfig()
用raise語(yǔ)句拋出一個(gè)錯(cuò)誤的實(shí)例:
raise xxxError(xxx)
assert斷言刀疙,同C舶赔,啟動(dòng)Python解釋器時(shí)可以用-O參數(shù)來(lái)關(guān)閉assert,關(guān)閉后谦秧,可以把所有的assert語(yǔ)句當(dāng)成pass來(lái)看
assert n !=0, 'n is zero!'
調(diào)試
啟動(dòng)Python的調(diào)試器pdb竟纳,讓程序以單步方式運(yùn)行,可以隨時(shí)查看運(yùn)行狀態(tài)
$ python -m pdb err.py
>/Users/michael/Github/learn-python3/samples/debug/err.py(2)<module>()
-> s ='0'
IDE:
Visual Studio Code:https://code.visualstudio.com/油够,需要安裝Python插件蚁袭。PyCharm:http://www.jetbrains.com/pycharm/
另外,Eclipse加上pydev插件也可以調(diào)試Python程序石咬。
單元測(cè)試
編寫(xiě)單元測(cè)試時(shí),我們需要編寫(xiě)一個(gè)測(cè)試類卖哎,從unittest.TestCase繼承
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
? ? def test_init(self):
? ? ? ? d = Dict(a=1, b='test')
? ? ? ? self.assertEqual(d.a,1)
? ? ? ? self.assertEqual(d.b,'test')
? ? ? ? self.assertTrue(isinstance(d, dict))
? ? def test_key(self):
? ? ? ? d = Dict()
? ? ? ? d['key'] ='value'
? ? ? ? self.assertEqual(d.key,'value')
以test開(kāi)頭的方法就是測(cè)試方法鬼悠,不以test開(kāi)頭的方法不被認(rèn)為是測(cè)試方法,測(cè)試的時(shí)候不會(huì)被執(zhí)行亏娜。
運(yùn)行單元測(cè)試
if __name__=='__main__':
? ? unittest.main()
文檔測(cè)試
我們編寫(xiě)注釋時(shí)焕窝,如果寫(xiě)上這樣的注釋,無(wú)疑更明確地告訴函數(shù)的調(diào)用者該函數(shù)的期望輸入和輸出维贺。并且它掂,Python內(nèi)置的“文檔測(cè)試”(doctest)模塊可以直接提取注釋中的代碼并執(zhí)行測(cè)試。
def abs(n):
????'''
????Function to get absolute value of number.? ? Example:
? ? >>> abs(1)
????1
????>>> abs(-1)
????1
????>>> abs(0)
????0
????'''
????return n if n >=0 else (-n)
IO編程
文件讀寫(xiě)
open()函數(shù)溯泣,傳入文件名和標(biāo)示符虐秋,如果文件不存在,open()函數(shù)就會(huì)拋出一個(gè)IOError的錯(cuò)誤垃沦,并且給出錯(cuò)誤碼和詳細(xì)的信息告訴你文件不存在客给。
如果文件打開(kāi)成功,接下來(lái)肢簿,調(diào)用read()方法可以一次讀取文件的全部?jī)?nèi)容靶剑,Python把內(nèi)容讀到內(nèi)存,用一個(gè)str對(duì)象表示池充。
最后一步是調(diào)用close()方法關(guān)閉文件桩引。文件使用完畢后必須關(guān)閉,因?yàn)槲募?duì)象會(huì)占用操作系統(tǒng)的資源收夸,并且操作系統(tǒng)同一時(shí)間能打開(kāi)的文件數(shù)量也是有限的坑匠。
由于文件讀寫(xiě)時(shí)都有可能產(chǎn)生IOError,一旦出錯(cuò)咱圆,后面的f.close()就不會(huì)調(diào)用笛辟。所以功氨,為了保證無(wú)論是否出錯(cuò)都能正確地關(guān)閉文件,我們可以使用try ... finally來(lái)實(shí)現(xiàn)手幢。但是每次都這么寫(xiě)實(shí)在太繁瑣捷凄,所以,Python引入了with語(yǔ)句來(lái)自動(dòng)幫我們調(diào)用close()方法:
try:
? ? f = open('/path/to/file','r')
? ? print(f.read())
finally:
? ? if f:
? ? ? ? f.close()with open('/path/to/file','r') as f:
? ? ?print(f.read())
要讀取非UTF-8編碼的文本文件围来,需要給open()函數(shù)傳入encoding參數(shù)跺涤。
StringIO/BytesIO
就是在內(nèi)存中創(chuàng)建的file-like Object,常用作臨時(shí)緩沖监透。
要把str寫(xiě)入StringIO桶错,我們需要先創(chuàng)建一個(gè)StringIO,然后胀蛮,像文件一樣寫(xiě)入即可, getvalue()方法用于獲得寫(xiě)入后的str院刁。
BytesIO實(shí)現(xiàn)了在內(nèi)存中讀寫(xiě)bytes,我們創(chuàng)建一個(gè)BytesIO粪狼,然后寫(xiě)入一些bytes
操作文件和目錄
信息和環(huán)境變量
>>> import os
>>> os.name????# 操作系統(tǒng)類型
'posix'
>>> os.uname()
posix.uname_result(sysname='Darwin', nodename='MichaelMacPro.local', release='14.3.0', version='Darwin Kernel Version 14.3.0: Mon Mar 23 11:59:05 PDT 2015; root:xnu-2782.20.48~5/RELEASE_X86_64', machine='x86_64')
>>> os.environ
environ({'VERSIONER_PYTHON_PREFER_32_BIT':'no','TERM_PROGRAM_VERSION':'326','LOGNAME':'michael','USER':'michael','PATH':'/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin', ...})
>>> os.environ.get('PATH')
'/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin'
>>> os.environ.get('x','default')
'default'
操作文件和目錄
# 查看當(dāng)前目錄的絕對(duì)路徑:
>>> os.path.abspath('.')
'/Users/michael'????
# 在某個(gè)目錄下創(chuàng)建一個(gè)新目錄退腥,首先把新目錄的完整路徑表示出來(lái):
>>> os.path.join('/Users/michael','testdir')
'/Users/michael/testdir'????
# 然后創(chuàng)建一個(gè)目錄:
>>> os.mkdir('/Users/michael/testdir')????
# 刪掉一個(gè)目錄:
>>> os.rmdir('/Users/michael/testdir')
把兩個(gè)路徑合成一個(gè)時(shí),不要直接拼字符串再榄,而要通過(guò)os.path.join()函數(shù)狡刘,這樣可以正確處理不同操作系統(tǒng)的路徑分隔符。同樣的道理困鸥,要拆分路徑時(shí)嗅蔬,也不要直接去拆字符串,而要通過(guò)os.path.split()函數(shù)疾就,這樣可以把一個(gè)路徑拆分為兩部分澜术,后一部分總是最后級(jí)別的目錄或文件名。os.path.splitext()可以直接讓你得到文件擴(kuò)展名虐译,很多時(shí)候非常方便瘪板。
# 對(duì)文件重命名:
>>> os.rename('test.txt','test.py')
# 刪掉文件:
>>> os.remove('test.py')
復(fù)制文件的函數(shù)居然在os模塊中不存在!原因是復(fù)制文件并非由操作系統(tǒng)提供的系統(tǒng)調(diào)用漆诽。幸運(yùn)的是shutil模塊提供了copyfile()的函數(shù)侮攀,還可以在shutil模塊中找到很多實(shí)用函數(shù),它們可以看做是os模塊的補(bǔ)充厢拭。
利用Python的特性來(lái)過(guò)濾文件
>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]
>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']
序列化
我們把變量從內(nèi)存中變成可存儲(chǔ)或傳輸?shù)倪^(guò)程稱之為序列化兰英,在Python中叫pickling,在其他語(yǔ)言中也被稱之為serialization供鸠,marshalling畦贸,flattening等等,都是一個(gè)意思。反過(guò)來(lái)薄坏,把變量?jī)?nèi)容從序列化的對(duì)象重新讀到內(nèi)存里稱之為反序列化趋厉,即unpickling。Python提供了pickle模塊來(lái)實(shí)現(xiàn)序列化胶坠。
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'
>>> f = open('dump.txt','wb')
>>> pickle.dump(d, f)
>>> f.close()
>>> f = open('dump.txt','rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age':20, 'score':88, 'name':'Bob'}
Pickle的問(wèn)題和所有其他編程語(yǔ)言特有的序列化問(wèn)題一樣君账,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容沈善,因此乡数,只能用Pickle保存那些不重要的數(shù)據(jù),不能成功地反序列化也沒(méi)關(guān)系闻牡。
JSON
JSON表示的對(duì)象就是標(biāo)準(zhǔn)的JavaScript語(yǔ)言的對(duì)象净赴,JSON和Python內(nèi)置的數(shù)據(jù)類型對(duì)應(yīng)如下:
Python內(nèi)置的json模塊提供了非常完善的Python對(duì)象到JSON格式的轉(zhuǎn)換。
如何序列化class對(duì)象罩润,https://docs.python.org/3/library/json.html#json.dumps
進(jìn)程和線程
多進(jìn)程
Python的os模塊封裝了常見(jiàn)的系統(tǒng)調(diào)用玖翅,其中就包括fork,可以在Python程序中輕松創(chuàng)建子進(jìn)程割以,由于Windows沒(méi)有fork調(diào)用烧栋,在Windows上無(wú)法運(yùn)行。
import os
print('Process (%s) start...'% os.getpid())
# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid ==0:
? ? ? print('I am child process (%s) and my parent is %s.'% (os.getpid(), os.getppid()))
else:
? ? ? print('I (%s) just created a child process (%s).'% (os.getpid(), pid))
multiprocessing
由于Python是跨平臺(tái)的拳球,自然也應(yīng)該提供一個(gè)跨平臺(tái)的多進(jìn)程支持。multiprocessing模塊就是跨平臺(tái)版本的多進(jìn)程模塊珍特。multiprocessing模塊提供了一個(gè)Process類來(lái)代表一個(gè)進(jìn)程對(duì)象祝峻。
from multiprocessing import Process
import os
# 子進(jìn)程要執(zhí)行的代碼
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('Child process will start.')
? ? p.start()
????p.join()
????print('Child process end.')
Pool
如果要啟動(dòng)大量的子進(jìn)程,可以用進(jìn)程池的方式批量創(chuàng)建子進(jìn)程:
from multiprocessing importPool
import os, time, random
def long_time_task(name):
????print('Run task %s (%s)...'% (name, os.getpid()))
????start = time.time()
????time.sleep(random.random() *3)
????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(long_time_task, args=(i,))
????????print('Waiting for all subprocesses done...')
? ? ? ? p.close()
????????p.join()
????????print('All subprocesses done.')
對(duì)Pool對(duì)象調(diào)用join()方法會(huì)等待所有子進(jìn)程執(zhí)行完畢扎筒,調(diào)用join()之前必須先調(diào)用close()莱找,調(diào)用close()之后就不能繼續(xù)添加新的Process了。
子進(jìn)程
subprocess模塊可以讓我們非常方便地啟動(dòng)一個(gè)子進(jìn)程嗜桌,然后控制其輸入和輸出奥溺。如果子進(jìn)程還需要輸入,則可以通過(guò)communicate()方法輸入骨宠。
import subprocess
print('$ nslookup www.python.org')
r = subprocess.call(['nslookup','www.python.org'])print('Exit code:', r)print('$ nslookup')
p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
print(output.decode('utf-8'))
print('Exit code:', p.returncode)
進(jìn)程間通信
Process之間肯定是需要通信的浮定,操作系統(tǒng)提供了很多機(jī)制來(lái)實(shí)現(xiàn)進(jìn)程間的通信。Python的multiprocessing模塊包裝了底層的機(jī)制层亿,提供了Queue桦卒、Pipes等多種方式來(lái)交換數(shù)據(jù)。
from multiprocessing import Process, Queue
import os, time, random# 寫(xiě)數(shù)據(jù)進(jìn)程執(zhí)行的代碼:
def write(q):
????print('Process to write: %s'% os.getpid())
????for value in ['A','B','C']:
????????print('Put %s to queue...'% value)
????????q.put(value)
????????time.sleep(random.random())# 讀數(shù)據(jù)進(jìn)程執(zhí)行的代碼:
def read(q):
????print('Process to read: %s'% os.getpid())
????while True:
????????value = q.get(True)
????????print('Get %s from queue.'% value)if__name__=='__main__':
# 父進(jìn)程創(chuàng)建Queue匿又,并傳給各個(gè)子進(jìn)程:
????q = Queue()
????pw = Process(target=write, args=(q,))
????pr = Process(target=read, args=(q,))
????# 啟動(dòng)子進(jìn)程pw方灾,寫(xiě)入:
????pw.start()
????# 啟動(dòng)子進(jìn)程pr,讀取:
????pr.start()
????# 等待pw結(jié)束:
????pw.join()
????# pr進(jìn)程里是死循環(huán),無(wú)法等待其結(jié)束裕偿,只能強(qiáng)行終止:
????pr.terminate()
多線程
Python的標(biāo)準(zhǔn)庫(kù)提供了兩個(gè)模塊:_thread和threading洞慎,_thread是低級(jí)模塊,threading是高級(jí)模塊嘿棘,對(duì)_thread進(jìn)行了封裝劲腿。絕大多數(shù)情況下,我們只需要使用threading這個(gè)高級(jí)模塊蔫巩。
import time, threading
# 新線程執(zhí)行的代碼:
def loop():
????print('thread %s is running...'% threading.current_thread().name)
????n =0
????while n <5:
? ? ? ? n = n +1
????????print('thread %s >>> %s'% (threading.current_thread().name, n))
????????time.sleep(1)
? ? print('thread %s ended.'% threading.current_thread().name)print('thread %s is running...'% threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.'% threading.current_thread().name)
Lock
lock = threading.Lock()
lock.acquire()
lock.release()
多核cpu多線程
因?yàn)镻ython的線程雖然是真正的線程谆棱,但解釋器執(zhí)行代碼時(shí),有一個(gè)GIL鎖:Global Interpreter Lock圆仔,任何Python線程執(zhí)行前垃瞧,必須先獲得GIL鎖,然后坪郭,每執(zhí)行100條字節(jié)碼个从,解釋器就自動(dòng)釋放GIL鎖,讓別的線程有機(jī)會(huì)執(zhí)行歪沃。這個(gè)GIL全局鎖實(shí)際上把所有線程的執(zhí)行代碼都給上了鎖嗦锐,所以,多線程在Python中只能交替執(zhí)行沪曙,即使100個(gè)線程跑在100核CPU上奕污,也只能用到1個(gè)核。
GIL是Python解釋器設(shè)計(jì)的歷史遺留問(wèn)題液走,通常我們用的解釋器是官方實(shí)現(xiàn)的CPython碳默,要真正利用多核,除非重寫(xiě)一個(gè)不帶GIL的解釋器缘眶。
所以嘱根,在Python中,可以使用多線程巷懈,但不要指望能有效利用多核该抒。如果一定要通過(guò)多線程利用多核,那只能通過(guò)C擴(kuò)展來(lái)實(shí)現(xiàn)顶燕,不過(guò)這樣就失去了Python簡(jiǎn)單易用的特點(diǎn)凑保。
不過(guò),也不用過(guò)于擔(dān)心割岛,Python雖然不能利用多線程實(shí)現(xiàn)多核任務(wù)愉适,但可以通過(guò)多進(jìn)程實(shí)現(xiàn)多核任務(wù)。多個(gè)Python進(jìn)程有各自獨(dú)立的GIL鎖癣漆,互不影響维咸。
ThreadLocal
一個(gè)ThreadLocal變量雖然是全局變量,但每個(gè)線程都只能讀寫(xiě)自己線程的獨(dú)立副本,互不干擾癌蓖。ThreadLocal解決了參數(shù)在一個(gè)線程中各個(gè)函數(shù)之間互相傳遞的問(wèn)題瞬哼。
import threading
# 創(chuàng)建全局ThreadLocal對(duì)象:
local_school = threading.local()def process_student():
????# 獲取當(dāng)前線程關(guān)聯(lián)的student:
????std = local_school.student
????print('Hello, %s (in %s)'% (std, threading.current_thread().name))def process_thread(name):
????# 綁定ThreadLocal的student:
????local_school.student = name
????process_student()t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
分布式進(jìn)程
Python的multiprocessing模塊不但支持多進(jìn)程,其中managers子模塊還支持把多進(jìn)程分布到多臺(tái)機(jī)器上租副。一個(gè)服務(wù)進(jìn)程可以作為調(diào)度者坐慰,將任務(wù)分布到其他多個(gè)進(jìn)程中,依靠網(wǎng)絡(luò)通信用僧。由于managers模塊封裝很好结胀,不必了解網(wǎng)絡(luò)通信的細(xì)節(jié),就可以很容易地編寫(xiě)分布式多進(jìn)程程序责循。
正則表達(dá)式
1. 在正則表達(dá)式中糟港,如果直接給出字符,就是精確匹配院仿。
2. \d 可以匹配一個(gè)數(shù)字秸抚;
3. \w 可以匹配一個(gè)字母或數(shù)字;
4. . 可以匹配任意字符歹垫;
5. 要匹配變長(zhǎng)的字符剥汤,在正則表達(dá)式中,用*表示任意個(gè)字符(包括0個(gè))排惨;
6. +表示至少一個(gè)字符吭敢;
7. ?表示0個(gè)或1個(gè)字符;
8. {n}表示n個(gè)字符暮芭,用{n,m}表示n-m個(gè)字符省有。
9.?\s可以匹配一個(gè)空格(也包括Tab等空白符)
來(lái)看一個(gè)復(fù)雜的例子:\d{3}\s+\d{3,8},我們來(lái)從左到右解讀一下:
????\d{3}表示匹配3個(gè)數(shù)字谴麦,例如'010';
????\s可以匹配一個(gè)空格(也包括Tab等空白符)伸头,所以\s+表示至少有一個(gè)空格匾效,例如匹配'? ','? ?'等恤磷;
????\d{3,8}表示3-8個(gè)數(shù)字面哼,例如'1234567'。
10.?用[]表示范圍扫步,比如:
????????[0-9a-zA-Z\_]可以匹配一個(gè)數(shù)字魔策、字母或者下劃線;
????????[0-9a-zA-Z\_]+可以匹配至少由一個(gè)數(shù)字河胎、字母或者下劃線組成的字符串闯袒,比如'a100','0_Z','Py3000'等等政敢;
????????[a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下劃線開(kāi)頭其徙,后接任意個(gè)由一個(gè)數(shù)字、字母或者下劃線組成的字符串喷户,也就是Python合法的變量唾那;
????????[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}更精確地限制了變量的長(zhǎng)度是1-20個(gè)字符(前面1個(gè)字符+后面最多19個(gè)字符)。
11. A|B可以匹配A或B褪尝,所以(P|p)ython可以匹配'Python'或者'python'闹获。
12. ^表示行的開(kāi)頭,^\d表示必須以數(shù)字開(kāi)頭河哑。
13. $表示行的結(jié)束避诽,\d$表示必須以數(shù)字結(jié)束。
re模塊
>>> import re
>>> re.match(r'^\d{3}\-\d{3,8}$', '010-12345')
<_sre.SRE_Match object; span=(0,9), match='010-12345'>
>>> re.match(r'^\d{3}\-\d{3,8}$', '010 12345')
>>>
match()方法判斷是否匹配灾馒,如果匹配成功茎用,返回一個(gè)Match對(duì)象,否則返回None睬罗。
切分字符串
用正則表達(dá)式切分字符串比用固定的字符更靈活
>>> 'a b c'.split(' ')
['a','b','','','c']
>>> re.split(r'\s+','a b c')
['a','b','c']
>>> re.split(r'[\s\,]+','a,b, c d')
['a','b','c','d']
>>> re.split(r'[\s\,\;]+','a,b;; c d')
['a','b','c','d']
分組
除了簡(jiǎn)單地判斷是否匹配之外轨功,正則表達(dá)式還有提取子串的強(qiáng)大功能。用()表示的就是要提取的分組(Group)容达。
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0,9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
貪婪匹配
正則匹配默認(rèn)是貪婪匹配古涧,也就是匹配盡可能多的字符。
>>> re.match(r'^(\d+)(0*)$','102300').groups()
('102300', '')
>>> re.match(r'^(\d+?)(0*)$','102300').groups()
('1023', '00')
由于\d+采用貪婪匹配花盐,直接把后面的0全部匹配了羡滑,結(jié)果0*只能匹配空字符串了。必須讓\d+采用非貪婪匹配(也就是盡可能少匹配)算芯,才能把后面的0匹配出來(lái)柒昏,加個(gè)?就可以讓\d+采用非貪婪匹配。
編譯
如果一個(gè)正則表達(dá)式要重復(fù)使用幾千次熙揍,出于效率的考慮职祷,我們可以預(yù)編譯該正則表達(dá)式,接下來(lái)重復(fù)使用時(shí)就不需要編譯這個(gè)步驟了届囚,直接匹配
>>> import re
# 編譯:
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')
編譯后生成Regular Expression對(duì)象有梆,由于該對(duì)象自己包含了正則表達(dá)式,所以調(diào)用對(duì)應(yīng)的方法時(shí)不用給出正則字符串意系。
常見(jiàn)模塊
內(nèi)建模塊
datetime
collections:?namedtuple,?deque,?defaultdict,?OrderedDict,?ChainMap,?Counter
base64
struct
harshlib
hmac
itertools
contextlib
urllib
xml
HTMLParser
第三方模塊
Pillow:圖像庫(kù)
request:網(wǎng)絡(luò)庫(kù)
chardet:檢測(cè)編碼
psutil:系統(tǒng)監(jiān)控
其他
virtualenv
virtualenv就是用來(lái)為一個(gè)應(yīng)用創(chuàng)建一套“隔離”的Python運(yùn)行環(huán)境
IDE
PyCharm
Komodo
引用
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
http://www.runoob.com/python3/python3-tutorial.html