假設有以下函數:程序員
def add(x,y): return x+y
好比說有個包含由兩個相加的數字組成的元組:shell
params = (1,2)
使用*
運算符對參數進行「分配」,不過是在調用而不是在定義時使用:編程
>>> add(*params) 3
======閉包
一樣,可使用 雙星號 運算符來處理字典。app
假設以前定義了hello_3
,那麼能夠這樣使用:函數式編程
>>> params = {'name':Sir Robin','greeting':'Well met'} >>> hello_3(**params) Well met.Sir Robin
星號只在 定義函數(容許使用不定數目的參數)或者 調用(「分割」字典或者序列)時纔有用。函數
在執行x=1
賦值語句後,名稱x
引用到值1
。這就像是使用字典同樣,鍵引用值。固然,變量和所對應的值用的是個「不可見」的字典。ui
內建的vars
函數能夠返回這個字典:spa
>>> x = 1 >>> scope = vars() >>> scope['x'] 1 >>> scope['x'] += 1 >>> x 2
這類「不可見字典」叫作 命名空間 或者 做用域 。除了全局做用域外,每一個函數調用都會建立一個新的做用域:翻譯
>>> def foo(): x = 42 ... >>> x = 1 >>> foo() >>> x 1
這裏的foo函數改變(重綁定)了變量x,可是在最後的時候,x並無變。這是由於當調用foo
的時候,新的命名空間就被建立了,它做用於foo
內的代碼塊。賦值語句x=42
只在內部做用域(局部命名空間)起做用,因此它並不影響外部(全局)做用域中的x
。
函數內的變量被稱爲局部變量(local variable),這是與全局變量相反的概念。參數的工做原理相似於局部變量,因此用全局變量的名字做爲參數名並無問題。
>>> def output(x): print x ... >>> x = 1 >>> y = 2 >>> output(y) 2
======
重綁定全局變量:
若是在函數內部將值賦予一個變量,它將會自動成爲局部變量——除非告知Python將其聲明爲全局變量:
>>> x = 1 >>> def change_global(): global x x = x + 1 >>> change_global() >>> x 2
======
Python的函數是能夠嵌套的:
def foo(): def bar(): print "Hello,World!" bar()
函數嵌套有一個很突出的應用,例如須要一個函數「建立」另外一個。也就意味着能夠像下面這樣(在其餘函數內)書寫函數:
def multiplier(factor): def multiplier(number): return number*factor returnmultiplyByFactor
一個函數位於另一個裏面,外層函數返回裏層函數。也就是說函數自己被返回了,但並無被調用。重要的是返回的函數還能夠訪問它的定義所在的做用域。換句話說,它「帶着」它的環境(和相關的局部變量)。
每次調用外層函數,它內部的函數都被從新綁定。factor
變量每次都有一個新的值。因爲Python的嵌套做用域,來自(`multiplier
的)外部做用域的這個變量,稍後會被內層函數訪問:
>>> double = multiplier(2) >>> double(5) 10 >>> triple = multiplier(3) >>> triple(3) 9 >>> multiplier(5)(4) 20
相似multiplayByFactor
函數存儲子封閉做用域的行爲叫作閉包(closure)。
遞歸的定義(包括遞歸函數定義)包括它們自身定義內容的引用。
關於遞歸,一個相似的函數定義以下:
def recursion(): return recursion()
理論上講,上述程序應該永遠地運行下去,然而每次調用函數都會用掉一點內存,在足夠的函數調用發生後(在以前的調用返回後),空間就不夠了,程序會以一個「超過最大遞歸深度」的錯誤信息結束。
這類遞歸就作無窮遞歸(infinite recursion),相似於以while True
開始的無窮循環,中間沒有break
或者return
語句。由於(理論上講)它永遠不會結束。
有用的遞歸函數包含如下幾個部分:
當函數直接返回值時有基本實例(最小可能性問題)
遞歸實例,包括一個或者多個問題較小部分的遞歸調用。
這裏的關鍵就是將問題分解成小部分,遞歸不可能永遠繼續下去,由於它老是以最小可能性問題結束,而這些問題又存儲在基本實例中的。
當每次函數被調用時,針對這個調用的新命名空間會被建立,意味着當函數調用「自身」時,實際上運行的是兩個不一樣的函數(或者說是同一個函數具備兩個不一樣的命名空間)。實際上,能夠將它想象成和同種類的一個生物進行對話的另外一個生物對話。
計算數n
的的階乘:
def factorial(n): result = n for i in range(1,n): result *= 1 return result
遞歸實現:
1的階乘是1;
大於1的數n的階乘是n
乘n-1
的階乘。
def factorial(n): if n == 1: return 1 else: return n * factorial(n-1)
======
計算冪
例子:power(x,n)
(x
爲n
的冪次)是x
自乘n-1
次的結果(因此x
用做乘數n
次。
def power(x,n): result = 1 for i in range(n): result *= x return result
遞歸實現:
對於任意數字x
來講,`power(x,0)是1;
對於任何大於0的書來講,power(x,n)
是x
乘以(x,n-1)
的結果。
def power(x,n): if n == 0: return 1 else: return x * power(x,n-1)
遞歸實現:
若是上下限相同,那麼就是數字所在位置,返回;
不然找到二者的中點(上下限的平均值),查找數字是在左側仍是在右側,繼續查找數字所在的那半部分。
def search(sequence,number,lower,upper): if lower == upper: assert number == sequence[upper] return upper else: #整數除法//,浮點數除法/ middle = (lower + upper) // 2 if number > sequence[middle]: return search(sequence,number,middle+1,upper) else: return search(sequence,number,lower,middle)
提示:標準庫中的bisect
模塊能夠很是有效地實現二分查找。
Python在應對「函數式編程」方面有一些有用的函數:map
、filter
和reduce
函數(Python3.0中都被移至fuctools
模塊中)。
map
和filter
在目前版本的Python並不是特別有用,而且可使用列表推導式代替。不過可使用map
函數將序列中的元素所有傳遞給一個函數:
>>> map(str,range(10)) #Equivalent to [str(i) for i in range(10)] ['0','1','2','3','4','5','6','7','8','9']
filter
函數能夠基於一個返回布爾值的函數對元素進行過濾。
#island 判斷字符變量是否爲字母或數字, #如果則返回非零,不然返回零 >>> def fun(x): return x.isalnum() >>> seq = ["foo","x41","?!","***"] >>> filter(func,seq) ['foo','x41']
本例中,使用列表推導式能夠不用專門定義一個函數:
>>> [x for x in seq if x.isalnum()] ['foo','x41']
事實上,還有個叫作lambda
表達式的特性,能夠建立短小的函數。
>>> filter(lambda x: x.isalnum().seq) ['foo','x41']
=======
reduce
函數通常來講不能輕鬆被列表推導式替代,可是一般用不到這個功能。它會將序列的前兩個元素與給定的函數聯合使用,而且將它們的返回值和第3個元素繼續聯合使用,直到整個序列都處理完畢,而且獲得一個最終結果。
可使用reduce
函數加上lambda x,y:x+y
(繼續使用相同的數字):
>>> numbers = [72,101,108,108,111,44,32,119,111,114,108,100,33] >>> reduce(lambda x,y:x+y,numbers) 1161
固然,這裏也可使用內建函數sum
。
抽象。抽象是隱藏多餘細節的藝術。定義處理細節的函數可讓程序更抽象。
函數定義。函數使用def
語句定義。它們是由語句組成的塊,能夠從「外部世界」獲取值(參數),也能夠返回一個或者多個值做爲運算的結果。
參數。函數從參數中獲得須要的信息,也就是函數調用時設定的變量。Python中有兩類參數:位置參數 和 關鍵數參數。參數在給定默認值時是可選的。
做用域。變量存儲在做用域(也叫做命名空間)中。Python有兩類主要的做用域——全局做用域 和 局部做用域。做用域能夠嵌套。
遞歸。 函數能夠調用自身即遞歸。一切用遞歸實現的功能都能用循環實現,可是有些時候遞歸函數更易讀。
函數式編程。Python有一些進行函數式編程的機制。包括lambda
表達式以及map
、filter
和reduce
函數。
| 函數 | 描述 |
| ------------- |:-------------|
| map(func,seq[,seq,...])
| 對序列中的每一個元素應用函數 |
| filter(fuc,seq)
| 返回其函數爲真的元素的列表 |
| reduce(func,seq[,initial])
| 等同於func(func(func(seq[0],seq[1],se1[2]...
|
| sum(seq)
| 返回seq
全部元素的和 |
| apply(func,args[,kwargs]]
| 調用函數,能夠提供參數 |
在面對對象程序設計中,術語對象(object)基本上能夠看作數據(特性)以及由一系列能夠存取、操做這些數據的方法所組成的集合。使用對象替代全局變量和函數的緣由可能有不少,其中對象最重要的優勢包括如下幾方面:
多態(Polymorphism):意味着能夠對不一樣類的對象使用一樣的操做,它們會像「被施了魔法通常」工做。
封裝(Encapsulation):對外部世界隱藏對象的工做細節。
繼承(Inheritance):以通用的類爲基礎創建專門的類對象。
術語多態的意思是「有多種形式」。多態意味着就算不知道變量所引用的對象類型是什麼,仍是能它進行操做,而它也會根據對象(或類)類型的不一樣而表現出不一樣的行爲。
repr
函數是多態特性的表明之一,能夠對任何東西使用:
def length_message(x): print "The length of",repr(x),"is",len(x)
>>> length_message('Fnord') The length of 'Fnord' is 5 >>> length_message([1,2,3]) The length of [1,2,3] is 3
不少函數和運算符都是多態的——你寫的絕大多數程序可能都是,只要使用多態函數和運算符,就會與「多態」發生關聯。事實上,惟一能毀掉多態的就是使用函數顯式地檢查類型,好比type
、isinstance
以及issubclass
函數等等。若是可能的話,應該盡力避免使用這些毀掉多態的方式。真正重要的是如何讓對象按照你所但願的方式工做,無論它是否是真正的類型(或者類)。
封裝是指向程序中的其餘部分隱藏對象的具體實現細節的原則。
可是封裝並不等同於多態,多態可讓用戶對於不知道什麼是類(對象類型)的對象進行方法調用,而封裝是能夠不用關心對象是如何構建的而直接進行使用。
基本上,須要將對象進行抽象,調用方法的時候不用關心其餘的東西,好比它是否干擾了全局變量。
能夠將其做爲 特性(attribute) 存儲。特性是做爲變量構成對象的一部分,事實上方法更像是綁定到函數上的屬性。
對象有着本身的狀態(state)。對象的狀態由它的特性(好比名稱)來描述。對象的方法能夠改變它的特性。因此就像是將一大堆函數(方法)捆在一塊兒,而且給予他們訪問變量(特性)的權力。它們能夠在函數調用之間保持保存的值。
略
類是一種對象,全部的對象都屬於某一個類,稱爲類的實例(instance)。
當一個對象所屬的類是另一個對象所屬類的子集時,前者就被稱爲後者的 子類(subclass),因此「百靈鳥類」是「鳥類」的子類。相反,「鳥類」是「百靈鳥類」的「超類」(superclass)。可是,在面向程序設計中,子類的關係是隱式的,由於一個類的定義取決於它所支持的方法。類的全部實例都會包含這些方法,因此全部子類的全部實例都有這些方法。定義子類只是個定義更多(也有多是重載已經存在的)方法的過程。
事實上,self
參數正是方法和參數的區別。方法(更專業一點能夠稱爲綁定方法)將它們的第一個參數綁定到所屬的實例上,所以無需顯式提供該參數。固然也能夠將特性綁定到一個普通函數上,這樣就不會有特殊的self
參數了:
>>> class Class: def method(self): print 'I hava a self' >>> def function(): print "I don't..." >>> instance = Class() >>> instance.method() I hava a self! >>> instance.method =function >>> instance.method() I don't...
注意,self
參數並不依賴於調用方法的方式,前面使用的是instance.method
(實例.方法
)的形式,能夠隨意使用其餘變量引用同一個方法:
>>> class Bird: song = 'Squaawk!' def sing(self): print self.song >>> bird = Bird() >>> bird.sing() Squaawk! >>> birdsong = bird.sing >>> birdsong() Squaawk!
儘管最後一個方法調用看起來與函數調用十分類似,可是變量birdsongs
引用綁定方法bird.sing
上,也就意味着這仍是會對self
參數進行訪問(也就是說,它仍舊綁定到類的相同實例上)。
默認狀況下,程序能夠從外部訪問一個對象的特性:
>>> c.name 'Sir Lancelot' >>> c.name = 'Sir Gumby' >>> c.getName() 'Sir Gumby'
爲了不這類事情的發生,應該使用私有(private)特性,這是外部對象沒法訪問到,但getName
和setName
等訪問器(accessor)可以訪問的特性。
Python並不直接支持私有防暑,爲了讓方法或者特性變爲私有(從外部沒法訪問),只要在它的名字前面加上雙下劃線便可。
class Secretive: def __inacessible(self): print "Bet you can't see me.." def accessible(self): print "The secret message is:" self.__inaccessible
如今,__inaccessible
從外界是沒法訪問的,而在類內部還能使用(好比從accessible
)訪問:
>>> s = Secretive() >>> s.__inaccessible() Traceback (most recent call last): File "<pyshell#112>",;ine 1, in ? s.__inaccessible() AttributeError: Secretive instance has no attribute '__inaccessible' >>> s.accessible() The secret message is: Bet you can't see me...
儘管雙下劃線有些奇怪,可是看起來像是其餘魚魚中的標準的私有方法。而在類的內部定義中,全部以雙下劃線開始的名字都被「翻譯」成前面加上單下劃線類名的形式。
>>> Secretive._Secret__inaccsible <unboud method Secretive.__inaccessible>
但實際上仍是可以在類外訪問這些私有方法,儘管不該該這麼作:
>>> s._Secretive.__inaccessible Bet you can't see me..
簡而言之,確保他人不會訪問對象的方法和特性是不可能的,可是這類「名稱變化」是提醒他們不該該訪問這些函數或者特性的強有力信號。
若是不須要使用這種方法可是又想讓其餘對象不要訪問內部數據,那麼可使用單下劃線,這不過是個習慣,但的確有實際效果。例如,前面有下劃線的名字都不會被帶星號的import
語句(from module import *
)導入。
下面的兩個語句幾乎等價:
def foo(x):return x*x foo = lambda X:x*x
二者都建立了返回參數平方的函數,並且都將變量foo
綁定到函數上。變量foo
能夠在全局(模塊)範圍內進行定義,也可處在局部的函數或方法內。定義類時,太陽的事情也會發生,全部位於class
語句中的代碼塊都在特殊的命名空間中執行——類命名空間(class namespace)。這個命名空間可由類內全部成員訪問。但並非全部Python程序員都知道類的定義其實就是執行代碼塊。
子類能夠拓展超類的定義。將其餘類名寫在class
語句後的圓括號內能夠指定超類。
若是想要查看一個類是不是另外一個的子類,可使用內建的issubclass
函數。
若是想要知道已知類的基類(們),能夠直接使用它的特殊特性__base__
:
一樣,還能使用isinstance
方法檢查一個對象是不是一個類的實例: