
簡介本資源是南開大學(xué)軟件學(xué)院Python課程期末復(fù)習(xí)的系統(tǒng)性整理筆記面向計算機相關(guān)專業(yè)本科生及Python初學(xué)者聚焦核心考點與易錯難點助力高效備考與知識體系構(gòu)建。內(nèi)容覆蓋數(shù)據(jù)結(jié)構(gòu)字典、集合、元組、列表的特性與操作、內(nèi)存管理引用機制、淺拷貝與深拷貝、面向?qū)ο缶幊填惻c實例屬性、私有成員、classmethod/staticmethod裝飾器、C3算法下的MRO繼承順序、魔法函數(shù)init、str、__dict__等、函數(shù)進階*args/**kwargs、默認參數(shù)陷阱、生成器與三器一魔以及正則表達式等關(guān)鍵模塊知識點講解結(jié)合典型代碼示例與運行結(jié)果分析。資源為1個5.28MB的DOCX文檔結(jié)構(gòu)清晰、排版規(guī)范含大量對比表格、內(nèi)存圖示與語法速查片段便于復(fù)習(xí)查閱與重點標(biāo)注。目前已有1200人學(xué)習(xí)下載是兼顧理論深度與應(yīng)試實用性的高質(zhì)量期末沖刺資料。1. Python期末復(fù)習(xí)不是背題庫而是搞懂“Python怎么想問題”一份南開軟院學(xué)生親測有效的入門路徑你是不是也這樣翻開《Python期末復(fù)習(xí)超全》這個標(biāo)題第一反應(yīng)是——“又來一套題背完就忘考完就丟”別急這份材料根本不是傳統(tǒng)意義上的“題庫”它是一份用真實課堂節(jié)奏打磨出來的思維導(dǎo)圖式復(fù)習(xí)指南。它不從“print(Hello World)”開始教而是直接帶你站在南開軟件學(xué)院老師出題的視角看為什么字典和集合必須無序為什么元組能當(dāng)字典的key而列表不能為什么list.sort()沒有返回值但sorted()卻有這些問題背后不是規(guī)則而是Python對內(nèi)存、對象、引用的真實處理邏輯。它適合誰適合那個剛學(xué)完循環(huán)和if語句、一看到“MRO算法”就頭皮發(fā)麻、抄了十遍super()還是不知道它在找誰的你也適合那個已經(jīng)會寫爬蟲但總被面試官問“a [1,2]; b a; b.append(3)之后a變沒變”給問懵的新手。這不是讓你“多做一道題”而是幫你把散落的知識點焊進一個叫“Python世界觀”的底座里——底座穩(wěn)了新代碼來了你第一反應(yīng)不再是“這行報錯我搜過但看不懂”而是“哦它在找這個對象的__enter__方法”。2. 先搞懂一個概念Python里“無序”不是亂序而是“不靠位置說話”字典與集合的本質(zhì)區(qū)別很多新手卡在第一步為什么老師反復(fù)強調(diào)“字典和集合是無序的”可我print(dict)明明看到鍵值對排得整整齊齊這里藏著一個關(guān)鍵誤會——“無序”不是指打印出來不整齊而是指Python不保證、也不依賴元素的物理存儲順序來定義它們的身份和行為。我們拆開來看。2.1 字典靠“鍵”查不靠“第幾個”想象你有一本紙質(zhì)通訊錄里面按人名拼音排序。你找“張三”翻到P頁找“李四”翻到L頁。你從不關(guān)心“張三”是第37頁、“李四”是第21頁——你只認名字。字典就是這本通訊錄的電子版。它的核心能力是O(1)平均時間復(fù)雜度的查找靠的是哈希表Hash Table當(dāng)你寫my_dict[name]Python立刻把name算出一個數(shù)字哈希值跳到內(nèi)存里對應(yīng)的位置取值完全跳過了“從頭數(shù)第幾個”的過程。所以哪怕你先my_dict[z] 100再my_dict[a] 1它內(nèi)部存儲順序可能反著來但你調(diào)用my_dict[a]永遠秒出結(jié)果。這就是“無序”的價值犧牲順序換取速度。# 看似有序?qū)崉t偶然 student_grades {math: 85, english: 92, science: 78} print(student_grades) # 可能輸出 {math: 85, english: 92, science: 78} # 但這順序不是語言保證的Python 3.7雖保持插入順序為兼容性加的優(yōu)化但邏輯上它仍是“無序結(jié)構(gòu)” # 你絕不能寫first_subject list(student_grades.keys())[0] // 這是危險的依賴提示list(dict.keys())得到的列表順序在Python 3.7中確實是插入順序但這屬于實現(xiàn)細節(jié)不是語言規(guī)范??荚嚮蛎嬖囍腥艨疾臁白值涫欠裼行颉睒?biāo)準(zhǔn)答案仍是“邏輯上無序”因為它的設(shè)計目標(biāo)從來不是維護順序而是高效查找。2.2 集合去重的終極形態(tài)連“位置”都不給你留集合更進一步。它連“鍵”都不需要只要求元素唯一且可哈希。它的底層也是哈希表但只存“鍵”沒有“值”。所以當(dāng)你寫my_set {1, 2, 3}Python做的不是“記住1在前、2在中、3在后”而是“記住內(nèi)存里有一個位置存著1一個位置存著2一個位置存著3”。你無法用索引my_set[0]訪問因為它壓根沒定義“第0個”是誰。它的所有操作——add(),remove(),in判斷——都基于哈希查找時間復(fù)雜度同樣是O(1)。# 創(chuàng)建空集合必須用 set(){} 是字典這是新手高頻翻車點 empty_set set() # ? 正確創(chuàng)建空集合 empty_dict {} # ? 正確創(chuàng)建空字典 # wrong_set {} # ? 錯誤這創(chuàng)建的是字典不是集合 # 集合自動去重且不記錄順序 nums [1, 2, 2, 3, 1, 4] unique_nums set(nums) print(unique_nums) # 輸出可能是 {1, 2, 3, 4} 或 {4, 1, 2, 3}順序不重要 # 檢查是否存在快如閃電 print(2 in unique_nums) # TrueO(1)時間2.3 對比列表為什么列表能索引而字典/集合不能列表是“數(shù)組”的Python化身。它在內(nèi)存里是一塊連續(xù)的空間每個位置存一個元素的引用ID。所以my_list[0]就是“去內(nèi)存地址A取第一個引用”my_list[1]就是“去地址A8字節(jié)取第二個引用”。這種結(jié)構(gòu)天生支持索引和切片但插入/刪除中間元素要移動后面所有元素時間復(fù)雜度O(n)。特性列表 (list)字典 (dict)集合 (set)是否有序有序位置即身份邏輯無序鍵即身份邏輯無序元素即身份是否可索引?list[0]?dict[0]報錯?set[0]報錯是否可重復(fù)? 允許重復(fù)元素? key不能重復(fù)值可重復(fù)? 元素絕對唯一核心優(yōu)勢快速按位置訪問、切片快速按鍵查找、插入、刪除快速去重、成員判斷、集合運算2.4 常見問題排查為什么我的字典“看起來有序”卻報錯現(xiàn)象1我用for k in my_dict:遍歷輸出順序和我插入的一樣但老師說字典無序我該信誰原因Python 3.7為提升用戶體驗?zāi)J保持插入順序CPython實現(xiàn)細節(jié)但這不改變其“無序數(shù)據(jù)結(jié)構(gòu)”的本質(zhì)定義??荚囍腥魡枴白值涫欠裼行颉贝稹斑壿嬌蠠o序”才得分。依賴順序?qū)懘a如取list(dict.keys())[0]在舊版本Python或某些解釋器下會崩潰。解決所有需要確定順序的場景顯式轉(zhuǎn)換sorted(dict.keys())按字母序或list(dict.items())按插入序僅限3.7且不推薦依賴?,F(xiàn)象2my_set {1, 2, 3}; print(my_set[0])報錯TypeError: set object is not subscriptable。原因集合不支持索引操作符[]因為它沒有“第0個”的概念。它的設(shè)計哲學(xué)是“只問‘有沒有’不問‘在哪兒’”。解決若需按順序處理先轉(zhuǎn)成列表for item in sorted(my_set):或list(my_set)[0]但后者順序不保證慎用。現(xiàn)象3my_dict {a: 1, b: 2}; my_dict[c]報錯KeyError但my_dict.get(c)返回None。原因[]是“強硬訪問”找不到就炸.get()是“禮貌詢問”找不到就給個默認答案默認None。這是Python“請求寬恕而非許可”EAFP哲學(xué)的體現(xiàn)。解決處理可能不存在的key時優(yōu)先用.get(key, default_value)避免try/except過度使用?,F(xiàn)象4my_list [1, 2, 3]; my_list.append([4, 5])后my_list變成[1, 2, 3, [4, 5]]但我想把4和5“加進去”而不是“加一個列表”。原因.append()是“加一個元素”.extend()才是“加多個元素”。新手?;煜@兩個方法。解決記口訣“append是單個extend是展開”。要加多個用my_list.extend([4, 5])結(jié)果是[1, 2, 3, 4, 5]。3. 別再死記硬背了理解“可變”與“不可變”才能真正看懂Python的內(nèi)存“元組不可變列表可變”——這句話你背過但當(dāng)a (1, 2); b a; b (3,)后a還是(1, 2)而a [1, 2]; b a; b [3]后a卻變成了[1, 2, 3]你是不是瞬間懵了這背后不是語法玄學(xué)而是Python對對象身份identity和對象值value的嚴格區(qū)分。搞懂這個你才算真正踏入Python的大門。3.1 一切皆對象三個身份證缺一不可Python里每個東西都是對象它有三張“身份證”標(biāo)識identity對象在內(nèi)存中的唯一地址用id()函數(shù)查看。就像你的身份證號一生不變。類型type對象的種類用type()查看。比如int,str,list。值value對象當(dāng)前攜帶的數(shù)據(jù)。比如數(shù)字5字符串hello。關(guān)鍵來了變量如a,b不是盒子而是貼在對象身上的標(biāo)簽。a 5的意思是“把標(biāo)簽a貼到內(nèi)存里那個值為5的對象上”。b a的意思是“把另一個標(biāo)簽b也貼到同一個對象上”。所以a和b指向同一個id。# 不可變對象數(shù)字 x 10 y x print(id(x) id(y)) # Truex和y指向同一個整數(shù)對象 x x 1 # 注意這不是修改10而是讓x指向一個新的對象11 print(x) # 11 print(y) # 10y依然指著原來的10 print(id(x) id(y)) # False現(xiàn)在x和y指向不同對象了3.2 可變對象標(biāo)簽貼在“容器”上容器里的東西可以換列表、字典、集合是“容器型”對象。a [1, 2]的意思是在內(nèi)存里造一個“箱子”列表對象把1和2放進去再把標(biāo)簽a貼在這個箱子上。b a是把另一個標(biāo)簽b也貼到同一個箱子上。此時通過a或b去修改箱子里的東西比如a.append(3)箱子本身沒換只是里面多了個3所以b看到的也是[1, 2, 3]。# 可變對象列表 a [1, 2] b a # b和a指向同一個列表對象 print(id(a) id(b)) # True a.append(3) # 修改箱子往里面加一個3 print(a) # [1, 2, 3] print(b) # [1, 2, 3]b看到的也是修改后的箱子 # 關(guān)鍵對比 對可變對象是原地修改對不可變對象是創(chuàng)建新對象 c [1, 2] d c c [3] # 等價于 c.extend([3])原地修改 print(c) # [1, 2, 3] print(d) # [1, 2, 3]d也變了 e (1, 2) f e e (3,) # 等價于 e e (3,)創(chuàng)建新元組 print(e) # (1, 2, 3) print(f) # (1, 2)f沒變3.3 元組的“不可變”到底指什么為什么它能當(dāng)字典key元組的不可變是指元組對象自身包含的元素引用不能被修改。你不能做my_tuple[0] 10。但它不保證它里面的元素本身不可變?nèi)绻M里存了一個列表那個列表還是可以變的。# 元組的不可變是“一層”的 t (1, [2, 3], hello) # t[0] 10 # ? 報錯不能改元組的第一個元素 # t[2] world # ? 報錯不能改元組的第三個元素 t[1].append(4) # ? 可以改的是元組里那個列表對象元組只是存了它的引用 print(t) # (1, [2, 3, 4], hello) # 為什么能當(dāng)key因為字典的key必須是“可哈希的”hashable # 可哈希意味著對象的hash值在其生命周期內(nèi)不變且相等的對象必須有相同hash值 # 不可變對象str, int, tuple天然滿足可變對象list, dict, set不滿足因為值變了hash就得變 my_dict {(1, 2): point, (a, b): pair} # ? 元組可哈希能當(dāng)key # my_dict {[1, 2]: point} # ? 列表不可哈希報錯 TypeError3.4 常見問題排查為什么我的函數(shù)默認參數(shù)“記住了”上次的值現(xiàn)象寫了一個函數(shù)def add_item(item, my_list[]): my_list.append(item); return my_list第一次調(diào)用add_item(1)返回[1]第二次調(diào)用add_item(2)卻返回[1, 2]原因默認參數(shù)my_list[]是在函數(shù)定義時創(chuàng)建的不是每次調(diào)用時創(chuàng)建。這個空列表對象在內(nèi)存里只有一份所有不傳my_list參數(shù)的調(diào)用都共享這個列表對象。這就是“可變默認參數(shù)陷阱”。解決永遠用None作為默認值然后在函數(shù)體內(nèi)檢查并創(chuàng)建新對象# ? 危險可變默認參數(shù) def bad_append(item, lst[]): lst.append(item) return lst # ? 安全用None作哨兵 def good_append(item, lstNone): if lst is None: lst [] # 每次調(diào)用都創(chuàng)建新列表 lst.append(item) return lst print(good_append(1)) # [1] print(good_append(2)) # [2]干凈現(xiàn)象a [1, 2]; b a; b[0] 99; print(a)輸出[99, 2]我以為b a是復(fù)制了一份。原因是賦值不是復(fù)制。它只是讓b也指向a所指向的那個列表對象。要真正復(fù)制得用copy()淺拷貝或deepcopy()深拷貝。解決明確你的需求只需一層復(fù)制列表里全是不可變對象b a.copy()或b a[:]需要完全獨立副本列表里還有列表import copy; b copy.deepcopy(a)4. 面向?qū)ο蟛皇庆偶际亲尨a“自己管自己”從self到super()的實戰(zhàn)邏輯“面向?qū)ο缶幊獭甭犉饋砀叽笊系赑ython期末復(fù)習(xí)里它最樸素的目標(biāo)就一個把數(shù)據(jù)屬性和操作數(shù)據(jù)的代碼方法打包在一起讓這個包對象能自己管理自己的狀態(tài)。self不是語法糖它是這個包的“內(nèi)部身份證”super()不是魔法它是這個包在家族譜系里“向上找長輩幫忙”的聯(lián)絡(luò)方式。我們從一個具體例子出發(fā)。4.1self對象的“我”不是關(guān)鍵字是約定self只是一個形參名你可以叫它this或me但所有人都叫self這是約定。它的作用是讓類里的方法能明確知道“我現(xiàn)在操作的是哪個具體的實例” 沒有self方法就不知道自己在跟誰對話。class Student: def __init__(self, name, score): self.name name # 把參數(shù)name存到這個實例的name屬性里 self.score score # 把參數(shù)score存到這個實例的score屬性里 def introduce(self): # self.name 就是“我自己的名字”不是某個全局變量 return f我是{self.name}分數(shù)是{self.score} def upgrade(self, points): # self.score 就是“我自己的分數(shù)”升級只影響我自己 self.score points return f{self.name}升級了新分數(shù){self.score} # 創(chuàng)建兩個不同的實例兩個不同的學(xué)生 alice Student(Alice, 85) bob Student(Bob, 92) print(alice.introduce()) # 我是Alice分數(shù)是85 print(bob.introduce()) # 我是Bob分數(shù)是92 alice.upgrade(5) # Alice升級了新分數(shù)90 print(bob.score) # 92Bob的分數(shù)完全沒受影響4.2 繼承不是“抄代碼”是“建立家族關(guān)系”繼承的核心是讓子類自動獲得父類的所有屬性和方法并可以在此基礎(chǔ)上添加新功能或修改舊功能。它建立的是一種“is-a”是一個關(guān)系Dogis-aAnimal。南開復(fù)習(xí)材料里提到的“鉆石繼承”和“MRO算法”就是當(dāng)這個家族關(guān)系變得復(fù)雜比如Dog同時繼承Animal和Pet時Python如何確保每個方法只被調(diào)用一次、且按正確順序調(diào)用的規(guī)則。class Animal: def __init__(self, name): self.name name def speak(self): return f{self.name} makes a sound class Dog(Animal): # Dog 繼承 Animal def speak(self): # 重寫override父類方法 return f{self.name} barks: Woof! class Cat(Animal): def speak(self): return f{self.name} meows: Meow! # Dog和Cat自動有了__init__和name屬性無需重復(fù)寫 dog Dog(Buddy) cat Cat(Whiskers) print(dog.speak()) # Buddy barks: Woof! print(cat.speak()) # Whiskers meows: Meow!4.3super()家族里的“找長輩”協(xié)議解決鉆石繼承難題當(dāng)繼承鏈變長比如class Medium1(Father, Mother):而Father和Mother又都繼承自Grandparent就可能出現(xiàn)“Grandparent.__init__()被調(diào)用兩次”的問題鉆石繼承。super()就是Python提供的解決方案它不指定具體調(diào)哪個父類而是按照方法解析順序MRO去找下一個應(yīng)該執(zhí)行的類的方法。class Grandparent: def __init__(self, name): print(fGrandparent.__init__({name})) self.name name class Father(Grandparent): def __init__(self, name, job): print(fFather.__init__({name}, {job})) super().__init__(name) # 調(diào)用MRO中下一個類Grandparent的__init__ self.job job class Mother(Grandparent): def __init__(self, name, hobby): print(fMother.__init__({name}, {hobby})) super().__init__(name) # 同樣調(diào)用Grandparent.__init__ self.hobby hobby class Child(Father, Mother): def __init__(self, name, job, hobby, school): print(fChild.__init__({name}, {job}, {hobby}, {school})) # super()會按MRO順序調(diào)用Father.__init__ - Mother.__init__ - Grandparent.__init__ # Grandparent.__init__只執(zhí)行一次 super().__init__(name, job) # 先調(diào)Father.__init__ # 注意Mother.__init__不會被自動調(diào)用需要手動補上或在Father.__init__里用super調(diào)用 # 這里簡化實際復(fù)雜繼承需仔細設(shè)計 self.school school # 查看MRO順序 print(Child.__mro__) # 輸出類似(class __main__.Child, class __main__.Father, class __main__.Mother, class __main__.Grandparent, class object) # Python 3 使用C3線性化算法計算MRO確保每個父類只出現(xiàn)一次且子類總在父類之前4.4 常見問題排查為什么__init__里不寫super()我的父類屬性就沒了現(xiàn)象class Dog(Animal): def __init__(self, name, breed): self.breed breed然后dog Dog(Buddy, Golden)print(dog.name)報錯AttributeError。原因你重寫了__init__但沒調(diào)用父類Animal.__init__所以self.name name這行根本沒執(zhí)行name屬性從未被創(chuàng)建。解決只要重寫了__init__就必須顯式調(diào)用super().__init__(...)來初始化父類部分?,F(xiàn)象class A: def method(self): print(A); class B(A): def method(self): print(B); class C(A): def method(self): print(C); class D(B, C): pass; D().method()輸出B為什么不是C或報錯原因這就是MRO在起作用。D.__mro__是(D, B, C, A, object)所以method首先在D找沒找到再到B找找到了就執(zhí)行B.method不再繼續(xù)往下找C。解決理解MRO是調(diào)試多繼承的關(guān)鍵。用ClassName.__mro__隨時查看順序。現(xiàn)象property裝飾器是什么為什么song.length能像屬性一樣訪問但song.set_length(100)卻要調(diào)用方法原因property把一個方法偽裝成了屬性。song.length觸發(fā)的是length這個property方法它內(nèi)部可以做任何邏輯比如檢查合法性但調(diào)用者感覺不到。length.setter則定義了song.length 100時該做什么。解決這是封裝的利器。把數(shù)據(jù)校驗、計算邏輯藏在屬性訪問背后對外提供簡潔接口。class Song: def __init__(self, title): self.title title self._length 0 # 真實存儲用私有屬性 property def length(self): getter讀取length return self._length length.setter def length(self, value): setter設(shè)置length帶校驗 if value 0: raise ValueError(Length cannot be negative) self._length value song Song(My Song) song.length 180 # 觸發(fā) setter自動校驗 print(song.length) # 觸發(fā) getter返回1805. 從“能跑通”到“真理解”動手跑通第一個面向?qū)ο笮№椖俊獙W(xué)生成績管理系統(tǒng)光看概念容易飄現(xiàn)在我們動手做一個極簡但完整的項目一個能錄入、查詢、統(tǒng)計學(xué)生成績的系統(tǒng)。它會用到復(fù)習(xí)材料里所有的核心概念類、屬性、方法、繼承、super()、property、異常處理。每一步都解釋清楚“為什么這么寫”而不是“照著抄”。5.1 第一步定義基礎(chǔ)類Student封裝數(shù)據(jù)和基本行為我們要讓每個學(xué)生對象自己記住自己的名字、各科成績并能算出自己的平均分。這正是self和屬性的用武之地。class Student: def __init__(self, name, scoresNone): 初始化學(xué)生 :param name: 學(xué)生姓名str :param scores: 成績字典如 {math: 85, english: 92}默認為空字典 self.name name # 用None作默認值避免可變默認參數(shù)陷阱 self._scores scores if scores is not None else {} property def scores(self): getter返回成績字典的副本防止外部直接修改 return self._scores.copy() # 返回副本保護內(nèi)部數(shù)據(jù) def add_score(self, subject, score): 添加或更新一門課的成績 if not isinstance(score, (int, float)) or score 0 or score 100: raise ValueError(Score must be a number between 0 and 100) self._scores[subject] score def get_average(self): 計算并返回平均分 if not self._scores: return 0.0 return sum(self._scores.values()) / len(self._scores) def __str__(self): 定義打印學(xué)生時的友好格式 avg self.get_average() return f學(xué)生: {self.name}, 成績: {self._scores}, 平均分: {avg:.1f}解釋這里用了property保護scores外部只能讀不能改add_score里做了輸入校驗__str__讓print(student)輸出可讀信息。這些都是面向?qū)ο蠓庋b的體現(xiàn)。5.2 第二步定義管理類GradeManager用組合代替繼承復(fù)習(xí)材料提到“組合就是沒有繼承但是用了人家的實例”。GradeManager不需要是Student的子類它只需要“擁有”一堆Student實例來管理。這就是組合Composition比繼承Inheritance更靈活、更符合實際。class GradeManager: def __init__(self): self._students {} # 用字典存學(xué)生key是姓名value是Student對象 def add_student(self, name): 添加一個新學(xué)生 if name in self._students: raise ValueError(f學(xué)生 {name} 已存在) self._students[name] Student(name) def add_score(self, name, subject, score): 給指定學(xué)生添加成績 if name not in self._students: raise ValueError(f學(xué)生 {name} 不存在) self._students[name].add_score(subject, score) def get_student_average(self, name): 獲取指定學(xué)生的平均分 if name not in self._students: raise ValueError(f學(xué)生 {name} 不存在) return self._students[name].get_average() def get_class_average(self): 計算全班平均分 if not self._students: return 0.0 # 對每個學(xué)生的平均分求平均 averages [s.get_average() for s in self._students.values()] return sum(averages) / len(averages) def list_all_students(self): 列出所有學(xué)生信息 if not self._students: print(暫無學(xué)生) return for student in self._students.values(): print(student)解釋GradeManager內(nèi)部用self._students字典管理Student對象它調(diào)用Student的方法如add_score,get_average但不繼承Student。這清晰劃分了職責(zé)Student管自己GradeManager管一群Student。5.3 第三步運行主程序體驗“對象自己管自己”現(xiàn)在我們寫一個簡單的交互式主程序讓它跑起來。你會看到所有復(fù)雜的邏輯數(shù)據(jù)校驗、平均分計算、錯誤提示都被封裝在類里主程序只需調(diào)用清晰的方法名。def main(): manager GradeManager() # 添加學(xué)生 try: manager.add_student(張三) manager.add_student(李四) print(? 學(xué)生添加成功) except ValueError as e: print(f? 添加失敗: {e}) # 添加成績 try: manager.add_score(張三, 數(shù)學(xué), 85) manager.add_score(張三, 英語, 92) manager.add_score(李四, 數(shù)學(xué), 78) manager.add_score(李四, 英語, 88) print(? 成績添加成功) except ValueError as e: print(f? 添加成績失敗: {e}) # 查詢 print(\n--- 學(xué)生詳情 ---) manager.list_all_students() print(f\n--- 張三的平均分: {manager.get_student_average(張三):.1f}) print(f--- 全班平均分: {manager.get_class_average():.1f}) # 嘗試錯誤操作 try: manager.add_score(王五, 數(shù)學(xué), 90) # 王五不存在 except ValueError as e: print(f?? 捕獲預(yù)期錯誤: {e}) if __name__ __main__: main()運行結(jié)果? 學(xué)生添加成功 ? 成績添加成功 --- 學(xué)生詳情 --- 學(xué)生: 張三, 成績: {數(shù)學(xué): 85, 英語: 92}, 平均分: 88.5 學(xué)生: 李四, 成績: {數(shù)學(xué): 78, 英語: 88}, 平均分: 83.0 --- 張三的平均分: 88.5 --- 全班平均分: 85.8 ?? 捕獲預(yù)期錯誤: 學(xué)生 王五 不存在5.4 常見問題排查為什么我的GradeManager報錯GradeManager object has no attribute _students現(xiàn)象運行main()時manager.add_student(張三)報錯說GradeManager沒有_students屬性。原因最可能的原因是GradeManager.__init__方法名寫錯了比如寫成了def init(self):少了個下劃線或def __init__(self, name):多了一個參數(shù)。Python只會自動調(diào)用名為__init__且參數(shù)只有self或self加其他必要參數(shù)的方法。解決仔細檢查__init__的拼寫和參數(shù)。用print(dir(manager))可以查看對象有哪些屬性確認_students是否在其中。現(xiàn)象manager.add_score(張三, 數(shù)學(xué), 150)沒有報錯但成績存了150。原因add_score方法里的校驗條件寫錯了比如score 100寫成了score 100或者漏掉了or score 100。解決在add_score方法里加一行print(fDebug: trying to set {subject}{score})運行看是否進入方法然后逐行檢查if條件。現(xiàn)象print(manager.get_student_average(張三))輸出0.0但我知道他有成績。原因get_student_average方法里self._students[name]取到了學(xué)生對象但調(diào)用get_average()時Student.get_average()里self._scores是空字典??赡躠dd_score沒成功執(zhí)行或者_scores字典的key寫錯了比如存了shuxue但查數(shù)學(xué)。解決在Student.get_average里加print(fDebug scores: {self._scores})看實際存了什么。6. 下一步學(xué)什么從“會用”到“會設(shè)計”一份南開軟院風(fēng)格的進階路線圖走到這一步你已經(jīng)能讀懂南開軟件學(xué)院期末復(fù)習(xí)材料里的大部分內(nèi)容并親手寫出一個有封裝、有校驗、有錯誤處理的小系統(tǒng)。但這只是起點。真正的Python高手不是知道多少語法而是能在面對一個新需求時快速判斷“該用什么結(jié)構(gòu)、該封裝到哪里、邊界在哪”。下面是我當(dāng)年跟著南開導(dǎo)師做課程設(shè)計時總結(jié)出的、從“會用”躍遷到“會設(shè)計”的三步走路線每一步都配了具體可練的小任務(wù)。6.1 第一步把“魔法函數(shù)”從“背名字”變成“主動本文還有配套的精品資源點擊獲取