函數python
abs(-20) //結果爲:20,絕對值函數git
def 函數名稱([參數1,參數2,參數3]):程序員
執行語句算法
return 編程
def test(r):json
s=3.14*r*r設計模式
return sapi
s=test(1)網絡
print s //結果爲:3.14數據結構
cmp(x,y) //比較函數,若是x<y
,返回-1
,若是x==y
,返回0
,若是x>y
,返回1
float('12.34') //結果爲:12.34
str(123) //結果爲:'123'
unicode(100) //結果爲:u'100'
bool(1) //結果爲:True
bool('') //結果爲:False
空語句 pass ,至關於佔位符,讓代碼先跑起來
據類型檢查能夠用內置函數
isinstanceisinstance
實現:
if not isinstance(x, (int, float)): raise TypeError('bad operand type')
函數能夠同時返回多個值,但其實就是一個tuple。
def test(a):
x=a+1
y=a+2
return x,y
r=test(1)
print r //結果爲(2,3)
def enroll(name,gender,age=6,city='BeiJing'): //參數後面加等號即爲默認參數,調取函數時選填
print 'name:',name
print 'gender:',gender
print 'age:',age
print 'city:'city
enroll('Ling','F')
enroll('ling','M',7)
enroll('ling','M',city='ShangHai')
默認參數如用到[],會有個坑,重複調用會記住上次的[],寫的時候最好這麼寫
def add_end(L=None) //使用None這個不變的對象來實現
if L is None:
L=[]
.....
可變參數
面向對象最重要的概念就是類(class)和實例(instance),必須牢記類是抽象的模板,好比Student類,而實例是根據類建立出來的一個個具體的對象,每一個對象都有相同的方法,可是各自的數據可能不一樣。仍以Student類爲例,在Python中,定義類是經過class關鍵字:後面緊接着是類名,即,類名一般是大寫開頭的單詞,緊接着是,表示該類是從哪一個類繼承下來的,繼承的概念咱們後面再講,一般,若是沒有合適的繼承類,就使用類,這是全部類最終都會繼承的類。
定義好了類,就能夠根據類建立出的實例,建立實例是經過類名+()實現的:
能夠看到,變量指向的就是一個Student的object,後面的是內存地址,每一個object的地址都不同,而自己則是一個類。能夠自由地給一個實例變量綁定屬性,好比,給實例綁定一個屬性:因爲類能夠起到模板的做用,所以,能夠在建立實例的時候,把一些咱們認爲必須綁定的屬性強制填寫進去。經過定義一個特殊的方法,在建立實例的時候,就把,等屬性綁上去:
注意到方法的第一個參數永遠是,表示建立的實例自己,所以,在方法內部,就能夠把各類屬性綁定到,由於就指向建立的實例自己。
有了方法,在建立實例的時候,就不能傳入空的參數了,必須傳入與方法匹配的參數,但不須要傳,Python解釋器本身會把實例變量傳進去:
普通的函數相比,在類中定義的函數只有一點不一樣,就是第一個參數永遠是實例變量,而且,調用時,不用傳遞該參數。除此以外,類的方法和普通函數沒有什麼區別,因此,仍然能夠用默認參數、可變參數和關鍵字參數。def calc(numbers): sum = 0 for n in numbers: sum = sum + n * n return sum可是調用時須要先組裝出一個list或者tuple數據封裝
面向對象編程的一個重要特色就是數據封裝。在上面的類中,每一個實例就擁有各自的和這些數據。咱們能夠經過函數來訪問這些數據,好比打印一個學生的成績:
可是,既然實例自己就擁有這些數據,要訪問這些數據,就沒有必要從外面的函數去訪問,能夠直接在類的內部定義訪問數據的函數,這樣,就把「數據」給封裝起來了。這些封裝數據的函數是和類自己是關聯起來的,咱們稱之爲類的方法:
要定義一個方法,除了第一個參數是外,其餘和普通函數同樣。要調用一個方法,只須要在實例變量上直接調用,除了不用傳遞,其餘參數正常傳入:
這樣一來,咱們從外部看類,就只須要知道,建立實例須要給出和,而如何打印,都是在類的內部定義的,這些數據和邏輯被「封裝」起來了,調用很容易,但卻不用知道內部實現的細節。
封裝的另外一個好處是能夠給類增長新的方法,好比:
一樣的,方法能夠直接在實例變量上調用,不須要知道內部實現細節:
小結
類是建立實例的模板,而實例則是一個一個具體的對象,各個實例擁有的數據都互相獨立,互不影響;
方法就是與實例綁定的函數,和普通函數不一樣,方法能夠直接訪問實例的數據;
經過在實例上調用方法,咱們就直接操做了對象內部的數據,但無需知道方法內部的實現細節。
和靜態語言不一樣,Python容許對實例變量綁定任何數據,也就是說,對於兩個實例變量,雖然它們都是同一個類的不一樣實例,但擁有的變量名稱均可能不一樣:
訪問限制
在Class內部,能夠有屬性和方法,而外部代碼能夠經過直接調用實例變量的方法來操做數據,這樣,就隱藏了內部的複雜邏輯。
可是,從前面Student類的定義來看,外部代碼仍是能夠自由地修改一個實例的、屬性:
若是要讓內部屬性不被外部訪問,能夠把屬性的名稱前加上兩個下劃線,在Python中,實例的變量名若是以開頭,就變成了一個私有變量(private),只有內部能夠訪問,外部不能訪問,因此,咱們把Student類改一改:
改完後,對於外部代碼來講,沒什麼變更,可是已經沒法從外部訪問和了:
這樣就確保了外部代碼不能隨意修改對象內部的狀態,這樣經過訪問限制的保護,代碼更加健壯。
可是若是外部代碼要獲取name和score怎麼辦?能夠給Student類增長和這樣的方法:
若是又要容許外部代碼修改score怎麼辦?能夠給Student類增長方法:
你也許會問,原先那種直接經過也能夠修改啊,爲何要定義一個方法大費周折?由於在方法中,能夠對參數作檢查,避免傳入無效的參數:
須要注意的是,在Python中,變量名相似的,也就是以雙下劃線開頭,而且以雙下劃線結尾的,是特殊變量,特殊變量是能夠直接訪問的,不是private變量,因此,不能用、這樣的變量名。
有些時候,你會看到以一個下劃線開頭的實例變量名,好比,這樣的實例變量外部是能夠訪問的,可是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,「雖然我能夠被訪問,可是,請把我視爲私有變量,不要隨意訪問」。
雙下劃線開頭的實例變量是否是必定不能從外部訪問呢?其實也不是。不能直接訪問是由於Python解釋器對外把變量改爲了,因此,仍然能夠經過來訪問變量:
可是強烈建議你不要這麼幹,由於不一樣版本的Python解釋器可能會把改爲不一樣的變量名。
總的來講就是,Python自己沒有任何機制阻止你幹壞事,一切全靠自覺。
繼承和多態
在OOP程序設計中,當咱們定義一個class的時候,能夠從某個現有的class繼承,新的class稱爲子類(Subclass),而被繼承的class稱爲基類、父類或超類(Base class、Super class)。
好比,咱們已經編寫了一個名爲的class,有一個方法能夠直接打印:
當咱們須要編寫Dog和Cat類時,就能夠直接從Animal類繼承:
對於Dog來講,Animal就是它的父類,對於Animal來講,Dog就是它的子類。Cat和Dog相似。
繼承有什麼好處?最大的好處是子類得到了父類的所有功能。因爲Animial實現了方法,所以,Dog和Cat做爲它的子類,什麼事也沒幹,就自動擁有了方法:
運行結果以下:
固然,也能夠對子類增長一些方法,好比Dog類:
繼承的第二個好處須要咱們對代碼作一點改進。你看到了,不管是Dog仍是Cat,它們的時候,顯示的都是,符合邏輯的作法是分別顯示和,所以,對Dog和Cat類改進以下:
再次運行,結果以下:
當子類和父類都存在相同的方法時,咱們說,子類的覆蓋了父類的,在代碼運行的時候,老是會調用子類的。這樣,咱們就得到了繼承的另外一個好處:多態。
要理解什麼是多態,咱們首先要對數據類型再做一點說明。當咱們定義一個class的時候,咱們實際上就定義了一種數據類型。咱們定義的數據類型和Python自帶的數據類型,好比str、list、dict沒什麼兩樣:
判斷一個變量是不是某個類型能夠用判斷:
看來a、b、c確實對應着list、Animal、Dog這3種類型。
可是等等,試試:
看來c不只僅是Dog,c仍是Animal!
不過仔細想一想,這是有道理的,由於Dog是從Animal繼承下來的,當咱們建立了一個Dog的實例時,咱們認爲的數據類型是Dog沒錯,但同時也是Animal也沒錯,Dog原本就是Animal的一種!
因此,在繼承關係中,若是一個實例的數據類型是某個子類,那它的數據類型也能夠被看作是父類。可是,反過來就不行:
Dog能夠當作Animal,但Animal不能夠當作Dog。
要理解多態的好處,咱們還須要再編寫一個函數,這個函數接受一個Animal類型的變量:
當咱們傳入Animal的實例時,就打印出:
當咱們傳入Dog的實例時,就打印出:
當咱們傳入Cat的實例時,就打印出:
看上去沒啥意思,可是仔細想一想,如今,若是咱們再定義一個Tortoise類型,也從Animal派生:
當咱們調用run_twice()時,傳入Tortoise的實例:
你會發現,新增一個Animal的子類,沒必要對run_twice()作任何修改,實際上,任何依賴Animal做爲參數的函數或者方法均可以不加修改地正常運行,緣由就在於多態。
多態的好處就是,當咱們須要傳入Dog、Cat、Tortoise……時,咱們只須要接收Animal類型就能夠了,由於Dog、Cat、Tortoise……都是Animal類型,而後,按照Animal類型進行操做便可。因爲Animal類型有方法,所以,傳入的任意類型,只要是Animal類或者子類,就會自動調用實際類型的方法,這就是多態的意思:
對於一個變量,咱們只須要知道它是Animal類型,無需確切地知道它的子類型,就能夠放心地調用方法,而具體調用的方法是做用在Animal、Dog、Cat仍是Tortoise對象上,由運行時該對象的確切類型決定,這就是多態真正的威力:調用方只管調用,無論細節,而當咱們新增一種Animal的子類時,只要確保方法編寫正確,不用管原來的代碼是如何調用的。這就是著名的「開閉」原則:
對擴展開放:容許新增Animal子類;
對修改封閉:不須要修改依賴Animal類型的等函數。
獲取對象信息
當咱們拿到一個對象的引用時,如何知道這個對象是什麼類型、有哪些方法呢?
使用type()
首先,咱們來判斷對象類型,使用函數:
基本類型均可以用判斷:
若是一個變量指向函數或者類,也能夠用判斷:
可是函數返回的是什麼類型呢?它返回type類型。若是咱們要在語句中判斷,就須要比較兩個變量的type類型是否相同:
可是這種寫法太麻煩,Python把每種type類型都定義好了常量,放在模塊裏,使用以前,須要先導入:
最後注意到有一種類型就叫,全部類型自己的類型就是,好比:
使用isinstance()
對於class的繼承關係來講,使用type()就很不方便。咱們要判斷class的類型,可使用函數。
咱們回顧上次的例子,若是繼承關係是:
那麼,就能夠告訴咱們,一個對象是不是某種類型。先建立3種類型的對象:
而後,判斷:
沒有問題,由於變量指向的就是Husky對象。
再判斷:
雖然自身是Husky類型,但因爲Husky是從Dog繼承下來的,因此,也仍是Dog類型。換句話說,判斷的是一個對象是不是該類型自己,或者位於該類型的父繼承鏈上。
所以,咱們能夠確信,仍是Animal類型:
同理,實際類型是Dog的也是Animal類型:
可是,不是Husky類型:
能用判斷的基本類型也能夠用判斷:
而且還能夠判斷一個變量是不是某些類型中的一種,好比下面的代碼就能夠判斷是不是str或者unicode:
因爲和都是從繼承下來的,因此,還能夠把上面的代碼簡化爲:
使用dir()
若是要得到一個對象的全部屬性和方法,可使用函數,它返回一個包含字符串的list,好比,得到一個str對象的全部屬性和方法:
相似的屬性和方法在Python中都是有特殊用途的,好比方法返回長度。在Python中,若是你調用函數試圖獲取一個對象的長度,實際上,在函數內部,它自動去調用該對象的方法,因此,下面的代碼是等價的:
咱們本身寫的類,若是也想用的話,就本身寫一個方法:
剩下的都是普通屬性或方法,好比返回小寫的字符串:
僅僅把屬性和方法列出來是不夠的,配合、以及,咱們能夠直接操做一個對象的狀態:
緊接着,能夠測試該對象的屬性:
若是試圖獲取不存在的屬性,會拋出AttributeError的錯誤:
能夠傳入一個default參數,若是屬性不存在,就返回默認值:
也能夠得到對象的方法:
小結
經過內置的一系列函數,咱們能夠對任意一個Python對象進行剖析,拿到其內部的數據。要注意的是,只有在不知道對象信息的時候,咱們纔會去獲取對象信息。若是能夠直接寫:
就不要寫:
一個正確的用法的例子以下:
假設咱們但願從文件流fp中讀取圖像,咱們首先要判斷該fp對象是否存在read方法,若是存在,則該對象是一個流,若是不存在,則沒法讀取。就派上了用場。
請注意,在Python這類動態語言中,有方法,不表明該fp對象就是一個文件流,它也多是網絡流,也多是內存中的一個字節流,但只要方法返回的是有效的圖像數據,就不影響讀取圖像的功能。
使用__slots__
正常狀況下,當咱們定義了一個class,建立了一個class的實例後,咱們能夠給該實例綁定任何屬性和方法,這就是動態語言的靈活性。先定義class:
而後,嘗試給實例綁定一個屬性:
還能夠嘗試給實例綁定一個方法:
可是,給一個實例綁定的方法,對另外一個實例是不起做用的:
爲了給全部實例都綁定方法,能夠給class綁定方法:
給class綁定方法後,全部實例都可調用:
一般狀況下,上面的方法能夠直接定義在class中,但動態綁定容許咱們在程序運行的過程當中動態給class加上功能,這在靜態語言中很難實現。
使用__slots__
可是,若是咱們想要限制class的屬性怎麼辦?好比,只容許對Student實例添加和屬性。
爲了達到限制的目的,Python容許在定義class的時候,定義一個特殊的變量,來限制該class能添加的屬性:
而後,咱們試試:
因爲沒有被放到中,因此不能綁定屬性,試圖綁定將獲得AttributeError的錯誤。
使用要注意,定義的屬性僅對當前類起做用,對繼承的子類是不起做用的:
除非在子類中也定義,這樣,子類容許定義的屬性就是自身的加上父類的。
使用@property
在綁定屬性時,若是咱們直接把屬性暴露出去,雖然寫起來很簡單,可是,沒辦法檢查參數,致使能夠把成績隨便改:
這顯然不合邏輯。爲了限制score的範圍,能夠經過一個方法來設置成績,再經過一個來獲取成績,這樣,在方法裏,就能夠檢查參數:
如今,對任意的Student實例進行操做,就不能爲所欲爲地設置score了:
可是,上面的調用方法又略顯複雜,沒有直接用屬性這麼直接簡單。
有沒有既能檢查參數,又能夠用相似屬性這樣簡單的方式來訪問類的變量呢?對於追求完美的Python程序員來講,這是必需要作到的!
還記得裝飾器(decorator)能夠給函數動態加上功能嗎?對於類的方法,裝飾器同樣起做用。Python內置的裝飾器就是負責把一個方法變成屬性調用的:
的實現比較複雜,咱們先考察如何使用。把一個getter方法變成屬性,只須要加上就能夠了,此時,自己又建立了另外一個裝飾器,負責把一個setter方法變成屬性賦值,因而,咱們就擁有一個可控的屬性操做:
注意到這個神奇的,咱們在對實例屬性操做的時候,就知道該屬性極可能不是直接暴露的,而是經過getter和setter方法來實現的。
還能夠定義只讀屬性,只定義getter方法,不定義setter方法就是一個只讀屬性:
上面的是可讀寫屬性,而就是一個只讀屬性,由於能夠根據和當前時間計算出來。
小結
普遍應用在類的定義中,可讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減小了出錯的可能性。
calc([1, 2, 3])
calc((1, 3, 5, 7))
在參數numbers前面加個*,就變爲可變參數,調用時就不須組裝list和tuple'ABCDEFG'[1:5:2] //結果爲:'BD',可對字符串進行切片calc([1, 2, 3])calc((1, 3, 5, 7))
def calc(*numbers):
calc(1,2,3)
calc()
此時若已有list或者tuple,能夠這樣調取nums=[1,2,3]calc(*nums)關鍵字參數def person(name, age, **kw): print 'name:', name, 'age:', age, 'other:', kw
person('Michael', 30) //結果爲:name: Michael age: 30 other: {}
kw = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **kw) //結果爲:name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
要注意定義可變參數和關鍵字參數的語法:
*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})
。使用
*args
和**kw
是Python的習慣寫法,固然也能夠用其餘參數名,但最好使用習慣用法。遞歸函數def fact(n): if n==1: return 1 return n * fact(n - 1)
使用遞歸函數須要注意防止棧溢出。在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出
解決遞歸調用棧溢出的方法是經過尾遞歸優化,事實上尾遞歸和循環的效果是同樣的,因此,把循環當作是一種特殊的尾遞歸函數也是能夠的。
尾遞歸是指,在函數返回的時候,調用自身自己,而且,return語句不能包含表達式。這樣,編譯器或者解釋器就能夠把尾遞歸作優化,使遞歸自己不管調用多少次,都只佔用一個棧幀,不會出現棧溢出的狀況。
切片L=[1,2,3,4,5,6,7,8]L[0:3] //結果爲:[1,2,3],從索引0開始取,直到取到索引3,但不包括索引3
L[:3] //結果爲:[1,2,3],索引0可省略
L[-8:] //結果爲:[1,2,3,4,5,6,7,8],從倒數第八個開始取,冒號後省略就取到最後一個L[::2] //結果爲:[1,3,5,7],第三個參數是每隔幾個取一個def calc(*numbers):
calc(1,2,3)
calc()
此時若已有list或者tuple,能夠這樣調取nums=[1,2,3]calc(*nums)關鍵字參數def person(name, age, **kw): print 'name:', name, 'age:', age, 'other:', kw
person('Michael', 30) //結果爲:name: Michael age: 30 other: {}
kw = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **kw) //結果爲:name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
要注意定義可變參數和關鍵字參數的語法:
*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})
。使用
*args
和**kw
是Python的習慣寫法,固然也能夠用其餘參數名,但最好使用習慣用法。遞歸函數def fact(n): if n==1: return 1 return n * fact(n - 1)
使用遞歸函數須要注意防止棧溢出。在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出
解決遞歸調用棧溢出的方法是經過尾遞歸優化,事實上尾遞歸和循環的效果是同樣的,因此,把循環當作是一種特殊的尾遞歸函數也是能夠的。
尾遞歸是指,在函數返回的時候,調用自身自己,而且,return語句不能包含表達式。這樣,編譯器或者解釋器就能夠把尾遞歸作優化,使遞歸自己不管調用多少次,都只佔用一個棧幀,不會出現棧溢出的狀況。
切片L=[1,2,3,4,5,6,7,8]L[0:3] //結果爲:[1,2,3],從索引0開始取,直到取到索引3,但不包括索引3
L[:3] //結果爲:[1,2,3],索引0可省略
L[-8:] //結果爲:[1,2,3,4,5,6,7,8],從倒數第八個開始取,冒號後省略就取到最後一個L[::2] //結果爲:[1,3,5,7],第三個參數是每隔幾個取一個def calc(*numbers):calc(1,2,3)calc()def person(name, age, **kw): print 'name:', name, 'age:', age, 'other:', kwperson('Michael', 30) //結果爲:name: Michael age: 30 other: {}kw = {'city': 'Beijing', 'job': 'Engineer'}person('Jack', 24, **kw) //結果爲:name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}*args**kwfunc(1, 2, 3)*argsfunc(*(1, 2, 3))func(a=1, b=2)**kwfunc(**{'a': 1, 'b': 2})*args**kwdef fact(n): if n==1: return 1 return n * fact(n - 1)迭代若是給定一個list或tuple,咱們能夠經過
for
循環來遍歷這個list或tuple,這種遍歷咱們稱爲迭代(Iteration)。默認狀況下,dict迭代的是key:
字符串也可迭代d = {'a': 1, 'b': 2, 'c': 3} for key in d: print key a c b
若是要迭代value能夠用for value in d.itervalues()
,若是要同時迭代key和value,能夠用for k, v in d.iteritems()
。那麼,如何判斷一個對象是可迭代對象呢?方法是經過collections模塊的Iterable類型判斷:
>>> from collections import Iterable >>> isinstance('abc', Iterable) # str是否可迭代 True >>> isinstance([1,2,3], Iterable) # list是否可迭代 True
最後一個小問題,若是要對list實現相似Java那樣的下標循環怎麼辦?Python內置的
enumerate
函數能夠把一個list變成索引-元素對,這樣就能夠在for
循環中同時迭代索引和元素自己:>>> for i, value in enumerate(['A', 'B', 'C']): ... print i, value ... 0 A 1 B 2 C
列表生成式
range(1,11)即爲list[1,2,3,4,5,6,7,8,9,10]
>>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
for循環後面還能夠加上if判斷,這樣咱們就能夠篩選出僅偶數的平方:
>>> [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']
生成器>>> d = {'x': 'A', 'y': 'B', 'z': 'C' } >>> [k + '=' + v for k, v in d.iteritems()] ['y=B', 'x=A', 'z=C']
經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。
因此,若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器(Generator)。
要建立一個generator,有不少種方法。第一種方法很簡單,只要把一個列表生成式的
[]
改爲()
,就建立了一個generator:>>> 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 0x104feab40>
建立
L
和g
的區別僅在於最外層的[]
和()
,L
是一個list,而g
是一個generator。咱們能夠直接打印出list的每個元素,但咱們怎麼打印出generator的每個元素呢?
若是要一個一個打印出來,能夠經過generator的
next()
方法:>>> g.next() 0 >>> g.next() 1 >>> g.next() 4 >>> g.next() 9 >>> g.next() 16 >>> g.next() 25 >>> g.next() 36 >>> g.next() 49 >>> g.next() 64 >>> g.next() 81
咱們講過,generator保存的是算法,每次調用
next()
,就計算出下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤。固然,上面這種不斷調用
next()
方法實在是太變態了,正確的方法是使用for
循環,由於generator也是可迭代對象:>>> g = (x * x for x in range(10)) >>> for n in g: ... print n ... 0 1 4 9 16 25 36 49 64 81
斐波拉契數列用列表生成式寫不出來,可是,用函數把它打印出來卻很容易:
def fib(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1
>>> fib(6) 1 1 2 3 5 8
變量可指向變量仔細觀察,能夠看出,
fib
函數其實是定義了斐波拉契數列的推算規則,能夠從第一個元素開始,推算出後續任意的元素,這種邏輯其實很是相似generator。也就是說,上面的函數和generator僅一步之遙。要把
fib
函數變成generator,只須要把print b
改成yield b
就能夠了:def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1
這就是定義generator的另外一種方法。若是一個函數定義中包含
yield
關鍵字,那麼這個函數就再也不是一個普通函數,而是一個generator:>>> fib(6) <generator object fib at 0x104feaaa0>
這裏,最難理解的就是generator和函數的執行流程不同。函數是順序執行,遇到return語句或者最後一行函數語句就返回。而變成generator的函數,在每次調用
next()
的時候執行,遇到yield
語句返回,再次執行時從上次返回的yield
語句處繼續執行。舉個簡單的例子,定義一個generator,依次返回數字1,3,5:
>>> def odd(): ... print 'step 1' ... yield 1 ... print 'step 2' ... yield 3 ... print 'step 3' ... yield 5 ... >>> o = odd() >>> o.next() step 1 1 >>> o.next() step 2 3 >>> o.next() step 3 5
f=absf(-10)結果爲10既然變量能夠指向函數,函數的參數能接收變量,那麼一個函數就能夠接收另外一個函數做爲參數,這種函數就稱之爲高階函數。
map/reduce
如
map()
函數接收兩個參數,一個是函數,一個是序列,map
將傳入的函數依次做用到序列的每一個元素,並把結果做爲新的list返回。def f(x):
return x*x
map(f,range(10))
map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])
再看reduce的用法。reduce把一個函數做用在一個序列[x1, x2, x3...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素作累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
首字母大寫 'LING'.capitalize()相似的還有大小寫upper(),lower()ford = {'a': 1, 'b': 2, 'c': 3} for key in d: print key a c bfor value in d.itervalues()for k, v in d.iteritems()>>> from collections import Iterable >>> isinstance('abc', Iterable) # str是否可迭代 True >>> isinstance([1,2,3], Iterable) # list是否可迭代 Trueenumeratefor>>> for i, value in enumerate(['A', 'B', 'C']): ... print i, value ... 0 A 1 B 2 C比方說對一個序列求和,就能夠用reduce實現:
>>> def add(x, y): ... return x + y ... >>> reduce(add, [1, 3, 5, 7, 9]) 25
列表生成式
range(1,11)即爲list[1,2,3,4,5,6,7,8,9,10]
>>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
for循環後面還能夠加上if判斷,這樣咱們就能夠篩選出僅偶數的平方:
>>> [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']
生成器>>> d = {'x': 'A', 'y': 'B', 'z': 'C' } >>> [k + '=' + v for k, v in d.iteritems()] ['y=B', 'x=A', 'z=C']
經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。
因此,若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器(Generator)。
要建立一個generator,有不少種方法。第一種方法很簡單,只要把一個列表生成式的
[]
改爲()
,就建立了一個generator:>>> 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 0x104feab40>
建立
L
和g
的區別僅在於最外層的[]
和()
,L
是一個list,而g
是一個generator。咱們能夠直接打印出list的每個元素,但咱們怎麼打印出generator的每個元素呢?
若是要一個一個打印出來,能夠經過generator的
next()
方法:>>> g.next() 0 >>> g.next() 1 >>> g.next() 4 >>> g.next() 9 >>> g.next() 16 >>> g.next() 25 >>> g.next() 36 >>> g.next() 49 >>> g.next() 64 >>> g.next() 81
咱們講過,generator保存的是算法,每次調用
next()
,就計算出下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤。固然,上面這種不斷調用
next()
方法實在是太變態了,正確的方法是使用for
循環,由於generator也是可迭代對象:>>> g = (x * x for x in range(10)) >>> for n in g: ... print n ... 0 1 4 9 16 25 36 49 64 81
斐波拉契數列用列表生成式寫不出來,可是,用函數把它打印出來卻很容易:
def fib(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1
>>> fib(6) 1 1 2 3 5 8
變量可指向變量仔細觀察,能夠看出,
fib
函數其實是定義了斐波拉契數列的推算規則,能夠從第一個元素開始,推算出後續任意的元素,這種邏輯其實很是相似generator。也就是說,上面的函數和generator僅一步之遙。要把
fib
函數變成generator,只須要把print b
改成yield b
就能夠了:def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1
這就是定義generator的另外一種方法。若是一個函數定義中包含
yield
關鍵字,那麼這個函數就再也不是一個普通函數,而是一個generator:>>> fib(6) <generator object fib at 0x104feaaa0>
這裏,最難理解的就是generator和函數的執行流程不同。函數是順序執行,遇到return語句或者最後一行函數語句就返回。而變成generator的函數,在每次調用
next()
的時候執行,遇到yield
語句返回,再次執行時從上次返回的yield
語句處繼續執行。舉個簡單的例子,定義一個generator,依次返回數字1,3,5:
>>> def odd(): ... print 'step 1' ... yield 1 ... print 'step 2' ... yield 3 ... print 'step 3' ... yield 5 ... >>> o = odd() >>> o.next() step 1 1 >>> o.next() step 2 3 >>> o.next() step 3 5
f=absf(-10)結果爲10既然變量能夠指向函數,函數的參數能接收變量,那麼一個函數就能夠接收另外一個函數做爲參數,這種函數就稱之爲高階函數。
>>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]for循環後面還能夠加上if判斷,這樣咱們就能夠篩選出僅偶數的平方:
>>> [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']>>> d = {'x': 'A', 'y': 'B', 'z': 'C' } >>> [k + '=' + v for k, v in d.iteritems()] ['y=B', 'x=A', 'z=C'][]()>>> 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 0x104feab40>Lg[]()Lgnext()>>> g.next() 0 >>> g.next() 1 >>> g.next() 4 >>> g.next() 9 >>> g.next() 16 >>> g.next() 25 >>> g.next() 36 >>> g.next() 49 >>> g.next() 64 >>> g.next() 81next()next()for>>> g = (x * x for x in range(10)) >>> for n in g: ... print n ... 0 1 4 9 16 25 36 49 64 81def fib(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1>>> fib(6) 1 1 2 3 5 8fibfibprint byield bdef fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1yield>>> fib(6) <generator object fib at 0x104feaaa0>next()yieldyield>>> def odd(): ... print 'step 1' ... yield 1 ... print 'step 2' ... yield 3 ... print 'step 3' ... yield 5 ... >>> o = odd() >>> o.next() step 1 1 >>> o.next() step 2 3 >>> o.next() step 3 5map()mapmap(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)>>> def add(x, y): ... return x + y ... >>> reduce(add, [1, 3, 5, 7, 9]) 25Python內建的
filter()
函數用於過濾序列。和
排序算法filter()map()filter()map()filter()TrueFalsemap()
相似,filter()
也接收一個函數和一個序列。和map()
不一樣的時,filter()
把傳入的函數依次做用於每一個元素,而後根據返回值是True
仍是False
決定保留仍是丟棄該元素。一般規定,對於兩個元素
xyx < y-1x == y0x > y1x
和y
,若是認爲x < y
,則返回-1
,若是認爲x == y
,則返回0
,若是認爲x > y
,則返回1
>>> sorted([36, 5, 12, 9, 21]) [5, 9, 12, 21, 36]
>>> sorted([36, 5, 12, 9, 21]) [5, 9, 12, 21, 36]sorted()reversed_cmpdef reversed_cmp(x, y): if x > y: return -1 if x < y: return 1 return 0此外,
sorted()
函數也是一個高階函數,它還能夠接收一個比較函數來實現自定義的排序。好比,若是要倒序排序,咱們就能夠自定義一個reversed_cmp
函數:def reversed_cmp(x, y): if x > y: return -1 if x < y: return 1 return 0
傳入自定義的比較函數
reversed_cmp
,就能夠實現倒序排序:>>> sorted([36, 5, 12, 9, 21], reversed_cmp) [36, 21, 12, 9, 5]
返回函數
匿名函數def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum
當咱們調用lazy_sum()
時,返回的並非求和結果,而是求和函數:>>> f = lazy_sum(1, 3, 5, 7, 9) >>> f <function sum at 0x10452f668>
調用函數f
時,才真正計算求和的結果:>>> f() 25
在這個例子中,咱們在函數lazy_sum
中又定義了函數sum
,而且,內部函數sum
能夠引用外部函數lazy_sum
的參數和局部變量,當lazy_sum
返回函數sum
時,相關參數和變量都保存在返回的函數中,這種稱爲「閉包(Closure)」的程序結構擁有極大的威力。請再注意一點,當咱們調用
lazy_sum()
時,每次調用都會返回一個新的函數,即便傳入相同的參數:>>> f1 = lazy_sum(1, 3, 5, 7, 9) >>> f2 = lazy_sum(1, 3, 5, 7, 9) >>> f1==f2 False
map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
關鍵字
lambda
表示匿名函數,冒號前面的x
表示函數參數。匿名函數有個限制,就是隻能有一個表達式,不用寫
return
,返回值就是該表達式的結果。用匿名函數有個好處,由於函數沒有名字,沒必要擔憂函數名衝突。
裝飾器
def now():print '2015-3-28'
f=nowf()f.__name__ //結果爲:'now',函數對象有個__name__屬性,能夠拿到函數的名字在面向對象(OOP)的設計模式中,decorator被稱爲裝飾模式。OOP的裝飾模式須要經過繼承和組合來實現,而Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator能夠用函數實現,也能夠用類實現。
decorator能夠加強函數的功能,定義起來雖然有點複雜,但使用起來很是靈活和方便。
如今,假設咱們要加強
now()
函數的功能,好比,在函數調用先後自動打印日誌,但又不但願修改now()
函數的定義,這種在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator)。本質上,decorator就是一個返回函數的高階函數。因此,咱們要定義一個能打印日誌的decorator,能夠定義以下:
def log(func): def wrapper(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return wrapper
觀察上面的
log
,由於它是一個decorator,因此接受一個函數做爲參數,並返回一個函數。咱們要藉助Python的@語法,把decorator置於函數的定義處:@log def now(): print '2013-12-25'
調用
now()
函數,不只會運行now()
函數自己,還會在運行now()
函數前打印一行日誌:>>> now() call now(): 2013-12-25
把@log
放到now()
函數的定義處,至關於執行了語句:now = log(now)
因爲
log()
是一個decorator,返回一個函數,因此,原來的now()
函數仍然存在,只是如今同名的now變量指向了新的函數,因而調用now()
將執行新函數,即在log()
函數中返回的wrapper()
函數。
wrapper()
函數的參數定義是(*args, **kw)
,所以,wrapper()
函數能夠接受任意參數的調用。在wrapper()
函數內,首先打印日誌,再緊接着調用原始函數。若是decorator自己須要傳入參數,那就須要編寫一個返回decorator的高階函數,寫出來會更復雜。
reversed_cmp>>> sorted([36, 5, 12, 9, 21], reversed_cmp) [36, 21, 12, 9, 5]返回函數傳入自定義的比較函數
reversed_cmp
,就能夠實現倒序排序:>>> sorted([36, 5, 12, 9, 21], reversed_cmp) [36, 21, 12, 9, 5]
返回函數
匿名函數def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum
當咱們調用lazy_sum()
時,返回的並非求和結果,而是求和函數:>>> f = lazy_sum(1, 3, 5, 7, 9) >>> f <function sum at 0x10452f668>
調用函數f
時,才真正計算求和的結果:>>> f() 25
在這個例子中,咱們在函數lazy_sum
中又定義了函數sum
,而且,內部函數sum
能夠引用外部函數lazy_sum
的參數和局部變量,當lazy_sum
返回函數sum
時,相關參數和變量都保存在返回的函數中,這種稱爲「閉包(Closure)」的程序結構擁有極大的威力。請再注意一點,當咱們調用
lazy_sum()
時,每次調用都會返回一個新的函數,即便傳入相同的參數:>>> f1 = lazy_sum(1, 3, 5, 7, 9) >>> f2 = lazy_sum(1, 3, 5, 7, 9) >>> f1==f2 False
map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
關鍵字
lambda
表示匿名函數,冒號前面的x
表示函數參數。匿名函數有個限制,就是隻能有一個表達式,不用寫
return
,返回值就是該表達式的結果。用匿名函數有個好處,由於函數沒有名字,沒必要擔憂函數名衝突。
裝飾器
def now():print '2015-3-28'
f=nowf()f.__name__ //結果爲:'now',函數對象有個__name__屬性,能夠拿到函數的名字在面向對象(OOP)的設計模式中,decorator被稱爲裝飾模式。OOP的裝飾模式須要經過繼承和組合來實現,而Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator能夠用函數實現,也能夠用類實現。
decorator能夠加強函數的功能,定義起來雖然有點複雜,但使用起來很是靈活和方便。
如今,假設咱們要加強
now()
函數的功能,好比,在函數調用先後自動打印日誌,但又不但願修改now()
函數的定義,這種在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator)。本質上,decorator就是一個返回函數的高階函數。因此,咱們要定義一個能打印日誌的decorator,能夠定義以下:
def log(func): def wrapper(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return wrapper
觀察上面的
log
,由於它是一個decorator,因此接受一個函數做爲參數,並返回一個函數。咱們要藉助Python的@語法,把decorator置於函數的定義處:@log def now(): print '2013-12-25'
調用
now()
函數,不只會運行now()
函數自己,還會在運行now()
函數前打印一行日誌:>>> now() call now(): 2013-12-25
把@log
放到now()
函數的定義處,至關於執行了語句:now = log(now)
因爲
log()
是一個decorator,返回一個函數,因此,原來的now()
函數仍然存在,只是如今同名的now變量指向了新的函數,因而調用now()
將執行新函數,即在log()
函數中返回的wrapper()
函數。
wrapper()
函數的參數定義是(*args, **kw)
,所以,wrapper()
函數能夠接受任意參數的調用。在wrapper()
函數內,首先打印日誌,再緊接着調用原始函數。若是decorator自己須要傳入參數,那就須要編寫一個返回decorator的高階函數,寫出來會更復雜。
匿名函數def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sumlazy_sum()>>> f = lazy_sum(1, 3, 5, 7, 9) >>> f <function sum at 0x10452f668>f>>> f() 25lazy_sumsumsumlazy_sumlazy_sumsumlazy_sum()>>> f1 = lazy_sum(1, 3, 5, 7, 9) >>> f2 = lazy_sum(1, 3, 5, 7, 9) >>> f1==f2 Falsedef lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum
當咱們調用lazy_sum()
時,返回的並非求和結果,而是求和函數:>>> f = lazy_sum(1, 3, 5, 7, 9) >>> f <function sum at 0x10452f668>
調用函數f
時,才真正計算求和的結果:>>> f() 25
在這個例子中,咱們在函數lazy_sum
中又定義了函數sum
,而且,內部函數sum
能夠引用外部函數lazy_sum
的參數和局部變量,當lazy_sum
返回函數sum
時,相關參數和變量都保存在返回的函數中,這種稱爲「閉包(Closure)」的程序結構擁有極大的威力。請再注意一點,當咱們調用
lazy_sum()
時,每次調用都會返回一個新的函數,即便傳入相同的參數:>>> f1 = lazy_sum(1, 3, 5, 7, 9) >>> f2 = lazy_sum(1, 3, 5, 7, 9) >>> f1==f2 False
map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
關鍵字
lambda
表示匿名函數,冒號前面的x
表示函數參數。匿名函數有個限制,就是隻能有一個表達式,不用寫
return
,返回值就是該表達式的結果。用匿名函數有個好處,由於函數沒有名字,沒必要擔憂函數名衝突。
裝飾器
def now():map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])lambdaxreturnprint '2015-3-28'now()now()def log(func): def wrapper(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return wrapperlog@log def now(): print '2013-12-25'now()now()now()>>> now() call now(): 2013-12-25@lognow()now = log(now)log()now()now()log()wrapper()wrapper()(*args, **kw)wrapper()wrapper()一個完整的decorator的寫法以下:
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return wrapper
import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print '%s %s():' % (text, func.__name__) return func(*args, **kw) return wrapper return decorator
import functools
是導入functools
模塊。模塊的概念稍候講解。如今,只需記住在定義wrapper()
的前面加上@functools.wraps(func)
便可。偏函數
functools.partial
就是幫助咱們建立一個偏函數的,不須要咱們本身定義int2()
,能夠直接使用下面的代碼建立一個新的函數int2
:因此,簡單總結>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85
functools.partial
的做用就是,把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單。
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return wrapperimport functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print '%s %s():' % (text, func.__name__) return func(*args, **kw) return wrapper return decoratorimport functoolsfunctoolswrapper()@functools.wraps(func)functools.partialint2()int2>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85functools.partial模塊#!/usr/bin/env python # -*- coding: utf-8 -*- ' a test module ' __author__ = 'Michael Liao' import sys def test(): args = sys.argv if len(args)==1: print 'Hello, world!' elif len(args)==2: print 'Hello, %s!' % args[1] else: print 'Too many arguments!' if __name__=='__main__': test()
第1行和第2行是標準註釋,第1行註釋可讓這個
hello.py
文件直接在Unix/Linux/Mac上運行,第2行註釋表示.py文件自己使用標準UTF-8編碼;第4行是一個字符串,表示模塊的文檔註釋,任何模塊代碼的第一個字符串都被視爲模塊的文檔註釋;
第6行使用
__author__
變量把做者寫進去,這樣當你公開源代碼後別人就能夠瞻仰你的大名;以上就是Python模塊的標準文件模板,固然也能夠所有刪掉不寫,可是,按標準辦事確定沒錯。
後面開始就是真正的代碼部分。
你可能注意到了,使用
sys
模塊的第一步,就是導入該模塊:import sys
導入
sys
模塊後,咱們就有了變量sys
指向該模塊,利用sys
這個變量,就能夠訪問sys
模塊的全部功能。
sys
模塊有一個argv
變量,用list存儲了命令行的全部參數。argv
至少有一個元素,由於第一個參數永遠是該.py文件的名稱,例如:運行
python hello.py
得到的sys.argv
就是['hello.py']
;運行
python hello.py Michael
得到的sys.argv
就是['hello.py', 'Michael]
。最後,注意到這兩行代碼:
if __name__=='__main__': test()
當咱們在命令行運行
hello
模塊文件時,Python解釋器把一個特殊變量__name__
置爲__main__
,而若是在其餘地方導入該hello
模塊時,if
判斷將失敗,所以,這種if
測試可讓一個模塊經過命令行運行時執行一些額外的代碼,最多見的就是運行測試。咱們能夠用命令行運行
hello.py
看看效果:$ python hello.py Hello, world! $ python hello.py Michael Hello, Michael!
若是啓動Python交互環境,再導入
hello
模塊:$ python Python 2.7.5 (default, Aug 25 2013, 00:04:04) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import hello >>>
導入時,沒有打印
Hello, word!
,由於沒有執行test()
函數。調用
hello.test()
時,才能打印出Hello, word!
:>>> hello.test() Hello, world!
別名
導入模塊時,還可使用別名,這樣,能夠在運行時根據當前環境選擇最合適的模塊。好比Python標準庫通常會提供
StringIO
和cStringIO
兩個庫,這兩個庫的接口和功能是同樣的,可是cStringIO
是C寫的,速度更快,因此,你會常常看到這樣的寫法:try: import cStringIO as StringIO except ImportError: # 導入失敗會捕獲到ImportError import StringIO
這樣就能夠優先導入
cStringIO
。若是有些平臺不提供cStringIO
,還能夠降級使用StringIO
。導入cStringIO
時,用import ... as ...
指定了別名StringIO
,所以,後續代碼引用StringIO
便可正常工做。還有相似
simplejson
這樣的庫,在Python 2.6以前是獨立的第三方庫,從2.6開始內置,因此,會有這樣的寫法:try: import json # python >= 2.6 except ImportError: import simplejson as json # python <= 2.5
因爲Python是動態語言,函數簽名一致接口就同樣,所以,不管導入哪一個模塊後續代碼都能正常工做。
若是採用面向對象的程序設計思想,咱們首選思考的不是程序的執行流程,而是
Student
這種數據類型應該被視爲一個對象,這個對象擁有name
和score
這兩個屬性(Property)。若是要打印一個學生的成績,首先必須建立出這個學生對應的對象,而後,給對象發一個print_score
消息,讓對象本身把本身的數據打印出來。class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print '%s: %s' % (self.name, self.score)
給對象發消息實際上就是調用對象對應的關聯函數,咱們稱之爲對象的方法(Method)。面向對象的程序寫出來就像這樣:
bart = Student('Bart Simpson', 59) lisa = Student('Lisa Simpson', 87) bart.print_score() lisa.print_score()
面向對象的設計思想是從天然界中來的,由於在天然界中,類(Class)和實例(Instance)的概念是很天然的。Class是一種抽象概念,好比咱們定義的Class——Student,是指學生這個概念,而實例(Instance)則是一個個具體的Student,好比,Bart Simpson和Lisa Simpson是兩個具體的Student:
因此,面向對象的設計思想是抽象出Class,根據Class建立Instance。
面向對象的抽象程度又比函數要高,由於一個Class既包含數據,又包含操做數據的方法。
數據封裝、繼承和多態是面向對象的三大特色。
類和實例#!/usr/bin/env python # -*- coding: utf-8 -*- ' a test module ' __author__ = 'Michael Liao' import sys def test(): args = sys.argv if len(args)==1: print 'Hello, world!' elif len(args)==2: print 'Hello, %s!' % args[1] else: print 'Too many arguments!' if __name__=='__main__': test()hello.py__author__sysimport syssyssyssyssyssysargvargvpython hello.pysys.argv['hello.py']python hello.py Michaelsys.argv['hello.py', 'Michael]if __name__=='__main__': test()hello__name____main__helloififhello.py$ python hello.py Hello, world! $ python hello.py Michael Hello, Michael!hello$ python Python 2.7.5 (default, Aug 25 2013, 00:04:04) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import hello >>>Hello, word!test()hello.test()Hello, word!>>> hello.test() Hello, world!StringIOcStringIOcStringIOtry: import cStringIO as StringIO except ImportError: # 導入失敗會捕獲到ImportError import StringIOcStringIOcStringIOStringIOcStringIOimport ... as ...StringIOStringIOsimplejsontry: import json # python >= 2.6 except ImportError: import simplejson as json # python <= 2.5Studentnamescoreprint_scoreclass Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print '%s: %s' % (self.name, self.score)bart = Student('Bart Simpson', 59) lisa = Student('Lisa Simpson', 87) bart.print_score() lisa.print_score()class Student(object): passclassStudent(object)objectStudentStudentStudent>>> bart = Student() >>> bart <__main__.Student object at 0x10a67a590> >>> Student <class '__main__.Student'>bart0x10a67a590Studentbartname>>> bart.name = 'Bart Simpson' >>> bart.name 'Bart Simpson'__init__namescoreclass Student(object): def __init__(self, name, score): self.name = name self.score = score__init__self__init__selfself__init____init__self>>> bart = Student('Bart Simpson', 59) >>> bart.name 'Bart Simpson' >>> bart.score 59selfStudentnamescore>>> def print_score(std): ... print '%s: %s' % (std.name, std.score) ... >>> print_score(bart) Bart Simpson: 59StudentStudentStudentclass Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print '%s: %s' % (self.name, self.score)selfself>>> bart.print_score() Bart Simpson: 59StudentnamescoreStudentStudentget_gradeclass Student(object): ... def get_grade(self): if self.score >= 90: return 'A' elif self.score >= 60: return 'B' else: return 'C'get_grade>>> bart.get_grade() 'C'>>> bart = Student('Bart Simpson', 59) >>> lisa = Student('Lisa Simpson', 87) >>> bart.age = 8 >>> bart.age 8 >>> lisa.age Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'age'namescore>>> bart = Student('Bart Simpson', 98) >>> bart.score 98 >>> bart.score = 59 >>> bart.score 59____class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print '%s: %s' % (self.__name, self.__score)實例變量.__name實例變量.__score>>> bart = Student('Bart Simpson', 98) >>> bart.__name Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute '__name'get_nameget_scoreclass Student(object): ... def get_name(self): return self.__name def get_score(self): return self.__scoreset_scoreclass Student(object): ... def set_score(self, score): self.__score = scorebart.score = 59class Student(object): ... def set_score(self, score): if 0 <= score <= 100: self.__score = score else: raise ValueError('bad score')__xxx____name____score___name__name__name_Student__name_Student__name__name>>> bart._Student__name 'Bart Simpson'__nameAnimalrun()class Animal(object): def run(self): print 'Animal is running...'class Dog(Animal): pass class Cat(Animal): passrun()run()dog = Dog() dog.run() cat = Cat() cat.run()Animal is running... Animal is running...class Dog(Animal): def run(self): print 'Dog is running...' def eat(self): print 'Eating meat...'run()Animal is running...Dog is running...Cat is running...class Dog(Animal): def run(self): print 'Dog is running...' class Cat(Animal): def run(self): print 'Cat is running...'Dog is running... Cat is running...run()run()run()run()a = list() # a是list類型 b = Animal() # b是Animal類型 c = Dog() # c是Dog類型isinstance()>>> isinstance(a, list) True >>> isinstance(b, Animal) True >>> isinstance(c, Dog) True>>> isinstance(c, Animal) Trueccc>>> b = Animal() >>> isinstance(b, Dog) Falsedef run_twice(animal): animal.run() animal.run()run_twice()>>> run_twice(Animal()) Animal is running... Animal is running...run_twice()>>> run_twice(Dog()) Dog is running... Dog is running...run_twice()>>> run_twice(Cat()) Cat is running... Cat is running...class Tortoise(Animal): def run(self): print 'Tortoise is running slowly...'>>> run_twice(Tortoise()) Tortoise is running slowly... Tortoise is running slowly...run()run()run()run()run()run_twice()type()type()>>> type(123) <type 'int'> >>> type('str') <type 'str'> >>> type(None) <type 'NoneType'>type()>>> type(abs) <type 'builtin_function_or_method'> >>> type(a) <class '__main__.Animal'>type()if>>> type(123)==type(456) True >>> type('abc')==type('123') True >>> type('abc')==type(123) Falsetypes>>> import types >>> type('abc')==types.StringType True >>> type(u'abc')==types.UnicodeType True >>> type([])==types.ListType True >>> type(str)==types.TypeType TrueTypeTypeTypeType>>> type(int)==type(str)==types.TypeType Trueisinstance()object -> Animal -> Dog -> Huskyisinstance()>>> a = Animal() >>> d = Dog() >>> h = Husky()>>> isinstance(h, Husky) Trueh>>> isinstance(h, Dog) Truehhisinstance()h>>> isinstance(h, Animal) Trued>>> isinstance(d, Dog) and isinstance(d, Animal) Trued>>> isinstance(d, Husky) Falsetype()isinstance()>>> isinstance('a', str) True >>> isinstance(u'a', unicode) True >>> isinstance('a', unicode) False>>> isinstance('a', (str, unicode)) True >>> isinstance(u'a', (str, unicode)) Truestrunicodebasestring>>> isinstance(u'a', basestring) Truedir()>>> dir('ABC') ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']__xxx____len__len()len()__len__()>>> len('ABC') 3 >>> 'ABC'.__len__() 3len(myObj)__len__()>>> class MyObject(object): ... def __len__(self): ... return 100 ... >>> obj = MyObject() >>> len(obj) 100lower()>>> 'ABC'.lower() 'abc'getattr()setattr()hasattr()>>> class MyObject(object): ... def __init__(self): ... self.x = 9 ... def power(self): ... return self.x * self.x ... >>> obj = MyObject()>>> hasattr(obj, 'x') # 有屬性'x'嗎? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有屬性'y'嗎? False >>> setattr(obj, 'y', 19) # 設置一個屬性'y' >>> hasattr(obj, 'y') # 有屬性'y'嗎? True >>> getattr(obj, 'y') # 獲取屬性'y' 19 >>> obj.y # 獲取屬性'y' 19>>> getattr(obj, 'z') # 獲取屬性'z' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'MyObject' object has no attribute 'z'>>> getattr(obj, 'z', 404) # 獲取屬性'z',若是不存在,返回默認值404 404>>> hasattr(obj, 'power') # 有屬性'power'嗎? True >>> getattr(obj, 'power') # 獲取屬性'power' <bound method MyObject.power of <__main__.MyObject object at 0x108ca35d0>> >>> fn = getattr(obj, 'power') # 獲取屬性'power'並賦值到變量fn >>> fn # fn指向obj.power <bound method MyObject.power of <__main__.MyObject object at 0x108ca35d0>> >>> fn() # 調用fn()與調用obj.power()是同樣的 81sum = obj.x + obj.ysum = getattr(obj, 'x') + getattr(obj, 'y')def readImage(fp): if hasattr(fp, 'read'): return readData(fp) return Nonehasattr()read()read()>>> class Student(object): ... pass ...>>> s = Student() >>> s.name = 'Michael' # 動態給實例綁定一個屬性 >>> print s.name Michael>>> def set_age(self, age): # 定義一個函數做爲實例方法 ... self.age = age ... >>> from types import MethodType >>> s.set_age = MethodType(set_age, s, Student) # 給實例綁定一個方法 >>> s.set_age(25) # 調用實例方法 >>> s.age # 測試結果 25>>> s2 = Student() # 建立新的實例 >>> s2.set_age(25) # 嘗試調用方法 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'set_age'>>> def set_score(self, score): ... self.score = score ... >>> Student.set_score = MethodType(set_score, None, Student)>>> s.set_score(100) >>> s.score 100 >>> s2.set_score(99) >>> s2.score 99set_scorenameage__slots__>>> class Student(object): ... __slots__ = ('name', 'age') # 用tuple定義容許綁定的屬性名稱 ...>>> s = Student() # 建立新的實例 >>> s.name = 'Michael' # 綁定屬性'name' >>> s.age = 25 # 綁定屬性'age' >>> s.score = 99 # 綁定屬性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score''score'__slots__scorescore__slots____slots__>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999__slots____slots____slots__s = Student() s.score = 9999set_score()get_score()set_score()class Student(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!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value>>> s = Student() >>> s.set_score(60) # ok! >>> s.get_score() 60 >>> s.set_score(9999) Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!@propertyclass Student(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!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value@property@property@property@score.setter>>> s = Student() >>> s.score = 60 # OK,實際轉化爲s.set_score(60) >>> s.score # OK,實際轉化爲s.get_score() 60 >>> s.score = 9999 Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!@propertyclass Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2014 - self._birthbirthageagebirth@property