第7章函數(shù)定義與使用_第1頁
第7章函數(shù)定義與使用_第2頁
第7章函數(shù)定義與使用_第3頁
第7章函數(shù)定義與使用_第4頁
第7章函數(shù)定義與使用_第5頁
已閱讀5頁,還剩76頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、第第7 7章章 函數(shù)定義與使用函數(shù)定義與使用董付國董付國微信公眾號:微信公眾號:PythonPython小屋小屋本章學(xué)習(xí)目標(biāo) 理解函數(shù)與代碼復(fù)用的關(guān)系 熟練掌握函數(shù)定義與調(diào)用的語法 理解遞歸函數(shù)執(zhí)行過程 理解嵌套定義函數(shù)的語法和執(zhí)行過程 理解位置參數(shù)、默認(rèn)值參數(shù)、關(guān)鍵參數(shù)和可變長度參數(shù)的原理并能夠熟練使用 理解實(shí)參序列解包的語法 熟練掌握變量作用域的概念 理解訪問變量時(shí)不同作用域的搜索順序 熟練掌握lambda表達(dá)式語法與應(yīng)用 理解生成器函數(shù)的工作原理 理解修飾器函數(shù)的工作原理7.1 函數(shù)定義與調(diào)用 函數(shù)只是一種封裝代碼的方式,在函數(shù)內(nèi)部用到的仍然是前面章節(jié)學(xué)習(xí)的內(nèi)置函數(shù)、運(yùn)算符、內(nèi)置類型、選

2、擇結(jié)構(gòu)、循環(huán)結(jié)構(gòu)、異常處理結(jié)構(gòu)以及后面章節(jié)將會(huì)學(xué)習(xí)的內(nèi)容,只是把這些功能代碼封裝起來然后提供一個(gè)接收輸入和返回結(jié)果的接口。 把用來解決某一類問題的功能代碼封裝成函數(shù),例如求和、最大值、排序等,可以在不同的程序中重復(fù)利用這些功能,使得代碼更加精煉,更加容易維護(hù)。 除了內(nèi)置函數(shù)、標(biāo)準(zhǔn)庫函數(shù)和擴(kuò)展庫函數(shù),Python也允許用戶自定義函數(shù),實(shí)際上標(biāo)準(zhǔn)庫函數(shù)、擴(kuò)展庫函數(shù)也是自定義函數(shù)的一種,只不過是別人寫好的我們直接使用就可以了。7.1.1 基本語法def 函數(shù)名(形參列表): 注釋 函數(shù)體7.1.1 基本語法 定義函數(shù)時(shí)需要注意的問題主要有:1)函數(shù)名和形參名建議使用“見名知義”的英文單詞或單詞組合;

3、2)不需要說明形參類型,調(diào)用函數(shù)時(shí)Python解釋器會(huì)根據(jù)實(shí)參的值自動(dòng)推斷和確定形參類型;3)不需要指定函數(shù)返回值類型,這由函數(shù)中return語句返回的值來確定;4)上面的語法中方括號表示其中的參數(shù)列表可有可無,即使該函數(shù)不需要接收任何參數(shù),也必須保留一對空的圓括號,如果需要接收多個(gè)形式參數(shù)應(yīng)使用逗號分隔相鄰的參數(shù);7.1.1 基本語法5)函數(shù)頭部括號后面的冒號必不可少;6)函數(shù)體相對于def關(guān)鍵字必須保持一定的空格縮進(jìn),函數(shù)體內(nèi)部的代碼縮進(jìn)與前面章節(jié)學(xué)過的選擇結(jié)構(gòu)、循環(huán)結(jié)構(gòu)、異常處理結(jié)構(gòu)以及第8章要學(xué)的with語句具有相同的要求;7)函數(shù)體前面三引號和里面的注釋可以不寫,但最好寫上,用簡短語

