2020-04-22
好程序員 Python培訓
好程序員Python教程系列-第8講:函數和模塊,在講解本章節的內容之前,我們先來研究一道數學題,請說出下面的方程有多少組正整數解。
事實上,上面的問題等同于將8個蘋果分成四組每組至少一個蘋果有多少種方案,所以答案應該是 。組合數的計算公式如下所示。
根據我們前面學習的知識,可以用循環做累乘來計算階乘,那么通過下面的Python代碼我們就可以計算出組合數 的值,代碼如下所示。
"""
輸入M和N計算C(M,N)
Version: 0.1
Author: 駱昊
"""
m = int(input('m = '))
n = int(input('n = '))
# 計算m的階乘
fm = 1
for num in range(1, m + 1):
fm *= num
# 計算n的階乘
fn = 1
for num in range(1, n + 1):
fn *= num
# 計算m-n的階乘
fm_n = 1
for num in range(1, m - n + 1):
fm_n *= num
# 計算C(M,N)的值
print(fm // fn // fm_n)
不知道大家是否注意到,上面的代碼中我們做了三次求階乘,雖然m、n、m - n的值各不相同,但是三段代碼并沒有實質性的區別,屬于重復代碼。的編程大師Martin Fowler先生曾經說過:“代碼有很多種壞味道,重復是壞的一種!”。要寫出高質量的代碼首先要解決的就是重復代碼的問題。對于上面的代碼來說,我們可以將計算階乘的功能封裝到一個稱之為“函數”的功能模塊中,在需要計算階乘的地方,我們只需要“調用”這個“函數”就可以了。
數學上的函數通常形如 或者 這樣的形式,在 中,f是函數的名字,x是函數的自變量,y是函數的因變量;而 中,g是函數名,x和y是函數的自變量,z是函數的因變量。Python中的函數跟這個結構是一致的,每個函數都有自己的名字、自變量和因變量。我們通常把Python中函數的自變量稱為函數的參數,而因變量稱為函數的返回值。
在Python中可以使用def關鍵字來定義函數,和變量一樣每個函數也應該有一個漂亮的名字,命名規則跟變量的命名規則是一致的。在函數名后面的圓括號中可以放置傳遞給函數的參數,就是我們剛才說到的函數的自變量,而函數執行完成后我們會通過return關鍵字來返回函數的執行結果,就是我們剛才說的函數的因變量。
我們可以用函數的知識對上面的代碼進行重構(不影響代碼執行結果的前提下對代碼的結構進行調整),重構之后的代碼如下所示。
"""
輸入M和N計算C(M,N)
Version: 0.1
Author: 駱昊
"""
# 定義函數:def是定義函數的關鍵字、fac是函數名,num是參數(自變量)
def fac(num):
"""求階乘"""
result = 1
for n in range(1, num + 1):
result *= n
# 返回num的階乘(因變量)
return result
m = int(input('m = '))
n = int(input('n = '))
# 當需要計算階乘的時候不用再寫重復代碼而是直接調用函數fac
# 調用函數的語法是在函數名后面跟上圓括號并傳入參數
print(fac(m) // fac(n) // fac(m - n))
在Python語言中,如果函數中沒有return語句,那么函數默認返回代表空值的None。另外,在定義函數時,函數也可以沒有自變量,但是函數名后面的圓括號是必須有的。Python中還允許函數的參數擁有默認值,例如我們把上一課中搖色子獲得點數的功能定義到一個函數中,我們可以寫出如下所示的代碼。
"""
參數的默認值1
Version: 0.1
Author: 駱昊
"""
from random import randint
# 定義搖色子的函數,n表示色子的個數,默認值為2
def roll_dice(n=2):
"""搖色子返回總的點數"""
total = 0
for _ in range(n):
total += randint(1, 6)
return total
# 如果沒有指定參數,那么n使用默認值2,表示搖兩顆色子
print(roll_dice())
# 傳入參數3,變量n被賦值為3,表示搖三顆色子獲得點數
print(roll_dice(3))
我們再來看一個為簡單的例子。
"""
參數的默認值2
Version: 0.1
Author: 駱昊
"""
def add(a=0, b=0, c=0):
"""三個數相加求和"""
return a + b + c
# 調用add函數,沒有傳入參數,那么a、b、c都使用默認值0
print(add()) # 0
# 調用add函數,傳入一個參數,那么該參數賦值給變量a, 變量b和c使用默認值0
print(add(1)) # 1
# 調用add函數,傳入兩個參數,1和2分別賦值給變量a和b,變量c使用默認值0
print(add(1, 2)) # 3
# 調用add函數,傳入三個參數,分別賦值給a、b、c三個變量
print(add(1, 2, 3)) # 6
# 傳遞參數時可以不按照設定的順序進行傳遞
print(add(c=50, a=100, b=200))
接下來,我們還可以實現一個對任意多個數求和的add函數,因為Python語言中的函數支持可變參數,所謂可變參數指的是在調用函數時,可以向函數傳入0個或任意多個參數。將來我們以團隊協作開發的模式做商業項目時,可能需要去設計一個函數給其他人使用,但我們又不知道函數的調用者會向該函數傳入多少個參數,這個時候可變參數就可以派上用場。下面的代碼演示了用可變參數實現對任意多個數求和的add函數。
"""
可變參數
Version: 0.1
Author: 駱昊
"""
# 在參數名前面的*表示args是一個可變參數
def add(*args):
total = 0
# 可變參數可以放在for循環中取出每個參數的值
for val in args:
total += val
return total
# 在調用add函數時可以傳入0個或任意多個參數
print(add())
print(add(1))
print(add(1, 2))
print(add(1, 2, 3))
print(add(1, 3, 5, 7, 9))
不管用什么樣的編程語言來寫代碼,給變量、函數起名字都是一個讓人頭疼的問題,因為我們會遇到命名沖突這種尷尬的情況。簡單的場景就是在同一個.py文件中定義了兩個同名的函數,如下所示。
def foo():
print('hello, world!')
def foo():
print('goodbye, world!')
foo() # 大家猜猜調用foo函數會輸出什么
當然上面的這種情況我們很容易就能避免,但是如果項目是團隊協作多人開發的時候,團隊中可能有多個程序員都定義了名為foo的函數,這種情況下怎么解決命名沖突呢?答案其實很簡單,Python中每個文件就代表了一個模塊(module),我們在不同的模塊中可以有同名的函數,在使用函數的時候我們通過import關鍵字導入指定的模塊再使用完全限定名的調用方式就可以區分到底要使用的是哪個模塊中的foo函數,代碼如下所示。
module1.py
def foo():
print('hello, world!')
module2.py
def foo():
print('goodbye, world!')
test.py
import module1
import module2
# 用“模塊名.函數名”的方式(完全限定名)調用函數,
module1.foo() # hello, world!
module2.foo() # goodbye, world!
在導入模塊時,還可以使用as關鍵字對模塊進行別名,這樣我們可以使用為簡短的完全限定名。
test.py
import module1 as m1
import module2 as m2
m1.foo() # hello, world!
m2.foo() # goodbye, world!
上面的代碼我們導入了定義函數的模塊,我們也可以使用from...import...語法從模塊中直接導入需要使用的函數,代碼如下所示。
test.py
from module1 import foo
foo() # hello, world!
from module2 import foo
foo() # goodbye, world!
但是,如果我們如果從兩個不同的模塊中導入了同名的函數,后導入的函數會覆蓋掉先前的導入,就像下面的代碼中,調用foo會輸出hello, world!,因為我們先導入了module2的foo,后導入了module1的foo 。如果兩個from...import...反過來寫,就是另外一番光景了。
test.py
from module2 import foo
from module1 import foo
foo() # hello, world!
如果想在上面的代碼中同時使用來自兩個模塊中的foo函數也是有辦法的,大家可能已經猜到了,還是用as關鍵字對導入的函數進行別名,代碼如下所示。
test.py
from module1 import foo as f1
from module2 import foo as f2
f1() # hello, world!
f2() # goodbye, world!
Python標準庫中提供了大量的模塊和函數來簡化我們的開發工作,我們之前用過的random模塊就為我們提供了生成隨機數和進行隨機抽樣的函數;而time模塊則提供了和時間操作相關的函數。隨著我們進一步的學習Python編程知識,我們還會用到多的模塊和函數。Python標準庫中還有一類函數是不需要import就能夠直接使用的,我們將其稱之為內置函數,這些內置函數都是很有用也是常用的,下面的表格列出了一部分的內置函數。
函數是功能相對獨立且會重復使用的代碼的封裝。學會使用定義和使用函數,就能夠寫出為的代碼。當然,Python語言的標準庫中已經為我們提供了大量的模塊和常用的函數,用好這些模塊和函數就能夠用少的代碼做多的事情。
開班時間:2021-04-12(深圳)
開班盛況開班時間:2021-05-17(北京)
開班盛況開班時間:2021-03-22(杭州)
開班盛況開班時間:2021-04-26(北京)
開班盛況開班時間:2021-05-10(北京)
開班盛況開班時間:2021-02-22(北京)
開班盛況開班時間:2021-07-12(北京)
預約報名開班時間:2020-09-21(上海)
開班盛況開班時間:2021-07-12(北京)
預約報名開班時間:2019-07-22(北京)
開班盛況Copyright 2011-2023 北京千鋒互聯科技有限公司 .All Right 京ICP備12003911號-5 京公網安備 11010802035720號