第一節:函數入門與定義函數
- 理解函數
- 所謂函數,就是為一段實現特定功能的代碼“取”個名字,以后即可通過該名字來執行(調用)這段代碼
- 從邏輯上看,函數相當于一個黑匣子
- 定義函數的語法
- 定義函數的三條鐵律
- 函數需要幾個關鍵的、需要動態變換的數據,這些數據就應該定義成函數的參數
- 函數需要傳出去幾個重要的數據(就是調用該函數的人希望得到的數據),這些數據應該定義成返回值
- 函數內部的實現過程
- 函數的語法:
- 定義函數的三條鐵律

函數的語法
# 定義函數,無參數def first(): # 之前學習的定義變量、賦值、運算、分支、循環等全部都可以寫在函數里面 print('first 函數') for i in range(5): print(i)first()# 只要執行函數,函數體的代碼就可以被重復的調用(復用)first()first()# 函數定義,有一個參數 namedef hello(name): print('hello 函數') print('您好,' + name)hello('python')hello('java')# 函數定義,有2個參數 name, agedef info(name, age): print('info函數') print('name參數', name) print('age參數', age)info(python, 90)info(java, 70)# 定義函數,有返回值def max(a, b): r = a if a > b else b # 返回值 return r# 返回值的意思,當程序調用函數之后,會得到N個值x = max(10, 20)print(x)x = max(30, 20)print(x)# print也是個函數,函數的嵌套print(max(20, 50))
- 為函數提供文檔
- 只要把一段字符串放在函數聲明之后、函數體之前,這段字符串就是函數的說明文檔
- Python內置的help()函數查看其它函數的幫助文檔
- 通過函數的_doc_屬性來查看函數的說明文檔
# 自定義說明文檔def test(a): ''' test 函數的說明,這是一個簡單的函數 a-代表什么意義 return- 代表什么意義 ''' # 空語句 passprint(test.__doc__)help(test)
第二節:多返回值函數與遞歸函數
- 多返回函數
- 多返回值,本質就是返回元組
- 程序即可返回元組,也可直接返回多個值(系統會自動將其封裝成元組)
- 獲取多返回值函數的返回值時
- 既可以用單個變量(元組)來獲取
- 也可以用多個變量獲取(元組解包)
import random# 希望該函數返回三個隨機的大寫字符def test(): # 生成三個隨機的大寫字符 c1 = chr(random.randint(65,90)) c2 = chr(random.randint(65,90)) c3 = chr(random.randint(65,90)) # 以元組的形式返回 return(c1, c2, c3) # 程序自動封裝成元組 return c1, c2, c3# r就是一個元組r = test()print(r)print(test())# 多返回值函數,即可用多個變量來接收返回值,也可用單獨變量來接收返回值c1, c2, c3 = test()print(c1)print(c2)print(c3)
- 遞歸函數
- 函數體內調用它自身,被稱為函數的遞歸
- 函數遞歸包含了一種隱式的循環,它會重復執行某段代碼,但是這種重復執行無需循環控制
- 遞歸函數的注意點
- 向已知的方向遞歸
- 讓遞歸有結束的時候,不要無限遞歸
# 無限遞歸的例子def foo(): print('111') print('222') foo()foo()# 計算N的階乘def frac(n): if n < 1: print("n不能小于1") return elif n == 1: return 1 else: # n的階乘總是等于上一個階乘*n # 函數調用自身 return frac(n-1) * n # 該函數的結束點是n==1,因此要向n==1方向遞歸 print(frac(5))print(frac(6))
第三節:關鍵字參數與參數默認值
- 關鍵字參數
- Python函數的參數名不是無意義的,Python允許調用函數時通過名字來傳入參數值
- 調用函數時支持兩種方式為參數指定值
- 位置參數:必須按照順序為每個參數指定參數值
- 關鍵字參數(命名參數):按參數名為參數指定參數值
def info(name, age, height): print("name:", name) print("age:", age) print("height:", height) # 位置參數,按照順序傳遞參數info('fkjava', 24, 183)# 關鍵字參數(命名參數)# 關鍵字參數的優勢是 1:不需要按照順序 2:可讀性更高info(age=34, name='java', height=179)# 混合使用info('python', height=178, age=45)# 混合使用錯誤:關鍵字參數必須位于位置參數的后面info(name='python', 178, 45)
- 參數的默認值
- 程序需要在定義函數的時候為一個或者多個形參指定默認值,這樣調用函數時就可以省略該形參傳入參數值,而是直接使用該形參的默認值
- 為形參指定默認值的語法格式如下:形參名 = 默認值
# 定義帶默認值的參數(一個參數有默認值)def info(age, name='python'): print('age參數為:', age) print('name參數為:', name) # 為參數指定默認值之后,調用時候可省略指定的參數值,該參數使用默認值 info(23)info(45, 'abc')info(11, 'Java')# 混合方式info(19, name='bcd')# 定義帶默認值的參數(兩個參數有默認值)def info(age=19, name='python'): print('age參數為:', age) print('name參數為:', name) info()info(20)# 如果你希望省略前面的參數指定參數值,后面的參數應該用關鍵字參數來傳入參數info(name='go')
第四節:參數搜集和逆向參數收集
- 參數收集
- 普通參數收集
- 在形參前面加上一個星號(*),這樣就意味著該參數可接收多個參數值,多個參數值被當成元組傳入
- 參數收集的本質就是一個元組:Python會將傳給帶*參數的多個值收集成一個元組
- Python允許個數可變的參數可以處于形參列表的任意位置,但是最多只能帶一個支持“普通”參數收集的參數
- 如果支持“普通”參數收集的形參位于前面,后面參數則需要使用關鍵字參數傳值
- 普通參數收集
# books參數支持收集,它可接受多個參數值def test(num, *books): print("num:", num) print("books:", books)# 將多個值自動封裝成一個元組test(5, "go", "python", "java")def info(*names, msg): for name in names: print("%s, %s" % (name, msg))# 如果你要為參數收集后的參數傳入參數值,需要用關鍵字參數 info("孫悟空", "豬八戒", "牛魔王", msg="歡迎大家") # 否則所有參數都會被參數收集成元組 info("孫悟空", "豬八戒", "牛魔王", "歡迎大家")
- 關鍵字參數收集
- 在參數前面添加兩個星號“**”,該參數支持關鍵字參數收集,收集的參數被當做dict處理
- 一個函數可同時支持普通參數收集和關鍵字參數收集
- 關鍵字參數收集
# books參數支持普通參數收集,它可接受多個參數值,socres支持關鍵字參數收集def test(num, *books, **socres): print("num:", num) print("books:", books) print("socres:", socres)test(20, 'fkjava', 'python', 'swift', 語文=89, 數學=93)def info(*names, msg, **socres): for name in names: print("%s, %s" % (name, msg)) print(socres)# 程序知道msg參數將是傳給msg的,因此socres參數不會收集它# dict的參數收集,它只收集不能明確傳入的關鍵字參數info("孫悟空", "豬八戒", "牛魔王", msg="歡迎大家", 語文=89, 數學=93)
- 逆向參數收集
- 在列表、元組前添加*,在字典之前添加**
def test(a, b): print(a) print(b)# 元組的逆向參數收集,以普通參數的形式為參數傳入參數值vals = (20, 40)# 調用函數時,元組不能自動解包# 默認情況下,元組是一個整體test(vals)# 這個語句是錯誤的# *對元組自動解包(逆向參數收集)test(*vals)# 列表的逆向參數收集,以普通參數的形式為參數傳入參數值msgs = ['aa', 'bb']test(*msgs)# 字典的逆向參數收集,以關鍵字參數的形式為參數傳入參數值# 簡單來說,**是將字典解析成關鍵字參數vals = {'a': 89, 'b': 93}test(**vals)
第五節:變量作用域
- 理解變量作用域
- 根據定義變量的位置,變量的作用域分為兩種:
- 局部變量:在函數中定義的變量包括參數,都被成為局部變量
- 全局變量:在函數外面、全局范圍內定義的變量,被稱為全局變量
- 根據定義變量的位置,變量的作用域分為兩種:
# 全局變量a = 35def info(): # 局部變量 b = 'fkjava' # 正確,局部變量只能在當前函數內訪問 print(b) # 正確,全局變量可以在任何函數內訪問 print(a) def info1(): # 局部變量 c = 'java' print(c) # 正確,全局變量可以在任何函數內訪問 print(a) # 錯誤,局部變量只能在定義局部變量的函數中使用 #print(b) info()info1()
- 變量字典
- 獲取變量字典
- globals():該函數返回全局范圍內搜所有變量組成的“變量字典”
- locals():該函數返回當前局部范圍內搜所有變量組成的“變量字典”
- vars(object):獲取指定對象的范圍內所有變量組成的“變量字典”,如果不傳入object參數,vars()和locals()作用完全相同
- 獲取變量字典
# 全局變量a = 35name = 'java'def info(): # 局部變量 b = 'fkjava' # 正確,局部變量只能在當前函數內訪問 print(b) # 正確,全局變量可以在任何函數內訪問 print(a) # 局部變量組成的數組 print(locals()) def info1(): # 局部變量 c = 'java' print(c) # 正確,全局變量可以在任何函數內訪問 print(a) # 錯誤,局部變量只能在定義局部變量的函數中使用 #print(b) # 局部變量組成的數組 print(locals())# 全局變量組成的數組print(globals()) # locals獲取當前范圍內的所有局部變量# 因此你在全局范圍調用locals函數的時候,它返回全部的全局變量# 簡單來說,你在全局范圍內,用globals和locals函數效果是一樣的print(locals()) info()info1()
- 處理局部變量遮蔽全局變量
- 全局變量默認可以在所有函數內訪問
- 如果在函數中定義了與全局變量同名的變量,此時就會發生局部變量的遮蔽(hide)全局變量的情形
# 解決方法一name = 'java'def info(): # 依然訪問全局變量name print(globals()['name']) # 在函數內對變量賦值,變成了定義新的name變量 name = 'python' print(name) info()# 全局變量name沒有改變print(name)# 解決方法二name = 'java'def info(): # 聲明作用:該函數中的name始終使用全局變量 global name # 依然訪問全局變量name print(name) # 前面已經聲明了name始終是全局變量 # 因此此處不是重新定義局部變量 name = 'python' print(name) info()# 全局變量name會被改變print(name)
第六節:局部函數
- 理解局部函數
- 放在函數體內的函數稱為局部函數
- 在默認情況下,局部函數對外部是隱藏的,局部函數只能在其封閉(enclosing)函數內使用
- 定義、使用局部函數
def foo(): print('foo函數') # 嵌套函數:在其他函數內定義的函數 def bar(): for i in range(5): print('bar函數') # 只能在foo函數內調用bar函數 bar()# 在此處調用bar函數出錯,局部函數只在它所在的封閉函數內有效# bar()foo()
- 封閉函數返回局部函數
- 封閉函數可以返回局部函數,以便程序在其他作用域中使用局部函數
- 如果封閉函數沒有將局部函數返回出來,那么局部函數將只能在封閉函數內部調用
def foo(): print('foo函數') # 嵌套函數:在其他函數內定義的函數 def bar(): for i in range(5): print('bar函數') # bar表示返回函數本身(函數也相當于一個值,是function類型的值) # bar()表示調用(執行)函數 return bar # foo函數的返回值時bar函數,因此此處是用變量r來保存bar函數r = foo()print(r)# 此時R引用bar函數本身,r的類型是functionprint(type(r))print('-' * 60)# 由于r是函數,因此程序可以調用它r()# 下面代碼看上去有點詭異# foo函數調用之后返回bar函數,bar函數也可調用foo()()
- 局部函數的變量遮蔽
- 局部函數內的變量也會遮蔽它所在的封閉函數的局部變量
- 避免方法:可用nonlocal進行聲明
def test(): name = 'fkjava' def info(): print('info 函數') # 聲明后面的name變量不是聲明新的局部變量,而是引用所在封閉函數的局部變量 nonlocal name print('name:', name) # 默認情況下,下面代碼是為info這個局部函數再次定義name局部變量 # 此時name局部變量就會遮蔽test函數的name變量 name = 'crazyit' # 在沒有用nonlocal聲明之前,此時打印會出錯,用nonlocal聲明之后,程序正常 print('name:', name) info() print(name)test()
- global與nonlocal總結
- 作用大致相同,都是用來避免變量遮蔽
- 區別:global用于聲明訪問全局變量。nonlocal用于聲明訪問局部函數所在封閉函數內的局部變量
- global與nonlocal總結
第七節:案例實操-定義計算N的階乘的函數
- 實現方法
- 方法一:使用循環計算階乘
- 控制循環計數器從1循環到N
- 讓循環計數器與前一個結果相乘,直到循環計數器等于N就得到了N的階乘
- 方法一:使用循環計算階乘
def fract(): r = 1 if n < 1: print('n不能小于1') return else: for i in range(1, n + 1): r *= i return rprint(fract(5))
- 方法二:運用遞歸計算階乘
- 經研究發現:N的階乘等于N乘以N-1的階乘,因此可借助于遞歸來實現
- N為1時,N的階乘是1,保證遞歸有結束點
- 方法二:運用遞歸計算階乘
def fract(): if n < 1: print('n不能小于1') return # 對于遞歸函數來說,必須保證在某個條件下,函數不再調用自身,遞歸結束 elif n == 1: return 1 else: # 遞歸:就是函數里面調用自身 return fract(n - 1) * n print(fract(5))
- 方法三:調用reduce函數計算階乘
- Python在functools模塊提供了reduce()函數,該函數使用指定函數對序列對象進行積累
- 可通過help(reduce)查看該函數的用法:reduce(function, sequence[, initial])
- 方法三:調用reduce函數計算階乘