4、言描述函數(shù)功能,使得接口更加友好;8)在函數(shù)體中使用return語句指定返回值,如果函數(shù)沒有return語句、有return語句但是沒有執(zhí)行到或者有return也執(zhí)行到了但是沒有返回任何值,Python都認(rèn)為返回的是空值None。7.1.1 基本語法 例例7-1 編寫函數(shù),接收一個(gè)大于0的整數(shù)或?qū)崝?shù)r表示圓的半徑,返回一個(gè)包含圓的周長與面積的元組,小數(shù)位數(shù)最多保留3位。然后編寫程序,調(diào)用剛剛定義的函數(shù)。7.1.1 基本語法from math import pidef get_area(r): 接收圓的半徑為參數(shù),返回包含周長和面積的元組 return (round(2*pi*r,3), rou

5、nd(pi*r*r,3)r = input(請輸入圓的半徑:)try: r = float(r) assert r0except: print(必須輸入大于0的整數(shù)或?qū)崝?shù))else: print(get_area(r)7.1.1 基本語法7.1.2 遞歸函數(shù)定義與調(diào)用 如果一個(gè)函數(shù)在執(zhí)行過程中特定條件下又調(diào)用了這個(gè)函數(shù)自己,叫作遞歸調(diào)用。 函數(shù)遞歸是遞歸算法的實(shí)現(xiàn),也可以理解為一種特殊的循環(huán),用來把一個(gè)大型的復(fù)雜問題層層轉(zhuǎn)化為一個(gè)與原來問題本質(zhì)相同但規(guī)模更小、更容易解決或描述的問題,只需要很少的代碼就可以描述解決問題過程中需要的大量重復(fù)計(jì)算。7.1.2 遞歸函數(shù)定義與調(diào)用 在編寫遞歸函數(shù)時(shí),應(yīng)

6、注意以下幾點(diǎn): 每次遞歸應(yīng)保持問題性質(zhì)不變; 每次遞歸應(yīng)使得問題規(guī)模變小或使用更簡單的輸入; 必須有一個(gè)能夠直接處理而不需要再次進(jìn)行遞歸的特殊情況來保證遞歸過程可以結(jié)束; 函數(shù)遞歸深度不能太大,否則會(huì)引起內(nèi)存崩潰。7.1.2 遞歸函數(shù)定義與調(diào)用 例例7-2 已知正整數(shù)的階乘計(jì)算公式為 ,并且已知1的階乘為1,也就是 。編寫遞歸函數(shù),接收一個(gè)正整數(shù)n,計(jì)算并返回n的階乘。123)2() 1()!1(!nnnnnn1! 1 7.1.2 遞歸函數(shù)定義與調(diào)用def fac(n): # 1的階乘為1,這是保證遞歸可以結(jié)束的條件 if n = 1: # 如果執(zhí)行到這個(gè)return語句,函數(shù)直接結(jié)束,不會(huì)再

7、執(zhí)行后面的代碼 return 1 # 遞歸調(diào)用函數(shù)自己,但使用更小的輸入,使得遞歸過程可以結(jié)束 return n * fac(n-1)# 調(diào)用函數(shù),計(jì)算并輸出5的階乘print(fac(5)7.1.3 函數(shù)嵌套定義 在Python中,允許函數(shù)的嵌套定義,也就是在一個(gè)函數(shù)的定義中再定義另一個(gè)函數(shù)。在內(nèi)層定義的函數(shù)中,除了可以使用內(nèi)層函數(shù)內(nèi)定義的變量,還可以訪問外層函數(shù)的參數(shù)和外層函數(shù)定義的變量以及全局變量和內(nèi)置對象。 除非特別必要,一般不建議過多使用嵌套定義函數(shù),因?yàn)槊看握{(diào)用外部函數(shù)時(shí),都會(huì)重新定義內(nèi)層函數(shù),運(yùn)行效率較低。7.1.3 函數(shù)嵌套定義 嵌套定義函數(shù)時(shí),外層函數(shù)使用內(nèi)層函數(shù)的形式有兩種

8、:一種是調(diào)用內(nèi)層函數(shù)并使用或返回內(nèi)層函數(shù)的返回值,另一種是返回內(nèi)層函數(shù)對象。在第二種形式中,外層函數(shù)返回內(nèi)層函數(shù)對象,也就是說外層函數(shù)的返回值又可以像函數(shù)一樣進(jìn)行調(diào)用并傳入?yún)?shù)。7.1.3 函數(shù)嵌套定義def outer1(a, b): # 定義內(nèi)層函數(shù) def inner(x): return x*(a+b) # 在外層函數(shù)中調(diào)用內(nèi)層函數(shù),返回內(nèi)層函數(shù)的返回值 return inner(3)print(outer1(3, 5)print(outer1(3, 6)7.1.3 函數(shù)嵌套定義def outer2(a, b): def inner(x): return x*(a+b) # 在外層函數(shù)

9、中沒有調(diào)用內(nèi)層函數(shù),而是返回內(nèi)層函數(shù)對象 return inner# 外層函數(shù)的返回值可以像函數(shù)一樣被調(diào)用print(outer2(3,5)(3)print(outer2(5,8)(0.5)7.2 函數(shù)參數(shù) 函數(shù)定義時(shí)圓括號內(nèi)是使用逗號分隔開的形參列表,函數(shù)可以有多個(gè)參數(shù),也可以沒有參數(shù),但定義和調(diào)用時(shí)必須要有一對圓括號,表示這是一個(gè)函數(shù)。調(diào)用函數(shù)時(shí)向其傳遞實(shí)參,將實(shí)參的引用傳遞給形參,在完成函數(shù)調(diào)用進(jìn)入函數(shù)內(nèi)部的瞬間,形參和實(shí)參引用的是同一個(gè)對象。7.2 函數(shù)參數(shù) 在函數(shù)內(nèi)部,形參相當(dāng)于局部變量。由于Python中變量存儲(chǔ)的是值的引用,直接修改形參的值實(shí)際上是修改了形參變量的引用,不會(huì)對實(shí)參

