使用doctest單元測試方式培訓講解:Python函數基礎
阿新 • • 發佈:2017-10-28
動態 獲得 直接 return 全部 Coding 位置順序 收集 顯式
# coding = utf-8 ‘‘‘ 函數聲明: def name([arg,... arg = value,... *arg, **kwarg]): suite 1. 當編譯器遇到 def,會生成創建函數對象指令。 也就是說 def 是執?行指令,?不僅僅是個語法關鍵字。 可以在任何地?方動態創建函數對象。 2. 可以使用默認參數、可變參數和關鍵字參數 arg = value 是默認參數 *args是可變參數,args接收的是一個tuple; **kwargs是關鍵字參數,kwargs接收的是一個dict。 lambda函數 不同於?用 def 定義復雜函數,lambda 只能是有返回值的簡單的表達式。使?用賦值語句會引發語法 錯誤,可以考慮?用函數代替。 ‘‘‘ ‘‘‘ ***************************************************** 1. 函數創建 函數是第一類對象,可作為其他函數的實參或返回值。 函數總是有返回值。就算沒有 return,默認也會返回 None。 ***************************************************** ‘‘‘ def test1(name): ‘‘‘ >>> test1(‘a‘).__name__ # 查看函數返回函數的名字 ‘a‘ >>> test1(‘b‘).__name__ # 查看函數返回函數的名字 ‘b‘ >>> test1(‘a‘)() # 調用函數返回的函數 call function a >>> print(test1(‘c‘)) # 調用函數,返回2個值:整型和字符串 (0, ‘test‘) ‘‘‘ if name == "a": def a(): print(‘call function a‘) return a elif name == "b": def b(): pass return b else: return 0, ‘test‘ ‘‘‘ ************************************************************* 2. 參數 2.1 函數的傳參方式靈活多變,可按位置順序傳參,也可不關?順序?命名實參。 ************************************************************* ‘‘‘ def test21(a, b): ‘‘‘ >>> test21(1,2) # 位置參數 1 2 >>> test21(b=3,a=4) # 命名參數 4 3 ‘‘‘ print(a, b) ‘‘‘ ************************************************************* 2.2 ?持參數默認值。不過要??, 默認值對象在創建函數時生成,所有調用都使?同?對象。 如果該默認值是可變類型,那麽就如同 C 靜態局部變量。 ************************************************************* ‘‘‘ def test22(x, lst=[]): ‘‘‘ >>> test22(1) [1] >>> test22(2) # 保持了上次調?用狀態。 [1, 2] >>> test22(3, []) # 顯式提供實參,不使?用默認值。 [3] >>> test22(3) # 再次使?用默認值,會繼續使用默認的列表對象 [1, 2, 3] ‘‘‘ lst.append(x) return lst ‘‘‘ ************************************************************* 2.3 默認參數後?不能有其他位置參數,除非是變參。 SyntaxError: def test23(a, b=1, c): pass 2.4 用 *args 收集 "多余" 的位置參數,**kwargs 收集 "額外" 的命名參數。 這兩個名字只是慣例,可 ?自由命名。 *args是可變參數,args接收的是一個tuple; **kwargs是關鍵字參數,kwargs接收的是一個dict。 ************************************************************* ‘‘‘ def test24(a, b=1, *args, **kwargs): ‘‘‘ >>> test24(0) 0 1 () {} >>> test24(0,2,3,4) 0 2 (3, 4) {} >>> test24(0,2,3,4,x=5) 0 2 (3, 4) {‘x‘: 5} # 可 "展開" 序列類型和字典,將全部元素當做多個實參使?用。如不展開的話,那僅是單個實參對象。 >>> test24(*range(5), **{‘x‘: 10, ‘y‘: 11}) 0 1 (2, 3, 4) {‘x‘: 10, ‘y‘: 11} >>> test24(range(5)) range(0, 5) 1 () {} # 單個 "*" 展開序列類型,或者僅是字典的主鍵列表。 # "**" 展開字典鍵值對。但如果沒有變參收集, 展開後多余的參數將引發異常。 >>> p = dict(a=20,b=21) >>> test24(p) {‘a‘: 20, ‘b‘: 21} 1 () {} >>> test24(*p) # 僅展開 keys(),test("a"、"b") a b () {} >>> test24(**p) # 展開 items(),test(a = 1, b = 2)。 20 21 () {} ‘‘‘ print(a) print(b) print(args) print(kwargs) ‘‘‘ ************************************************************* 3. 作用域 3.1 函數形參和內部變量都存儲在 locals 名字空間中。 ************************************************************* ‘‘‘ def test31(a, b=1): ‘‘‘ >>> test31(100) {‘s‘: ‘Hello Python‘, ‘b‘: 1, ‘a‘: 100} ‘‘‘ s = ‘Hello Python‘ print(locals()) ‘‘‘ ************************************************************* 3.2 除?使用 global、nonlocal 特別聲明, 否則,在函數內部使用賦值語句,總是在 locals 名字空間中 新建一個對象關聯。 註意:"賦值" 是指名字指向新的對象,??通過名字改變對象狀態。 名字查找順序: locals -> enclosing function -> globals -> __builtins__ ? locals: 函數內部名字空間,包括局部變量和形參。 ? enclosing function: 外部嵌套函數的名字空間。 ? globals: 函數定義所在模塊的名字空間。 ? __builtins__: 內置模塊的名字空間。 如果不修改全局變量也可以不使用global關鍵字。 Python3 提供了 nonlocal 關鍵字,用來修改外部 嵌套函數名字空間, python2.7 沒有。 nonlocal關鍵字用來在函數或其他作用域中使用外層(非全局)變量。 ************************************************************* ‘‘‘ x, y = 1, 2 # 在模塊級別直接定義的全局變量 def test321(): global x # 引用全局變量 x = 11 y = 21 # 這裏的 y 是局部變量 global z # 在函數內部定義一個全局變量,而沒有在模塊級別定義 z = 3 print(‘ in test321(): x=%d; y=%d; z=%d‘ % (x, y, z)) def test322(): print(‘ in test322(): x=%d; y=%d ; z=%d‘ % (x, y, z)) # 直接訪問所有全局變量,包括變量z # 測試 print(‘ out 1: x=%d; y=%d; z=undefine‘ % (x, y)) test321() test322() print(‘ out 2: x=%d; y=%d ; z=%d‘ % (x, y, z)) ‘‘‘ ************************************************************* 3.3 閉包 閉包(closure)是函數式編程的重要的語法結構。 閉包也是一種組織代碼的結構,它同樣提高了代碼的可重復使用性。 ************************************************************* ‘‘‘ def lineConfig(a,b): ‘‘‘ 這個例子中,函數line與環境變量a,b構 成閉包。 在創建閉包的時候,我們通過line_conf的參數a,b說明了這兩個環境變量的取值, 這樣我們就確定了函數的最終形式(y = x + 1和y = 4x + 5)。 我們只需要變換參數a,b,就可以獲得不同的直線表達函數。 由此,我們可以看到,閉包也具有提高代碼可復用性的作用。 >>> y1 = lineConfig(1, 1) # 定義直線 y = x + 1 >>> p1 = y1(2) # 計算y坐標,獲取x=2時y1的值 >>> print(p1) 3 >>> lstPoint = [y1(x) for x in range(3)] # 計算y坐標列表 >>> print(lstPoint) [1, 2, 3] >>> y2 = lineConfig(4, 5) # 定義直線 y = 4x + 5 >>> p2 = y2(x=2) # 計算y坐標,獲取x=2時y2的值 >>> print(p2) 13 ‘‘‘ def line(x): return a*x + b return line def newCounter(i=0): ‘‘‘ 兩個獨立的計數器 >>> c1 = newCounter() >>> c2 = newCounter(2) >>> print(c1(),c1(),c1()) 1 2 3 >>> print(c2(),c2(),c2()) 3 4 5 ‘‘‘ def counter(): nonlocal i i = i + 1 return i return counter if __name__ == ‘__main__‘: import doctest doctest.testmod(verbose=True)
使用doctest單元測試方式培訓講解:Python函數基礎