您好,登錄后才能下訂單哦!
怎么在Python中使用模塊?相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
$$x_1 + x_2 + x_3 + x_4 = 8$$
事實(shí)上,上面的問題等同于將8個(gè)蘋果分成四組每組至少一個(gè)蘋果有多少種方案。想到這一點(diǎn)問題的答案就呼之欲出了。
$$C_M^N =\frac{M!}{N!(M-N)!}, \text{(M=7, N=3)} $$
可以用Python的程序來計(jì)算出這個(gè)值,代碼如下所示。
""" 輸入M和N計(jì)算C(M,N) """ m = int(input('m = ')) n = int(input('n = ')) fm = 1 for num in range(1, m + 1): fm *= num fn = 1 for num in range(1, n + 1): fn *= num fmn = 1 for num in range(1, m - n + 1): fmn *= num print(fm // fn // fmn)
函數(shù)的作用
不知道大家是否注意到,在上面的代碼中,我們做了3次求階乘,這樣的代碼實(shí)際上就是重復(fù)代碼。編程大師Martin Fowler先生曾經(jīng)說過:“代碼有很多種壞味道,重復(fù)是最壞的一種!”,要寫出高質(zhì)量的代碼首先要解決的就是重復(fù)代碼的問題。對(duì)于上面的代碼來說,我們可以將計(jì)算階乘的功能封裝到一個(gè)稱之為“函數(shù)”的功能模塊中,在需要計(jì)算階乘的地方,我們只需要“調(diào)用”這個(gè)“函數(shù)”就可以了。
定義函數(shù)
在Python中可以使用def關(guān)鍵字來定義函數(shù),和變量一樣每個(gè)函數(shù)也有一個(gè)響亮的名字,而且命名規(guī)則跟變量的命名規(guī)則是一致的。在函數(shù)名后面的圓括號(hào)中可以放置傳遞給函數(shù)的參數(shù),這一點(diǎn)和數(shù)學(xué)上的函數(shù)非常相似,程序中函數(shù)的參數(shù)就相當(dāng)于是數(shù)學(xué)上說的函數(shù)的自變量,而函數(shù)執(zhí)行完成后我們可以通過return關(guān)鍵字來返回一個(gè)值,這相當(dāng)于數(shù)學(xué)上說的函數(shù)的因變量。
在了解了如何定義函數(shù)后,我們可以對(duì)上面的代碼進(jìn)行重構(gòu),所謂重構(gòu)就是在不影響代碼執(zhí)行結(jié)果的前提下對(duì)代碼的結(jié)構(gòu)進(jìn)行調(diào)整,重構(gòu)之后的代碼如下所示。
def factorial(num): """ 求階乘 :param num: 非負(fù)整數(shù) :return: num的階乘 """ result = 1 for n in range(1, num + 1): result *= n return result m = int(input('m = ')) n = int(input('n = ')) # 當(dāng)需要計(jì)算階乘的時(shí)候不用再寫循環(huán)求階乘而是直接調(diào)用已經(jīng)定義好的函數(shù) print(factorial(m) // factorial(n) // factorial(m - n))
說明: Python的math模塊中其實(shí)已經(jīng)有一個(gè)factorial函數(shù)了,事實(shí)上要計(jì)算階乘可以直接使用這個(gè)現(xiàn)成的函數(shù)而不用自己定義。下面例子中的某些函數(shù)其實(shí)Python中也是內(nèi)置了,我們這里是為了講解函數(shù)的定義和使用才把它們又實(shí)現(xiàn)了一遍,實(shí)際開發(fā)中不建議做這種低級(jí)的重復(fù)性的工作。
函數(shù)的參數(shù)
函數(shù)是絕大多數(shù)編程語言中都支持的一個(gè)代碼的“構(gòu)建塊”,但是Python中的函數(shù)與其他語言中的函數(shù)還是有很多不太相同的地方,其中一個(gè)顯著的區(qū)別就是Python對(duì)函數(shù)參數(shù)的處理。在Python中,函數(shù)的參數(shù)可以有默認(rèn)值,也支持使用可變參數(shù),所以Python并不需要像其他語言一樣支持函數(shù)的重載,因?yàn)槲覀冊(cè)诙x一個(gè)函數(shù)的時(shí)候可以讓它有多種不同的使用方式,下面是兩個(gè)小例子。
from random import randint def roll_dice(n=2): """ 搖色子 :param n: 色子的個(gè)數(shù) :return: n顆色子點(diǎn)數(shù)之和 """ total = 0 for _ in range(n): total += randint(1, 6) return total def add(a=0, b=0, c=0): return a + b + c # 如果沒有指定參數(shù)那么使用默認(rèn)值搖兩顆色子 print(roll_dice()) # 搖三顆色子 print(roll_dice(3)) print(add()) print(add(1)) print(add(1, 2)) print(add(1, 2, 3)) # 傳遞參數(shù)時(shí)可以不按照設(shè)定的順序進(jìn)行傳遞 print(add(c=50, a=100, b=200))
我們給上面兩個(gè)函數(shù)的參數(shù)都設(shè)定了默認(rèn)值,這也就意味著如果在調(diào)用函數(shù)的時(shí)候如果沒有傳入對(duì)應(yīng)參數(shù)的值時(shí)將使用該參數(shù)的默認(rèn)值,所以在上面的代碼中我們可以用各種不同的方式去調(diào)用add函數(shù),這跟其他很多語言中函數(shù)重載的效果是一致的。
其實(shí)上面的add函數(shù)還有更好的實(shí)現(xiàn)方案,因?yàn)槲覀兛赡軙?huì)對(duì)0個(gè)或多個(gè)參數(shù)進(jìn)行加法運(yùn)算,而具體有多少個(gè)參數(shù)是由調(diào)用者來決定,我們作為函數(shù)的設(shè)計(jì)者對(duì)這一點(diǎn)是一無所知的,因此在不確定參數(shù)個(gè)數(shù)的時(shí)候,我們可以使用可變參數(shù),代碼如下所示。
# 在參數(shù)名前面的*表示args是一個(gè)可變參數(shù) # 即在調(diào)用add函數(shù)時(shí)可以傳入0個(gè)或多個(gè)參數(shù) def add(*args): total = 0 for val in args: total += val return total print(add()) print(add(1)) print(add(1, 2)) print(add(1, 2, 3)) print(add(1, 3, 5, 7, 9))
用模塊管理函數(shù)
對(duì)于任何一種編程語言來說,給變量、函數(shù)這樣的標(biāo)識(shí)符起名字都是一個(gè)讓人頭疼的問題,因?yàn)槲覀儠?huì)遇到命名沖突這種尷尬的情況。最簡單的場景就是在同一個(gè).py文件中定義了兩個(gè)同名函數(shù),由于Python沒有函數(shù)重載的概念,那么后面的定義會(huì)覆蓋之前的定義,也就意味著兩個(gè)函數(shù)同名函數(shù)實(shí)際上只有一個(gè)是存在的。
def foo(): print('hello, world!') def foo(): print('goodbye, world!') # 下面的代碼會(huì)輸出什么呢? foo()
當(dāng)然上面的這種情況我們很容易就能避免,但是如果項(xiàng)目是由多人協(xié)作進(jìn)行團(tuán)隊(duì)開發(fā)的時(shí)候,團(tuán)隊(duì)中可能有多個(gè)程序員都定義了名為foo的函數(shù),那么怎么解決這種命名沖突呢?答案其實(shí)很簡單,Python中每個(gè)文件就代表了一個(gè)模塊(module),我們?cè)诓煌哪K中可以有同名的函數(shù),在使用函數(shù)的時(shí)候我們通過import關(guān)鍵字導(dǎo)入指定的模塊就可以區(qū)分到底要使用的是哪個(gè)模塊中的foo函數(shù),代碼如下所示。
module1.py
def foo(): print('hello, world!')
module2.py
def foo(): print('goodbye, world!')
test.py
from module1 import foo # 輸出hello, world! foo() from module2 import foo # 輸出goodbye, world! foo()
也可以按照如下所示的方式來區(qū)分到底要使用哪一個(gè)foo函數(shù)。
test.py
import module1 as m1 import module2 as m2 m1.foo() m2.foo()
但是如果將代碼寫成了下面的樣子,那么程序中調(diào)用的是最后導(dǎo)入的那個(gè)foo,因?yàn)楹髮?dǎo)入的foo覆蓋了之前導(dǎo)入的foo。
test.py
from module1 import foo from module2 import foo # 輸出goodbye, world! foo()
test.py
from module2 import foo from module1 import foo # 輸出hello, world! foo()
需要說明的是,如果我們導(dǎo)入的模塊除了定義函數(shù)之外還中有可以執(zhí)行代碼,那么Python解釋器在導(dǎo)入這個(gè)模塊時(shí)就會(huì)執(zhí)行這些代碼,事實(shí)上我們可能并不希望如此,因此如果我們?cè)谀K中編寫了執(zhí)行代碼,最好是將這些執(zhí)行代碼放入如下所示的條件中,這樣的話除非直接運(yùn)行該模塊,if條件下的這些代碼是不會(huì)執(zhí)行的,因?yàn)橹挥兄苯訄?zhí)行的模塊的名字才是“__main__”。
module3.py
def foo(): pass def bar(): pass # __name__是Python中一個(gè)隱含的變量它代表了模塊的名字 # 只有被Python解釋器直接執(zhí)行的模塊的名字才是__main__ if __name__ == '__main__': print('call foo()') foo() print('call bar()') bar()
test.py
import module3 # 導(dǎo)入module3時(shí) 不會(huì)執(zhí)行模塊中if條件成立時(shí)的代碼 因?yàn)槟K的名字是module3而不是__main__
練習(xí)
練習(xí)1:實(shí)現(xiàn)計(jì)算求最大公約數(shù)和最小公倍數(shù)的函數(shù)。
def gcd(x, y): (x, y) = (y, x) if x > y else (x, y) for factor in range(x, 0, -1): if x % factor == 0 and y % factor == 0: return factor def lcm(x, y): return x * y // gcd(x, y)
練習(xí)2:實(shí)現(xiàn)判斷一個(gè)數(shù)是不是回文數(shù)的函數(shù)。
def is_palindrome(num): temp = num total = 0 while temp > 0: total = total * 10 + temp % 10 temp //= 10 return total == num
練習(xí)3:實(shí)現(xiàn)判斷一個(gè)數(shù)是不是素?cái)?shù)的函數(shù)。
def is_prime(num): for factor in range(2, num): if num % factor == 0: return False return True if num != 1 else False
練習(xí)4:寫一個(gè)程序判斷輸入的正整數(shù)是不是回文素?cái)?shù)。
if __name__ == '__main__': num = int(input('請(qǐng)輸入正整數(shù): ')) if is_palindrome(num) and is_prime(num): print('%d是回文素?cái)?shù)' % num)
通過上面的程序可以看出,當(dāng)我們將代碼中重復(fù)出現(xiàn)的和相對(duì)獨(dú)立的功能抽取成函數(shù)后,我們可以組合使用這些函數(shù)來解決更為復(fù)雜的問題,這也是我們?yōu)槭裁匆x和使用函數(shù)的一個(gè)非常重要的原因。
最后,我們來討論一下Python中有關(guān)變量作用域的問題。
def foo(): b = 'hello' def bar(): # Python中可以在函數(shù)內(nèi)部再定義函數(shù) c = True print(a) print(b) print(c) bar() # print(c) # NameError: name 'c' is not defined if __name__ == '__main__': a = 100 # print(b) # NameError: name 'b' is not defined foo()
上面的代碼能夠順利的執(zhí)行并且打印出100和“hello”,但我們注意到了,在bar函數(shù)的內(nèi)部并沒有定義a和b兩個(gè)變量,那么a和b是從哪里來的。我們?cè)谏厦娲a的if分支中定義了一個(gè)變量a,這是一個(gè)全局變量(global variable),屬于全局作用域,因?yàn)樗鼪]有定義在任何一個(gè)函數(shù)中。在上面的foo函數(shù)中我們定義了變量b,這是一個(gè)定義在函數(shù)中的局部變量(local variable),屬于局部作用域,在foo函數(shù)的外部并不能訪問到它;但對(duì)于foo函數(shù)內(nèi)部的bar函數(shù)來說,變量b屬于嵌套作用域,在bar函數(shù)中我們是可以訪問到它的。bar函數(shù)中的變量c屬于局部作用域,在bar函數(shù)之外是無法訪問的。事實(shí)上,Python查找一個(gè)變量時(shí)會(huì)按照“局部作用域”、“嵌套作用域”、“全局作用域”和“內(nèi)置作用域”的順序進(jìn)行搜索,前三者我們?cè)谏厦娴拇a中已經(jīng)看到了,所謂的“內(nèi)置作用域”就是Python內(nèi)置的那些隱含標(biāo)識(shí)符min、len等都屬于內(nèi)置作用域)。
再看看下面這段代碼,我們希望通過函數(shù)調(diào)用修改全局變量a的值,但實(shí)際上下面的代碼是做不到的。
def foo(): a = 200 print(a) # 200 if __name__ == '__main__': a = 100 foo() print(a) # 100
在調(diào)用foo函數(shù)后,我們發(fā)現(xiàn)a的值仍然是100,這是因?yàn)楫?dāng)我們?cè)诤瘮?shù)foo中寫a = 200的時(shí)候,是重新定義了一個(gè)名字為a的局部變量,它跟全局作用域的a并不是同一個(gè)變量,因?yàn)榫植孔饔糜蛑杏辛俗约旱淖兞縜,因此foo函數(shù)不再搜索全局作用域中的a。如果我們希望在foo函數(shù)中修改全局作用域中的a,代碼如下所示。
def foo(): global a a = 200 print(a) # 200 if __name__ == '__main__': a = 100 foo() print(a) # 200
我們可以使用global關(guān)鍵字來指示foo函數(shù)中的變量a來自于全局作用域,如果全局作用域中沒有a,那么下面一行的代碼就會(huì)定義變量a并將其置于全局作用域。同理,如果我們希望函數(shù)內(nèi)部的函數(shù)能夠修改嵌套作用域中的變量,可以使用nonlocal關(guān)鍵字來指示變量來自于嵌套作用域,請(qǐng)大家自行試驗(yàn)。
在實(shí)際開發(fā)中,我們應(yīng)該盡量減少對(duì)全局變量的使用,因?yàn)槿肿兞康淖饔糜蚝陀绊戇^于廣泛,可能會(huì)發(fā)生意料之外的修改和使用,除此之外全局變量比局部變量擁有更長的生命周期,可能導(dǎo)致對(duì)象占用的內(nèi)存長時(shí)間無法被垃圾回收。事實(shí)上,減少對(duì)全局變量的使用,也是降低代碼之間耦合度的一個(gè)重要舉措,同時(shí)也是對(duì)迪米特法則的踐行。減少全局變量的使用就意味著我們應(yīng)該盡量讓變量的作用域在函數(shù)的內(nèi)部,但是如果我們希望將一個(gè)局部變量的生命周期延長,使其在函數(shù)調(diào)用結(jié)束后依然可以訪問,這時(shí)候就需要使用閉包,這個(gè)我們?cè)诤罄m(xù)的內(nèi)容中進(jìn)行講解。
說明: 很多人經(jīng)常會(huì)將“閉包”一詞和“匿名函數(shù)”混為一談,但實(shí)際上它們是不同的概念,如果想提前了解這個(gè)概念,推薦看看維基百科或者知乎上對(duì)這個(gè)概念的討論。
說了那么多,其實(shí)結(jié)論很簡單,從現(xiàn)在開始我們可以將Python代碼按照下面的格式進(jìn)行書寫,這一點(diǎn)點(diǎn)的改進(jìn)其實(shí)就是在我們理解了函數(shù)和作用域的基礎(chǔ)上跨出的巨大的一步。
def main(): # Todo: Add your code here pass if __name__ == '__main__': main()
看完上述內(nèi)容,你們掌握怎么在Python中使用模塊的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。