10、造成影響。但如果傳遞過來的實(shí)參是列表、字典、集合或其他類型的可變對象,在函數(shù)內(nèi)通過形參是有可能會(huì)影響實(shí)參的,這取決于函數(shù)內(nèi)使用形參的方式。7.2 函數(shù)參數(shù)def demo(num): # 剛剛進(jìn)入函數(shù)時(shí),形參與實(shí)參引用相同的對象 result = num # 內(nèi)置函數(shù)id()用來查看對象的內(nèi)存地址,不用過多關(guān)心 # 這里重點(diǎn)關(guān)心的是變量result、num的內(nèi)存地址與函數(shù)外的變量num相同 print(id(num), id(result) while num 1: # 每次執(zhí)行都會(huì)修改變量num和result的引用 num = num - 1 result = result * num re

11、turn resultnum = 5print(num, id(num)# 調(diào)用函數(shù),傳遞實(shí)參print(demo(num)# 原來的實(shí)參變量沒有受任何影響,內(nèi)存地址不變print(num, id(num)7.2 函數(shù)參數(shù) 如果調(diào)用函數(shù)時(shí)傳遞的是列表、字典、集合這樣的可變對象,函數(shù)內(nèi)部的代碼是否會(huì)影響實(shí)參的值要分兩種情況:1)如果在函數(shù)內(nèi)部像上面的代碼一樣直接修改形參的引用,仍不會(huì)影響實(shí)參;2)如果在函數(shù)內(nèi)部使用下標(biāo)的形式或者調(diào)用對象自身提供的原地操作方法,例如列表的append()、insert()、pop()等方法,或者集合的add()、discard()等方法,代碼執(zhí)行結(jié)果是反映到函數(shù)外

12、的實(shí)參對象的,也就是會(huì)影響實(shí)參。7.2 函數(shù)參數(shù)def demo(test_list, test_dict, test_set): # 在列表尾部追加元素 test_list.append(666) # 在列表開始位置插入元素 test_list.insert(0, 666) # 如果字典中有“鍵”為name的元素就修改對應(yīng)的“值”,否則插入新元素 test_dictname = xiaoming # 如果集合中沒有元素666就放進(jìn)去,如果已經(jīng)存在就忽略 test_set.add(666)data_list = 1, 2, 3data_dict = name: xiaohong, age: 2

13、3data_set = 1, 2, 3demo(data_list, data_dict, data_set)print(data_list, data_dict, data_set, sep=n)7.2.1 位置參數(shù)def func(a, b, c): return sum(a,b,c)print(func(1,2,3)print(func(4,5,6)7.2.1 位置參數(shù) 在Python 3.8之前的版本中,不允許在自定義函數(shù)中聲明參數(shù)必須使用位置參數(shù)的形式進(jìn)行傳遞。在Python 3.8以及更新的版本中,允許在定義函數(shù)時(shí)設(shè)置一個(gè)斜線“/”作為參數(shù),斜線“/”本身并不是真正的參數(shù),僅用來說

14、明該位置之前的所有參數(shù)必須以位置參數(shù)的形式進(jìn)行傳遞。7.2.1 位置參數(shù) def func(a, b, c, /): return sum(a,b,c) func(3, 5, 7)15 func(3, 5, c=7)Traceback (most recent call last): File , line 1, in func(3,5,c=7)TypeError: func() got some positional-only arguments passed as keyword arguments: c7.2.2 默認(rèn)值參數(shù) Python支持默認(rèn)值參數(shù),在定義函數(shù)時(shí)可以為形參設(shè)置默認(rèn)值。

15、調(diào)用帶有默認(rèn)值參數(shù)的函數(shù)時(shí),可以不用為設(shè)置了默認(rèn)值的形參進(jìn)行傳遞實(shí)參,此時(shí)函數(shù)將會(huì)直接使用函數(shù)定義時(shí)設(shè)置的默認(rèn)值,當(dāng)然也可以通過顯式傳遞實(shí)參來替換其默認(rèn)值。7.2.2 默認(rèn)值參數(shù) 在定義帶有默認(rèn)值參數(shù)的函數(shù)時(shí),任何一個(gè)默認(rèn)值參數(shù)右邊都不能再出現(xiàn)沒有默認(rèn)值的普通位置參數(shù),否則會(huì)拋出SyntaxError異常并提示“non-default argument follows default argument”。def 函數(shù)名(,形參名=默認(rèn)值): 函數(shù)體7.2.2 默認(rèn)值參數(shù)def func(message, times=3): return message*timesprint(func(重要的事

16、情說三遍!)print(func(不重要的事情只說一遍!, 1)print(func(特別重要的事情說五遍!, 5)7.2.2 默認(rèn)值參數(shù) 如果定義函數(shù)時(shí)需要為部分變量設(shè)置默認(rèn)值,一定要注意盡量使用整數(shù)、實(shí)數(shù)、復(fù)數(shù)、元組、字符串、空值None或True/False這樣的不可變對象,要避免使用列表、字典、集合這樣的可變對象作為參數(shù)的默認(rèn)值。函數(shù)參數(shù)默認(rèn)值是在定義函數(shù)時(shí)創(chuàng)建的對象,并且把默認(rèn)值的引用保存在函數(shù)的特殊成員“_defaults_”中,這是一個(gè)元組,里面保存了函數(shù)每個(gè)參數(shù)默認(rèn)值的引用,每次調(diào)用函數(shù)且不為帶默認(rèn)值的參數(shù)傳遞實(shí)參時(shí),都會(huì)使用特殊成員“_defaults_”里保存的引用。如果參

17、數(shù)默認(rèn)值是可變對象并且在函數(shù)內(nèi)部有使用下標(biāo)或?qū)ο笞陨淼脑夭僮鞣椒▽?shù)進(jìn)行操作的語句,會(huì)影響后續(xù)調(diào)用。7.2.2 默認(rèn)值參數(shù)from random import randrangedef func(x, y=3, z=): # y的默認(rèn)值是整數(shù),不可變對象 # 下面的語句不會(huì)影響原來的默認(rèn)值 y = 5 # z的默認(rèn)值是列表,可變對象 # 下面的語句會(huì)影響參數(shù)的默認(rèn)值 z.append(randrange(1000) print(x, y, z)# 不給默認(rèn)值參數(shù)傳遞實(shí)參,會(huì)影響參數(shù)z的默認(rèn)值print(func._defaults_, end=n=n)func(3)print(func._

18、defaults_, end=n=n)func(8)print(func._defaults_, end=n=n)# 顯式為z傳遞實(shí)參,不影響默認(rèn)值func(33, 55, 1,2)print(func._defaults_, end=n=n)func(66, 88, 3,4)print(func._defaults_, end=n=n)func(666)print(func._defaults_, end=n=n)7.2.2 默認(rèn)值參數(shù)7.2.3 關(guān)鍵參數(shù) 關(guān)鍵參數(shù)是指調(diào)用函數(shù)時(shí)按參數(shù)名字進(jìn)行傳遞的形式,明確指定哪個(gè)實(shí)參傳遞給哪個(gè)形參。通過這樣的調(diào)用方式,實(shí)參順序可以和形參順序不一致,但不影

19、響參數(shù)的傳遞結(jié)果,避免了用戶需要牢記參數(shù)位置和順序的麻煩,使得函數(shù)的調(diào)用和參數(shù)傳遞更加靈活方便。7.2.3 關(guān)鍵參數(shù)def func(a, b, c): return fa=,b=,c=print(func(a=3, c=5, b=8)print(func(c=5, a=3, b=8)7.2.3 關(guān)鍵參數(shù) 在Python 3.8之前的版本中,不允許自定義函數(shù)聲明某個(gè)或某些參數(shù)必須以關(guān)鍵參數(shù)的形式進(jìn)行傳遞。在Python 3.8以及更新的版本中,允許在自定義函數(shù)中使用單個(gè)星號“*”作為參數(shù),但單個(gè)星號并不是真正的參數(shù),僅用來說明該位置后面的所有參數(shù)必須以關(guān)鍵參數(shù)的形式進(jìn)行傳遞。7.2.3 關(guān)鍵參

20、數(shù) def func(a, *, b, c): return fa=,b=,c= print(func(3, 5, 8)TypeError: func() takes 1 positional argument but 3 were given print(func(3, b=5, c=8)a=3,b=5,c=8 print(func(3, c=5, b=8)a=3,b=8,c=5# 關(guān)鍵參數(shù)的參數(shù)名必須是在函數(shù)定義中存在的 print(func(3, b=4, c=5, d=6)TypeError: func() got an unexpected keyword argument d7.2

21、.4 可變長度參數(shù) 可變長度參數(shù)是指形參對應(yīng)的實(shí)參數(shù)量不確定,一個(gè)形參可以接收多個(gè)實(shí)參。在定義函數(shù)時(shí)主要有兩種形式:*parameter和*parameter,前者用來接收任意多個(gè)位置實(shí)參并將其放在一個(gè)元組中,后者接收任意多個(gè)關(guān)鍵參數(shù)并將其放入字典中。7.2.4 可變長度參數(shù)def demo(a, b, c, *p): print(a, b, c) print(p)demo(1, 2, 3, 4, 5, 6)print(=*10)demo(1, 2, 3, 4, 5, 6, 7, 8)7.2.4 可變長度參數(shù)def demo(*p): for item in p.items(): print(

22、item)demo(x=1, y=2, z=3)print(=*10)demo(a=4, b=5, c=6, d=7)7.3 變量作用域-7.3.1 變量作用域的分類 變量起作用的代碼范圍稱為變量的作用域,不同作用域內(nèi)變量名字可以相同,互不影響。從變量作用域或者搜索順序的角度來看,Python有局部變量、nonlocal變量、全局變量和內(nèi)置對象。 如果在函數(shù)內(nèi)只有引用某個(gè)變量值的操作而沒有為其賦值的操作,該變量默認(rèn)為全局變量、外層函數(shù)的變量或者內(nèi)置命名空間中的成員,如果都不是則會(huì)拋出異常并提示沒有定義。如果在函數(shù)內(nèi)有為變量賦值的操作,該變量就被認(rèn)為是局部變量,除非在函數(shù)內(nèi)賦值操作之前用關(guān)鍵字g

23、lobal或nonlocal進(jìn)行了聲明。7.3.1 變量作用域的分類 在Python中有兩種創(chuàng)建全局變量的方式:1)在函數(shù)外部使用賦值語句創(chuàng)建的變量默認(rèn)為全局變量,其作用域?yàn)閺亩x的位置開始一直到文件結(jié)束;2)在函數(shù)內(nèi)部使用關(guān)鍵字global聲明變量為全局變量,其作用域從該函數(shù)調(diào)用語句開始一直到文件結(jié)束。7.3.1 變量作用域的分類 Python關(guān)鍵字global有兩個(gè)作用:1)對于在函數(shù)外創(chuàng)建的全局變量,如果需要在函數(shù)內(nèi)修改這個(gè)變量的值,并要將這個(gè)結(jié)果反映到函數(shù)外,可以在函數(shù)內(nèi)使用關(guān)鍵字global聲明要使用這個(gè)全局變量。2)如果一個(gè)變量在函數(shù)外沒有定義,在函數(shù)內(nèi)部也可以直接將一個(gè)變量聲明為

24、全局變量,該函數(shù)執(zhí)行后,將增加一個(gè)新的全局變量。7.3.1 變量作用域的分類def demo(): global x # 聲明或創(chuàng)建全局變量,必須在使用變量x之前執(zhí)行該語句 x = 3 # 修改全局變量的值 y = 4 # 局部變量 print(x, y) # 使用變量x和y的值x = 5 # 在函數(shù)外部定義了全局變量xdemo() # 本次調(diào)用修改了全局變量x的值print(x)try: print(y)except: print(不存在變量y)del x # 刪除全局變量xtry: print(x)except: print(不存在變量x)demo() # 本次調(diào)用創(chuàng)建了全局變量print

25、(x)7.3.1 變量作用域的分類 除了局部變量和全局變量,Python還支持使用nonlocal關(guān)鍵字定義一種介于二者之間的變量。 關(guān)鍵字nonlocal聲明的變量一般用于嵌套函數(shù)定義的場合,會(huì)引用距離最近的非全局作用域的變量(例如,在嵌套函數(shù)定義的場合中,內(nèi)層函數(shù)可以把外層函數(shù)中的變量定義為nonlocal變量),要求聲明的變量已經(jīng)存在,關(guān)鍵字nonlocal不會(huì)創(chuàng)建新變量。7.3.1 變量作用域的分類def scope_test(): def do_local(): spam = 我是局部變量 def do_nonlocal(): nonlocal spam # 這時(shí)要求spam必須是已

26、存在的變量 spam = 我不是局部變量,也不是全局變量 def do_global(): global spam # 如果全局作用域內(nèi)沒有spam,自動(dòng)新建 spam = 我是全局變量 spam = 原來的值 do_local() print(局部變量賦值后:, spam) do_nonlocal() print(nonlocal變量賦值后:, spam) do_global() print(全局變量賦值后:, spam)scope_test()print(全局變量:, spam)7.3.2 作用域的搜索順序 在Python程序中試圖訪問一個(gè)變量時(shí),搜索順序遵循LEGB原則,也就是優(yōu)先在當(dāng)前

27、局部作用域(Local)中查找變量,如果能找到就使用;在局部作用域中找不到就繼續(xù)到閉包作用域(嵌套函數(shù)定義中的外層函數(shù),Enclosing)查找,如果能找到就使用;如果不存在外層函數(shù)或在外層作用域中仍不存在該變量就嘗試到全局作用域(Global)中查找,如果找到就使用;如果全局作用域中仍不存在該變量就嘗試到內(nèi)置命名空間或內(nèi)置作用域(Builtin)查找,如果找到就使用,仍不存在就拋出NameError異常提示標(biāo)識符沒有定義。7.3.2 作用域的搜索順序x = 3def outer(): y = 5 # 這個(gè)自定義函數(shù)和內(nèi)置函數(shù)名字相同 # 在當(dāng)前作用域和更內(nèi)層作用域中影響內(nèi)置函數(shù)map()的正

28、常使用 def map(): return 我是假的map()函數(shù) def inner(): x = 7 y = 9 # 最內(nèi)層的作用域內(nèi),局部變量(Local)x,y優(yōu)先被訪問 # 在局部作用域、閉包作用域、全局作用域內(nèi)都不存在函數(shù)max, # 最后在內(nèi)置作用域(Builtin)內(nèi)搜索到函數(shù)max # 當(dāng)前作用域中不存在map,但在外層的閉包作用域內(nèi)搜索到了, # 所以并沒有調(diào)用內(nèi)置函數(shù)map,被攔截了 print(inner:, x, y, max(x,y), map() inner() # 在當(dāng)前作用域(閉包,Enclosing)內(nèi),y可以直接訪問 # 在當(dāng)前作用域內(nèi)不存在x,繼續(xù)到全局

29、作用域(Global)去搜索 # 當(dāng)前作用域內(nèi)不存在函數(shù)max,外層全局作用域也不存在, # 最后在內(nèi)置作用域(Builtin)內(nèi)搜索到函數(shù)max # 當(dāng)前作用域中有個(gè)map,直接調(diào)用了,沒有調(diào)用內(nèi)置函數(shù)map() print(outer:, x, y, max(x,y), map()outer()# 在當(dāng)前作用域中沒有map()函數(shù)的定義,在內(nèi)置作用域中搜索到函數(shù)并調(diào)用print(list(map(str, range(5)# 當(dāng)前作用域中有x,可以直接訪問,但不存在y# 由于當(dāng)前處于全局作用域,按Python變量搜索順序,繼續(xù)在內(nèi)置作用域搜索# 不會(huì)去搜索Enclosing和Local作用

30、域,但在內(nèi)置作用域內(nèi)也不存在y,# 所以代碼引發(fā)異常print(outside:, x, y, max(x,y)7.3.3 變量的可見性 和作用域比較接近的還有個(gè)概念是變量的可見性,是指變量可以被使用的代碼范圍。 正常來講,一個(gè)變量自定義開始一直到所在函數(shù)結(jié)束或所在程序文件結(jié)束之前都是可以使用的,是可見的,除非在更內(nèi)層的作用域內(nèi)被同名變量暫時(shí)隱藏,也就是說作用域可見性。7.4 lambda表達(dá)式語法與應(yīng)用 lambda表達(dá)式常用來聲明匿名函數(shù),也就是沒有名字的、臨時(shí)使用的小函數(shù),雖然也可以使用lambda表達(dá)式定義具名函數(shù),但很少這樣使用。lambda表達(dá)式只能包含一個(gè)表達(dá)式,表達(dá)式的計(jì)算結(jié)果

31、相當(dāng)于函數(shù)的返回值。lambda表達(dá)式的語法如下:lambda 形參列表: 表達(dá)式7.4 lambda表達(dá)式語法與應(yīng)用 下面代碼中的函數(shù)func和lambda表達(dá)式func在功能上是完全等價(jià)的。def func(a, b, c): return sum(a,b,c)func = lambda a,b,c: sum(a,b,c)7.4 lambda表達(dá)式語法與應(yīng)用 lambda表達(dá)式常用在臨時(shí)需要一個(gè)函數(shù)的功能但又不想定義函數(shù)的場合,例如內(nèi)置函數(shù)sorted(iterable, /, *, key=None, reverse=False)、max(iterable, *, default=obj

32、, key=func)、min(iterable, *, default=obj, key=func)和列表方法sort(*, key=None, reverse=False)的key參數(shù),內(nèi)置函數(shù)map(func, *iterables)、filter(function or None, iterable)以及標(biāo)準(zhǔn)庫函數(shù)functools.reduce(function, sequence, initial)的第一個(gè)參數(shù)。 lambda表達(dá)式是Python函數(shù)式編程的重要體現(xiàn)。7.4 lambda表達(dá)式語法與應(yīng)用from random import samplefrom functools

33、import reduce# 生成隨機(jī)數(shù)據(jù),包含5個(gè)子列表,每個(gè)子列表中包含10個(gè)整數(shù)# 每個(gè)整數(shù)介于0,20)區(qū)間,同一個(gè)子列表中的整數(shù)不重復(fù)data = sample(range(20), 10) for i in range(5)# 按子列表的原始順序輸出,每個(gè)子列表占一行print(*data, sep=n, end=n=n)# 按子列表從小到大順序輸出,每個(gè)子列表占一行# 列表比較大小的規(guī)則見2.2.2節(jié)print(*sorted(data), sep=n, end=n=n)7.4 lambda表達(dá)式語法與應(yīng)用# 按每個(gè)子列表中第2個(gè)元素升序輸出print(*sorted(data,

34、 key=lambda row:row1), sep=n, end=n=n)# 按每個(gè)子列表第2個(gè)元素升序輸出,如果第2個(gè)元素相等再按第4個(gè)升序輸出print(*sorted(data, key=lambda row:(row1,row3), sep=n, end=n=n)# 第一個(gè)子列表中所有元素連乘的結(jié)果print(reduce(lambda x,y:x*y, data0), end=n=n)# 第二個(gè)子列表中所有元素連乘的結(jié)果print(reduce(lambda x,y:x*y, data1), end=n=n)# 每個(gè)子列表的第一個(gè)元素組成的新列表print(list(map(lam

35、bda row:row0, data), end=n=n)7.4 lambda表達(dá)式語法與應(yīng)用# 對角線元素組成的列表# 注意,如果行數(shù)大于列數(shù),代碼會(huì)出錯(cuò)并提示下標(biāo)越界print(list(map(lambda row:rowdata.index(row), data), end=n=n)# 最后一個(gè)元素最大的子列表print(max(data, key=lambda row:row-1), end=n=n)# 所有元素之和為偶數(shù)的子列表print(*filter(lambda row:sum(row)%2=0, data), sep=n, end=n=n)# 所有元素之和小于等于80的子列

