函數的意義
函數是組織好的,可重複使用的,用來實現單一,或相關聯功能的代碼段。
函數能提高應用的模塊性,和代碼的重複利用率。你已經知道Python提供了許多內置函數,比如print()。但你也可以自己創建函數,這被叫做用戶自定義函數。
函數的定義
函數代碼塊以 def 關鍵詞開頭,後接函數標識符名稱和圓括號()。
任何傳入參數和自變量必須放在圓括號中間。圓括號之間可以用於定義參數。
函數的第一行語句可以選擇性地使用文檔字符串—用於存放函數說明。
函數內容以冒號起始,並且縮進。
return [表達式] 結束函數,選擇性地返回一個值給調用方。不帶表達式的return相當於返回 None。
如
def fun(形參): "內容" return True fun(實參) |
函數調用
定義一個函數只給了函數一個名稱,指定了函數裏包含的參數,和代碼塊結構。
這個函數的基本結構完成以後,你可以通過另一個函數調用執行,也可以直接從Python提示符執行。
最簡單的例子
def fun(): print "OK" fun() |
函數參數
定義函數時,申明的是形式參數簡稱形參,調用函數時,傳遞的參數是實際參數簡稱實參。而形參又可以分爲四種
必備參數
關鍵字參數
默認參數
不定長參數
必備參數
必備參數須以正確的順序傳入函數。調用時的數量必須和聲明時的一樣
def f1(a): print "OK" f1() def f2(a,b) print(a+b) f2(3+4) |
返回結果:OK和7
注:調用必備參數時,必須實參和形參位置對應,否則會報錯。
關鍵字參數
關鍵字參數和函數調用關係緊密,函數調用使用關鍵字參數來確定傳入的參數值。
使用關鍵字參數允許函數調用時參數的順序與聲明時不一致,因爲 Python 解釋器能夠用參數名匹配參數值。
def info(name,age): print "Name %s"%name print"Age %s"%age info("flash",18) info("aa",19) |
輸出結果
Name flash Age 18 Name aa Age 19 |
默認參數
調用函數時,缺省參數的值如果沒有傳入,則被認爲是默認值。
def info(name,age,sex="male"): print"Name %s"%name print "Age %s"%age print "Sex %s"%sex info("flash",18) info("aa",19,sex="female") |
輸出結果對比
Name flash Age 18 Sex male Name aa Age 19 Sex female |
不定長參數
你可能需要一個函數能處理比當初聲明時更多的參數。這些參數叫做不定長參數(又稱動態參數),和上述2種參數不同,聲明時不會命名。一種是無名名字參數,另一種是有命名參數。
無命名參數(輸出的是元組) def add(*args): sum = 0 for i in args: sum += i print(sum) add(1,2,3,4,5) 有命名參數(輸出的是字典) def fun(**kwargs): for i in kwargs: print "%s:%s"%(i,kwargs[i]) fun(name="flash",age=22) |
Lambda函數
lambda函數也叫匿名函數,即,函數沒有具體的名稱。
f=lambda 賦的參數:參數要做什麼
然後調用:f(2)
def f(x):
return x**2
print f(4)
把函數改寫成:g=lambda x:x**2
print g(4)
若LAMBDA要跟多個參數,要做什麼的參數也要對應
a=range(10)
b=range(10)
g=lambda x,y:x*y,a,b
sorted排序:可以給字典排序
a={'b':4,'a':2,'c':3}
sorted(a.items(),key=lambda x:x[1]) ###items()把字典轉換成tuple
map函數:
map(func, seq1[, seq2,…])
第一個參數接受一個函數名,後面的參數接受一個或多個可迭代的序列,返回的是一個集合。
Python函數編程中的map()函數是將func作用於seq中的每一個元素,並將所有的調用的結果作爲一個list返回。如果func爲None,作用同zip()。
a=range(10)
map(lambda x:x**2,a)#求a的平方
print map(lambda x: x % 2, range(7))
#使用列表解析>>>print [x % 2for x in range(7)][0,1,0,1,0,1,0]
***將元組轉換成list***>>>map(int, (1,2,3))[1,2,3]***將字符串轉換成list***>>>map(int,'1234')[1,2,3,4]***提取字典的key,並將結果存放在一個list中***>>>map(int, {1:2,2:3,3:4})[1,2,3]***字符串轉換成元組,並將結果以列表的形式返回***>>>map(tuple,'agdf')[('a',), ('g',), ('d',), ('f',)]#將小寫轉成大寫def u_to_l (s):return s.upper()print map(u_to_l,'asdfd')
函數返回值
return語句[表達式]退出函數,選擇性地向調用方返回一個表達式。不帶參數值的return語句返回None。
函數在執行過程中只要遇到return語句,就會可以停止執行並返回結果,so 也可以理解爲 return 語句代表着函數的結束,return 後面的都不會執行的。
如果未在函數中指定return,那這個函數的返回值爲None
return多個對象,解釋器會把這多個對象組裝成一個元組作爲一個一個整體結果輸出
>>> def sayHi2(**kargs):
... if kargs.has_key('name'):
... print kargs['name']
... print kargs
return kargs['name']
...
>>> result=sayHi2(name='aa',age=29,phone=222222)
aa
{'phone': 222222, 'age': 29, 'name': 'aa'}
>>> print result
None
若在def 中加return kargs['name'],print result就不會是None
函數作用域
一個程序的所有的變量並不是在哪個位置都可以訪問的。訪問權限決定於這個變量是在哪裏賦值的。變量的作用域決定了在哪一部分程序你可以訪問哪個特定的變量名稱。
大致可以分爲四種情況
L:local,局部作用域,即函數中定義的變量;
E:enclosing,嵌套的父級函數的局部作用域,即包含此函數的上級函數的局部作用域,但不是全局的;
G:globa,全局變量,就是模塊級別定義的變量;
B:built-in,系統固定模塊裏面的變量,比如int, bytearray等。 搜索變量的優先級順序依次是:作用域局部>外層作用域>當前模塊中的全局>python
total = 0; # 這是一個全局變量 # 可寫函數說明 def sum( arg1, arg2 ): #返回2個參數的和." total = arg1 + arg2; # total在這裏是局部變量. print "函數內是局部變量 : ", total return total; #調用sum函數 sum( 10, 20 ); print "函數外是全局變量 : ", total |
函數遞歸
特性
調用自身函數
有一個結束條件
遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。
使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出
舉個簡單例子,求5的階乘
def f(i): if i == 1: return 1 return i * f(i-1) print f(5) |
小結
使用遞歸函數的優點是邏輯簡單清晰,缺點是過深的調用會導致棧溢出。
針對尾遞歸優化的語言可以通過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的編程語言只能通過尾遞歸實現循環。
Python標準的解釋器沒有針對尾遞歸做優化,任何遞歸函數都存在棧溢出的問題。
高階函數
高階函數英文叫Higher-order function。
變量可以指向函數,函數的參數能接收變量,那麼一個函數就可以接收另一個函數作爲參數,這種函數就稱之爲高階函數。
函數名可以進行賦值
函數名可以作爲函數參數,還可以作爲函數的返回值
舉個例子如
def f(n): return n *n def f1(a,b,f): set = f(a) + f(b) return set print f1(1,2,f)<br>返回值爲 5 |
閉包函數
什麼是閉包函數,看一個簡單的例子
def foo(): x = 5 def func(): print(x) return func foo()()<br>#返回值是5 |
我們會想,x = 5是屬於foo函數的局部變量,爲什麼可以調用?這就是閉包的閉包的特性。
定義:如果在一個內部函數裏,對在外部作用域(但不是在全局作用域)的變量進行引用,那麼內部函數就被認爲是閉包(closure)。
所以對上述例子中,func()就是閉包函數。
生成器
通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅佔用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。
所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出後續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator。
要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:
L = [x * x for x in range(10)]#list g = (x * x for x in range(10))#生成器 |
生成器保存的是算法,如果調用元素中的只,最好的辦法就是使用for循環,因爲生成器也是一個可迭代的對象。
g = (x * x for x in range(10)) for n in g: print(n) |
如果算法過去複雜龐大,我們可以使用函數調用來實現。比如斐波那契數列
def fib(max): n, a, b = 0, 0, 1 while n < max-1: #print(b)<br> yield b a, b = b, a + b n = n + 1 return fib(6)<br>#這就是定義generator的另一種方法。如果一個函數定義中包含<code>yield</code>關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator: |
兩種方法
next(f()) ----計算出一個值注意:生成器在創建的時候已經決定出計算出值的個數,超出next的次數就會報StopIteration
send("參數") 傳參給yield的變量。注意:第一次不能傳參數f().send(None) ==next(f())
迭代器
我們已經知道,可以直接作用於for循環的數據類型有以下幾種:
一類是集合數據類型,如list、tuple、dict、set、str等。
一類是generator,包括生成器和帶yield的generator function。
def sayHi2(**kargs):
for i in range(10):
time.sleep(1)
yield 'Loop',i
result=sayHi2(name='ma',age=19,phone=222222)
print result.next()
print result.next()
print result.next()
......
滿足迭代器協議:
內部有next方法
內部有iter()方法
這些可以直接作用於for循環的對象統稱爲可迭代對象:Iterable。
如何去判斷一個對象是不是一個可迭代的對象,可以使用內置函數進行判斷
>>>from collections import Iterable >>> isinstance([], Iterable) True #如果是返回True,否返回False |
注:
凡是可作用於for循環的對象都是Iterable類型;
凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
這裏我們也許會問,都已經是可迭代的了,爲什麼還不是Iterator?這是因爲Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它纔會計算。
Iterator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。
裝飾器
什麼是裝飾器?
裝飾器本質上是一個函數,該函數用來處理其他函數,它可以讓其他函數在不需要修改代碼的前提下增加額外的功能,裝飾器的返回值也是一個函數對象。本質就是將原函數封裝到另外一個函數裏面,讓其等於一個新函數,在執行新函數的內容。
首先舉個簡單的例子,在我們事先寫好的許多函數中,如果我們想要添加一個新的功能,不可能往每個函數寫一遍,這是一個不明智的做法。這裏我們就會用到裝飾器。例如
import time def run(func): def wrapper(): start=time.clock() func() end=time.clock() print 'This function costs',end - start return wrapper sayHi=run(sayHi) sayHi() ###這2個結合就是等於@run ###### import time def run(func):###接收一個參數,需要被包裝的函數 def wrapper():##包裝的過程 start=time.clock() func() end=time.clock() print 'This function costs',end - start return wrapper###返回一個包裝的函數體 @run def sayHi(): print 'hellow word' time.sleep(0.1) def tell(): print "OK" sayHi() tell() ###### |
這是一個最簡單的裝飾器。其中@符號是python中表示裝飾器的語法。
當然裝飾器還可以指定參數去更方便的執行例如
import time def logger(flag = "True"): def show_time(f): def func(): start = time.time() f() end = time.time() if flag =="True": print("打印日誌") print(end - start) return func return show_time @logger("true") def foo1(): print("OK!!!!!!!!!!!!!") time.sleep(1) foo1() def foo2(): print("OK!!!!!!!!!!!!!") time.sleep(2) foo2() |
也可以傳入不定長參數做簡單的計算
import time def show_time(f): def func(*args,**kwargs): start = time.time() f(*args,**kwargs) end = time.time() print(end - start) return func @show_time def foo1(*args,**kwargs): Sum = 0 for i in args: Sum += i print(Sum) time.sleep(1) foo1(1,2,3) |
內置函數:
abs(n)
divmod(a,b)##趣商的餘數 divmod(8,2)-->(4,0)
pow(a,b)冪
round(a)#4舍5入
sum(list)#LIST的值相加
小結:
在面向對象(OOP)的設計模式中,decorator被稱爲裝飾模式。OOP的裝飾模式需要通過繼承和組合來實現,而Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator可以用函數實現,也可以用類實現。
使用面向對象編程,使程序結構化,基礎學習下,類的簡單實例過程:
#!/usr/bin/env python # _*_ coding:utf8 _*_ class Person: name="aaaa"#類變量 def __init__(self,name,age): print " 我是構造函數__init__" print "類初始化方法" self.Name=name self.Age=age def __talk(self): print"我是私有屬性哦......" def name(self): print"Hello,master,my name is %s"% self.Name def __del__(self): print "我是析構函數哦....." if __name__=='__main__': p=Person('AAA',12)#類實例化後,會執行構造函數 p.name() p._Person__talk()#調用私有方法 #!/usr/bin/env python # _*_ coding:utf8 _*_ class Person: bb='number'#類變量可以直接被調用self.bb def sayHi(self): print "i am sayHi" method_var='number' self.method=method_var#把method_var定義成類變量 def getVar(self,aa): if aa==self.bb:#使用類變量 print "use sayhi" else: print "bye" p=Person() p.sayHi() p.getVar('number')