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