36、表print(*filter(lambda row:sum(row) 100: # 元素大于100時(shí)結(jié)束循環(huán) print(i, end= ) breakprint()7.5 生成器函數(shù)定義與使用def func(): yield from abcdefg # 使用yield表達(dá)式創(chuàng)建生成器gen = func()print(next(gen) # 使用內(nèi)置函數(shù)next()獲取下一個(gè)元素print(next(gen)for item in gen: # 遍歷剩余的所有元素 print(item)7.5 生成器函數(shù)定義與使用def gen(): yield 1 yield 2 yield 3x,

37、y, z = gen() # 生成器對象支持序列解包print(x, y, z)print(*gen() # 這也是序列解包的用法7.5 生成器函數(shù)定義與使用 例例7-3 編寫生成器函數(shù),模擬標(biāo)準(zhǔn)庫itertools中count()函數(shù)工作原理。標(biāo)準(zhǔn)庫itertools中的count(start=0, step=1)函數(shù)可以實(shí)現(xiàn)從指定的數(shù)字開始以指定的步長進(jìn)行無限計(jì)數(shù),該函數(shù)返回值為count對象,也屬于迭代器對象。在程序中,使用while循環(huán)實(shí)現(xiàn)無限次重復(fù),每次循環(huán)使用yield語句產(chǎn)生一個(gè)值。7.5 生成器函數(shù)定義與使用def myCount(start=0, step=1): while

38、 True: yield start start = start + stepc = myCount(48, 7)for _ in range(10): print(next(c), end=,)print(next(c)7.6 修飾器函數(shù)定義與使用 修飾器是函數(shù)嵌套定義的一個(gè)重要應(yīng)用。 修飾器本質(zhì)上也是一個(gè)函數(shù),只不過這個(gè)函數(shù)接收其他函數(shù)作為參數(shù)并對其進(jìn)行一定改造之后返回新函數(shù),可以用來對其他函數(shù)進(jìn)行統(tǒng)一的補(bǔ)充或者功能的修改,也可以用來改變一個(gè)函數(shù)的表現(xiàn)形式。 修飾器函數(shù)返回的是內(nèi)部定義的函數(shù),外層函數(shù)中的代碼并沒有調(diào)用內(nèi)層函數(shù)。7.6 修飾器函數(shù)定義與使用 Python標(biāo)準(zhǔn)庫函數(shù)funct

39、ools.partial()也可以看作一個(gè)修飾器函數(shù),可以用來根據(jù)一個(gè)已有函數(shù)來固定其部分功能得到一個(gè)新的可調(diào)用對象(往往稱作偏函數(shù))。 標(biāo)準(zhǔn)庫函數(shù)functools.lru_cache()也是一個(gè)很常用的修飾器函數(shù),可以用來為函數(shù)增加緩沖區(qū)從而避免一些重復(fù)計(jì)算。7.6 修飾器函數(shù)定義與使用from functools import partialdef func(a, b, c): return a+b+c# 創(chuàng)建偏函數(shù),固定其中部分參數(shù)func_ab = partial(func, c=0)func_ac = partial(func, b=0)func_bc = partial(func

40、, a=0)# 調(diào)用前面創(chuàng)建的偏函數(shù)print(func(1, 2, 3)print(func_ab(4, 5)print(func_ac(6, c=7)print(func_bc(b=8, c=9)7.6 修飾器函數(shù)定義與使用 例例7-4 編寫函數(shù)模擬登錄不同系統(tǒng)的原理與過程,然后創(chuàng)建修飾器函數(shù),根據(jù)原始的登錄函數(shù)進(jìn)行改造快速創(chuàng)建不同服務(wù)器的快速登錄入口,使得登錄時(shí)不需要指定登錄哪個(gè)服務(wù)器。7.6 修飾器函數(shù)定義與使用users = zhangsan:111, lisi:222, wangwu:333, zhaoliu:444servers = (server1, server2, serv

41、er3)# 完整的登錄函數(shù),可以檢查任意用戶對任意服務(wù)器的登錄def login(username, password, server): if password=users.get(username) and server in servers: print(fusername 您好,歡迎來到server!) else: print(用戶名或密碼錯(cuò)誤)7.6 修飾器函數(shù)定義與使用# 創(chuàng)建修飾器函數(shù)def partial(func, *args1, *kwargs1): # 內(nèi)層函數(shù)wrapped()的參數(shù)args是被修飾的函數(shù)func()接收的位置參數(shù) # 內(nèi)層函數(shù)wrapped()的參數(shù)k

42、wargs是被修飾的函數(shù)func()接收的關(guān)鍵參數(shù) def wrapped(*args, *kwargs): return func(*args1, *args, *kwargs,*kwargs1) return wrapped7.6 修飾器函數(shù)定義與使用# 使用修飾器函數(shù),根據(jù)完整的登錄函數(shù),創(chuàng)建適用于不同服務(wù)器的函數(shù)# 相當(dāng)于創(chuàng)建不同服務(wù)器的快速登錄入口login_server1 = partial(login, server=server1)login_server2 = partial(login, server=server2)login_server3 = partial(logi

43、n, server=server3)login_server1(zhangsan, 111)login_server2(zhangsan, 111)login_server2(zhangsan, 111)login_server3(wangwu, 333)login_server3(wangwu, 345)7.6 修飾器函數(shù)定義與使用 例例7-5 定義兩個(gè)修飾器函數(shù)before()和after(),分別在被修飾的函數(shù)功能代碼前后多輸出一句話,然后使用這兩個(gè)修飾器函數(shù)去修飾其他的函數(shù),驗(yàn)證修飾器函數(shù)的功能。code例7-5.py7.7 綜合例題解析 例例7-6 編寫遞歸函數(shù),判斷給定的字符串te

