從零開始學Python:第八課-函數和模塊

在講解本章節的內容之前,我們先來研究一道數學題,請說出下面的方程有多少組正整數解。

從零開始學Python:第八課-函數和模塊

 

你可能已經想到了,這個問題其實等同於將8個蘋果分成四組且每組至少一個蘋果有多少種方案,因此該問題還可以進一步等價於在分隔8個蘋果的7個空隙之間插入三個隔板將蘋果分成四組有多少種方案,也就是從7個空隙選出3個空隙放入隔板的組合數,所以答案是

從零開始學Python:第八課-函數和模塊

 

組合數的計算公式如下所示:

從零開始學Python:第八課-函數和模塊

 

根據我們前面學習的知識,可以用循環做累乘來計算階乘,那麼通過下面的Python代碼我們就可以計算出組合數

從零開始學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先生曾經說過:“代碼有很多種壞味道,重複是最壞的一種!”。要寫出高質量的代碼首先要解決的就是重複代碼的問題。對於上面的代碼來說,我們可以將計算階乘的功能封裝到一個稱爲“函數”的代碼塊中,在需要計算階乘的地方,我們只需要“調用函數”就可以了。

定義函數

從零開始學Python:第八課-函數和模塊

 

在Python中可以使用def關鍵字來定義函數,和變量一樣每個函數也應該有一個漂亮的名字,命名規則跟變量的命名規則是一致的。在函數名後面的圓括號中可以放置傳遞給函數的參數,就是我們剛纔說到的函數的自變量,而函數執行完成後我們會通過return關鍵字來返回函數的執行結果,就是我們剛纔說的函數的因變量。一個函數要執行的代碼塊(要做的事情)也是通過縮進的方式來表示的,跟之前分支和循環結構的代碼塊是一樣的。大家不要忘了def那一行的最後面還有一個:,之前提醒過大家,那是在英文輸入法狀態下輸入的冒號。

我們可以通過函數對上面的代碼進行重構。所謂重構,是在不影響代碼執行結果的前提下對代碼的結構進行調整。重構之後的代碼如下所示。

"""
輸入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))

函數的參數

參數的默認值

如果函數中沒有return語句,那麼函數默認返回代表空值的None。另外,在定義函數時,函數也可以沒有自變量,但是函數名後面的圓括號是必須有的。Python中還允許函數的參數擁有默認值,我們可以把上一課“CRAPS賭博遊戲”的搖色子獲得點數的功能封裝成函數,代碼如下所示。

"""
參數的默認值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))    # 350

注意:帶默認值的參數必須放在不帶默認值的參數之後,否則將產生SyntaxError錯誤,錯誤消息是:non-default argument follows default argument,翻譯成中文的意思是“沒有默認值的參數放在了帶默認值的參數後面”。

可變參數

接下來,我們還可以實現一個對任意多個數求和的add函數,因爲Python語言中的函數可以通過星號表達式語法來支持可變參數。所謂可變參數指的是在調用函數時,可以向函數傳入0個或任意多個參數。將來我們以團隊協作的方式開發商業項目時,很有可能要設計函數給其他人使用,但有的時候我們並不知道函數的調用者會向該函數傳入多少個參數,這個時候可變參數就可以派上用場。下面的代碼演示了用可變參數實現對任意多個數求和的add函數。

"""
可變參數

Version: 0.1
Author: 駱昊
"""


# 用星號表達式來表示args可以接收0個或任意多個參數
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))

從零開始學Python:第八課-函數和模塊

 

用模塊管理函數

不管用什麼樣的編程語言來寫代碼,給變量、函數起名字都是一個讓人頭疼的問題,因爲我們會遇到命名衝突這種尷尬的情況。最簡單的場景就是在同一個.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標準庫中的math模塊中已經有了,實際開發中並不需要我們自己編寫,而math模塊中還包括了計算正弦、餘弦、指數、對數等一系列的數學函數。隨着我們進一步的學習Python編程知識,我們還會用到更多的模塊和函數。

Python標準庫中還有一類函數是不需要import就能夠直接使用的,我們將其稱之爲內置函數,這些內置函數都是很有用也是最常用的,下面的表格列出了一部分的內置函數。

從零開始學Python:第八課-函數和模塊

 

簡單的總結

函數是功能相對獨立且會重複使用的代碼的封裝。學會使用定義和使用函數,就能夠寫出更爲優質的代碼。當然,Python語言的標準庫中已經爲我們提供了大量的模塊和常用的函數,用好這些模塊和函數就能夠用更少的代碼做更多的事情。

Python 900集全套視頻教程(全家桶)
https://pan.baidu.com/s/1cU5lDWq9gh0cQ7hCnXUiGA

你們要的Python視頻教程,我也整理好了,嫌看字累,視頻給安排上,寵不寵粉就說吧!

學習的可以留言或者私我:“Python” 獲取!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章