python Function(函數)
- 2020 年 1 月 13 日
- 筆記
函數是python為了程式碼最大程度地重用和最小化程式碼冗餘而提供的基本程式結構。函數是一種設計工具,它能讓程式設計師將複雜的系統分解為可管理的部件; 函數用於將相關功能打包並參數化。 在python中可以創建如下4種函數: 1)、全局函數:定義在模組中(直接定義在模組中的函數)。 2)、局部函數:嵌套於其它函數中(在函數中再定義的函數)。 3)、lambda函數:表達式。匿名函數(它僅是一個表達式),它可以出現在任何位置,很高的錄活性。 4)、方法:與特定數據類型關聯的函數,並且只能與數據類型相關一起使用。定義在類中的函數。 python也提供了很多內置函數 函數與過程的區別: 函數都有return返回值。返回一個對象 創建函數 def functionName(parameters): suite 相關概念: def 是一個可執行語句;因此可以出現在任何能夠使用的地方,甚至可以嵌套於其它語句,例if或while中。def創建了一個對象 並將其賦值給一個變數名(即函數名); return用於返回結果對象,其為可選項;無return語句的函數自動返回一個None對象;返回多個值時,彼此間使用逗號分隔,且組合為元組形式返回一個對象。 def語句運行之後,可以在程式中通過函數名後附加括弧進行調用 。 例1:
def printName(): print "hello" printName() def testFun(): pass testFun()
例2:(注意函數的返回值)
def login(username): if username == "Thompson": print "登錄成功" else: print "登錄失敗" if __name__ == "__main__" : uname=raw_input("Please enter your name:") login(uname) 例3:(注意函數的返回值) def login(username): if username == "Thompson": return "登錄成功" else: return "登錄失敗" def detail(username): print username,"detail information" if __name__ == "__main__" : uname=raw_input("Please enter your name:") result = login(uname) if result == "登錄成功" : detail(uname) else: print result
例4:函數可以定義多個形參,可以為形參設置默認值,但設置默認值的形參必須放置在參數列表的最後一個
def func1(username,action="聽課"): print username,":",action func1("tom") func1("eric","吃飯") 例5: def func1(username,where="北京",action="聽課"): print username,":去",where,action func1("tom") func1("jack","上海") func1("eric",action="吃飯")
函數的作用域:(變數查找的名稱空間) 變數名在程式中賦值的位置決定了其能夠被訪問到的範圍。函數定義了本地作用域,即函數內定義的變數,只能生效於本函數內部。模組定義了全局作用域。即在本python腳本中定義的變數,生效於本腳本的任意位置。 變數名引用分三個作用域:首先本地、然後函數內、接著是全局,最後是內置。 說明:在函數1中嵌套的函數2,在函數2中定義的變數稱為「本地」;在函數1中定義的變數稱為函數外層的; Python創建、改變或查找變數名都是在名稱空間中進行;在程式碼中變數名被賦值的位置決定了其能被訪問到的範圍。
函數定義了本地作用域,而模組定義了全局作用域;每個模組都是一個全局作用域,因此全局作用域的範圍僅限於單個程式文件;每次對函數的調用都會創建一個新的本地作用域,賦值的變數除非聲明為全局變數,否則均為本地變數。 所有的變數名都可以歸納為本地、全局或內置(由__builtin__模組提供)
def f1(): y = 3 print y f1() print y 拋出異常 #!/usr/bin/python27 x = 32 def f1(): x = 43 print x f1() print x chmod +x test1.py ./test1.py x = 32 def f1(): y = 43 print x,y f1() print x,y global x 將x定義為全局變數(在本地範圍內定義全局變數時使用) x = 43 print x
LEGB 原則: local → enclosing → global → builtin 例:
x = 5 z = "global" def f1(): x = "from f1" y = 3 print x,z def f2(): x = "from f2" print x,y,z f2() f1() 輸出結果為: from f1 global from f2 3 global def f1(): x =3 def f2(): y = "hello" print x,y return f2 a1 = f1() type(a1) a1()
函數的參數: def funcName(arg1,arg2….): 例:
def f1(x): print x f1(4) f1('abcd') def f2(x,y): print x+y f2(3,4) f2("hello","world") m=3;n=4 def f3(x,y): x -= 1 print x,y f3(m,n) print m,n 注意:比較結果值。m、n為數值,是不可變類型,所以m、n的數據不變。 l1 = [1,2,3] def f4(x) x.pop() print x f4(l1) 將l1對象傳遞給函數f5 print l1 比較兩次的結果相同,因為列表為可變類型 l1 = [1,2,3] def f5(x): x.pop() print x f5(l1[:]) 將l1做切片之後的所有值傳遞給函數f5
函數參數的匹配模型: 默認情況下,參數通過其位置進行傳遞,從左至右,這意味著必須精確地傳遞和函數頭部參數一樣多的參數。但也可以通過關鍵字參數、默認參數或參數容器等 改變這種機制。 位置:從左至右 關鍵字參數:使用"name=value"的語法通過參數名進行匹配(如果關鍵字參數與位置參數混合時,位置參數必須放在參數列表的左邊,所有位置參數寫完後,才可以放置關鍵字參數) 默認參數:定義函數時使用"name=value"的語法直接給變數一個值,從而傳入的值可以少於參數個數。(混用有默認和無默認值的參數時,無默認值的參數需放前面) 可變參數:定義函數時使用*開頭的參數,可用於收集任意多基於位置的參數;定義函數時使用**,收集關鍵字參數。 可變參數解包:調用函數時,使用*開頭的參數,可用於將參數集合打散,從而傳遞任意多基於位置或關鍵字的參數。 例:
def f10(*x): print x f10(m) f10(m,n) f10(m,n,z) def f11(**x): print x f11(x=1,y=2,z=9) #收集關鍵字參數,並以字典的形式返回。 def f12(x,*y): #可變參數必須寫在後面,不可變參數必須寫在左側(前面) print x,y f12(m,n,o) #函數調用時,將m的值傳遞給x,將n與o的值傳遞給y,y會以元組的形式返回結果 def f15(*x,**y): print x print y f15(m,n,o,i=3,j=6) #位置參數被"*x"收集,而關鍵字參數被"**y"收集 l1=['sun','mon','tus'] x,y,z = l1 print x,y,z #將列表分解賦值 def f17(x,y,z): print x,y,z f17(*l1) #將l1的值分解,並分別傳遞給變數x、y、z。要求被分解對象的元素個數 要與 函數定義的形參個數相同。 #調用函數時使用*是為了分解參數對象,定義時使用*是為了整合。 def f18(x,*y): print x print y f18(m,*l1) #將m的值傳遞給x,將 l1對象中的元素分解後,傳遞給y d1={'key1':'v1','key2':'v2','key3':77} def f19(x,*y,**z): print x print y print z f19(m,*l3,**d1) #將m的值傳遞給x,將l3對象中的元素分解後以元組的形式傳遞給y,將d1字典對象中的鍵值對兒分解,並以字典的形式傳遞給z #順序必須是:先位置參數、再任意位置參數、最後任意關鍵字參數 f19(m,n,o,**d1) #將m傳遞給x,將n與o傳遞給y,將字典d1對象分解後傳遞給z f19(m,n,z,key1='v1',key2='v2') #將m傳遞給x,將n與o傳遞給y,將關鍵參數key1='v1',key2='v2'傳遞給z
python閉包:lexical closure 在函數嵌套環境中,如果外層函數直接返回內層函數(即把內層函數做為一個對象當做返回值),而且內層函數調用了外層函數的變數,那麼內層函數會自動記憶外層函數的變數值。 它稱為函數的閉合,也稱為工廠函數。 例: def f1(x): def f2(y): return y ** x return f2 函數有嵌套,外層函數會將整個內層函數做為一個函數對象返回,而且內層函數還調用了外層函數定義的變數,此時內層函數會記憶外層函數的相關變數值,這種函數稱為閉合函數。 f3=f1(3) 執行f1(3),將3傳遞給x,然後外層函數將「內層函數整體」作為一個對象的方式返回並賦值給f3變數。此時的f3 就是內層函數對象,此時的內層函數對象已記憶了x的值為3。 type(f3) 查看f3的類型為函數對象 f3(2) 調用f3這個函數對象,並將2傳遞給y變數,則最終結果返回 2 ** 3 (即2的3次方) f3(3) 本次調用就是計算 3 ** 3 f3(4) 本次調用就是計算 4 ** 3 以上的函數也稱為工廠函數或閉合函數或函數的閉合特性。 例: f3 = f1(4) f3(3) 本次就是計算的 3 ** 4 (即3的4次方) 內層函數才是一個真正的函數,而外層函數僅僅是內層函數運行的環境而已。外層函數主要是為內層函數提供運行環境。 def startPos(m,n): def newPos(x,y): print "The old position is (%d,%d),and the new position is (%d,%d)." % (m,n,m+x,n+y) return newPos action = startPos(10,10) action(1,2) action(-1,3) 匿名函數lambda: lambda僅僅是個表達式,但它有函數的特性;def是個語句。 lambda運算符 lambda args: expression args: 以逗號分隔的參數列表 expression : 用到args中各參數的表達式 lambda語句定義的程式碼必須是合法的表達式,不能出現多條件語句(可使用if的三元表達式)和其它非表達式語句,如for和while等。 lambda的首要用途是指定短小的回調函數 lambda將返回一個函數而不是將函數賦值給某變數名。 注意:lambda是一個表達式而非語句;lambda是一個單個表達式,而不是一個程式碼塊 例: f20 = lambda x,y: x+y lambda本身沒有名稱,在調用時才為它賦一個變數名(函數名)。 f20(3,4) 它返回3+4的結果;它的效果等同於以下函數: def f20(x,y): return x+y f20(3,4) f=lambda x,y,z: x+y+z f(4,5,6) f2=(lambda x,y,z=10: x+y+z) f2(4,5) def 語句創建的函數將賦值給某變數名,而lambda表達式則直接返回函數。lambda可以實現簡單函數速寫的功能。 l3 = [ (lambda x: x*2), (lambda y: y*3) ] for i in l3: print i(4) i的值為lambda函數,i(4)表示將4傳遞到函數中,最終返回函數的值 裝飾器 也稱為函數裝飾器,它自身是個函數,作用是能夠增強其它函數的功能。 函數程式碼重用,函數功能在不同場景中重用。 1)、裝飾器本身是一個函數,用於裝飾其它函數 2)、增強被裝飾函數的功能: 裝飾器需要接收一個函數對象做為參數,然後對其函數做包裝(即增強該函數的功能)。 裝飾器是一個很著名的設計模式,經常被用於有切面需求的場景,較為經典的有插入日誌、性能 測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,就可以 抽離出大量函數中 與函數功能本身無關的雷同程式碼 並繼續重用。概括的講,裝飾器的作用是 為已經存在的對象添加額外的功能。 例1:使用裝飾函數在函數執行前和執行後分別附加額外功能
def myfunc(): print "myfunc called" def deco(func): print "before myfunc() called" func() print "after myfunc() called" return func deco(myfunc)
In [5]: def f1(): ...: print "hello uplooking" ...: print "welcome to python" ...: In [6]: def f2(func): ...: print "before ........" ...: func() ...: print "after........" ...: In [7]: f2(f1) before ........ hello uplooking welcome to python after........
例2:使用語法糖@來裝飾函數
def deco(func): print "before myfunc() called" func() print "after myfunc() called" return func @deco def myfunc(): print "myfunc called"
In [11]: def f3(func): print "before .............." func() print "after ..............." ....: In [12]: @f3 def f4(): print "xxxxxxxxxxxx" print "yyyyyyyyyyyyy" ....: before .............. xxxxxxxxxxxx yyyyyyyyyyyyy after ............... 或: def myfunc(x): return x def deco(func): def _deco(x): return func(x)*x # 不需要返回func,實際上應返回原函數的返回值 return _deco myfunc=deco(myfunc) #前一個myfunc僅是自定義的變數名,後一個myfunc是上面的函數名;等號右側的表達式返回一個函數 print myfunc(2) #執行返回的新函數(也就是說將上面定義的myfunc函數代入deco的_deco本地函數,然後執行) In [21]: def f1(number): return number+10 ....: In [22]: def deco(func): def _deco(x): return func(x)*x return _deco ....: In [23]: f2=deco(f1) In [24]: f2(5) Out[24]: 75
例3:使用內嵌包裝函數來確保每次新函數都被調用 內嵌包裝函數的形參和返回值與原函數相同,裝飾函數返回內嵌包裝函數對象
def deco(func): def _deco(): print "before myfunc() called" func() print "after myfunc() called" return _deco @deco def myfunc(): #將myfunc()函數作為參數的形式傳遞給deco函數的func參數 print "myfunc called" myfunc()
例4:對帶參數的函數進行裝飾 內嵌包裝函數的形參和返回值與原函數相同,裝飾函數返回內嵌包裝函數對象
def deco(func): def _deco(a,b): print "before myfunc() called" ret=func(a,b) print "after myfunc() called" return ret return _deco @deco def myfunc(a,b): print "myfunc(%s,%s) called" % (a,b) return a+b myfunc(1,2)
In [27]: def deco(func): def _deco(x,y): print ".....function before......." result=func(x,y) print ".....function after ......" return result return _deco ....: In [28]: @deco ....: def f1(i,j): ....: print "%d * %d = %d" % (i,j,i*j) ....: return i*j ....: In [31]: f1(4,6) .....function before....... 4 * 6 = 24 .....function after ...... Out[31]: 24
例5:對參數數量不確定的函數進行裝飾
def deco(func): def _deco(*args,**kwargs): print "before %s called." % func.__name__ ret=func(*args,**kwargs) print "after %s called. result: %s" % (func.__name__, ret) return ret return _deco @deco def myfunc(a, b): print "myfunc(%s,%s) called." % (a, b) return a+b @deco def myfunc2(a, b, c): print "myfunc2(%s,%s,%s) called." % (a, b, c) return a+b+c myfunc(1, 2) myfunc(3, 4) myfunc2(1, 2, 3) myfunc2(3, 4, 5)
例6:在例5的基礎上,讓裝飾器帶參數,和上一示例相比在外層多了一層包裝。裝飾函數名實際上應更有意義些
def deco(arg): def _deco(func): def __deco(): print "before %s called [%s]." % (func.__name__, arg) func() print "after %s called [%s]." % (func.__name__, arg) return __deco return _deco @deco("mymodule") def myfunc(): print(" myfunc() called.") @deco("module2") def myfunc2(): print(" myfunc2() called.") myfunc() myfunc2()
In [48]: def deco(arg): ....: def _deco(func): ....: def __deco(*args): ....: print "======function before======" ....: func(*args) ....: print "function after: %s , %s " % (func.__name__,arg) ....: return __deco ....: return _deco ....: In [49]: @deco("module1") ....: def f1(var): ....: print "enter arguments:%s" %var ....: In [50]: @deco("module2") ....: def f2(x,y): ....: print "%d * %d = %d" % (x,y,x*y) ....: In [55]: f1("test") ======function before====== enter arguments:test function after: f1 , module1 In [56]: f2(4,6) ======function before====== 4 * 6 = 24 function after: f2 , module2 In [57]:
例7:讓裝飾器帶 類 參數
class locker: def __init__(self): print("locker.__init__() should be not called.") @staticmethod def acquire(): print("locker.acquire() called.(這是靜態方法)") @staticmethod def release(): print(" locker.release() called.(不需要對象實例)") def deco(cls): '''cls 必須實現acquire和release靜態方法''' def _deco(func): def __deco(): print("before %s called [%s]." % (func.__name__, cls)) cls.acquire() try: return func() finally: cls.release() return __deco return _deco @deco(locker) def myfunc(): print(" myfunc() called.") myfunc() myfunc()
遞歸 遞歸需要邊界條件,遞歸前進段和遞歸返回段:
def fact(n): if n<=1: return 1 else: return n*fact(n-1)
fact(3) 就相當於 3 * fact(3-1) = 3 * 2 * face(1) = 3 * 2 * 1 fact(6) fact(10) Python函數式編程: 函數式編程:也稱為泛函編程,是一種編程范型。它將電腦運算視為數據上的函數計算,並且避免狀態以及可變數據。函數式程式語言最重要的基礎是lambda演算,而且lambda演算的函數可以接受函數當作輸入、輸出。 python支援有限的函數式編程功能: filter(func,seq) 調用一個布爾函數func來迭代遍歷每上seq中的元素;返回一個使func返回值為true的元素的序列。 map(func,seq1[,seq2…]) 將函數func作用於給定序列(seq1)的每個元素,並用一個列表來提供返回值;如果func為None,func表現為一個身份函數,返回一個含有每個序列中元素集合的n個元組的列表。 reduce(func,seq[,init]) 將二元函數作用於seq序列的元素,每次攜帶一對(先前的結果以及下一個序列元素),連續地將現有的結果和下一個值作用在獲得的隨後的結果上,最終將序列減少到一個單一的返回值;如果指定初始值init,第一個比較會是init和序列中的第一個元素,而不是序列的前兩個元素。 filter過濾器 例:
def f1(x): if x > 20: return True else: return False l1=[1,5,10,30,80,100] filter(f1,l1) 返回一個符合條件的列表
filter()為已知序列的每個元素調用給定的布爾函數 調用中,返回值為非零值的元素將被添加至一個列表中 作業:/etc/passwd文件中,返回 /bin/bash字串的用戶名 map()映射器 map() 將函數調用"映射"到每個序列的對應元素上,並返回一個含有所有返回值的列表 map()將使用func函數對不同序列的同一個元素作運算處理,並將每個元素的處理結果整合成一個元組,最後將所有元組再返回成一個元組列表 例:
l1=[0,1,2,3,4,5,6] l2=['Sun','M','Tu','W','T','F','S'] map(None,l1,l2) 由於func為None,所以將返回[(0,'Sun'),(1,'M'),(2,'Tu'),(3,'W'),(4,'T'),(5,'S'),(6,'S')] 的一個元組列表 def f3(x): return x*2 map(f3,l1) map(f3,l2) def f4(x,y): return x*2,y*2 map(f4,l1,l2)
reduce() 只接收兩個參數,返回一個值
def f5(x,y): return x+y reduce(f5,l1) 即將l1列表中的每個元素相加,返回結果 reduce(f5,l1,10) 將初始值加上l1 列表中的每個元素,返回最終結果。 >>> list1=range(10) >>> print list1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> filter(lambda x:x%2==0,list1) [0, 2, 4, 6, 8] >>> map(lambda x:x**2,list1) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> list2=map(lambda x:x**2,list1) >>> reduce(lambda x,y:x+y, list2)
zip函數
list1=[1,2,3,4] list2=['a','b','c','d'] list3=[11,22,33,44] print zip(list1,list2,list3) 輸出結果為:[(1, 'a', 11), (2, 'b', 22), (3, 'c', 33), (4, 'd', 44)]
隨機函數: import random print random.random() #生成0至1之間的小數 print random.randint(1,5) #生成1至5的整數 print random.randrange(1,5) #生成1至4的整數 例:生成六位隨機數 code=[] for i in range(6): if i==random.randint(1,5): code.append(str(i)) else: code.append(chr(random.randint(65,90))) print "".join(code) 內置md5函數: import hashlib hash=hashlib.md5() hash.update("admin") print hash.hexdigest() print hash.digest() 常用內置函數: print 3*4 print eval("3*4") #算術運算字元串表達式 print divmod(9,4) #返回表達式的商及餘數 print pow(2, 10) #返回2的10次方 print chr(65) #返回Ascii碼65對應的字元 print ord("a") #返回字元"a"對應的Ascii碼 list2=["aa","bb","cc"] for item in enumerate(list2): print item #遍歷出列表中每個元素的值與對應下標 id type import reload help dir var dir()和vars()的區別就是dir()只列印屬性(屬性,屬性……)而vars()則列印屬性與屬性的值(屬性:屬性值……) 自定義字元串的格式化輸出: str1='I am {0},the working of the {1}' print str1.format("tom","computer") 輸出結果為:I am tom,the working of the computer 執行函數時也可以使用apply(function_name())的方式 總結: 在python語言中,定義(聲明)函數時,使用def語句。 def function_name(arg1,….): func_suite 當程式遇到def時就會生成一個函數對象,並且這個函數對象被命名為func_name(該名稱從某種角度上也可以理解成一個變數名,只是該變數名是為了引用一個函數對象而建立的),並生成函數體。此函數對象靠函數名來引用。函數體內部的語句只有在函數被調用時才會被執行,而函數結束時( 函數返回後),其內部生成的數據都會被銷毀。 列表解析與生成器: for i in ( j**2 for j in range(1,11)): print i 生成器不能相列表解析一樣將某個元素彈出或添加新元素或切片等。因為生成器並不是列表,它只是模擬了列表的一些行為。所以列表的很多功能在生成器中是無法實現的。 可以使用list將生成器轉換成列表 list((i**2 for i in range(1,11))) 生成器本身是延遲計算。可以使用自定義函數實現生成器的功能,如下: def genNum(x): y = 0 while y <= x: yield y yield將返回一個生成器對象。而且每次函數執行都會記錄前一次的結果。 y +=1 g1=genNum(10) type(g1) g1.next() g1.next() def genNum2(n): m = 1 while m <= n: yield m**2 m +=1 g2 = genNum2(20) for i in g2: print i 函數中使用yield,會返回一個生成器對象。 協程:yield寫在表達式右側,實現協程目的 函數的設計規範: 耦合性: (1)儘可能通過參數授受輸入,適就更多場景。以及通過return產生輸出,以保證函數的獨立性。 (2)盡量減少使用全局變數進行函數間通訊。 (3)不要在函數中修改可變類型的參數。 (4) 避免直接改變定義在另外一個模組中的變數。 聚合性: (1)每個函數都應該有一個單一的、統一的目標。 (2)每個函數的功能都應該相對簡單。 輸入:參數、全局變數、文件/流 輸出:return語句、可變參數、全局變數、文件/流