44、xt是否為回文,也就是從前向后讀和從后向前讀都是一樣的字符串。7.7 綜合例題解析def check(text): # 遞歸結(jié)束條件,長度為0或1的字符串是回文 if len(text) in (0,1): return True # 遞歸結(jié)束條件,首尾字符不相等的字符串不是回文 if text0 != text-1: return False return check(text1:-1)texts = (eye, rotator, madam, level, indeed, sky, python, 畫中畫, 天外天, 拜拜, 您吃了嗎, 上海自來水來自海上, 霧鎖山頭山鎖霧)for tex

45、t in texts: print(ftext:check(text)7.7 綜合例題解析 例例7-7 編寫函數(shù),模擬猜數(shù)游戲。系統(tǒng)隨機(jī)在參數(shù)指定的范圍內(nèi)產(chǎn)生一個(gè)數(shù),玩家最大猜測次數(shù)也由參數(shù)指定,每次猜測之后系統(tǒng)會(huì)根據(jù)玩家的猜測進(jìn)行提示,玩家則可以根據(jù)系統(tǒng)的提示對下一次的猜測進(jìn)行適當(dāng)調(diào)整,直到猜對或者次數(shù)用完。7.7 綜合例題解析 例例7-8 編寫函數(shù),計(jì)算形式如a + aa + aaa + aaaa + . + aaa.aaa的表達(dá)式前n項(xiàng)的值,其中a為小于10的自然數(shù)。7.7 綜合例題解析def demo(a, n): assert type(a)=int and 0a1, 參數(shù)n必須為正

46、整數(shù) result, t = 0, 0 for i in range(n): t = t*10 + a result = result + t return resultprint(demo(1, 9)print(demo(6, 8)7.7 綜合例題解析 例例7-9 編寫函數(shù),接收一個(gè)正偶數(shù)為參數(shù),輸出兩個(gè)素?cái)?shù),并且這兩個(gè)素?cái)?shù)之和等于原來的正偶數(shù)。如果存在多組符合條件的素?cái)?shù),則全部輸出。7.7 綜合例題解析def demo(n): if not (isinstance(n,int) and n0 and n%2=0): return 數(shù)據(jù)不合適 def isPrime(p): if p in (2,3,5): return True # 對于大于6的素?cái)?shù),對6的余數(shù)必然是1或5 if p%6 not in (1,5): return False # 但對6的余數(shù)為1或5的正整數(shù)不一定都是素?cái)?shù),需要進(jìn)一步判斷 m = int(p*0.5) + 1 for i in ran

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論