標籤:局部變數 2.7 接收 位置順序 空間 內建模組 range 具名引數 直接
# coding = utf-8‘‘‘函式宣告:def name([arg,... arg = value,... *arg, **kwarg]): suite1. 當編譯器遇到 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 linedef 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 counterif __name__ == ‘__main__‘: import doctest doctest.testmod(verbose=True)
使用doctest單元測試方式培訓講解:Python函數基礎