import functoolsdef fn(x, y): return x * y def fract(): if n < 1: print('n不能小于1') return else: ''' # fn(ele1, ele2)->r # fn(r,ele3)->r # fn(r,ele4)->r ''' # 方法一: return functools.reduce(fn, range(1, n + 1)) # 方法二: # lambda x ,y: x * y 的本質就是一個函數 return functools.reduce(lambda x ,y : x * y, range(1, n + 1))print(fract(5))
第八節:案例實操-定義計算矩陣轉置的函數
- 使用循環進行轉置
- 首先創建一個長度與原矩陣第一個元素長度相等的新列表
- 使用遍歷原矩陣的每個元素,再使用嵌套循環遍歷每個元素,將列表中的元素添加到新列表對應的列表元素中
matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]'''# 轉置就是行變列 列變行# 轉置之前1 2 3 45 6 7 89 10 11 12# 轉置之后1 5 92 6 103 7 114 8 12'''def printmatrix(m): # 列表嵌套列表,因此ele也是列表 for ele in m: # 打印一行 for e in ele: print('%2d' % e, end=' ') print(' ') def transformmatrix(m) : # m[0]有幾個元素,說明原矩陣有多少列 # 列轉成行 rt = [[] for i in m[0]] for ele in m: for i in range(len(ele)): # rt[i]代表新矩陣的第i行 # ele[i]代表原矩陣當前行的第i列 rt[i].append(ele[i]) return rtprintmatrix(matrix)print('-' * 60)printmatrix(ransformmatrix(matrix))
- 使用zip()函數轉置
- zip函數的作用正是合并多個序列:多個序列第一個元素合并成第一個元素,多個序列第二個元素合并成第二個元素......
- 運用逆向函數收集即可

matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]def printmatrix(m): # 列表嵌套列表,因此ele也是列表 for ele in m: # 打印一行 for e in ele: print('%2d' % e, end=' ') print(' ') def transformmatrix(m) : # 逆向參數收集,將矩陣中多個列表轉換成多個參數,傳給zip return list(zip(*m))printmatrix(matrix)print('-' * 60)printmatrix(ransformmatrix(matrix))
- 使用numpy模塊轉置
- numpy模塊提供transpose函數執行轉置,該函數的返回值是numpy的內置類型:array
- 調用array的tolist()方法可將array轉換成list列表
matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]def printmatrix(m): # 列表嵌套列表,因此ele也是列表 for ele in m: # 打印一行 for e in ele: print('%2d' % e, end=' ') print(' ') def transformmatrix(m) : # 使用numpy模塊的transpose函數進行轉置 import numpy return numpy.transpose(m).tolist()printmatrix(matrix)print('-' * 60)printmatrix(ransformmatrix(matrix))