1. 函數概述
在編程的語境下,函數 (function) 是指一個有命名的、執行某個計算的語句序列 (sequence of statements) 。函數可以針對某類問題建立通用解決步驟(算法),函數減少了重復代碼,從而讓程序更簡潔、易讀、易於操作。
函數由對象、語句、表達式組成。
函數執行特定的操作並返回一個值(無返回值則隱式返回 None)
函數編程是面向過程的。
Python函數代碼結構和調用如下:
2. 變量
2.1 局部作用域與全局作用域、global語句
如果全局作用域變量在局部作用域沒有被定義(賦值,或者作為參數),則全局作用域變量可以被局部作用域讀取
>>> def func(): print(a) # 這種寫法是不好的 >>> a = 2 # a是全局作用域變量,但可以被局部作用域讀取 >>> func() 2
如果變量在局部作用域中被定義了,則局部作用域不會再讀取全局作用域的變量,如果在變量被定義前讀取,則會引發錯誤,下面這個例子,Python 編譯函數的定義體時, 會先判斷 b 是局部變量, 因為在函數中給它賦值了。
>>> b = 5 >>> def func(a): print(a) print(b) # 嘗試打印b變量出錯,程序終止 b = 8 >>> func(3) 3 Traceback (most recent call last): File "<pyshell#6>", line 1, in <module> func(3) File "<pyshell#5>", line 3, in func print(b) UnboundLocalError: local variable 'b' referenced before assignment
如果在函數中賦值時想讓解釋器把 b 當成全局變量, 要使用 global 語句聲明:
>>> b = 6 >>> def func(a): global b # global語句聲明了變量b為全局變量 print(a) print(b) b = 8 >>> func(3) 3 6
2.2 閉包和自由變量、nonlocal語句
自由變量(free variable),是指未在本地作用域中綁定的變量。如果自由變量綁定的值是可變的,則在閉包中仍然可以操作該變量,如果是不可變的(數字、字符串等),則在閉包中重新綁定自由變量會出錯
def make_averager(): count = 0 total = 0 def averager(new_value): count += 1 total += new_value return total / count return averager >>> avg = make_averager() >>> avg(10) Traceback (most recent call last): ... UnboundLocalError: local variable 'count' referenced before assignment
要讓閉包把變量標記為自由變量,可以用nonlocal語句聲明,nonlocal語句解決了上面的問題
def make_averager(): count = 0 total = 0 def averager(new_value): nonlocal count, total # 聲明count、total為自由變量 count += 1 total += new_value return total / count return averager
2.2 變量賦值的一些經驗(for循環中)
在下面這個例子中,words = Regex2.sub(replace,words,1) 這一句實際上如同增強賦值,如果將words換為其他名稱,如a或b等都得不到想要的結果,而且,這樣做也省去后面讀寫文件的一些麻煩
# (文件讀寫)瘋狂填詞2.py ''' 創建一個瘋狂填詞( Mad Libs)程序,它將讀入文本文件, 並讓用戶在該文本 文件中出現 ADJECTIVE、 NOUN、 ADVERB 或 VERB 等單詞的地方, 加上他們自 己的文本。例如,一個文本文件可能看起來像這樣: The ADJECTIVE panda walked to the NOUN and then VERB. A nearby NOUN was unaffected by these events. 程序將找到這些出現的單詞, 並提示用戶取代它們。 Enter an adjective: silly Enter a noun: chandelier Enter a verb: screamed Enter a noun: pickup truck 以下的文本文件將被創建: The silly panda walked to the chandelier and then screamed. A nearby pickup truck was unaffected by these events. 結果應該打印到屏幕上, 並保存為一個新的文本文件。 ''' import re def mad_libs(filename_path, save_path): with open(filename_path,'r') as strings: # 相對路徑下的文檔 words = strings.read() Regex = re.compile(r'\w[A-Z]+') # \w :匹配1個任何字母、數字或下划線 finds = Regex.findall(words) for i in finds: replace = input('輸入你想替換 {} 的單詞:\n'.format(i)) Regex2 = re.compile(i) words = Regex2.sub(replace,words,1) # 這個變量必須要是words與上面一致否則只打印最后替換的一個,可以畫棧堆圖跟蹤這個變量的值 print(words) # strings.close() 不用這一行,with 上下文管理器會自動關閉 with open(save_path,'a') as txt: txt.write(words + '\n') #分行寫 txt.close() # save_txt = open('保存瘋狂填詞文檔.txt','a') # save_txt.write(words) # save_txt.close() if __name__ == '__main__': filename_path = input('輸入要替換的txt文本路徑:') # '瘋狂填詞原始文檔.txt' save_path = input('輸入要保存的文件路徑(包含文件名稱):') # '保存瘋狂填詞文檔.txt' mad_libs(filename_path, save_path)
3. 參數
3.1 形參和實參
在def語句中,位於函數名后面的變量通常稱為形參,而調用函數時提供的值稱為實參。在函數內部重新關聯參數(即綁定,也就是賦值)時,函數外部的變量不受影響。
>>> def try_to_change(n): ... n = 'Mr. Gumby' ... >>> name = 'Mrs. Entity' >>> try_to_change(name) >>> name 'Mrs. Entity'
4. 棧堆圖
參考自《像計算機科學家一樣思考Python》
# 棧堆圖.py def print_twice ( bruce ): print ( bruce ) print ( bruce ) def cat_twice (part1 , part2 ): cat = part1 + part2 print_twice (cat) line1 = 'Bing tiddle' line2 = 'tiddle bang .' cat_twice (line1 , line2 )
1. 每個函數用一個棧幀 (frame) 表示。一個棧幀就是一個線框,函數名在旁邊,形參以及 函數內部的變量則在里面。前面例子的堆棧圖如圖 3.1所示。 2. 這些線框排列成棧的形式,說明了哪個函數調用了哪個函數等信息。在此例中,print_twice 被 cat_twice 調用,cat_twice 又被 __main__ 調用,__main__ 是一個表示最上層棧幀的特殊名 字。當你在所有函數之外創建一個變量時,它就屬於 __main__。 3. 每個形參都指向其對應實參的值。因此,part1 和 line1 的值相同,part2 和 line2 的值相 同,bruce 和 cat 的值相同。 4. 如果函數調用時發生錯誤,Python 會打印出錯函數的名字以及調用它的函數的名字,以 及調用后面這個函數 的名字,一直追溯到 __main__ 為止。 例如,如果你試圖在 print_twice 里面訪問 cat ,你將獲得一個 NameError : 5. 這個函數列表被稱作回溯 (traceback) 。它告訴你發生錯誤的是哪個程序文件,錯誤在 哪一行,以及當時在執行哪個函數。它還會顯示引起錯誤的那一行代碼。 回溯中的函數順序,與堆棧圖中的函數順序一致。出錯時正在運行的那個函數則位於回 溯信息的底部。 |
3. 函數代碼實踐(源自《Python編程快速上手 讓繁瑣工作自動化》)
''' 編寫一個名為 collatz()的函數,它有一個名為 number 的參數。如果參數是偶數,那么 collatz()就打印出 number // 2, 並返回該值。 如果 number 是奇數, collatz()就打印並返回 3 * number + 1。 讓用戶輸入一個整數, 並不斷對這個數調用 collatz(), 直到函數返回值1 (令人驚奇的是, 這個序列對於任何整數都有效, 利用這個序列,你遲早會得到 1! 既使數學家也不能確定為什么。 你的程序在研究所謂的“Collatz序列”, 它有時候被稱為“最簡單的、 不可能的數學問題”)。 在前面的項目中添加 try 和 except 語句,檢測用戶是否輸入了一個非整數的字符串。正常情況下, int()函數在傳入一個非整數字符串時,會產生 ValueError 誤, 比如 int('puppy')。在 except 子句中,向用戶輸出一條信息,告訴他們必須輸入一個整數。 ''' def collatz(number): if number == 1: return 1 elif number % 2 == 0: numbers = number // 2 print(numbers) collatz(numbers) elif number % 2 == 1: numbers = 3*number + 1 print(numbers) collatz(numbers) try: number = int(input("請輸入一個整數->:")) collatz(number) except ValueError: print("please input a integer number")