入門初學(xué)者短期內(nèi)快速掌握Python的經(jīng)典全面教程(專家學(xué)習(xí)筆記)
本博客將自己初步學(xué)習(xí)Python過(guò)程中的階段性整理與沉淀。難免存在理解不夠嚴(yán)謹(jǐn)或表述不夠完善之處,歡迎各位讀者在評(píng)論區(qū)留言指正、交流探討,這對(duì)我和后續(xù)讀者都會(huì)非常有價(jià)值。
同時(shí)說(shuō)明一下:從當(dāng)前基礎(chǔ)學(xué)習(xí)和實(shí)踐驗(yàn)證來(lái)看,當(dāng)前準(zhǔn)備的這些分享內(nèi)容,確實(shí)是可以在一天半的時(shí)間內(nèi)就可以快速學(xué)習(xí)完成并可以理解的,希望對(duì)我們彼此都有所收獲?。?!
一、快速了解 Python 和 環(huán)境準(zhǔn)備
(一)Python 快速介紹
Python 是一種 簡(jiǎn)潔、強(qiáng)大、易讀 的編程語(yǔ)言,廣泛應(yīng)用于 Web 開發(fā)、數(shù)據(jù)分析、人工智能、自動(dòng)化運(yùn)維 等領(lǐng)域。它由 Guido van Rossum 在 1991 年設(shè)計(jì),因其清晰的語(yǔ)法和強(qiáng)大的生態(tài)系統(tǒng),迅速成為全球最受歡迎的編程語(yǔ)言之一。

在 2017 年底,Stack Overflow 發(fā)布的數(shù)據(jù)顯示,Python 已經(jīng)超越 JavaScript 成為了該平臺(tái)上提問(wèn)流量最多的編程語(yǔ)言。
(二)Jupyter Notebook:從夢(mèng)想到行業(yè)標(biāo)配
Jupyter Notebook 的誕生源于創(chuàng)始人 Fernando Pérez一個(gè)大膽的想法:打造一個(gè)能整合 Julia(Ju)、Python(Py)和 R(ter) 這三種科學(xué)計(jì)算語(yǔ)言的通用計(jì)算平臺(tái)。但目前Jupyter 早已超越最初的設(shè)想,成為一個(gè)支持幾乎所有編程語(yǔ)言的多功能計(jì)算環(huán)境。
從 2017 年起,北美頂尖高校紛紛全面擁抱 Jupyter Notebook:
- 學(xué)術(shù)界:斯坦福的 CS231N《計(jì)算機(jī)視覺與神經(jīng)網(wǎng)絡(luò)》 課程2017 年便完全遷移到 Jupyter Notebook、UC Berkeley 的 《數(shù)據(jù)科學(xué)基礎(chǔ)》 課程也在同年改用 Jupyter 作為唯一的作業(yè)平臺(tái)。
- 工業(yè)界: Facebook盡管大型后端開發(fā)仍依賴傳統(tǒng) IDE,但幾乎所有 內(nèi)部分析、機(jī)器學(xué)習(xí)訓(xùn)練和數(shù)據(jù)探索 都基于 Jupyter Notebook 運(yùn)行。同時(shí) Google 的 AI Research 部門 Google Brain,也是清一色地全部使用 Jupyter Notebook(改進(jìn)定制版,叫 Google Colab)。
(三)Mac 上安裝 Python 和 Jupyter Notebook 的步驟
| 步驟 | 命令 | 說(shuō)明 |
|---|---|---|
| 1. 安裝 Homebrew | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | Mac 的包管理工具(如果未安裝) |
| 2. 安裝 Python 3 | brew install python | 安裝最新的 Python 3 |
| 3. 驗(yàn)證 Python 版本 | python3 --version | 確保安裝成功,顯示 Python 3.x.x |
| 4. 安裝 Jupyter Notebook | pip3 install jupyter | 通過(guò) pip3 安裝 Jupyter |
| 5. 檢查 Jupyter 是否可用 | jupyter --version | 確保 Jupyter 已正確安裝 |
| 6. 啟動(dòng) Jupyter Notebook | jupyter notebook | 打開 Jupyter Notebook,瀏覽器自動(dòng)打開 |
這樣,我們就可以在 Mac 上使用 Jupyter Notebook 進(jìn)行 Python 開發(fā)了!
備注:
- 如果你不習(xí)慣也可以直接idea,畢竟這么多年習(xí)慣了java開發(fā),我就是兩個(gè)都安裝了,無(wú)所謂!
- 如果不想安裝也可以在線使用:Jupyter 官方在線工具
二、Python 基礎(chǔ):變量、數(shù)據(jù)類型與輸入輸出
Python 以其 簡(jiǎn)潔、易讀 的語(yǔ)法受到歡迎,而理解變量、數(shù)據(jù)類型和輸入輸出是學(xué)習(xí) Python 的第一步。這部分內(nèi)容構(gòu)成了 Python 編程的基石。這部分代碼我寫的時(shí)候都是直接在idea中,網(wǎng)絡(luò)版本有時(shí)很慢。Jupyter 官方在線工具
(一)變量:數(shù)據(jù)的存儲(chǔ)與引用
變量(Variable)是用于存儲(chǔ)數(shù)據(jù)的容器,在 Python 中變量不需聲明類型,直接賦值即可創(chuàng)建。
1. 變量的定義與賦值
x = 10 # 整數(shù)變量 name = "Alice" # 字符串變量 pi = 3.14159 # 浮點(diǎn)數(shù)變量 is_python_fun = True # 布爾變量
Python 是動(dòng)態(tài)類型語(yǔ)言,變量的類型是根據(jù)賦值內(nèi)容自動(dòng)推導(dǎo)的。
2. 變量命名規(guī)則
變量名必須 以字母或下劃線(_)開頭,不能以數(shù)字開頭。
變量名只能包含 字母、數(shù)字和下劃線,不能包含空格或特殊符號(hào)。
變量名區(qū)分大小寫(
age和Age是不同的變量)。推薦使用 小寫字母+下劃線(
snake_case)風(fēng)格,如user_name,符合 Python 代碼規(guī)范(PEP 8)。
示例:正確與錯(cuò)誤的變量命名
valid_name = "OK" # 正確 _valid_123 = 42 # 正確 2nd_value = "wrong" # ? 錯(cuò)誤:不能以數(shù)字開頭 user-name = "error" # ? 錯(cuò)誤:不能包含 `-`
(二)數(shù)據(jù)類型(Data Types)
Python 具有豐富的數(shù)據(jù)類型,主要包括:
| 數(shù)據(jù)類型 | 示例 | 說(shuō)明 |
|---|---|---|
| 整數(shù)(int) | x = 42 | 存儲(chǔ)整數(shù),如 10, -5, 1000 |
| 浮點(diǎn)數(shù)(float) | pi = 3.14 | 存儲(chǔ)小數(shù),如 3.1415, -0.5 |
| 字符串(str) | name = "ZYF" | 存儲(chǔ)文本,如 "hello" |
| 布爾值(bool) | is_valid = True | 只有 True 或 False |
| 列表(list) | nums = [1, 2, 3] | 有序可變的集合 |
| 元組(tuple) | colors = ('red', 'blue') | 有序不可變的集合 |
| 字典(dict) | person = {"name": "Alice", "age": 25} | 鍵值對(duì)存儲(chǔ) |
| 集合(set) | unique_nums = {1, 2, 3} | 無(wú)序不重復(fù)元素集合 |
Python 提供 type() 函數(shù)來(lái)查看變量的數(shù)據(jù)類型:
x = 42 print(type(x)) # 輸出:<class 'int'>
Python 允許不同類型之間的轉(zhuǎn)換:
age = "25" age = int(age) # 將字符串轉(zhuǎn)換為整數(shù) pi = 3.14 pi_str = str(pi) # 將浮點(diǎn)數(shù)轉(zhuǎn)換為字符串
(三)輸入與輸出(Input & Output)
1. 標(biāo)準(zhǔn)輸入(input)
input() 用于從用戶獲取輸入,所有輸入默認(rèn)是 字符串類型:
name = input("請(qǐng)輸入你的名字: ")
print("你好,", name)
如果需要數(shù)值類型,需要進(jìn)行 類型轉(zhuǎn)換:
age = int(input("請(qǐng)輸入你的年齡: ")) # 輸入默認(rèn)是字符串,需要轉(zhuǎn)換成整數(shù)
print("你明年就", age + 1, "歲了!")
基本驗(yàn)證:

2. 標(biāo)準(zhǔn)輸出(print)
print() 用于向控制臺(tái)輸出內(nèi)容:
name = "Alice"
age = 25
print("姓名:", name, "年齡:", age) # 多個(gè)參數(shù)用逗號(hào)分隔
格式化輸出(推薦使用 f-string,Python 3.6+ 支持):
print(f"姓名: {name}, 年齡: {age}") # 推薦的寫法? 小練習(xí)題
嘗試自己編寫代碼練習(xí)以下問(wèn)題:
定義一個(gè)變量
temperature,存儲(chǔ)36.6,并使用print()輸出"體溫: 36.6 攝氏度"。編寫一個(gè)程序,提示用戶輸入姓名和年齡,并打印
"你好,XX!你今年 YY 歲"(其中XX和YY由用戶輸入)。
Python 的變量、數(shù)據(jù)類型和輸入輸出構(gòu)成了編程的基礎(chǔ),熟練掌握這些概念后,就可以進(jìn)行更復(fù)雜的邏輯編寫了!
三、控制流:讓代碼具備決策能力
在編程中,控制流 決定了代碼的執(zhí)行順序,使程序能夠做出決策(條件判斷),或重復(fù)執(zhí)行任務(wù)(循環(huán))。通過(guò) 布爾值、邏輯運(yùn)算、if 語(yǔ)句、for 和 while 循環(huán),我們可以讓 Python 代碼變得更加智能和高效。這部分我會(huì)總結(jié)下這些關(guān)鍵概念,其是構(gòu)建更具邏輯性程序的基礎(chǔ)。
(一)布爾值與邏輯判斷
在 Python 中,布爾值(Boolean) 是控制程序邏輯的基礎(chǔ)。它用于 條件判斷、循環(huán)控制和邏輯運(yùn)算,讓代碼具備決策能力。
1. 布爾值(Boolean)
布爾值只有兩個(gè)取值:
True # 代表 "真" False # 代表 "假"
布爾值本質(zhì)上是整數(shù)的特殊形式,其中:
True == 1 # 結(jié)果為 True False == 0 # 結(jié)果為 True
布爾值的基本使用
is_python_fun = True is_raining = False print(is_python_fun) # 輸出:True print(type(is_python_fun)) # 輸出:<class 'bool'>
2. 比較運(yùn)算符(Comparison Operators)
比較運(yùn)算符用于比較兩個(gè)值,返回 True 或 False。
| 運(yùn)算符 | 含義 | 示例 | 結(jié)果 |
|---|---|---|---|
== | 等于 | 5 == 5 | True |
!= | 不等于 | 5 != 3 | True |
> | 大于 | 10 > 3 | True |
< | 小于 | 2 < 8 | True |
>= | 大于等于 | 5 >= 5 | True |
<= | 小于等于 | 3 <= 2 | False |
代碼示例
a = 10 b = 5 print(a > b) # True print(a == b) # False print(a != b) # True
3. 布爾運(yùn)算符(Boolean Operators)
Python 提供了 and、or 和 not 三種邏輯運(yùn)算符,用于組合多個(gè)布爾表達(dá)式。
| 運(yùn)算符 | 含義 | 示例 | 結(jié)果 |
|---|---|---|---|
and | 邏輯與(都為 True 時(shí)才為 True) | True and False | False |
or | 邏輯或(只要一個(gè)為 True 就是 True) | True or False | True |
not | 邏輯非(取反) | not True | False |
3.1and邏輯與
x = 5 y = 10 print(x > 0 and y > 5) # True,因?yàn)閮蓚€(gè)條件都成立 print(x > 0 and y < 5) # False,因第二個(gè)條件不成立
3.2or邏輯或
x = 5 y = 10 print(x > 0 or y < 5) # True,只要有一個(gè)條件為 True 即可 print(x < 0 or y < 5) # False,兩個(gè)條件都為 False
3.3not邏輯非
is_python_fun = True print(not is_python_fun) # False,因?yàn)槿》戳?
4. 混合布爾運(yùn)算(優(yōu)先級(jí))
運(yùn)算符的 優(yōu)先級(jí) 從高到低依次為:not(最高)、and、or(最低)
知道黃曉明的“鬧太套”英文梗,這里直接記成“鬧安套”就好了
print(True or False and False) # 等價(jià)于 True or (False and False) → True or False → True print(not True or False) # 等價(jià)于 (not True) or False → False or False → False
5. 布爾值與其他數(shù)據(jù)類型的轉(zhuǎn)換
在 Python 中,所有數(shù)據(jù)類型 都可以轉(zhuǎn)換為布爾值:
以下情況視為
False:0(整數(shù) 0)0.0(浮點(diǎn)數(shù) 0)""(空字符串)[](空列表){}(空字典)None(特殊值,表示“空”)
其他情況均為
True。
5.1 使用bool()進(jìn)行類型轉(zhuǎn)換
print(bool(0)) # False
print(bool("")) # False
print(bool([])) # False
print(bool(42)) # True
print(bool("hello")) # True
5.2 在if語(yǔ)句中使用
name = "Alice"
if name: # 相當(dāng)于 if bool(name) == True
print("名字有效")
else:
print("名字為空")
輸出:
名字有效
? 小練習(xí)題
練習(xí) 1:布爾運(yùn)算
判斷以下表達(dá)式的結(jié)果:
True and False or Truenot (False or True)(10 > 5) and (3 != 3)
練習(xí) 2:用戶輸入比較
讓用戶輸入兩個(gè)數(shù)字,比較它們的大小,并輸出 "第一個(gè)數(shù)字更大" 或 "第二個(gè)數(shù)字更大"。
# 提示:使用 input() 和 if 語(yǔ)句
布爾值和邏輯判斷是 條件判斷和循環(huán) 的基礎(chǔ),熟練掌握它們后,代碼將更加智能化! ??
(二)?? 條件判斷(if語(yǔ)句)
在編程中,我們經(jīng)常需要根據(jù)不同條件來(lái)執(zhí)行不同的代碼。這就要用到 條件判斷語(yǔ)句(if statement)。Python 中使用 if、elif 和 else 關(guān)鍵字來(lái)實(shí)現(xiàn)條件分支。
1. 基本 if 語(yǔ)句
語(yǔ)法結(jié)構(gòu):
if 條件:
語(yǔ)句塊
? 當(dāng)“條件”為 True 時(shí),語(yǔ)句塊才會(huì)被執(zhí)行。
示例:
age = 20
if age >= 18:
print("你已經(jīng)成年了")
輸出:
你已經(jīng)成年了
2. if-else 結(jié)構(gòu)(兩分支選擇)
用于處理“要么這樣,要么那樣”的情況。
語(yǔ)法結(jié)構(gòu):
if 條件:
語(yǔ)句塊1
else:
語(yǔ)句塊2
示例:
temperature = 15
if temperature > 20:
print("北京天氣今天很暖和")
else:
print("北京天氣今天有點(diǎn)冷")
輸出:
北京天氣今天有點(diǎn)冷
3. if-elif-else 結(jié)構(gòu)(多分支選擇)
用于處理多個(gè)條件判斷的情況(相當(dāng)于“多項(xiàng)選擇”)。
語(yǔ)法結(jié)構(gòu):
if 條件1:
語(yǔ)句塊1
elif 條件2:
語(yǔ)句塊2
elif 條件3:
語(yǔ)句塊3
else:
默認(rèn)語(yǔ)句塊
示例:
score = 85
if score >= 90:
print("優(yōu)秀")
elif score >= 80:
print("良好")
elif score >= 60:
print("及格")
else:
print("不及格")
輸出:
良好
??
elif是 "else if" 的縮寫。Python 中沒(méi)有switch語(yǔ)句,if-elif-else是推薦的替代方案。
4. 嵌套 if 語(yǔ)句
嵌套 指的是在一個(gè) if 塊內(nèi)部再寫 if 判斷??梢杂脕?lái)表示更復(fù)雜的邏輯結(jié)構(gòu)。
示例:
age = 25
is_student = True
if age < 30:
if is_student:
print("你是年輕的學(xué)生")
else:
print("你是年輕的上班族")
輸出:
你是年輕的學(xué)生
?? 注意縮進(jìn)層級(jí),Python 是靠縮進(jìn)來(lái)識(shí)別代碼塊的!
5. 條件表達(dá)式(三元運(yùn)算符)
Python 支持一種簡(jiǎn)潔的寫法:在一行中完成 if-else 判斷。
語(yǔ)法:
變量 = 值1 if 條件 else 值2
示例:
age = 16 status = "成年" if age >= 18 else "未成年" print(status)
輸出:
未成年
適合用于 根據(jù)條件選擇一個(gè)值賦給變量 的情況。
小結(jié)一下吧
| 類型 | 場(chǎng)景示例 |
|---|---|
if | 只有一個(gè)條件 |
if-else | 兩種可能,二選一 |
if-elif-else | 多種情況,依次判斷 |
嵌套 if | 條件套條件,多層判斷 |
| 三元表達(dá)式 | 簡(jiǎn)潔地賦值,適合一行判斷 |
? 小練習(xí)題
練習(xí) 1:分?jǐn)?shù)等級(jí)判斷器
讓用戶輸入一個(gè) 0~100 的整數(shù),判斷其屬于哪個(gè)等級(jí):
90 以上:優(yōu)秀
80~89:良好
70~79:中等
60~69:及格
低于 60:不及格
其他情況提示“輸入有誤”
練習(xí) 2:三角形合法性判斷
輸入三條邊的長(zhǎng)度,判斷是否能組成一個(gè)三角形(任意兩邊之和大于第三邊),并進(jìn)一步判斷:
是等邊三角形
是等腰三角形
是普通三角形
否則輸出“不合法的三角形”
練習(xí) 3:模擬 ATM 登錄與權(quán)限檢查(嵌套)
假設(shè)用戶名為
admin,密碼為8888,登錄成功后再檢查是否為管理員(管理員輸入"yes"才能繼續(xù)操作)。
練習(xí) 4:三元運(yùn)算小測(cè)試
輸入一個(gè)數(shù)字,輸出
"正數(shù)"、"負(fù)數(shù)"或"零",用三元運(yùn)算符 實(shí)現(xiàn)。
(三)循環(huán)(Loops)
在編程中,我們常常需要重復(fù)執(zhí)行某些操作,比如遍歷列表、處理每一行數(shù)據(jù)、執(zhí)行某個(gè)動(dòng)作直到滿足條件等等。Python 提供了兩種主要的循環(huán)結(jié)構(gòu):while 循環(huán)和 for 循環(huán),配合控制語(yǔ)句(如 break、continue)可以構(gòu)建出豐富的循環(huán)邏輯。
1. while循環(huán)(基于條件重復(fù)執(zhí)行)
? 基本語(yǔ)法
while 條件表達(dá)式:
循環(huán)體
每次循環(huán)前都會(huì)檢查“條件表達(dá)式”的值;
條件為
True→ 執(zhí)行循環(huán)體;條件為
False→ 結(jié)束循環(huán)。
?? 示例:打印 1 到 5 的數(shù)字
i = 1
while i <= 5:
print(i)
i += 1
??break語(yǔ)句(提前終止循環(huán))
break 用于立即跳出整個(gè)循環(huán)結(jié)構(gòu),不管循環(huán)條件是否還為 True。
i = 1
while True:
print(i)
if i == 3:
break
i += 1
輸出:1, 2, 3,然后退出循環(huán)
??continue語(yǔ)句(跳過(guò)當(dāng)前迭代)
continue 用于跳過(guò)本次循環(huán)中剩下的語(yǔ)句,直接進(jìn)入下一次判斷。
i = 0
while i < 5:
i += 1
if i == 3:
continue
print(i)
輸出:1, 2, 4, 5(跳過(guò)了 3)
2.for循環(huán)(用于遍歷序列)
? 基本語(yǔ)法
for 變量 in 可迭代對(duì)象:
循環(huán)體
可迭代對(duì)象包括:字符串、列表、字典、集合、元組等。
?? 遍歷字符串
for ch in "hello":
print(ch)
?? 遍歷列表
fruits = ['apple', 'banana', 'orange']
for fruit in fruits:
print(fruit)
?? 遍歷字典
info = {"name": "Tom", "age": 20}
for key in info:
print(key, "=>", info[key])
或使用 .items() 遍歷鍵值對(duì):
for key, value in info.items():
print(f"{key}: {value}")
?? 遍歷集合
s = {"apple", "banana", "cherry"}
for item in s:
print(item)
??range()函數(shù)與數(shù)值循環(huán)
for i in range(1, 6):
print(i)
range(n):0 到 n-1range(start, end):start 到 end-1range(start, end, step):按步長(zhǎng)生成
??enumerate()結(jié)合索引遍歷
想同時(shí)獲取元素和下標(biāo)時(shí)使用 enumerate():
colors = ['red', 'green', 'blue']
for index, color in enumerate(colors):
print(f"{index}: {color}")
3. 循環(huán)控制語(yǔ)句
?break:提前終止整個(gè)循環(huán)
通常與 if 配合使用,用于在滿足某個(gè)條件時(shí)立即退出循環(huán)。
for i in range(1, 10):
if i == 5:
break
print(i)
?continue:跳過(guò)當(dāng)前迭代
用于跳過(guò)某些不符合條件的值。
for i in range(1, 6):
if i % 2 == 0:
continue
print(i) # 輸出 1 3 5(跳過(guò)偶數(shù))
?else語(yǔ)句在循環(huán)中的作用
else 可以與 for 或 while 循環(huán)一起使用:
當(dāng)循環(huán)沒(méi)有被
break中斷時(shí),else中的語(yǔ)句會(huì)執(zhí)行。for i in range(1, 5): if i == 10: break else: print("未被 break,循環(huán)正常結(jié)束")這在處理搜索類問(wèn)題時(shí)特別有用:
nums = [1, 3, 5, 7] target = 4 for num in nums: if num == target: print("找到目標(biāo)") break else: print("未找到目標(biāo)")
小結(jié)一下吧
| 循環(huán)類型 | 用途 | 特點(diǎn) |
|---|---|---|
while | 條件控制循環(huán) | 不確定次數(shù)時(shí)使用 |
for | 遍歷序列 | 更簡(jiǎn)潔,適合固定結(jié)構(gòu) |
break | 跳出循環(huán) | 終止整個(gè)循環(huán)體 |
continue | 跳過(guò)本次迭代 | 繼續(xù)下一輪 |
else | 循環(huán)結(jié)構(gòu)補(bǔ)充 | 僅在循環(huán)未被 break時(shí)執(zhí)行 |
? 小練習(xí)題
?? 練習(xí)題 1:打印 1~100 中所有能被 7 整除的數(shù)
?? 練習(xí)題 2:使用while計(jì)算從 1 累加到 n 的和(n 由用戶輸入)
?? 練習(xí)題 3:找出列表中第一個(gè)大于 50 的元素,并輸出其值和索引
?? 練習(xí)題 4:輸出 1~30 中除了能被 3 整除的數(shù)(使用 continue)
?? 練習(xí)題 5:統(tǒng)計(jì)一段字符串中元音字母的個(gè)數(shù)(不區(qū)分大小寫)
?? 練習(xí)題 6:使用for和else實(shí)現(xiàn)一個(gè)猜數(shù)字小游戲
四、函數(shù)與相關(guān)基礎(chǔ)知識(shí):讓代碼會(huì)“思考”的魔法工具
在編程的世界里,函數(shù)就像是程序的“積木”:它們能把一段可復(fù)用的操作封裝起來(lái),想用就調(diào)用,修改也方便,堪稱優(yōu)雅代碼的起點(diǎn)。
無(wú)論是打印一行文字,計(jì)算一個(gè)數(shù)列的和,還是訓(xùn)練一個(gè) AI 模型,本質(zhì)上你做的都是“定義功能 + 調(diào)用功能” —— 這正是函數(shù)的使命。
(一)函數(shù)基礎(chǔ)
1. 什么是函數(shù)?為什么要使用函數(shù)?
在編程中,函數(shù)是一個(gè)非常重要的概念,它可以被理解為“功能塊”,用來(lái)封裝一段代碼,供其他地方調(diào)用。通過(guò)函數(shù),開發(fā)者可以:
提高代碼復(fù)用性:避免重復(fù)寫相同的代碼,簡(jiǎn)化開發(fā)和維護(hù)。
增強(qiáng)可讀性:將復(fù)雜的任務(wù)拆解成小的、獨(dú)立的單元,便于理解。
分隔邏輯:函數(shù)讓代碼的邏輯結(jié)構(gòu)更加清晰和模塊化,有助于團(tuán)隊(duì)協(xié)作開發(fā)。
例如,我們可以定義一個(gè)函數(shù)來(lái)計(jì)算兩個(gè)數(shù)字的和,而不需要每次都重復(fù)寫加法的操作。只要定義一次,后續(xù)就可以隨時(shí)調(diào)用。
2. 使用def定義函數(shù)
在 Python 中,函數(shù)是通過(guò) def 關(guān)鍵字來(lái)定義的。它的基本語(yǔ)法如下:
def function_name(parameters):
# 函數(shù)體
return result
def是 Python 中定義函數(shù)的關(guān)鍵字function_name是你給函數(shù)命名的名稱,符合命名規(guī)則parameters是函數(shù)的輸入(可以沒(méi)有)return是返回結(jié)果的關(guān)鍵字(如果不返回任何值,則默認(rèn)為None)
示例:一個(gè)簡(jiǎn)單的加法函數(shù)
def add(a, b):
return a + b
# 調(diào)用函數(shù)
result = add(3, 5)
print(result) # 輸出: 8
這里我們定義了一個(gè) add 函數(shù),輸入?yún)?shù)是 a 和 b,它們會(huì)被加在一起并返回結(jié)果。
3. 函數(shù)的調(diào)用和執(zhí)行流程
定義函數(shù)后,調(diào)用函數(shù)就能執(zhí)行該函數(shù)的代碼。Python 會(huì)根據(jù)函數(shù)調(diào)用的順序進(jìn)入函數(shù)體,并執(zhí)行其中的代碼。
當(dāng)函數(shù)被調(diào)用時(shí),程序會(huì)暫停當(dāng)前位置,跳轉(zhuǎn)到函數(shù)體執(zhí)行代碼,直到遇到
return語(yǔ)句(或者函數(shù)執(zhí)行完畢)才返回。如果沒(méi)有
return語(yǔ)句,則返回None。
示例:簡(jiǎn)單的函數(shù)調(diào)用與返回值
def greet(name):
return f"Hello, {name}!"
message = greet("Alice")
print(message) # 輸出: Hello, Alice!
這里的 greet 函數(shù)通過(guò) return 語(yǔ)句返回了一個(gè)字符串,調(diào)用時(shí)傳入了參數(shù) "Alice",最終返回 "Hello, Alice!"。
4.print()與return的區(qū)別與使用場(chǎng)景
在 Python 中,print() 和 return 都用于輸出數(shù)據(jù),但它們有顯著的不同:
print()用于將信息輸出到控制臺(tái),主要用于調(diào)試、輸出中間結(jié)果或與用戶交互。return用于函數(shù)的輸出,它將值返回給調(diào)用者,可以被其他代碼再次使用。
示例:print()和return對(duì)比
# 使用 print()
def greet_print(name):
print(f"Hello, {name}!")
greet_print("Alice") # 輸出: Hello, Alice!
# 使用 return
def greet_return(name):
return f"Hello, {name}!"
message = greet_return("Alice")
print(message) # 輸出: Hello, Alice!
greet_print使用print()輸出值,但無(wú)法把它傳遞給其他部分的代碼。greet_return使用return返回結(jié)果,這個(gè)返回值可以在代碼的其他地方使用或存儲(chǔ)。
小結(jié)一下吧
函數(shù)是代碼復(fù)用、模塊化的核心工具。
使用
def定義函數(shù),return返回值。print()用于輸出調(diào)試信息,return用于返回計(jì)算結(jié)果。
(二)函數(shù)參數(shù)
函數(shù)參數(shù)是函數(shù)接收外部輸入的方式。理解和掌握函數(shù)參數(shù)的使用,不僅能幫助你編寫更靈活的代碼,還能讓你的程序更具擴(kuò)展性和可維護(hù)性。
1. 位置參數(shù)
位置參數(shù)是最常見的函數(shù)參數(shù)類型,它是指參數(shù)傳遞時(shí)位置的順序決定了每個(gè)參數(shù)的含義。
例如,在函數(shù)定義時(shí),參數(shù) a 和 b 的位置決定了它們的意義。當(dāng)調(diào)用函數(shù)時(shí),傳遞的參數(shù)值會(huì)根據(jù)位置匹配到相應(yīng)的參數(shù)。
示例:位置參數(shù)
def add(a, b):
return a + b
result = add(3, 5) # 位置參數(shù):3 被賦值給 a,5 被賦值給 b
print(result) # 輸出: 8
這里的 add 函數(shù)有兩個(gè)位置參數(shù):a 和 b。我們調(diào)用 add(3, 5) 時(shí),3 會(huì)賦值給 a,5 會(huì)賦值給 b,最終返回它們的和。
2. 關(guān)鍵字參數(shù)
關(guān)鍵字參數(shù)允許你在調(diào)用函數(shù)時(shí)顯式地指定每個(gè)參數(shù)的名稱。這樣可以不關(guān)心參數(shù)的順序,只需要知道參數(shù)名。
示例:關(guān)鍵字參數(shù)
def greet(name, age):
return f"Hello, {name}! You are {age} years old."
message = greet(age=25, name="Alice") # 參數(shù)順序不重要
print(message) # 輸出: Hello, Alice! You are 25 years old.
在調(diào)用 greet 函數(shù)時(shí),age 和 name 的順序不再重要,關(guān)鍵字參數(shù)通過(guò)指定 age=25 和 name="Alice" 來(lái)傳遞值。
3. 默認(rèn)參數(shù)
默認(rèn)參數(shù)是指在定義函數(shù)時(shí),為某些參數(shù)提供默認(rèn)值。如果調(diào)用時(shí)沒(méi)有提供這些參數(shù)的值,Python 會(huì)使用默認(rèn)值。
示例:默認(rèn)參數(shù)
def greet(name, age=18): # age 有默認(rèn)值
return f"Hello, {name}! You are {age} years old."
message1 = greet("Alice") # 調(diào)用時(shí)沒(méi)有提供 age,使用默認(rèn)值
message2 = greet("Bob", 30) # 調(diào)用時(shí)提供了 age,覆蓋默認(rèn)值
print(message1) # 輸出: Hello, Alice! You are 18 years old.
print(message2) # 輸出: Hello, Bob! You are 30 years old.
在 greet 函數(shù)中,age 有默認(rèn)值 18。如果調(diào)用時(shí)沒(méi)有傳入 age,就會(huì)使用默認(rèn)值;如果傳入了 age,則會(huì)覆蓋默認(rèn)值。
4. 可變參數(shù):*args與**kwargs
有時(shí)候我們不知道函數(shù)需要接收多少個(gè)參數(shù),這時(shí)可以使用 可變參數(shù)。
*args用于接收位置參數(shù),它將接收多余的位置參數(shù)并將其打包成一個(gè)元組。**kwargs用于接收關(guān)鍵字參數(shù),它將接收多余的關(guān)鍵字參數(shù)并將其打包成一個(gè)字典。
示例:*args和**kwargs
def example(*args, **kwargs):
print("args:", args)
print("kwargs:", kwargs)
# 調(diào)用函數(shù)時(shí)傳入不同數(shù)量的參數(shù)
example(1, 2, 3, name="Alice", age=25)
# 輸出:
# args: (1, 2, 3)
# kwargs: {'name': 'Alice', 'age': 25}
*args將所有位置參數(shù)(1,2,3)打包成一個(gè)元組(1, 2, 3)。**kwargs將所有關(guān)鍵字參數(shù)(name="Alice",age=25)打包成一個(gè)字典{'name': 'Alice', 'age': 25}。
5. 參數(shù)順序規(guī)范
當(dāng)一個(gè)函數(shù)同時(shí)使用位置參數(shù)、默認(rèn)參數(shù)和可變參數(shù)時(shí),有一定的順序規(guī)范,必須遵循以下順序:
位置參數(shù)
默認(rèn)參數(shù)
*args**kwargs
示例:參數(shù)順序
def function(a, b=2, *args, c, **kwargs):
print(a, b, args, c, kwargs)
# 調(diào)用時(shí),按照順序傳遞參數(shù)
function(1, c=3, d=4, e=5)
# 輸出: 1 2 () 3 {'d': 4, 'e': 5}
a是位置參數(shù),必須最先提供。b是默認(rèn)參數(shù),如果不提供則使用默認(rèn)值。*args用于接收多余的所有位置參數(shù)。c是一個(gè)關(guān)鍵字參數(shù),調(diào)用時(shí)必須顯式指定。**kwargs用于接收多余的所有關(guān)鍵字參數(shù)。
小結(jié)一下吧
位置參數(shù):根據(jù)參數(shù)順序傳遞值。
關(guān)鍵字參數(shù):顯式指定參數(shù)名和值。
默認(rèn)參數(shù):函數(shù)定義時(shí)為參數(shù)提供默認(rèn)值。
可變參數(shù):
*args用于接收多位置參數(shù),**kwargs用于接收多關(guān)鍵字參數(shù)。參數(shù)順序:位置參數(shù) > 默認(rèn)參數(shù) >
*args>**kwargs。
(三)函數(shù)返回值
在 Python 中,函數(shù)不僅可以執(zhí)行某些操作,還可以將結(jié)果“返回”給調(diào)用者。這就是 返回值(Return Value) 的概念。
通過(guò) return 語(yǔ)句,函數(shù)可以把處理的結(jié)果交給外部使用。如果函數(shù)沒(méi)有 return,那它默認(rèn)返回的是 None。
1. 使用return返回結(jié)果
基本語(yǔ)法:
def function_name(...):
...
return result
示例:
def square(x):
return x * x
result = square(5)
print(result) # 輸出: 25
函數(shù) square 返回的是 x 的平方,調(diào)用后我們可以拿到這個(gè)結(jié)果并繼續(xù)使用。
2. 返回多個(gè)值(元組)
Python 支持從一個(gè)函數(shù)中返回多個(gè)值,這些值實(shí)際上被打包成一個(gè)元組返回。
示例:
def get_name_and_age():
name = "Alice"
age = 30
return name, age
n, a = get_name_and_age()
print(n) # Alice
print(a) # 30
多值返回的本質(zhì)是:
return name, age等價(jià)于return (name, age),然后通過(guò)拆包語(yǔ)法接收。
3. 函數(shù)無(wú)返回值時(shí)的表現(xiàn)
如果函數(shù)中沒(méi)有 return 語(yǔ)句,或者只是 return 而沒(méi)有值,那默認(rèn)返回的是 None。
示例:
def say_hello():
print("Hello!")
result = say_hello()
print(result) # 輸出: None
這個(gè)函數(shù) say_hello() 雖然做了事情(打?。珱](méi)有 return,因此返回值是 None。
4. 提前終止函數(shù)執(zhí)行
return 不僅能返回值,還能用來(lái)提前終止函數(shù)的執(zhí)行。
示例:
def divide(a, b):
if b == 0:
return "除數(shù)不能為 0"
return a / b
print(divide(10, 0)) # 輸出: 除數(shù)不能為 0
print(divide(10, 2)) # 輸出: 5.0
當(dāng) b 是 0 時(shí),函數(shù)遇到第一個(gè) return 后立即終止執(zhí)行,不會(huì)繼續(xù)往下運(yùn)行。
5. 將返回值作為表達(dá)式使用
函數(shù)返回值可以被用在其他表達(dá)式或函數(shù)中,像這樣:
def add(a, b):
return a + b
print(add(2, 3) * 10) # 輸出: 50
add(2, 3) 會(huì)返回 5,然后再與 10 相乘。
小結(jié)一下吧
| 內(nèi)容 | 示例 | 返回值說(shuō)明 |
|---|---|---|
| 返回單個(gè)值 | return x | 返回一個(gè)對(duì)象 |
| 返回多個(gè)值 | return x, y | 返回一個(gè)元組 |
| 無(wú) return 或 return None | return / 無(wú) return | 返回 None |
| 提前結(jié)束函數(shù) | if ...: return ... | 直接退出函數(shù) |
| 返回值可以參與表達(dá)式 | add(1, 2) * 3 | 函數(shù)結(jié)果用于計(jì)算 |
(四)函數(shù)返回值類型注解(Type Hint / Type Annotation)
1. Python 3 引入的類型注解(Type Hints)
先用一句話理解:
def add(a: int, b: int) -> int:
return a + b?? -> int 的意思是:
這個(gè)函數(shù)“設(shè)計(jì)上”應(yīng)該返回一個(gè)
int
不是強(qiáng)制,只是“聲明”。這是 給“人”和“工具”看的,不是給 Python 解釋器強(qiáng)制執(zhí)行的。
注意Python 不會(huì)強(qiáng)制檢查!下面的代碼 是合法的:
def add(a: int, b: int) -> int:
return "not a number"Python 不會(huì)報(bào)錯(cuò)。?類型注解的作用在于:
IDE(PyCharm)
靜態(tài)檢查工具(mypy、pyright)
閱讀代碼的人(包括未來(lái)的你)
2. 為什么現(xiàn)在的 Python 項(xiàng)目都強(qiáng)烈推薦用?
2.1 PyCharm 會(huì)變得非常聰明
def foo() -> dict[str, int]:
return {"a": 1}
你輸入:
result = foo() result["a"].
PyCharm 會(huì)自動(dòng)提示:?? 這是 int,有哪些方法
2.2 提前發(fā)現(xiàn) bug(不用等運(yùn)行)
def get_age() -> int:
return "18"
PyCharm / mypy 會(huì)直接警告你:
Expected
int, gotstr
3. 常見的返回值寫法(你一定會(huì)遇到)
3.1 返回多個(gè)類型(Union)
from typing import Union
def parse(s: str) -> Union[int, None]:
if s.isdigit():
return int(s)
return None
Python 3.10+ 更推薦:
def parse(s: str) -> int | None:
...
3.2 返回列表 / 字典
def get_names() -> list[str]:
return ["Alice", "Bob"]
def get_scores() -> dict[str, int]:
return {"math": 90}
3.3 返回自定義對(duì)象
class User:
...
def get_user() -> User:
return User()
五、字符串操作模塊
在 Python 中,字符串(str)是最常用、最重要的數(shù)據(jù)類型之一。無(wú)論你是在處理用戶輸入、讀取文件、構(gòu)建網(wǎng)頁(yè)內(nèi)容、分析文本數(shù)據(jù),幾乎都繞不開對(duì)字符串的處理。
現(xiàn)在我們總結(jié)掌握 Python 字符串的各種操作方法,從最基本的創(chuàng)建與訪問(wèn),到復(fù)雜的格式化,我們需要學(xué)會(huì)如何清洗、切割、替換、判斷和重組字符串,并為后續(xù)的數(shù)據(jù)處理與文本分析打下堅(jiān)實(shí)基礎(chǔ)。
(一)字符串基礎(chǔ)
在 Python 中,字符串(str)是用來(lái)表示文本的數(shù)據(jù)類型。本部分介紹字符串的創(chuàng)建方式、基本特性、訪問(wèn)技巧以及常見的基本操作。
1. 字符串的定義與創(chuàng)建
字符串可以用 單引號(hào) '、雙引號(hào) " 或 三引號(hào) ''' / """ 括起來(lái)。
# 單引號(hào) s1 = 'hello' # 雙引號(hào) s2 = "world" # 三引號(hào)(支持多行) s3 = '''This is a multi-line string.'''
?? Python 中單引號(hào)和雙引號(hào)作用相同,主要用于避免轉(zhuǎn)義沖突。
2. 字符串是不可變對(duì)象
字符串是 不可變的(immutable):創(chuàng)建后無(wú)法修改,只能重新賦值。
s = "hello" # s[0] = 'H' # ? 錯(cuò)誤!字符串不能直接修改 s = "Hello" # ? 只能重新賦值
3. 字符串的索引與切片
字符串可以看作字符的序列,可以通過(guò)索引或切片訪問(wèn)其部分內(nèi)容。
s = "Python" # 索引(從 0 開始) print(s[0]) # P print(s[-1]) # n(最后一個(gè)字符) # 切片 [start:end:step] print(s[0:2]) # Py(不包括 end) print(s[::2]) # Pto(每隔1個(gè)) print(s[::-1]) # nohytP(反轉(zhuǎn)字符串)
? 切片非常強(qiáng)大,是字符串處理中不可或缺的工具。
4. 字符串的連接與重復(fù)
# 拼接字符串
a = "Hello"
b = "World"
c = a + " " + b
print(c) # Hello World
# 重復(fù)字符串
print("ha" * 3) # hahaha
5. 字符串的成員運(yùn)算
s = "python"
print("p" in s) # True
print("z" not in s) # True
6. 字符串與類型轉(zhuǎn)換
num = 123
text = str(num) # 數(shù)字轉(zhuǎn)字符串
print("數(shù)字是:" + text)
# 也可以使用 f-string(推薦)
print(f"數(shù)字是:{num}")
小結(jié)一下吧
| 操作類型 | 常用方法 / 語(yǔ)法 |
|---|---|
| 創(chuàng)建字符串 | 'abc', "abc", '''abc''' |
| 索引 | s[0], s[-1] |
| 切片 | s[1:4], s[::-1] |
| 拼接與重復(fù) | +, * |
| 判斷是否存在 | in, not in |
| 類型轉(zhuǎn)換 | str(x), f"{x}" |
(二)字符串常見操作方法
Python 的字符串類型(str)內(nèi)建了大量實(shí)用的方法,幫助我們完成查找、替換、對(duì)齊、大小寫轉(zhuǎn)換、格式化等各類文本處理任務(wù)。本節(jié)將系統(tǒng)講解所有主流、實(shí)用的方法,并配合用例說(shuō)明。
1. 大小寫轉(zhuǎn)換
| 方法 | 作用 |
|---|---|
.lower() | 轉(zhuǎn)為小寫 |
.upper() | 轉(zhuǎn)為大寫 |
.capitalize() | 首字母大寫,其余小寫 |
.title() | 每個(gè)單詞首字母大寫 |
.swapcase() | 大小寫互換 |
s = "hello World" print(s.lower()) # hello world print(s.upper()) # HELLO WORLD print(s.capitalize()) # Hello world print(s.title()) # Hello World print(s.swapcase()) # HELLO wORLD
2. 查找與判斷
| 方法 | 功能 |
|---|---|
.find(sub) | 找到子串首次出現(xiàn)的位置(找不到返回 -1) |
.rfind(sub) | 從右側(cè)開始查找 |
.index(sub) | 與 find 類似,但找不到拋出異常 |
.startswith(prefix) | 是否以某前綴開頭 |
.endswith(suffix) | 是否以某后綴結(jié)尾 |
.in | 判斷子串是否存在 |
s = "hello python"
print(s.find("py")) # 6
print(s.startswith("he")) # True
print(s.endswith("on")) # True
print("py" in s) # True
3. 刪除空白符與字符清洗
| 方法 | 功能 |
|---|---|
.strip() | 刪除兩端空白符 |
.lstrip() | 刪除左側(cè)空白符 |
.rstrip() | 刪除右側(cè)空白符 |
.replace(old, new) | 替換子串 |
s = " hello world "
print(s.strip()) # "hello world"
s2 = "python,java,c"
print(s2.replace(",", " | ")) # python | java | c
4. 字符串對(duì)齊與填充
| 方法 | 說(shuō)明 |
|---|---|
.center(width, char) | 居中填充 |
.ljust(width, char) | 左對(duì)齊填充 |
.rjust(width, char) | 右對(duì)齊填充 |
.zfill(width) | 用 0 填充左側(cè)數(shù)字部分 |
print("hi".center(10, "-")) # ----hi----
print("hi".ljust(10, ".")) # hi........
print("hi".rjust(10, "*")) # ********hi
print("42".zfill(5)) # 00042
5. 拆分與連接
| 方法 | 功能 |
|---|---|
.split(sep) | 拆分為列表 |
.rsplit(sep) | 從右拆分 |
.splitlines() | 拆分多行字符串 |
'sep'.join(list) | 用分隔符連接列表為字符串 |
s = "apple,banana,grape"
print(s.split(",")) # ['apple', 'banana', 'grape']
lines = "line1\nline2\nline3"
print(lines.splitlines()) # ['line1', 'line2', 'line3']
words = ['one', 'two', 'three']
print("-".join(words)) # one-two-three
6. 內(nèi)容判斷類方法(返回布爾值)
| 方法 | 判斷類型 |
|---|---|
.isalpha() | 是否全字母 |
.isdigit() | 是否全數(shù)字 |
.isalnum() | 是否全是字母或數(shù)字 |
.isspace() | 是否全是空白符 |
.isupper() | 是否全為大寫 |
.islower() | 是否全為小寫 |
.istitle() | 是否符合標(biāo)題規(guī)范(首字母大寫) |
print("abc".isalpha()) # True
print("123".isdigit()) # True
print("abc123".isalnum()) # True
print(" ".isspace()) # True
7. 高級(jí)字符串格式化(f-string)
name = "Alice"
age = 30
# 推薦方式:f-string(Python 3.6+)
print(f"{name} is {age} years old.") # Alice is 30 years old
# 老式:format()
print("{} is {} years old.".format(name, age))
# 占位對(duì)齊控制
print(f"|{name:^10}|{age:>3}|") # 居中+右對(duì)齊:| Alice | 30|
8. 轉(zhuǎn)義字符與原始字符串
| 字符 | 含義 |
|---|---|
\n | 換行 |
\t | 制表符 |
\\ | 反斜杠 |
r"" | 原始字符串,不轉(zhuǎn)義 |
print("hello\nworld") # 換行
print(r"c:\new\folder") # c:\new\folder
9. 其他實(shí)用技巧
長(zhǎng)字符串拼接:
s = ( "This is a very long string " "split across multiple lines " "but still valid." )字符串比較支持
<,>,==等字典序?qū)Ρ龋?/p>print("apple" < "banana") # True
小結(jié):常用字符串方法分類對(duì)照表
| 類型 | 常用方法 |
|---|---|
| 查找判斷 | find(), startswith(), in |
| 修改清洗 | strip(), replace() |
| 大小寫處理 | lower(), capitalize() |
| 對(duì)齊填充 | center(), zfill() |
| 格式化 | f"", format() |
| 判斷內(nèi)容 | isdigit(), isalpha() |
| 分割拼接 | split(), join() |
| 其他 | len(), 轉(zhuǎn)義、比較等 |
(三)字符串切片與索引
1. 字符串索引(正向索引 / 負(fù)向索引)
Python 字符串是序列類型,每個(gè)字符都有對(duì)應(yīng)的索引位置。我們可以通過(guò)索引訪問(wèn)字符串中的單個(gè)字符。
?? 正向索引(從 0 開始)
s = "python" print(s[0]) # p print(s[1]) # y print(s[5]) # n
| 字符 | p | y | t | h | o | n |
|---|---|---|---|---|---|---|
| 索引 | 0 | 1 | 2 | 3 | 4 | 5 |
?? 負(fù)向索引(從 -1 開始,表示從右往左)
s = "python" print(s[-1]) # n print(s[-2]) # o print(s[-6]) # p
| 字符 | p | y | t | h | o | n |
|---|---|---|---|---|---|---|
| 索引 | -6 | -5 | -4 | -3 | -2 | -1 |
注意:
超出索引范圍(如
s[10])會(huì)拋出IndexError。字符串是 不可變對(duì)象,不能通過(guò)索引修改某個(gè)字符。
2. 字符串切片([start:stop:step])
切片(slice)是從字符串中提取子串的一種方式,語(yǔ)法格式為:
s[start : stop : step]
start:起始索引(包含)stop:終止索引(不包含)step:步長(zhǎng)(默認(rèn)為 1)
基礎(chǔ)切片
s = "Hello, Python!" print(s[0:5]) # Hello print(s[7:13]) # Python print(s[:5]) # Hello(從頭開始) print(s[7:]) # Python!(直到結(jié)尾)
使用負(fù)數(shù)索引切片
s = "abcdefg" print(s[-4:-1]) # def print(s[-7:-4]) # abc
添加步長(zhǎng)(step)
s = "0123456789" print(s[::2]) # 02468(每?jī)蓚€(gè)字符取一個(gè)) print(s[1::3]) # 147(從索引 1 開始,每隔 3 個(gè)取)
反向切片(步長(zhǎng)為負(fù)數(shù))
s = "abcdef" print(s[::-1]) # fedcba(字符串反轉(zhuǎn)) print(s[-1:-7:-1]) # fedcba(等同上行)
切片邊界記憶口訣
包頭不包尾:起始位置包含,結(jié)束位置不包含。
步長(zhǎng)方向一致:正數(shù)步長(zhǎng)從左到右,負(fù)數(shù)步長(zhǎng)從右到左。
索引可以為負(fù):負(fù)索引從末尾向前數(shù)。
3. 字符串的遍歷
遍歷字符串可以逐個(gè)訪問(wèn)其字符,常用于統(tǒng)計(jì)、查找、生成新字符串等任務(wù)。
基礎(chǔ)遍歷
s = "hello"
for c in s:
print(c)
輸出:
h e l l o
帶索引遍歷(推薦:使用 enumerate)
s = "hello"
for index, char in enumerate(s):
print(f"{index} -> {char}")
輸出:
0 -> h 1 -> e 2 -> l 3 -> l 4 -> o
經(jīng)典應(yīng)用場(chǎng)景小結(jié)
| 場(chǎng)景 | 示例 |
|---|---|
| 提取子串 | s[3:7] |
| 字符串反轉(zhuǎn) | s[::-1] |
| 判斷回文 | s == s[::-1] |
| 截取前綴/后綴 | s[:3] 或 s[-3:] |
| 每隔一個(gè)字符取 | s[::2] |
(四)字符串格式化
Python 提供了三種主要的字符串格式化方式:
%占位符格式化(經(jīng)典寫法)str.format()方法格式化(兼容寫法)f-string 格式化(推薦方式,Python 3.6+)
1.%占位符格式化(C 風(fēng)格)
這是最早期的格式化方式,語(yǔ)法類似于 C 語(yǔ)言:
name = "Alice"
age = 25
print("My name is %s and I am %d years old." % (name, age))
| 占位符 | 含義 |
|---|---|
%s | 字符串 |
%d | 整數(shù) |
%f | 浮點(diǎn)數(shù)(默認(rèn)小數(shù)點(diǎn)后 6 位) |
%.2f | 浮點(diǎn)數(shù)保留 2 位小數(shù) |
缺點(diǎn):可讀性差、容易出錯(cuò),不推薦用于新代碼。
2.str.format()方法格式化
引入于 Python 2.7 / 3.0,支持位置參數(shù)、關(guān)鍵字參數(shù),更靈活:
name = "Alice"
age = 25
print("My name is {} and I am {} years old.".format(name, age))
?? 支持位置參數(shù) / 關(guān)鍵字參數(shù)
print("Hello, {0}. You are {1}.".format("Bob", 30))
print("Hello, {name}. You are {age}.".format(name="Bob", age=30))
?? 支持格式控制
pi = 3.1415926
print("Pi is {:.2f}".format(pi)) # Pi is 3.14
缺點(diǎn):寫法較冗長(zhǎng),推薦 f-string 替代。
3.f-string 格式化(Python 3.6+,推薦?)
最現(xiàn)代、最簡(jiǎn)潔的格式化方式,代碼更清晰,推薦作為首選寫法。
name = "Alice"
age = 25
print(f"My name is {name} and I am {age} years old.")
?? 支持表達(dá)式
a = 5
b = 3
print(f"{a} + = {a + b}")
?? 支持格式化控制
pi = 3.1415926
print(f"Pi rounded to 2 digits: {pi:.2f}")
?? 與函數(shù)、數(shù)據(jù)結(jié)構(gòu)結(jié)合
user = {"name": "Bob", "score": 88}
print(f"{user['name']} scored {user['score']}")
三種方式對(duì)比
| 特性 | % 格式化 | str.format() | f-string |
|---|---|---|---|
| 可讀性 | ? | ? | ??? |
| 功能強(qiáng)大 | 一般 | ?? | ??? |
| 寫法簡(jiǎn)潔 | 一般 | ? | ??? |
| 推薦程度 | ? 不推薦 | ? 過(guò)渡用法 | ??? 首選 |
小結(jié)一下吧
新代碼推薦 f-string,語(yǔ)法簡(jiǎn)潔、表達(dá)能力強(qiáng)。
str.format()適用于兼容舊版本 Python。%占位符方式適合了解,不建議用于正式項(xiàng)目中。
六、列表、字典與集合 —— Python 中的數(shù)據(jù)組織能力
(一)為什么需要“數(shù)據(jù)結(jié)構(gòu)”
在前面的章節(jié)中,我們已經(jīng)學(xué)習(xí)了 變量、基本數(shù)據(jù)類型、控制流和函數(shù)以及字符串。這些知識(shí)可以幫助我們描述一個(gè)值、處理一次邏輯、完成一個(gè)功能。
但在真實(shí)的程序中,我們面對(duì)的往往不是“一個(gè)值”,而是:
一組數(shù)據(jù)
多條記錄
多個(gè)對(duì)象
一批狀態(tài)或配置項(xiàng)
這正是 “數(shù)據(jù)結(jié)構(gòu)”存在的意義。
1. 單個(gè)變量的局限性
先看一個(gè)最簡(jiǎn)單的例子。如果我們只需要保存一個(gè)人的名字,用一個(gè)變量就足夠:
name = "張彥峰"
但如果現(xiàn)在要保存 10 個(gè)、100 個(gè)、甚至更多名字 呢?你顯然不可能這樣寫:
name1 = "盧政伯" name2 = "呂鴻昌" name3 = "秦學(xué)磊"
這種寫法存在明顯問(wèn)題:
變量數(shù)量不可控
無(wú)法方便地進(jìn)行循環(huán)處理
代碼幾乎不可維護(hù)
這時(shí),我們需要一種方式,把“一組相關(guān)的數(shù)據(jù)”組織在一起。
2. 從“一個(gè)值”到“一組值”
繼續(xù)剛才的例子,如果我們希望把多個(gè)名字放在一起,Python 提供了 列表(list) 這樣的數(shù)據(jù)結(jié)構(gòu):
names = ["張彥峰","盧政伯","呂鴻昌","秦學(xué)磊"]
這樣做的好處是:
所有名字屬于同一個(gè)整體
可以通過(guò)循環(huán)統(tǒng)一處理
可以按順序訪問(wèn)、修改、刪除
這一步,本質(zhì)上是從:“一個(gè)變量保存一個(gè)值”升級(jí)為:“一個(gè)變量保存一組有結(jié)構(gòu)的數(shù)據(jù)”
3. 一條記錄 vs 多條記錄
再看一個(gè)更貼近實(shí)際開發(fā)的例子。假設(shè)我們要表示一個(gè)用戶的信息:
user_name = "張彥峰" user_age = 18 user_email = "zyf@alibaba.com"
這種寫法的問(wèn)題在于:
信息是分散的
很難整體傳遞或管理
字段之間缺乏明確的“歸屬關(guān)系”
這時(shí),更合理的做法是使用 字典(dict):
user = {
"name": "張彥峰",
"age": 18,
"email": "zyf@alibaba.com"
}
字典的特點(diǎn)是:
通過(guò) key(鍵) 來(lái)訪問(wèn)數(shù)據(jù)
每個(gè)字段都有清晰的含義
非常適合描述“一條記錄”
如果現(xiàn)在有 多條用戶記錄,我們就可以把多個(gè)字典放進(jìn)一個(gè)列表中:
users = [
{"name": "張彥峰", "age": 18},
{"name": "張琴", "age": 18}
]
你可以看到:數(shù)據(jù)結(jié)構(gòu)是可以組合使用的,這正是后面處理嵌套數(shù)據(jù)(如 JSON)的基礎(chǔ)。
4. 最常用的幾種數(shù)據(jù)結(jié)構(gòu)
在本章中,我們將系統(tǒng)學(xué)習(xí) Python 中最常用的幾種數(shù)據(jù)結(jié)構(gòu):
列表(list):用于存儲(chǔ)有序、可變的一組數(shù)據(jù)
字典(dict):用于存儲(chǔ)鍵值映射關(guān)系
集合(set):用于去重和集合運(yùn)算
元組(tuple):用于表示不可變的數(shù)據(jù)組合
通過(guò)這一章,你將逐步建立起這樣的能力:
根據(jù)問(wèn)題特點(diǎn),選擇合適的數(shù)據(jù)結(jié)構(gòu),并高效地操作它們
這也是后續(xù)學(xué)習(xí) 嵌套結(jié)構(gòu)、JSON 數(shù)據(jù)解析、配置處理 的必要基礎(chǔ)。
(二)列表(List):有序數(shù)據(jù)的容器
列表是 Python 中最常用且強(qiáng)大的數(shù)據(jù)結(jié)構(gòu)之一,廣泛用于存儲(chǔ)有序、可變的數(shù)據(jù)。作為一個(gè)容器類型,它不僅能存儲(chǔ)多種數(shù)據(jù)類型,還能高效支持?jǐn)?shù)據(jù)的添加、刪除、修改和訪問(wèn)。掌握列表的使用是理解 Python 數(shù)據(jù)處理能力的重要基礎(chǔ)。
1. 列表的基本概念
列表(list)是一個(gè)有序的容器,可以存儲(chǔ)任意類型的數(shù)據(jù),包括整數(shù)、字符串、布爾值,甚至是其他列表。列表的核心特點(diǎn)包括:
有序性:列表中的元素是按順序排列的,每個(gè)元素都有一個(gè)對(duì)應(yīng)的索引,索引從 0 開始。
可變性:列表是可變的,可以在原地修改列表的內(nèi)容,例如添加、刪除或更新元素。
支持多種數(shù)據(jù)類型:列表可以包含不同類型的元素,甚至可以存儲(chǔ)其他容器類型(如列表、字典等)。
# 示例:包含整數(shù)的列表 numbers = [1, 2, 3, 4, 5] # 示例:包含不同數(shù)據(jù)類型的列表 info = ["Alice", 18, True, 3.14, ["nested", "list"]]
關(guān)鍵點(diǎn):與字符串(str)類似,列表也支持通過(guò)索引訪問(wèn)和切片操作,但字符串是不可變的,而列表可以直接修改。
2. 列表的創(chuàng)建與訪問(wèn)
創(chuàng)建列表
列表的創(chuàng)建非常簡(jiǎn)便,可以通過(guò)方括號(hào) [] 來(lái)定義??梢詣?chuàng)建空列表、包含多個(gè)元素的列表,或是包含混合類型元素的列表。
# 空列表 empty_list = [] # 含有多個(gè)整數(shù)的列表 numbers = [1, 2, 3, 4, 5] # 含有不同類型的元素的列表 mixed = [1, "Alice", True, 3.14]
訪問(wèn)列表元素
通過(guò)索引訪問(wèn)列表中的元素,索引從 0 開始。列表還支持負(fù)索引,允許從列表的末尾開始訪問(wèn)元素。
numbers = [10, 20, 30, 40] # 正向索引 print(numbers[0]) # 10 print(numbers[3]) # 40 # 負(fù)向索引 print(numbers[-1]) # 40 print(numbers[-2]) # 30
此外,列表支持切片操作,可以通過(guò)指定起始和結(jié)束索引來(lái)獲取子列表。
# 切片示例 print(numbers[1:3]) # [20, 30] print(numbers[:2]) # [10, 20] print(numbers[2:]) # [30, 40]
注意:切片操作返回的是新列表,原列表內(nèi)容不受影響。
3. 列表的常用修改操作
列表是可變的,意味著可以在原地修改其內(nèi)容。常見的修改操作包括添加元素、刪除元素和修改元素。
添加元素
append():將元素添加到列表的末尾。insert():將元素插入到指定的索引位置。extend():批量追加
# 示例:添加元素 nums = [1, 2, 3] nums.append(4) # 在末尾添加元素 nums.insert(1, 1.5) # 在索引 1 處插入元素 nums.extend([3, 4]) # [1, 2, 3, 4] print(nums) # 輸出: [1, 1.5, 2, 3, 4, 3, 4]
刪除元素
remove():刪除第一個(gè)匹配到的元素。pop():刪除并返回指定索引位置的元素(默認(rèn)刪除最后一個(gè)元素)。del:根據(jù)索引刪除元素。clear():清空列表
# 示例:刪除元素 nums.remove(1.5) # 刪除元素 1.5 popped_element = nums.pop() # 刪除并返回最后一個(gè)元素 del nums[0] # 刪除索引 0 處的元素 print(nums) # 輸出: [2, 3, 4, 3]
修改元素
通過(guò)索引可以直接修改列表中的元素:
nums[0] = 10 # 將索引 0 處的元素修改為 10 print(nums) # 輸出: [10, 3]
4. 列表的遍歷與循環(huán)配合
遍歷列表元素
遍歷列表元素時(shí),最常用的是 for 循環(huán)。可以直接遍歷列表的每個(gè)元素,或者使用 range() 函數(shù)配合索引來(lái)遍歷列表。
# 直接遍歷元素
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit) # 輸出每個(gè)水果
# 使用索引遍歷
for i in range(len(fruits)):
print(i, fruits[i]) # 輸出索引和值
# 使用 enumerate 獲取索引和值
for idx, fruit in enumerate(fruits):
print(idx, fruit) # 輸出索引和值
應(yīng)用場(chǎng)景:
只關(guān)心值:直接遍歷元素。
需要索引:使用
range()或enumerate()。
5. 列表的常用內(nèi)置操作與函數(shù)
Python 提供了豐富的內(nèi)置函數(shù)和方法來(lái)操作列表。以下是一些常用操作:
len():返回列表的長(zhǎng)度。in:檢查元素是否存在于列表中。count():統(tǒng)計(jì)元素在列表中出現(xiàn)的次數(shù)。index():查找元素首次出現(xiàn)的位置。sort():原地排序。sorted():返回一個(gè)新排序的列表。reverse():原地反轉(zhuǎn)列表。
nums = [4, 2, 7, 1] print(len(nums)) # 輸出: 4 print(2 in nums) # 輸出: True print(nums.count(2)) # 輸出: 1 print(nums.index(7)) # 輸出: 2 nums.sort() # 原地排序 sorted_nums = sorted(nums) # 返回新列表 nums.reverse() # 原地反轉(zhuǎn)
注意:sort() 和 reverse() 會(huì)改變?cè)斜?,?sorted() 返回新列表。
6. 列表推導(dǎo)式(List Comprehension)
列表推導(dǎo)式是 Python 中非常強(qiáng)大且簡(jiǎn)潔的語(yǔ)法,它允許在一行代碼內(nèi)創(chuàng)建一個(gè)新的列表。相比于傳統(tǒng)的 for 循環(huán),列表推導(dǎo)式更加簡(jiǎn)潔且執(zhí)行效率高。
# 基本語(yǔ)法:生成平方數(shù)列表 squares = [x**2 for x in range(5)] print(squares) # 輸出: [0, 1, 4, 9, 16] # 帶條件的推導(dǎo)式:生成偶數(shù)的平方數(shù)列表 even_squares = [x**2 for x in range(5) if x % 2 == 0] print(even_squares) # 輸出: [0, 4, 16]
列表推導(dǎo)式的優(yōu)勢(shì):
簡(jiǎn)潔性:可以用一行代碼替代多行。
可讀性:語(yǔ)法清晰、易懂。
性能:避免了手動(dòng)使用
append()。
7. 淺拷貝(Shallow Copy)與深拷貝(Deep Copy)
在 Python 中,理解淺拷貝與深拷貝的前提,是理解一個(gè)核心事實(shí):
Python 中的變量,本質(zhì)上保存的是對(duì)象的引用(reference),而不是對(duì)象本身。
因此,“拷貝”并不等價(jià)于“生成一個(gè)完全獨(dú)立的新對(duì)象”,是否真正獨(dú)立,取決于拷貝的層級(jí)。
7.1 對(duì)象、引用與容器的關(guān)系模型
先從一個(gè)最小示例理解引用關(guān)系:
a = [1, 2, 3] b = a
此時(shí):
a與b指向 同一個(gè) list 對(duì)象對(duì)
a的任何修改,都會(huì)體現(xiàn)在b上
a[0] = 100 print(b) # [100, 2, 3]
這不是拷貝,而是 引用綁定(aliasing)。
7.2 淺拷貝(Shallow Copy)
淺拷貝的定義:創(chuàng)建一個(gè)新的容器對(duì)象,但容器中保存的元素仍然是對(duì)原對(duì)象中元素的引用。
換句話說(shuō):
外層容器是新的
內(nèi)層對(duì)象是共享的
常見的淺拷貝方式
以下方式都會(huì)產(chǎn)生淺拷貝:
b = a.copy() b = a[:] b = list(a)
淺拷貝的行為示意
a = [[1, 2], [3, 4]] b = a.copy()
內(nèi)存關(guān)系可以抽象為:
a ──? list ──? [ L1 , L2 ] b ──? list ──? [ L1 , L2 ]
a和b是兩個(gè)不同的列表對(duì)象但它們內(nèi)部的
L1、L2指向同一個(gè)子列表對(duì)象
淺拷貝的典型“坑”
a = [[1, 2], [3, 4]] b = a.copy() b[0][0] = 100 print(a) # [[100, 2], [3, 4]]
原因分析:
b[0]與a[0]指向同一子列表修改子列表,會(huì)影響所有引用它的容器
淺拷貝何時(shí)是安全的
列表中包含 不可變對(duì)象,后續(xù)操作只涉及 整體替換,不修改內(nèi)部對(duì)象
a = [1, 2, 3, "abc"] b = a.copy() b[0] = 100 # 不影響 a print(a) # [1, 2, 3, 'abc'] print(b) # [100, 2, 3, 'abc']
7.3 深拷貝(Deep Copy)
深拷貝的定義:遞歸地復(fù)制對(duì)象本身以及其內(nèi)部引用的所有子對(duì)象,生成完全獨(dú)立的對(duì)象樹。
特點(diǎn):
外層容器獨(dú)立
內(nèi)層對(duì)象也完全獨(dú)立
修改任何層級(jí)都不會(huì)相互影響
深拷貝的標(biāo)準(zhǔn)方式
import copy b = copy.deepcopy(a)
深拷貝的行為示意
a = [[1, 2], [3, 4]] b = copy.deepcopy(a)
內(nèi)存模型:
a ──? list ──? [ L1 , L2 ] b ──? list ──? [ L1' , L2' ]
L1 ≠ L1'L2 ≠ L2'
深拷貝示例驗(yàn)證
a = [[1, 2], [3, 4]] b = copy.deepcopy(a) b[0][0] = 100 print(a) # [[1, 2], [3, 4]]
7.4 淺拷貝 vs 深拷貝的核心對(duì)比
| 維度 | 淺拷貝 | 深拷貝 |
|---|---|---|
| 外層容器 | 新對(duì)象 | 新對(duì)象 |
| 內(nèi)層對(duì)象 | 共享引用 | 完全獨(dú)立 |
| 性能 | 高 | 相對(duì)較低 |
| 內(nèi)存占用 | 少 | 多 |
| 修改風(fēng)險(xiǎn) | 高(嵌套結(jié)構(gòu)) | 極低 |
8. list * n與淺拷貝的本質(zhì)關(guān)系
a = [[0]] * 3
等價(jià)于:
sub = [0] a = [sub, sub, sub]
因此:
a[0][0] = 1 print(a) # [[1], [1], [1]]
這是淺拷貝的一種極端形式:
外層列表新建
內(nèi)層對(duì)象全部指向同一個(gè)引用
正確構(gòu)造獨(dú)立子列表的方式
a = [[0] for _ in range(3)]
9. 列表的常見坑
淺拷貝問(wèn)題
當(dāng)使用 * 運(yùn)算符復(fù)制包含可變?cè)氐牧斜頃r(shí),可能會(huì)發(fā)生意外的引用問(wèn)題。例如:
a = [[1, 2]] * 3 a[0][0] = 100 print(a) # 輸出: [[100, 2], [100, 2], [100, 2]]
原因:所有子列表都引用了同一個(gè)內(nèi)存地址,修改一個(gè)元素會(huì)影響所有子列表??梢允褂?copy.deepcopy() 來(lái)避免這種問(wèn)題。
遍歷時(shí)修改列表
直接在遍歷列表時(shí)修改列表,可能會(huì)導(dǎo)致不預(yù)期的結(jié)果。例如:
nums = [1, 2, 3, 4]
for n in nums:
if n % 2 == 0:
nums.remove(n) # 刪除偶數(shù)元素
print(nums) # 輸出: [1, 3]
解決辦法:遍歷列表的副本,如 nums[:],或者使用列表推導(dǎo)式來(lái)過(guò)濾元素。
# 解決方法:遍歷副本
for n in nums[:]:
if n % 2 == 0:
nums.remove(n)
小結(jié)一下吧
列表是 有序、可變 的數(shù)據(jù)容器,是 Python 數(shù)據(jù)結(jié)構(gòu)的核心之一
支持索引訪問(wèn)、切片操作、原地修改和高效遍歷
提供大量?jī)?nèi)置方法,配合列表推導(dǎo)式可以寫出高效、優(yōu)雅的代碼
常見風(fēng)險(xiǎn)點(diǎn)主要集中在:
淺拷貝與引用共享
遍歷過(guò)程中修改列表
誤用
list * n構(gòu)造嵌套結(jié)構(gòu)
熟練掌握列表,將為后續(xù)學(xué)習(xí)字典、集合、嵌套數(shù)據(jù)結(jié)構(gòu)以及 JSON / 數(shù)據(jù)分析場(chǎng)景打下堅(jiān)實(shí)基礎(chǔ)。
(三)字典(dict):鍵值映射的數(shù)據(jù)結(jié)構(gòu)
字典(dict)是 Python 中最核心、使用頻率極高的一種內(nèi)置數(shù)據(jù)結(jié)構(gòu),用于存儲(chǔ) 鍵值對(duì)(key-value)映射關(guān)系。它通過(guò) key 直接定位 value,而不是像列表那樣依賴下標(biāo)位置,因此在表達(dá)“記錄型數(shù)據(jù)”“配置數(shù)據(jù)”“映射關(guān)系”時(shí)具有不可替代的優(yōu)勢(shì)。
從底層實(shí)現(xiàn)角度看,Python 字典基于 哈希表(Hash Table) 實(shí)現(xiàn),在絕大多數(shù)場(chǎng)景下,其查找、插入、刪除操作的時(shí)間復(fù)雜度都接近 O(1)。
1. 字典的設(shè)計(jì)思想
為什么需要字典?
列表適合解決以下問(wèn)題:
數(shù)據(jù) 有明確順序
主要通過(guò) 位置(索引) 訪問(wèn)
但在很多真實(shí)業(yè)務(wù)中,我們更關(guān)心的是:
“這個(gè)人的 年齡 是多少?”
“這個(gè)配置項(xiàng) timeout 對(duì)應(yīng)的值是多少?”
此時(shí),如果仍然依賴下標(biāo)訪問(wèn):
可讀性差
容易出錯(cuò)
不利于擴(kuò)展和維護(hù)
字典的核心思想是:
用有意義的 key,直接映射到對(duì)應(yīng)的 value
對(duì)比示例
# 使用列表表示用戶信息(語(yǔ)義不清晰)
user_list = ["Alice", 18, "alice@example.com"]
# 使用字典表示用戶信息(結(jié)構(gòu)清晰)
user_dict = {
"name": "Alice",
"age": 18,
"email": "alice@example.com"
}
字典的優(yōu)勢(shì)
字段含義清晰,代碼自解釋
不依賴順序,結(jié)構(gòu)穩(wěn)定
便于新增、刪除字段
非常貼近 JSON / 配置文件 / 數(shù)據(jù)庫(kù)記錄的結(jié)構(gòu)
2. 字典的基本使用
創(chuàng)建字典
# 使用花括號(hào)(最常見)
user = {"name": "Alice", "age": 18}
# 使用 dict 構(gòu)造函數(shù)
user2 = dict(name="Bob", age=20)
# 從鍵值對(duì)列表創(chuàng)建
pairs = [("name", "Charlie"), ("age", 22)]
user3 = dict(pairs)
訪問(wèn) value
print(user["name"]) # Alice
print(user.get("age")) # 18
重要約束說(shuō)明
key 必須唯一:后定義的 key 會(huì)覆蓋之前的值
key 必須是不可變類型:
合法:
str、int、float、tuple非法:
list、dict、set
# 合法
d = {(1, 2): "ok"}
# 非法(會(huì)拋出 TypeError)
d = {[1, 2]: "error"}
3. 字典的增刪改查(CRUD)
新增 / 修改(統(tǒng)一語(yǔ)法)
user["email"] = "alice@example.com" # 新增 user["age"] = 19 # 修改
字典不會(huì)區(qū)分“新增”和“修改”,是否存在 key 決定行為。
刪除元素
# 刪除指定 key
del user["email"]
# 刪除并返回 value
age = user.pop("age")
# 指定默認(rèn)值,避免 KeyError
age = user.pop("age", None)
查詢
user["name"] # 不存在會(huì)拋 KeyError
user.get("email") # 不存在返回 None
user.get("email", "N/A") # 返回默認(rèn)值
是否存在某個(gè) key
"name" in user # True / False "email" not in user # True / False
4. 字典的遍歷方式
遍歷 key(默認(rèn)行為)
for key in user:
print(key, user[key])
遍歷 value
for value in user.values():
print(value)
遍歷 key-value 對(duì)(最常用)
for key, value in user.items():
print(key, value)
遍歷時(shí)的注意事項(xiàng)
遍歷過(guò)程中 不建議修改字典結(jié)構(gòu)(增刪 key)
若需要修改,可先遍歷
list(user.keys())
5. 字典的常用方法與典型模式
常用方法速覽
user.keys() # 所有 key(視圖對(duì)象) user.values() # 所有 value user.items() # (key, value) 對(duì) user.clear() # 清空字典 user.copy() # 淺拷貝
setdefault:安全設(shè)置默認(rèn)值
user.setdefault("age", 18)
等價(jià)于:
if "age" not in user:
user["age"] = 18
update:批量更新
user.update({"email": "alice@example.com", "age": 20})
典型模式一:計(jì)數(shù)器
words = ["apple", "banana", "apple", "cherry"]
counter = {}
for word in words:
counter[word] = counter.get(word, 0) + 1
print(counter)
典型模式二:分組
students = [
("Alice", "A"),
("Bob", "B"),
("Charlie", "A")
]
groups = {}
for name, grade in students:
groups.setdefault(grade, []).append(name)
print(groups) #{'A': ['Alice', 'Charlie'], 'B': ['Bob']}
6. 字典的拷貝與可變性
淺拷貝(Shallow Copy)
d1 = {"a": [1, 2]}
d2 = d1.copy()
d2["a"].append(3)
print(d1) # {'a': [1, 2, 3]}
淺拷貝只復(fù)制“引用”,不會(huì)復(fù)制內(nèi)部的可變對(duì)象。
深拷貝(Deep Copy)
import copy
d1 = {"a": [1, 2]}
d2 = copy.deepcopy(d1)
d2["a"].append(3)
print(d1) # {'a': [1, 2]}
7. 字典與列表的組合(復(fù)雜結(jié)構(gòu)建模)
列表中放字典
users = [
{"name": "Alice", "age": 18},
{"name": "Bob", "age": 20}
]
for user in users:
print(user["name"], user["age"])
字典中放列表
grades = {
"Alice": [90, 95, 88],
"Bob": [75, 80, 82]
}
for name, scores in grades.items():
avg = sum(scores) / len(scores)
print(name, avg)
多層嵌套字典
users = {
"Alice": {"age": 18, "email": "alice@example.com"},
"Bob": {"age": 20, "email": "bob@example.com"}
}
print(users["Alice"]["email"])
小結(jié)一下吧
字典是 鍵值映射容器,核心優(yōu)勢(shì)是按 key 快速訪問(wèn)
基于哈希表實(shí)現(xiàn),查找效率高
支持完整的增刪改查操作
提供豐富的方法與常見編程模式
可與列表自由組合,構(gòu)建任意復(fù)雜數(shù)據(jù)結(jié)構(gòu)
熟練掌握字典,是后續(xù)學(xué)習(xí) JSON 處理、配置管理、Web 開發(fā)、數(shù)據(jù)分析 的關(guān)鍵基礎(chǔ)。
(四)集合(set):去重與集合運(yùn)算
集合(set)是 Python 中用于存儲(chǔ)唯一元素并支持數(shù)學(xué)集合運(yùn)算的一種內(nèi)置數(shù)據(jù)結(jié)構(gòu)。其核心價(jià)值在于:
天然去重
高效的成員判斷(O(1) 平均時(shí)間復(fù)雜度)
直觀表達(dá)集合關(guān)系(交集、并集、差集等)
在實(shí)際開發(fā)中,集合常用于數(shù)據(jù)清洗、權(quán)限控制、標(biāo)簽計(jì)算、關(guān)系判斷等場(chǎng)景。
1. 什么是集合(set)
集合的概念來(lái)源于數(shù)學(xué)中的集合:一組不重復(fù)的元素構(gòu)成的整體。
# 使用大括號(hào)創(chuàng)建集合
fruits = {"apple", "banana", "cherry"}
print(fruits)
# 輸出順序不固定,例如:{'banana', 'apple', 'cherry'}
# 使用 set() 構(gòu)造函數(shù)
nums = set([1, 2, 3, 2, 1])
print(nums) # {1, 2, 3},自動(dòng)去重
核心特性
無(wú)序(unordered):不支持索引和切片操作;元素順序不穩(wěn)定,每次打印結(jié)果可能不同
元素唯一(unique):相同元素只會(huì)保留一份,適合用于去重、排重邏輯
成員判斷高效:
x in set的時(shí)間復(fù)雜度為 O(1),明顯快于列表的 O(n)僅支持可哈希(hashable)類型:合法元素:
int、float、str、tuple;非法元素:list、dict、set
s = {(1, 2), "abc", 10} # 合法
# s = {[1, 2], 3} # TypeError: unhashable type: 'list'
2. 集合的創(chuàng)建方式
# 空集合(注意不能用 {})
empty_set = set()
# 從列表 / 元組 / 字符串創(chuàng)建
set([1, 2, 2, 3])
set(("a", "b", "a"))
set("hello") # {'h', 'e', 'l', 'o'}
??
{}表示空字典,而不是空集合。
3. 集合的基本操作
添加元素
nums = {1, 2, 3}
nums.add(4)
print(nums) # {1, 2, 3, 4}
add():一次只能添加一個(gè)元素
刪除元素
nums.remove(2) # 元素不存在會(huì)拋 KeyError nums.discard(5) # 元素不存在不會(huì)報(bào)錯(cuò) popped = nums.pop() # 隨機(jī)刪除并返回一個(gè)元素 nums.clear() # 清空集合
對(duì)比說(shuō)明:
| 方法 | 元素不存在時(shí) | 使用建議 |
|---|---|---|
| remove | 拋異常 | 確保元素一定存在 |
| discard | 不報(bào)錯(cuò) | 更安全,推薦 |
| pop | 隨機(jī)刪除 | 不依賴具體元素 |
成員判斷
nums = {1, 2, 3}
print(2 in nums) # True
print(5 not in nums) # True
集合是做“是否存在”判斷的首選結(jié)構(gòu)。
4. 集合運(yùn)算(重點(diǎn))
集合支持與數(shù)學(xué)集合一致的運(yùn)算語(yǔ)義。
a = {1, 2, 3}
b = {3, 4, 5}
并集(Union)
print(a | b)
print(a.union(b))
# {1, 2, 3, 4, 5}
交集(Intersection)
print(a & b)
print(a.intersection(b))
# {3}
差集(Difference)
print(a - b)
print(a.difference(b))
# {1, 2}
對(duì)稱差集(Symmetric Difference)
print(a ^ b)
print(a.symmetric_difference(b))
# {1, 2, 4, 5}
5. 集合關(guān)系判斷
a = {1, 2}
b = {1, 2, 3}
print(a.issubset(b)) # True 子集
print(b.issuperset(a)) # True 超集
print(a.isdisjoint({4})) # True 是否無(wú)交集
常用于:權(quán)限校驗(yàn)、條件覆蓋判斷。
6. 可變集合與不可變集合
set(可變)
支持 add / remove
不能作為字典的 key
frozenset(不可變)
fs = frozenset([1, 2, 3])
一旦創(chuàng)建不可修改
可作為字典 key 或集合元素
常用于配置、常量集合
7. 典型應(yīng)用場(chǎng)景
去重
nums = [1, 2, 2, 3, 4, 4] unique_nums = list(set(nums))
權(quán)限 / 標(biāo)簽判斷
user_roles = {"admin", "editor"}
required_roles = {"editor", "viewer"}
if user_roles & required_roles:
print("有權(quán)限")
查找共同元素
team_a = {"Alice", "Bob", "Charlie"}
team_b = {"Bob", "David"}
common = team_a & team_b
數(shù)據(jù)差異分析
old_users = {1, 2, 3}
new_users = {2, 3, 4}
lost = old_users - new_users # 流失用戶
added = new_users - old_users # 新增用戶
8. 與列表、字典的對(duì)比
| 結(jié)構(gòu) | 是否有序 | 是否可重復(fù) | 核心用途 |
|---|---|---|---|
| list | 是 | 是 | 順序數(shù)據(jù) |
| dict | 是(3.7+) | key 不可重復(fù) | 映射關(guān)系 |
| set | 否 | 否 | 去重 / 關(guān)系計(jì)算 |
小結(jié)一下吧
集合是 無(wú)序、不重復(fù) 的數(shù)據(jù)結(jié)構(gòu)
成員判斷和集合運(yùn)算效率極高
核心能力:
去重
并 / 交 / 差 / 對(duì)稱差
子集 / 超集判斷
是處理“關(guān)系型數(shù)據(jù)”和“唯一性約束”的利器
掌握集合后,你可以用更簡(jiǎn)潔、更高效的方式解決大量實(shí)際問(wèn)題,為后續(xù)復(fù)雜數(shù)據(jù)結(jié)構(gòu)和算法學(xué)習(xí)打下堅(jiān)實(shí)基礎(chǔ)。
(五)元組(tuple):不可變序列
元組(tuple)是 Python 中的一種有序、不可變的序列類型。它在語(yǔ)法和使用方式上與列表(list)非常相似,但核心差異在于:一旦創(chuàng)建,元組的結(jié)構(gòu)不能被修改。
在實(shí)際開發(fā)中,元組常用于:
表示固定含義的數(shù)據(jù)組合
作為函數(shù)的多返回值
作為字典的 key
在某些場(chǎng)景下,替代列表以提升安全性和性能
1. 元組的基本概念
元組的核心特性
有序(Ordered):元素按順序存儲(chǔ),可以通過(guò)索引訪問(wèn)
不可變(Immutable):元組創(chuàng)建后,不能修改、添加、刪除元素
可包含任意類型:支持混合類型,支持嵌套結(jié)構(gòu)
point = (10, 20)
info = ("Alice", 18, True, ["Python", "Go"])
元素訪問(wèn)
print(point[0]) # 10 print(info[1]) # 18 print(info[-1]) # ['Python', 'Go']
元組 vs 列表(核心對(duì)比)
| 特性 | 列表 list | 元組 tuple |
|---|---|---|
| 是否可變 | 可變 | 不可變 |
| 是否有序 | 是 | 是 |
| 是否可作為 dict key | 否 | 是 |
| 使用場(chǎng)景 | 動(dòng)態(tài)數(shù)據(jù) | 固定數(shù)據(jù) |
| 語(yǔ)法 | [ ] | ( ) |
2. 創(chuàng)建元組的多種方式
使用圓括號(hào)(最常見)
t = (1, 2, 3)
省略圓括號(hào)(語(yǔ)法層面支持)
t = 1, 2, 3
? 常用于函數(shù)返回多個(gè)值或簡(jiǎn)單賦值場(chǎng)景
? 不建議初學(xué)階段頻繁使用,容易降低可讀性
單元素元組(非常重要)
single = (5,) # 正確 single2 = (5) # 錯(cuò)誤,這是整數(shù)
?? 結(jié)論:是否為元組,關(guān)鍵不在于括號(hào),而在于 逗號(hào)
使用tuple()構(gòu)造函數(shù)
nums = [1, 2, 3]
chars = "abc"
print(tuple(nums)) # (1, 2, 3)
print(tuple(chars)) # ('a', 'b', 'c')
3. 元組的索引、切片與遍歷
索引訪問(wèn)
t = (10, 20, 30, 40) print(t[0]) # 10 print(t[-1]) # 40
切片操作
print(t[1:3]) # (20, 30) print(t[:2]) # (10, 20)
注意:
切片返回的是 新元組
原元組不會(huì)發(fā)生任何變化
遍歷元組
for item in t:
print(item)
4. 元組的不可變性(核心理解)
不允許的操作
t = (1, 2, 3) # t[0] = 10 # TypeError # t.append(4) # AttributeError # del t[1] # TypeError
元組中包含可變對(duì)象的情況(重點(diǎn))
t = (1, [2, 3]) t[1].append(4) print(t) # (1, [2, 3, 4])
如何理解?
元組不可變:指的是
??t[1]這個(gè)“引用”不能被替換列表可變:
?? 列表內(nèi)部的內(nèi)容仍然可以修改
?? 一句話總結(jié):
元組保證的是“結(jié)構(gòu)不變”,而不是“內(nèi)容絕對(duì)不變”
5. 元組解包(非常重要的能力)
基本解包
point = (10, 20) x, y = point print(x, y) # 10 20
解包與函數(shù)返回值
def get_min_max(nums):
return min(nums), max(nums)
min_val, max_val = get_min_max([3, 7, 1, 9])
print(min_val, max_val) # 1 9
使用*進(jìn)行擴(kuò)展解包
a, *b = (1, 2, 3, 4) print(a) # 1 print(b) # [2, 3, 4]
6. 元組的常見應(yīng)用場(chǎng)景
作為字典的 key
location = {}
location[(10, 20)] = "Home"
print(location)
原因:
字典 key 必須是 不可變類型
列表不能作為 key,元組可以
函數(shù)返回多個(gè)值(Pythonic 寫法)
def user_info():
return "Alice", 18, True
name, age, active = user_info()
表示“業(yè)務(wù)含義固定”的數(shù)據(jù)
HOST = "localhost" PORT = 8080 CONFIG = (HOST, PORT)
?? 語(yǔ)義層面表達(dá):
“這組數(shù)據(jù)不應(yīng)該被修改”
7. 元組的常用方法與內(nèi)置函數(shù)
元組方法非常少,這是其設(shè)計(jì)的一部分:
t = (1, 2, 2, 3) print(t.count(2)) # 2 print(t.index(3)) # 3
常配合使用的內(nèi)置函數(shù):
len(t) max(t) min(t) sum(t)
8. 元組與列表的深入對(duì)比
| 維度 | 列表 | 元組 |
|---|---|---|
| 內(nèi)存占用 | 較高 | 較低 |
| 創(chuàng)建速度 | 較慢 | 較快 |
| 安全性 | 易被修改 | 更安全 |
| 可讀性 | 偏數(shù)據(jù)容器 | 偏結(jié)構(gòu)表達(dá) |
小結(jié)一下吧
元組是 不可變的有序序列
與列表相似,但語(yǔ)義更偏向 “固定結(jié)構(gòu)”
常見用途:
函數(shù)多返回值
字典 key
不可修改的配置數(shù)據(jù)
支持:
索引、切片、遍歷
解包(非常重要)
(六)總結(jié):如何選擇合適的數(shù)據(jù)結(jié)構(gòu)
? 至此,你已經(jīng)系統(tǒng)掌握 Python 的四大基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)::
列表(list):有序、可變的序列
字典(dict):鍵值映射
集合(set):無(wú)序、不重復(fù)
元組(tuple):不可變的序列
每種數(shù)據(jù)結(jié)構(gòu)都有自己的特點(diǎn)和適用場(chǎng)景,選擇合適的數(shù)據(jù)結(jié)構(gòu)能夠讓程序更高效、更易維護(hù)、更易理解。
1. 數(shù)據(jù)結(jié)構(gòu)選擇對(duì)比表
| 數(shù)據(jù)結(jié)構(gòu) | 特性 | 使用場(chǎng)景 | 注意事項(xiàng) |
|---|---|---|---|
| 列表 list | 有序,可變,可包含任意類型 | 保存順序數(shù)據(jù),循環(huán)處理,多次增刪改 | 遍歷修改時(shí)要小心;切片返回新列表 |
| 字典 dict | 鍵值映射,key 唯一,可變 | 記錄型數(shù)據(jù)(用戶信息、配置、狀態(tài)映射),快速查找 | key 必須不可變;get 可提供默認(rèn)值 |
| 集合 set | 無(wú)序,不重復(fù),高效成員判斷 | 去重、集合運(yùn)算、權(quán)限判斷、標(biāo)簽判斷 | 無(wú)索引;遍歷順序不可控 |
| 元組 tuple | 有序,不可變,可包含任意類型 | 固定數(shù)據(jù)、函數(shù)多值返回、字典 key | 內(nèi)部可變對(duì)象仍可修改;不可修改元素 |
2. 如何選擇數(shù)據(jù)結(jié)構(gòu)
選擇數(shù)據(jù)結(jié)構(gòu)可以按以下幾個(gè)維度:
1?? 是否需要順序
是 → 列表或元組
否 → 集合或字典
2?? 是否需要唯一性
是 → 集合
否 → 列表或元組
3?? 是否需要通過(guò)名字訪問(wèn)
是 → 字典
否 → 列表或集合
4?? 是否需要可變性
可變 → 列表、字典、集合
不可變 → 元組、不可變字典(擴(kuò)展應(yīng)用)
3. 常見組合場(chǎng)景
在實(shí)際開發(fā)中,這些數(shù)據(jù)結(jié)構(gòu)經(jīng)常組合使用:
| 場(chǎng)景 | 數(shù)據(jù)結(jié)構(gòu)組合 | 示例 |
|---|---|---|
| 多條記錄,每條記錄有多個(gè)字段 | 列表 + 字典 | users = [{"name": "Alice", "age":18}, {"name": "Bob", "age":20}] |
| 字典中某字段有多個(gè)值 | 字典 + 列表 | grades = {"Alice":[90,95], "Bob":[80,82]} |
| 去重的列表 | 列表 + 集合 | unique_nums = list(set(nums)) |
| 函數(shù)返回多個(gè)值 | 元組 | min_val, max_val = get_min_max([3,7,1,9]) |
4. 選用原則總結(jié)
順序性優(yōu)先 → 列表/元組
鍵值訪問(wèn)優(yōu)先 → 字典
唯一性 / 集合運(yùn)算 → 集合
數(shù)據(jù)固定不變 → 元組
復(fù)雜嵌套 → 根據(jù)組合場(chǎng)景選擇最合適的容器
?? 核心思想:
數(shù)據(jù)結(jié)構(gòu)是工具,目的是高效組織和操作數(shù)據(jù)。
不要只記語(yǔ)法,要根據(jù)問(wèn)題特性選擇最合適的數(shù)據(jù)結(jié)構(gòu)。
七、結(jié)構(gòu)化數(shù)據(jù):Python 中的數(shù)據(jù)組織與處理
(一)為什么需要結(jié)構(gòu)化數(shù)據(jù)建模
在前面的章節(jié)中,我們已經(jīng)學(xué)習(xí)了 Python 提供的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu):列表(list)、字典(dict)、集合(set)和元組(tuple)。
這些數(shù)據(jù)結(jié)構(gòu)在表達(dá)簡(jiǎn)單數(shù)據(jù)時(shí)非常高效,但當(dāng)我們開始處理真實(shí)世界中的業(yè)務(wù)數(shù)據(jù)時(shí),很快就會(huì)發(fā)現(xiàn):僅靠基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),往往難以支撐復(fù)雜數(shù)據(jù)的長(zhǎng)期演進(jìn)與工程化使用。
結(jié)構(gòu)化數(shù)據(jù)建模,正是為了解決這一問(wèn)題而出現(xiàn)的。
1. 現(xiàn)實(shí)世界數(shù)據(jù)的天然復(fù)雜性
現(xiàn)實(shí)中的數(shù)據(jù)幾乎從來(lái)不是“單值”的,而是具有以下典型特征:
多字段(Multi-attribute):一個(gè)用戶不僅有名字,還包含年齡、郵箱、狀態(tài)、注冊(cè)時(shí)間等多個(gè)屬性;一個(gè)訂單不僅有金額,還包含商品列表、支付方式、物流信息等
層級(jí)關(guān)系(Hierarchical):用戶包含地址信息;訂單包含多個(gè)商品;商品又包含規(guī)格、價(jià)格、庫(kù)存等子結(jié)構(gòu)
實(shí)體之間存在關(guān)聯(lián):一對(duì)多:一個(gè)用戶對(duì)應(yīng)多個(gè)訂單;多對(duì)一:多個(gè)訂單屬于同一個(gè)商家;多對(duì)多:用戶與標(biāo)簽、商品與分類
狀態(tài)與行為并存:數(shù)據(jù)不僅是“靜態(tài)描述”,還會(huì)涉及狀態(tài)變化(如訂單狀態(tài)流轉(zhuǎn)),以及業(yè)務(wù)行為(如校驗(yàn)、計(jì)算、轉(zhuǎn)換)
這些特征決定了:現(xiàn)實(shí)世界的數(shù)據(jù),本質(zhì)上是結(jié)構(gòu)化、層級(jí)化、并且語(yǔ)義明確的。
2. 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)的能力邊界
Python 的 list 和 dict 是非常強(qiáng)大的工具,但它們的設(shè)計(jì)初衷是通用容器,而不是“領(lǐng)域模型”。
以字典為例:
字典可以存任意鍵值對(duì)
但它并不關(guān)心:
這個(gè)字段是否必須存在
這個(gè)字段應(yīng)該是什么類型
不同字段之間是否存在約束關(guān)系
隨著業(yè)務(wù)復(fù)雜度的上升,單純使用 dict 會(huì)逐漸暴露出問(wèn)題:
結(jié)構(gòu)不透明:字段依賴“約定”而非“定義”,閱讀代碼時(shí)無(wú)法一眼看出數(shù)據(jù)結(jié)構(gòu)
缺乏約束:鍵名拼寫錯(cuò)誤只能在運(yùn)行時(shí)報(bào)錯(cuò),類型錯(cuò)誤難以及早發(fā)現(xiàn)
可維護(hù)性下降:字段增加或調(diào)整會(huì)波及大量代碼,缺乏統(tǒng)一的數(shù)據(jù)入口
業(yè)務(wù)語(yǔ)義缺失:
data["status"] == 1本身無(wú)法說(shuō)明業(yè)務(wù)含義,需要額外注釋或上下文才能理解
這意味著:基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)更適合“存數(shù)據(jù)”,但并不適合“表達(dá)數(shù)據(jù)含義”。
3. 從“數(shù)據(jù)容器”到“數(shù)據(jù)模型”的轉(zhuǎn)變
當(dāng)系統(tǒng)規(guī)模逐漸擴(kuò)大時(shí),我們需要的不再只是一個(gè)“能裝東西的容器”,而是一個(gè)明確表達(dá)業(yè)務(wù)含義的數(shù)據(jù)模型(Data Model)。
數(shù)據(jù)模型關(guān)注的不僅是“數(shù)據(jù)長(zhǎng)什么樣”,還包括:
字段的業(yè)務(wù)語(yǔ)義
字段之間的結(jié)構(gòu)關(guān)系
數(shù)據(jù)的合法性規(guī)則
數(shù)據(jù)可支持的操作與行為
這種轉(zhuǎn)變,本質(zhì)上是:從“如何存數(shù)據(jù)”,升級(jí)為“如何描述現(xiàn)實(shí)世界中的對(duì)象”。
4. Python 在結(jié)構(gòu)化數(shù)據(jù)建模上的天然優(yōu)勢(shì)
Python 之所以適合結(jié)構(gòu)化數(shù)據(jù)建模,主要得益于以下能力:
靈活的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)(list / dict)
完整的面向?qū)ο篌w系(Class)
強(qiáng)大的類型注解支持
內(nèi)建 JSON 處理能力
dataclass 等高級(jí)建模工具
這些能力,使得 Python 可以自然地完成從:原始數(shù)據(jù) → 半結(jié)構(gòu)化數(shù)據(jù) → 領(lǐng)域模型 → 工程化數(shù)據(jù)體系的逐層演進(jìn)。
小結(jié)一下吧
現(xiàn)實(shí)世界的數(shù)據(jù)天然是結(jié)構(gòu)化的
基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)適合存儲(chǔ),但不適合長(zhǎng)期建模
結(jié)構(gòu)化數(shù)據(jù)建模是代碼工程化的必經(jīng)之路
Python 提供了從簡(jiǎn)單到復(fù)雜的完整建模能力
在接下來(lái)的學(xué)習(xí)中,我們將從嵌套數(shù)據(jù)結(jié)構(gòu)開始,逐步引入 JSON 處理,最終過(guò)渡到 基于 Class 的結(jié)構(gòu)化數(shù)據(jù)建模,構(gòu)建可維護(hù)、可擴(kuò)展的 Python 數(shù)據(jù)體系。
(二)Python 中的嵌套結(jié)構(gòu)化數(shù)據(jù)
在明確了為什么需要結(jié)構(gòu)化數(shù)據(jù)建模之后,我們首先要面對(duì)的是:如何在 Python 中表達(dá)“多字段 + 多層級(jí)”的數(shù)據(jù)結(jié)構(gòu)。
在不引入面向?qū)ο笾?,Python 最直接、最常見的做法,就是使用嵌套的數(shù)據(jù)結(jié)構(gòu)——即在 list 或 dict 中再包含 list 或 dict。
嵌套結(jié)構(gòu),是 Python 邁向結(jié)構(gòu)化數(shù)據(jù)處理的第一步。
1. 嵌套結(jié)構(gòu)化數(shù)據(jù)的基本思想
所謂嵌套結(jié)構(gòu),本質(zhì)上是:用 dict 表達(dá)“對(duì)象”,用 list 表達(dá)“集合”,并通過(guò)層級(jí)組合還原現(xiàn)實(shí)關(guān)系。
這種思想非常直觀,也幾乎不需要額外語(yǔ)法成本,因此被廣泛用于:
接口返回?cái)?shù)據(jù)
配置文件
臨時(shí)數(shù)據(jù)組織
JSON 原始載體
2. 字典嵌套字典:表達(dá)對(duì)象的層級(jí)屬性
適用場(chǎng)景:一個(gè)對(duì)象內(nèi)部存在子結(jié)構(gòu)
user = {
"id": 1001,
"name": "Alice",
"profile": {
"age": 28,
"email": "alice@example.com",
"address": {
"city": "Beijing",
"zipcode": "100000"
}
}
}
結(jié)構(gòu)理解
user:整體對(duì)象profile:用戶的擴(kuò)展屬性address:更細(xì)粒度的子結(jié)構(gòu)
這種結(jié)構(gòu)清晰地反映了現(xiàn)實(shí)語(yǔ)義:地址不屬于用戶本身,而屬于用戶的資料信息。
訪問(wèn)方式
city = user["profile"]["address"]["city"]
此時(shí)你已經(jīng)可以感受到第一個(gè)問(wèn)題:
訪問(wèn)路徑依賴結(jié)構(gòu)穩(wěn)定
任意一層缺失,都會(huì)導(dǎo)致運(yùn)行期錯(cuò)誤
3. 字典嵌套列表:表達(dá)一對(duì)多關(guān)系
適用場(chǎng)景:一個(gè)主體擁有多個(gè)子對(duì)象
order = {
"order_id": "ORD-001",
"user_id": 1001,
"items": [
{"sku": "SKU-1", "price": 100, "quantity": 2},
{"sku": "SKU-2", "price": 50, "quantity": 1}
]
}
結(jié)構(gòu)理解
order是訂單對(duì)象items是訂單下的商品集合每個(gè)商品仍然是一個(gè)“對(duì)象”
典型處理邏輯
total_amount = 0
for item in order["items"]:
total_amount += item["price"] * item["quantity"]
此時(shí)一個(gè)非常關(guān)鍵的變化已經(jīng)出現(xiàn):業(yè)務(wù)計(jì)算邏輯,開始直接寫在“嵌套結(jié)構(gòu)遍歷”之中。
這是后續(xù)引入 Class 建模的重要?jiǎng)訖C(jī)。
4. 列表嵌套字典:同構(gòu)對(duì)象的集合
適用場(chǎng)景:多個(gè)結(jié)構(gòu)相同的對(duì)象
users = [
{"id": 1, "name": "Alice", "active": True},
{"id": 2, "name": "Bob", "active": False},
{"id": 3, "name": "Charlie", "active": True}
]
遍歷與篩選
active_users = []
for user in users:
if user["active"]:
active_users.append(user)
這種結(jié)構(gòu)在接口返回?cái)?shù)據(jù)中極為常見,但它也暴露了嵌套結(jié)構(gòu)的本質(zhì)問(wèn)題之一:
字段完全依賴字符串約定
沒(méi)有任何顯式結(jié)構(gòu)定義
錯(cuò)誤只能在運(yùn)行期發(fā)現(xiàn)
5. 多層混合嵌套:真實(shí)系統(tǒng)中的常態(tài)
在真實(shí)系統(tǒng)中,嵌套結(jié)構(gòu)往往是多種形式的組合:
response = {
"code": 0,
"data": {
"user": {
"id": 1001,
"orders": [
{
"order_id": "ORD-001",
"items": [
{"sku": "SKU-1", "price": 100}
]
}
]
}
}
}
訪問(wèn)路徑示例
sku = response["data"]["user"]["orders"][0]["items"][0]["sku"]
此類代碼的特征非常明顯:
訪問(wèn)鏈路極長(zhǎng)
強(qiáng)依賴數(shù)據(jù)結(jié)構(gòu)細(xì)節(jié)
可讀性和可維護(hù)性迅速下降
6. 防御式訪問(wèn):必要但有限的改進(jìn)
為了避免異常,開發(fā)中常使用防御式寫法:
user = response.get("data", {}).get("user", {})
orders = user.get("orders", [])
if orders:
first_order = orders[0]
這種方式可以:
降低運(yùn)行期錯(cuò)誤概率
提高代碼健壯性
但需要清醒認(rèn)識(shí)到:防御式訪問(wèn)解決的是“不崩潰”,而不是“結(jié)構(gòu)合理”。
7. 嵌套結(jié)構(gòu)的工程性問(wèn)題總結(jié)
通過(guò)以上示例,可以清晰看到嵌套結(jié)構(gòu)在工程中的局限:
結(jié)構(gòu)定義分散:數(shù)據(jù)結(jié)構(gòu)存在于代碼路徑中,而非集中定義
業(yè)務(wù)語(yǔ)義缺失:字段只是字符串,無(wú)法承載規(guī)則和含義
強(qiáng)耦合:結(jié)構(gòu)變更會(huì)影響大量訪問(wèn)代碼
邏輯與數(shù)據(jù)混雜:業(yè)務(wù)計(jì)算被迫寫在遍歷和索引中
當(dāng)代碼中大量出現(xiàn)如下形式時(shí):
data["a"]["b"][0]["c"]["d"]
通常意味著:嵌套結(jié)構(gòu)已經(jīng)達(dá)到了可維護(hù)性的上限。
8. 嵌套結(jié)構(gòu)在整體體系中的正確定位
嵌套 dict / list 并不是“錯(cuò)誤做法”,而是階段性工具:
| 使用場(chǎng)景 | 評(píng)價(jià) |
|---|---|
| 原始 JSON 承載 | 合適 |
| 臨時(shí)數(shù)據(jù)處理 | 合適 |
| 腳本 / Demo | 合適 |
| 核心業(yè)務(wù)模型 | 不合適 |
| 長(zhǎng)期維護(hù)系統(tǒng) | 不合適 |
它更適合作為:數(shù)據(jù)的“中間形態(tài)”,而非最終模型。
小結(jié)一下吧
嵌套結(jié)構(gòu)是 Python 表達(dá)復(fù)雜數(shù)據(jù)的基礎(chǔ)方式
它通過(guò) dict + list 組合還原現(xiàn)實(shí)關(guān)系
隨著層級(jí)加深,維護(hù)成本迅速上升
這正是引入 JSON 規(guī)范與 Class 建模的直接動(dòng)因
接著我們將進(jìn)入 半結(jié)構(gòu)化數(shù)據(jù):JSON 在 Python 中的處理,你會(huì)發(fā)現(xiàn):JSON 的便利性,并沒(méi)有從根本上解決嵌套結(jié)構(gòu)的問(wèn)題,而只是讓它“標(biāo)準(zhǔn)化”了。
(三)半結(jié)構(gòu)化數(shù)據(jù):JSON 在 Python 中的處理
在上一節(jié)中,我們已經(jīng)看到:通過(guò) list 與 dict 的嵌套,Python 可以完整表達(dá)復(fù)雜數(shù)據(jù)結(jié)構(gòu),但可維護(hù)性迅速下降。
在真實(shí)工程中,這類嵌套結(jié)構(gòu)往往并不是“手寫”的,而是來(lái)自:
HTTP 接口返回
配置文件
消息隊(duì)列
日志或存儲(chǔ)系統(tǒng)
這些數(shù)據(jù)最常見的載體形式,就是 JSON(JavaScript Object Notation)。
1. 什么是半結(jié)構(gòu)化數(shù)據(jù)
從數(shù)據(jù)組織角度看,數(shù)據(jù)大致可以分為三類:
非結(jié)構(gòu)化數(shù)據(jù):文本、圖片、音頻
結(jié)構(gòu)化數(shù)據(jù):表結(jié)構(gòu)、強(qiáng)約束模型
半結(jié)構(gòu)化數(shù)據(jù):結(jié)構(gòu)存在,但約束較弱
JSON 正是典型的半結(jié)構(gòu)化數(shù)據(jù)格式,其特點(diǎn)是:
結(jié)構(gòu)清晰(層級(jí)、鍵值)
可嵌套
無(wú)強(qiáng)類型約束
字段可選、可變
這使得 JSON 非常適合數(shù)據(jù)交換,但不適合直接作為業(yè)務(wù)模型。
2. JSON 的數(shù)據(jù)模型與 Python 的映射關(guān)系
JSON 本身支持的數(shù)據(jù)類型非常有限:
| JSON 類型 | Python 對(duì)應(yīng)類型 |
|---|---|
| object | dict |
| array | list |
| string | str |
| number | int / float |
| boolean | bool |
| null | None |
也正因如此,JSON 在 Python 中的本質(zhì)就是:嵌套的 dict 與 list
換句話說(shuō):JSON 并沒(méi)有解決嵌套結(jié)構(gòu)的問(wèn)題,只是把它“標(biāo)準(zhǔn)化”了。
3. Python 中的 JSON 解析(反序列化)
Python 內(nèi)置 json 模塊,用于 JSON 與 Python 對(duì)象之間的轉(zhuǎn)換。
import json
json_str = '''
{
"id": 1001,
"name": "Alice",
"active": true,
"roles": ["admin", "editor"]
}
'''
data = json.loads(json_str)
解析后:
data是一個(gè) dict所有字段完全不受約束
訪問(wèn)方式與普通嵌套結(jié)構(gòu)一致
name = data["name"] roles = data["roles"]
4. 從文件加載 JSON 數(shù)據(jù)
JSON 文件是配置與數(shù)據(jù)交換的常見形式(這里暫時(shí)先理解,后續(xù)這部分會(huì)在新博客單獨(dú)講解):
import json
with open("config.json", "r", encoding="utf-8") as f:
config = json.load(f)
此時(shí)你獲得的是:
一個(gè)完全動(dòng)態(tài)的嵌套結(jié)構(gòu)
所有字段都只能“運(yùn)行期驗(yàn)證”
5. JSON 的典型嵌套結(jié)構(gòu)示例
考慮一個(gè)真實(shí)接口返回示例:
response = {
"code": 0,
"message": "success",
"data": {
"user": {
"id": 1001,
"name": "Alice",
"orders": [
{
"order_id": "ORD-001",
"items": [
{"sku": "SKU-1", "price": 100, "quantity": 2}
]
}
]
}
}
}
業(yè)務(wù)代碼往往會(huì)這樣寫:
orders = response["data"]["user"]["orders"] first_item_price = orders[0]["items"][0]["price"]
到這里,你應(yīng)該已經(jīng)非常熟悉這種訪問(wèn)模式——它與嵌套結(jié)構(gòu)沒(méi)有任何本質(zhì)區(qū)別。
6. JSON 帶來(lái)的“結(jié)構(gòu)假象”
JSON 的一個(gè)常見誤區(qū)是:“有格式 ≠ 有模型”
雖然 JSON 看起來(lái)“結(jié)構(gòu)清晰”,但它存在以下天然問(wèn)題:
字段完全不受約束:缺字段不會(huì)報(bào)錯(cuò),多字段也不會(huì)報(bào)錯(cuò)
類型不可靠:
price可能是數(shù)字,也可能是字符串,錯(cuò)誤只能在運(yùn)行期暴露業(yè)務(wù)語(yǔ)義缺失:
status = 1的含義無(wú)法自解釋,需要依賴外部文檔或注釋結(jié)構(gòu)變更極具破壞性:任意字段調(diào)整都會(huì)影響訪問(wèn)鏈
JSON 解決的是“數(shù)據(jù)如何傳輸”,而不是“數(shù)據(jù)如何使用”。
7. 防御式 JSON 解析的常見寫法
為了避免運(yùn)行期異常,代碼中常見如下寫法:
data = response.get("data", {})
user = data.get("user", {})
orders = user.get("orders", [])
if orders:
order_id = orders[0].get("order_id")
這種寫法與上面講的嵌套方式依舊沒(méi)啥區(qū)別,更重要的是:仍然沒(méi)有一個(gè)“明確的數(shù)據(jù)模型”。
8. JSON 在工程體系中的正確角色
在成熟系統(tǒng)中,JSON 的定位應(yīng)當(dāng)非常明確:
| 角色 | 是否合適 |
|---|---|
| 網(wǎng)絡(luò)傳輸格式 | 非常合適 |
| 配置文件 | 合適 |
| 接口輸入 / 輸出 | 合適 |
| 業(yè)務(wù)內(nèi)部模型 | 不合適 |
| 核心領(lǐng)域?qū)ο?/span> | 不合適 |
JSON 更適合作為:系統(tǒng)邊界上的數(shù)據(jù)格式,而非系統(tǒng)內(nèi)部的結(jié)構(gòu)表達(dá)。
系統(tǒng)引入結(jié)構(gòu)化建模的階段,Python 給出的答案是:
Class
dataclass
明確的數(shù)據(jù)模型定義
JSON ↔ Model 的轉(zhuǎn)換邊界
小結(jié)一下吧
JSON 是典型的半結(jié)構(gòu)化數(shù)據(jù)格式
在 Python 中,JSON 本質(zhì)仍是嵌套 dict / list
JSON 適合傳輸,不適合直接承載業(yè)務(wù)模型
防御式解析無(wú)法從根本上解決結(jié)構(gòu)問(wèn)題
這直接引出了:為什么必須使用 Class 進(jìn)行結(jié)構(gòu)化建模
(四)面向?qū)ο蟮慕Y(jié)構(gòu)化建模:Class 的引入
在前面的講解中無(wú)論哪一步,數(shù)據(jù)始終以同一種形態(tài)存在:dict,只是內(nèi)容變“干凈”了而已。
接下來(lái)將通過(guò)一組連續(xù)示例說(shuō)明:為什么必須引入 Class,以及 Class 在工程中應(yīng)該如何使用。
1. 問(wèn)題起點(diǎn):業(yè)務(wù)邏輯正在“侵蝕”數(shù)據(jù)結(jié)構(gòu)
先看一個(gè)非常真實(shí)的場(chǎng)景,接口返回訂單數(shù)據(jù):
order_data = {
"order_id": "ORD-001",
"price": "100",
"quantity": 2
}
業(yè)務(wù)中需要計(jì)算訂單金額,代碼往往這樣寫:
total_amount = int(order_data["price"]) * order_data["quantity"]
這段代碼沒(méi)有語(yǔ)法問(wèn)題,但存在嚴(yán)重的工程隱患:
類型轉(zhuǎn)換是“臨時(shí)的”
規(guī)則是“隱含的”
任意地方都可以重復(fù)這段邏輯
此時(shí)系統(tǒng)出現(xiàn)了第一個(gè)危險(xiǎn)信號(hào):業(yè)務(wù)規(guī)則開始散落在 dict 操作中。
2. 第一次建模:用 Class 固定結(jié)構(gòu)與類型
我們引入最基礎(chǔ)的 Class,僅做一件事:把“字段定義”和“類型規(guī)則”集中到一個(gè)地方
class Order:
def __init__(self, order_id, price, quantity):
self.order_id = order_id
self.price = int(price)
self.quantity = int(quantity)
此時(shí)發(fā)生了三件非常重要的變化:
結(jié)構(gòu)集中:
Order明確有哪些字段類型前置:類型轉(zhuǎn)換只發(fā)生一次
使用成本下降:后續(xù)代碼不再關(guān)心字段細(xì)節(jié)
業(yè)務(wù)代碼變?yōu)椋?/p>
order = Order("ORD-001", "100", 2)
3. 第二次建模:讓業(yè)務(wù)行為回歸模型
現(xiàn)在再看金額計(jì)算邏輯。如果繼續(xù)寫在外部:
total = order.price * order.quantity
雖然比 dict 好,但語(yǔ)義仍然分散。正確的工程做法是:只要邏輯“屬于對(duì)象”,就應(yīng)進(jìn)入對(duì)象本身
class Order:
def __init__(self, order_id, price, quantity):
self.order_id = order_id
self.price = int(price)
self.quantity = int(quantity)
def total_amount(self):
return self.price * self.quantity
此時(shí):
金額計(jì)算的業(yè)務(wù)含義被明確命名
外部代碼無(wú)需關(guān)心實(shí)現(xiàn)細(xì)節(jié)
total = order.total_amount()
4. 工程規(guī)范一:Class 不是“數(shù)據(jù)殼”,而是“業(yè)務(wù)單元”
到這里,可以明確一個(gè)重要規(guī)范:
一個(gè) Class 不應(yīng)該只是 dict 的替代品,而應(yīng)該是一個(gè)“可用對(duì)象”。
如果一個(gè) Class:
只有屬性
沒(méi)有任何方法
那它的價(jià)值是非常有限的。
5. 第三次建模:讓 Class 成為解析與清洗的邊界
回到真實(shí)輸入數(shù)據(jù)的問(wèn)題。接口返回的往往是 不可信 JSON:
raw_data = {
"order_id": "ORD-001",
"price": None,
"quantity": "2"
}
工程上正確的做法是:讓 Class 成為“不可信數(shù)據(jù) → 可信對(duì)象”的邊界
class Order:
def __init__(self, data: dict):
self.order_id = data.get("order_id")
self.price = self._to_int(data.get("price"), default=0)
self.quantity = self._to_int(data.get("quantity"), default=1)
def _to_int(self, value, default=0):
try:
return int(value)
except (TypeError, ValueError):
return default
此時(shí):
所有防御邏輯被“封裝”
外部世界不再處理臟數(shù)據(jù)
構(gòu)造完成的對(duì)象即“可用對(duì)象”
6. 工程規(guī)范二:業(yè)務(wù)代碼只接觸“可信對(duì)象”
正確的調(diào)用方式應(yīng)當(dāng)是:
order = Order(raw_data) total = order.total_amount()
而不是:
# ? 不推薦 total = int(raw_data["price"]) * int(raw_data["quantity"])
規(guī)范總結(jié):
解析、清洗、校驗(yàn),必須發(fā)生在模型內(nèi)部
業(yè)務(wù)代碼不應(yīng)再做任何字段判斷
7. 第四次建模:用對(duì)象組合替代嵌套結(jié)構(gòu)
考慮一個(gè)更真實(shí)的訂單結(jié)構(gòu):
raw_order = {
"order_id": "ORD-001",
"items": [
{"sku": "SKU-1", "price": "100"},
{"sku": "SKU-2", "price": "50"}
]
}
如果繼續(xù)使用嵌套 dict:
total = 0
for item in raw_order["items"]:
total += int(item["price"])
正確的建模方式是:用對(duì)象組合替代嵌套結(jié)構(gòu)
class OrderItem:
def __init__(self, data):
self.sku = data.get("sku")
self.price = int(data.get("price", 0))
class Order:
def __init__(self, data):
self.order_id = data.get("order_id")
self.items = [OrderItem(item) for item in data.get("items", [])]
def total_amount(self):
return sum(item.price for item in self.items)
此時(shí),結(jié)構(gòu)發(fā)生了質(zhì)變:
嵌套 dict → 對(duì)象關(guān)系
遍歷邏輯 → 模型內(nèi)部能力
8. 工程規(guī)范三:嵌套數(shù)據(jù) ≠ 嵌套 dict,而是“對(duì)象層級(jí)”
這是結(jié)構(gòu)化建模中非常重要的一條規(guī)范:
嵌套關(guān)系應(yīng)通過(guò)“對(duì)象組合”表達(dá),而不是嵌套 dict 訪問(wèn)路徑。
一旦遵循這條規(guī)范:
訪問(wèn)路徑顯著變短
代碼可讀性成倍提升
結(jié)構(gòu)演進(jìn)成本大幅下降
小結(jié)一下吧
通過(guò)示例可以總結(jié)出 Class 建模的核心工程規(guī)范:
Class 是模型,不是數(shù)據(jù)殼
結(jié)構(gòu)、語(yǔ)義、行為必須統(tǒng)一
解析與清洗是模型的職責(zé)
業(yè)務(wù)代碼只操作可信對(duì)象
嵌套關(guān)系用對(duì)象組合表達(dá)
到這里,你已經(jīng)完成了:
從“操作數(shù)據(jù)” → “使用模型” 的關(guān)鍵轉(zhuǎn)變
(五)復(fù)雜對(duì)象組合與領(lǐng)域建模
在真實(shí)系統(tǒng)中,所謂“復(fù)雜數(shù)據(jù)”,并不僅僅是 層級(jí)多,而是同時(shí)具備:
多層嵌套結(jié)構(gòu)
不同子結(jié)構(gòu)承擔(dān)不同職責(zé)
局部規(guī)則 + 全局規(guī)則并存
數(shù)據(jù)來(lái)自不可信邊界(接口 / MQ / DB)
這一節(jié),我們用一個(gè) 接近真實(shí)電商/交易系統(tǒng)的數(shù)據(jù)模型,完整展示 Python 中的復(fù)雜領(lǐng)域建模方式。
1. 原始輸入:近真實(shí)系統(tǒng)級(jí) JSON 結(jié)構(gòu)
這是一個(gè) 近真實(shí)工程中常見復(fù)雜度 的訂單數(shù)據(jù)(已適度抽象):
raw_order = {
"order_id": "ORD-9001",
"user": {
"id": "U-1001",
"profile": {
"name": "Alice",
"level": "VIP"
}
},
"items": [
{
"sku": "SKU-1",
"pricing": {
"unit_price": "100",
"currency": "CNY"
},
"quantity": 2,
"discounts": [
{"type": "PROMOTION", "amount": "10"},
{"type": "VIP", "amount": "5"}
]
}
],
"payment": {
"method": "CREDIT_CARD",
"transactions": [
{"tx_id": "TX-1", "amount": "185"},
{"tx_id": "TX-2", "amount": "10"}
]
}
}
特點(diǎn):
最深層級(jí):5 層
同時(shí)包含:列表嵌套 + 對(duì)象嵌套
金額、規(guī)則、行為全部混在 JSON 中
如果直接用 dict 操作,這類代碼幾乎不可維護(hù)。
2. 第一步:識(shí)別領(lǐng)域與聚合邊界(不是拆字段)
復(fù)雜建模的第一步不是寫類,而是劃分領(lǐng)域?qū)ο?/strong>。
從業(yè)務(wù)語(yǔ)義出發(fā):
| JSON 片段 | 領(lǐng)域?qū)ο?/th> |
|---|---|
| order | Order(聚合根) |
| user | User |
| profile | UserProfile |
| items | OrderItem |
| pricing | Pricing |
| discounts | Discount |
| payment | Payment |
| transactions | PaymentTransaction |
原則:
只要一個(gè)子結(jié)構(gòu)有“獨(dú)立業(yè)務(wù)含義”,就值得一個(gè) Class。
3. 底層值對(duì)象(Value Object)建模
Discount(最小業(yè)務(wù)單元)
class Discount:
def __init__(self, data):
self.type = data.get("type")
self.amount = int(data.get("amount", 0))
Pricing(價(jià)格不是一個(gè)字段,而是一組規(guī)則)
class Pricing:
def __init__(self, data):
self.unit_price = int(data.get("unit_price", 0))
self.currency = data.get("currency")
def total(self, quantity):
return self.unit_price * quantity
4. OrderItem:多層對(duì)象的“局部聚合”
class OrderItem:
def __init__(self, data):
self.sku = data.get("sku")
self.quantity = int(data.get("quantity", 1))
self.pricing = Pricing(data.get("pricing", {}))
self.discounts = [
Discount(d) for d in data.get("discounts", [])
]
def discount_amount(self):
return sum(d.amount for d in self.discounts)
def subtotal(self):
return self.pricing.total(self.quantity) - self.discount_amount()
注意這里已經(jīng)出現(xiàn)了領(lǐng)域行為協(xié)同:
折扣規(guī)則不在 Order
價(jià)格規(guī)則不在 Order
OrderItem 自洽完成金額計(jì)算
5. 用戶領(lǐng)域:嵌套對(duì)象中的語(yǔ)義建模
class UserProfile:
def __init__(self, data):
self.name = data.get("name")
self.level = data.get("level")
def is_vip(self):
return self.level == "VIP"
class User:
def __init__(self, data):
self.user_id = data.get("id")
self.profile = UserProfile(data.get("profile", {}))
6. 支付領(lǐng)域:獨(dú)立聚合,不混入訂單邏輯
class PaymentTransaction:
def __init__(self, data):
self.tx_id = data.get("tx_id")
self.amount = int(data.get("amount", 0))
class Payment:
def __init__(self, data):
self.method = data.get("method")
self.transactions = [
PaymentTransaction(tx)
for tx in data.get("transactions", [])
]
def paid_amount(self):
return sum(tx.amount for tx in self.transactions)
7. Order:真正的“領(lǐng)域聚合根”
class Order:
def __init__(self, data):
self.order_id = data.get("order_id")
self.user = User(data.get("user", {}))
self.items = [OrderItem(i) for i in data.get("items", [])]
self.payment = Payment(data.get("payment", {}))
def order_amount(self):
return sum(item.subtotal() for item in self.items)
def is_fully_paid(self):
return self.payment.paid_amount() >= self.order_amount()
注意:
Order 不參與任何底層計(jì)算
Order 只做“聚合協(xié)調(diào)”
各領(lǐng)域職責(zé)清晰、不交叉
8. 工程級(jí)規(guī)范總結(jié)(復(fù)雜場(chǎng)景)
這一示例隱含了一套非常重要的工程規(guī)范:
規(guī)范一:不要讓聚合根“變胖”
金額計(jì)算下沉到 OrderItem
折扣規(guī)則不寫在 Order
規(guī)范二:多層嵌套 ≠ 多層 dict
每一層都是對(duì)象
每一層都有行為
規(guī)范三:領(lǐng)域隔離
Payment 不知道 OrderItem 的存在
Order 不知道 Discount 的細(xì)節(jié)
規(guī)范四:結(jié)構(gòu)變化可控
JSON 結(jié)構(gòu)變化只影響局部 Class
這一節(jié)完成的不是“示例升級(jí)”,而是復(fù)雜度躍遷:
從 2 層嵌套 → 5 層嵌套
從數(shù)據(jù)組合 → 領(lǐng)域協(xié)作
從“能跑” → “可演進(jìn)、可維護(hù)”
你現(xiàn)在已經(jīng)具備:用 Python Class 構(gòu)建真實(shí)業(yè)務(wù)領(lǐng)域模型的能力
(六)JSON ↔ Class 的雙向轉(zhuǎn)換:邊界層與 DTO 設(shè)計(jì)
在 (五) 中,我們已經(jīng)構(gòu)建了一套復(fù)雜、內(nèi)聚、可演進(jìn)的領(lǐng)域模型。但此時(shí)如果直接將這些 Class 用在接口層或存儲(chǔ)層,會(huì)立即遇到新的問(wèn)題:
接口層需要 JSON
MQ / RPC 需要可序列化結(jié)構(gòu)
數(shù)據(jù)庫(kù)存儲(chǔ)結(jié)構(gòu)與領(lǐng)域模型不一致
外部系統(tǒng)字段經(jīng)常變動(dòng)
這引出了一個(gè)非常重要的工程問(wèn)題:領(lǐng)域模型不應(yīng)直接暴露在系統(tǒng)邊界。
1. 為什么不能讓領(lǐng)域模型“裸奔”到邊界
一個(gè)常見但危險(xiǎn)的做法是:
json.dumps(order.__dict__)
這種方式存在嚴(yán)重問(wèn)題:
結(jié)構(gòu)不可控:私有字段、內(nèi)部狀態(tài)一并暴露
語(yǔ)義污染:內(nèi)部對(duì)象結(jié)構(gòu)被外部依賴
變更風(fēng)險(xiǎn)極高:內(nèi)部模型調(diào)整會(huì)破壞接口
無(wú)法做版本治理:接口字段無(wú)法演進(jìn)
工程結(jié)論:領(lǐng)域模型 ≠ 接口數(shù)據(jù)模型
2. 邊界層的核心職責(zé)
在工程上,通常需要一個(gè)邊界層(Boundary Layer),它負(fù)責(zé):
JSON → DTO
DTO → Domain Model
Domain Model → DTO
DTO → JSON
其中,DTO(Data Transfer Object) 是核心中介。
3. DTO 的設(shè)計(jì)原則(非常重要)
DTO 與領(lǐng)域模型的關(guān)系,應(yīng)遵循以下原則:
只包含數(shù)據(jù),不包含業(yè)務(wù)行為
結(jié)構(gòu)扁平,面向接口需求
允許字段冗余或命名不一致
變化頻率遠(yuǎn)高于領(lǐng)域模型
邊界轉(zhuǎn)換集中化
一個(gè)成熟系統(tǒng)中,應(yīng)當(dāng)遵循:
所有 JSON ↔ Domain 的轉(zhuǎn)換,都集中在邊界層完成
而不是:Controller 中拼 dict; Service 中解析字段; Model 中關(guān)心接口格式
3.1 示例:為 Order 定義輸入 DTO
接口輸入 JSON:
{
"order_id": "ORD-9001",
"user_id": "U-1001",
"items": [
{
"sku": "SKU-1",
"unit_price": "100",
"quantity": 2
}
]
}
對(duì)應(yīng) DTO:
class OrderCreateDTO:
def __init__(self, data):
self.order_id = data.get("order_id")
self.user_id = data.get("user_id")
self.items = data.get("items", [])
DTO 的特點(diǎn):
不做復(fù)雜解析
不包含嵌套對(duì)象建模
只服務(wù)接口需求
3.2 DTO → Domain Model(反序列化邊界)
將 DTO 轉(zhuǎn)換為領(lǐng)域模型,是邊界層的核心工作之一。
class OrderFactory:
@staticmethod
def from_create_dto(dto: OrderCreateDTO):
raw = {
"order_id": dto.order_id,
"user": {"id": dto.user_id},
"items": [
{
"sku": item["sku"],
"pricing": {"unit_price": item["unit_price"]},
"quantity": item["quantity"],
"discounts": []
}
for item in dto.items
]
}
return Order(raw)
注意幾點(diǎn):
DTO 不知道領(lǐng)域模型
領(lǐng)域模型不依賴 DTO
轉(zhuǎn)換邏輯集中在 Factory / Assembler 中
3.3 Domain Model → DTO(序列化邊界)
對(duì)外輸出時(shí),同樣不能直接暴露領(lǐng)域模型。
class OrderResponseDTO:
def __init__(self, order: Order):
self.order_id = order.order_id
self.total_amount = order.order_amount()
self.paid = order.is_fully_paid()
再由 DTO 轉(zhuǎn)為 JSON:
response = json.dumps(order_response_dto.__dict__)
3.4. 復(fù)雜結(jié)構(gòu)下的 DTO 拆分策略
當(dāng)結(jié)構(gòu)進(jìn)一步復(fù)雜時(shí),應(yīng)避免:
class BigDTO:
...
而采用:
OrderDTO
ItemDTO
PaymentDTO
再由 Assembler 負(fù)責(zé)組合。
3.5 防腐層(Anti-Corruption Layer)視角
在對(duì)接外部系統(tǒng)時(shí),DTO 還承擔(dān)著一個(gè)更重要的角色:
防止外部模型“污染”內(nèi)部領(lǐng)域模型
字段改名、結(jié)構(gòu)變更、類型異常,都被限制在 DTO 層。
小結(jié)一下吧
通過(guò)本節(jié),你應(yīng)該建立起以下清晰認(rèn)知:
領(lǐng)域模型只服務(wù)領(lǐng)域邏輯
DTO 是系統(tǒng)邊界的語(yǔ)言
JSON 永遠(yuǎn)不應(yīng)直接進(jìn)入領(lǐng)域模型
Factory / Assembler 是關(guān)鍵角色
邊界清晰,系統(tǒng)才能長(zhǎng)期演進(jìn)
(七)使用 dataclass 與類型系統(tǒng)增強(qiáng)結(jié)構(gòu)化建模
在前面的分享中,我們已經(jīng)用“原生 Class”完成了復(fù)雜領(lǐng)域建模。但你可能已經(jīng)感受到幾個(gè)現(xiàn)實(shí)問(wèn)題:
__init__代碼大量重復(fù)字段類型只能靠約定
對(duì)象結(jié)構(gòu)難以一眼看清
重構(gòu)時(shí)缺乏工具輔助
這正是 dataclass 與類型系統(tǒng) 登場(chǎng)的時(shí)機(jī)。
1. 為什么在復(fù)雜建模階段引入 dataclass
dataclass 并不是“語(yǔ)法糖”,而是結(jié)構(gòu)化建模的工程工具。
它解決的核心問(wèn)題是:
| 問(wèn)題 | 原生 Class | dataclass |
|---|---|---|
| 字段聲明 | 分散在 __init__ | 集中聲明 |
| 類型表達(dá) | 隱式 | 顯式 |
| 可讀性 | 中等 | 高 |
| 維護(hù)成本 | 高 | 低 |
工程結(jié)論:當(dāng)模型成為系統(tǒng)核心資產(chǎn)時(shí),必須使用 dataclass 或等價(jià)機(jī)制。
2. 從原生 Class 到 dataclass:逐步演進(jìn)
先看之前寫的的一個(gè)典型類:
class Discount:
def __init__(self, data):
self.type = data.get("type")
self.amount = int(data.get("amount", 0))
使用 dataclass 重構(gòu)后:
from dataclasses import dataclass
@dataclass
class Discount:
type: str
amount: int
但這只是第一步,真實(shí)工程不會(huì)直接這樣寫。
3. dataclass 中的構(gòu)造邊界:__post_init__
在領(lǐng)域模型中:
字段是“結(jié)構(gòu)”
校驗(yàn)、轉(zhuǎn)換是“規(guī)則”
dataclass 提供 __post_init__ 來(lái)承載規(guī)則。
from dataclasses import dataclass
@dataclass
class Discount:
type: str
amount: int
def __post_init__(self):
self.amount = int(self.amount)
規(guī)范:類型轉(zhuǎn)換、校驗(yàn)邏輯應(yīng)放在 __post_init__,而不是外部代碼。
4. 復(fù)雜對(duì)象中的 dataclass 組合
重構(gòu) Pricing:
from dataclasses import dataclass
@dataclass
class Pricing:
unit_price: int
currency: str = "CNY"
def total(self, quantity: int) -> int:
return self.unit_price * quantity
OrderItem:
from dataclasses import dataclass, field
from typing import List
@dataclass
class OrderItem:
sku: str
pricing: Pricing
quantity: int = 1
discounts: List[Discount] = field(default_factory=list)
def subtotal(self) -> int:
discount = sum(d.amount for d in self.discounts)
return self.pricing.total(self.quantity) - discount
此時(shí),模型結(jié)構(gòu)已經(jīng)一眼可讀。
5. 類型系統(tǒng)帶來(lái)的“隱性收益”
引入類型標(biāo)注后,產(chǎn)生的收益包括:
IDE 自動(dòng)補(bǔ)全
靜態(tài)檢查(mypy / pyright)
重構(gòu)安全性
模型即文檔
例如:
def calculate_total(items: List[OrderItem]) -> int:
...
類型本身就表達(dá)了設(shè)計(jì)意圖。
6. dataclass 與不可變建模(高級(jí)用法)
在部分領(lǐng)域中(如支付、賬務(wù)),對(duì)象應(yīng)當(dāng)是不可變的。
@dataclass(frozen=True)
class PaymentTransaction:
tx_id: str
amount: int
不可變對(duì)象的工程意義:
防止?fàn)顟B(tài)被隨意修改
更容易推理
更適合并發(fā)場(chǎng)景
7. dataclass ≠ DTO:職責(zé)仍然要分清
一個(gè)常見誤區(qū)是:“既然 dataclass 很方便,那 DTO 也用它吧?”
可以,但要遵守原則:
| 對(duì)象 | 是否可用 dataclass | 是否包含行為 |
|---|---|---|
| DTO | 可以 | 不應(yīng)該 |
| Domain Model | 強(qiáng)烈推薦 | 必須 |
關(guān)鍵點(diǎn)不是工具,而是職責(zé)。
8. 工程規(guī)范:領(lǐng)域模型的現(xiàn)代 Python 寫法
綜合前面內(nèi)容,可以形成一套明確規(guī)范:
領(lǐng)域模型使用 dataclass
字段必須有類型
規(guī)則寫在方法或
__post_init__嵌套關(guān)系通過(guò)類型表達(dá)
不可變對(duì)象優(yōu)先用于關(guān)鍵領(lǐng)域
小結(jié)一下吧
通過(guò) dataclass 與類型系統(tǒng):
模型從“能用”變?yōu)?ldquo;可靠”
結(jié)構(gòu)從“隱式”變?yōu)?ldquo;顯式”
重構(gòu)從“危險(xiǎn)”變?yōu)?ldquo;可控”
(八)領(lǐng)域模型與數(shù)據(jù)庫(kù)模型的邊界:ORM 與持久化設(shè)計(jì)
前面整體學(xué)習(xí)了解了:
復(fù)雜領(lǐng)域建模
明確的邊界與 DTO
dataclass + 類型系統(tǒng)增強(qiáng)
系統(tǒng)幾乎不可避免地會(huì)進(jìn)入一個(gè)危險(xiǎn)階段:為了“省事”,開始讓領(lǐng)域模型向數(shù)據(jù)庫(kù)結(jié)構(gòu)妥協(xié)。
這一節(jié)的目標(biāo),是明確劃清領(lǐng)域模型與持久化模型的邊界。
1. 一個(gè)常見但致命的誤區(qū)
很多系統(tǒng)會(huì)出現(xiàn)如下代碼風(fēng)格:
class Order(Base):
__tablename__ = "orders"
id = Column(String, primary_key=True)
total_amount = Column(Integer)
看似優(yōu)雅,實(shí)際上隱藏著幾個(gè)問(wèn)題:
領(lǐng)域模型被 ORM 污染
數(shù)據(jù)庫(kù)結(jié)構(gòu)決定了領(lǐng)域結(jié)構(gòu)
業(yè)務(wù)規(guī)則被擠出模型
工程結(jié)論:ORM Model ≠ Domain Model
2. 三種模型的職責(zé)劃分(必須牢記)
在成熟系統(tǒng)中,通常存在三類模型:
| 模型類型 | 關(guān)注點(diǎn) | 變化頻率 |
|---|---|---|
| DTO | 接口 / 邊界 | 高 |
| Domain Model | 業(yè)務(wù)規(guī)則 | 中 |
| Persistence Model(ORM) | 表結(jié)構(gòu) | 低 |
它們解決的是完全不同的問(wèn)題。
3. 領(lǐng)域模型為什么不應(yīng)該“繼承 ORM”
原因并不抽象:
數(shù)據(jù)庫(kù)字段不是業(yè)務(wù)語(yǔ)言
ORM 強(qiáng)制扁平化結(jié)構(gòu)
懶加載/會(huì)話污染領(lǐng)域行為
測(cè)試復(fù)雜度急劇上升
領(lǐng)域模型應(yīng)當(dāng):
與數(shù)據(jù)庫(kù)無(wú)關(guān)
可以脫離 ORM 獨(dú)立運(yùn)行與測(cè)試
4. 正確的工程結(jié)構(gòu)示意
一個(gè)合理的工程分層應(yīng)當(dāng)是:

領(lǐng)域模型永遠(yuǎn)不直接操作數(shù)據(jù)庫(kù)。
5. ORM Model:只做“表的映射”
以 SQLAlchemy 為例(僅作為概念說(shuō)明):
class OrderORM(Base):
__tablename__ = "orders"
order_id = Column(String, primary_key=True)
total_amount = Column(Integer)
特點(diǎn):
結(jié)構(gòu)與表一一對(duì)應(yīng)
沒(méi)有業(yè)務(wù)行為
允許妥協(xié)命名
6. Repository:連接領(lǐng)域與持久化的橋梁
Repository 是最關(guān)鍵、但最容易被忽略的一層。
class OrderRepository:
def save(self, order: Order):
raise NotImplementedError
def get(self, order_id: str) -> Order:
raise NotImplementedError
領(lǐng)域?qū)又灰蕾?接口,而不是實(shí)現(xiàn)。
7. ORM → Domain 的裝配(反序列化)
class OrderRepositoryImpl(OrderRepository):
def get(self, order_id: str) -> Order:
orm = self.session.query(OrderORM).get(order_id)
raw = {
"order_id": orm.order_id,
"items": self._load_items(orm),
"payment": self._load_payment(orm)
}
return Order(raw)
注意:
ORM 結(jié)構(gòu)被“翻譯”
Domain Model 完全不知道 ORM 的存在
8. Domain → ORM 的持久化(序列化)
def save(self, order: Order):
orm = OrderORM(
order_id=order.order_id,
total_amount=order.order_amount()
)
self.session.add(orm)
即使結(jié)構(gòu)重復(fù),也要接受。重復(fù)是隔離的代價(jià),也是穩(wěn)定性的來(lái)源。
9. 工程規(guī)范:持久化永遠(yuǎn)是“外部關(guān)心的事”
領(lǐng)域模型中 絕對(duì)不應(yīng)出現(xiàn):
sessioncommitsave()ORM 注解
如果你在領(lǐng)域模型中看到這些,說(shuō)明:領(lǐng)域已經(jīng)被侵蝕。
小結(jié)一下吧
通過(guò)本節(jié),你應(yīng)當(dāng)形成以下堅(jiān)定認(rèn)知:
領(lǐng)域模型不為數(shù)據(jù)庫(kù)服務(wù)
ORM 只是技術(shù)實(shí)現(xiàn)
Repository 隔離變化
結(jié)構(gòu)重復(fù)是合理且必要的
領(lǐng)域模型必須可以脫離數(shù)據(jù)庫(kù)獨(dú)立存在
(九)校驗(yàn)、異常與領(lǐng)域約束建模
現(xiàn)在我們已經(jīng)解決了:
復(fù)雜結(jié)構(gòu)如何建模
邊界如何隔離
如何工程化增強(qiáng)模型
如何與數(shù)據(jù)庫(kù)解耦
但到目前為止,仍有一個(gè)問(wèn)題被刻意回避了:如果數(shù)據(jù)是“非法的”,怎么辦?
現(xiàn)實(shí)系統(tǒng)中,非法數(shù)據(jù)是常態(tài),而不是例外。
1. 一個(gè)常見但危險(xiǎn)的現(xiàn)狀
很多系統(tǒng)對(duì)“非法數(shù)據(jù)”的處理方式是:
if price <= 0:
return None
或者:
try:
...
except Exception:
pass
這些寫法的問(wèn)題不在于“能不能跑”,而在于:
錯(cuò)誤被吞掉
規(guī)則被隱式化
系統(tǒng)進(jìn)入“不可預(yù)期狀態(tài)”
工程結(jié)論:校驗(yàn)不是工具層問(wèn)題,而是領(lǐng)域模型的職責(zé)。
2. 什么是“領(lǐng)域約束”(Domain Constraint)
領(lǐng)域約束指的是:
業(yè)務(wù)上不允許被破壞的規(guī)則
與技術(shù)、存儲(chǔ)、接口無(wú)關(guān)
一旦違反,系統(tǒng)就不應(yīng)繼續(xù)執(zhí)行
例如:
訂單金額不能為負(fù)
支付金額不能小于 0
已完成訂單不能再次支付
這些規(guī)則 不屬于 Controller,也不屬于 Service,而是:領(lǐng)域模型自身的邊界。
3. 領(lǐng)域異常:用“異常”表達(dá)規(guī)則破壞
在領(lǐng)域建模中,異常不是“程序錯(cuò)誤”,而是:對(duì)業(yè)務(wù)規(guī)則被破壞的明確聲明
首先,定義領(lǐng)域異常基類:
class DomainError(Exception):
pass
然后為關(guān)鍵規(guī)則定義具體異常:
class InvalidPriceError(DomainError):
pass
class PaymentAmountError(DomainError):
pass
class OrderStateError(DomainError):
pass
規(guī)范:不要在領(lǐng)域中直接拋 ValueError、TypeError。
4. 在模型內(nèi)部執(zhí)行校驗(yàn)(而不是外部 if)
以 OrderItem 為例:
from dataclasses import dataclass
@dataclass
class OrderItem:
sku: str
quantity: int
def __post_init__(self):
if self.quantity <= 0:
raise InvalidPriceError("quantity must be positive")
此時(shí):
非法對(duì)象 根本無(wú)法被構(gòu)造
錯(cuò)誤在最早階段暴露
5. 行為級(jí)校驗(yàn):規(guī)則與動(dòng)作綁定
不是所有校驗(yàn)都發(fā)生在構(gòu)造階段。
例如:支付行為。
class Order:
...
def pay(self, amount: int):
if amount <= 0:
raise PaymentAmountError("payment must be positive")
if self.is_fully_paid():
raise OrderStateError("order already paid")
self.payment.add_transaction(amount)
關(guān)鍵原則:規(guī)則應(yīng)當(dāng)與“動(dòng)作”綁定,而不是與“字段”綁定。
6. 領(lǐng)域狀態(tài)與約束的結(jié)合
復(fù)雜系統(tǒng)中,狀態(tài)本身就是約束來(lái)源。
class OrderStatus:
CREATED = "CREATED"
PAID = "PAID"
CANCELED = "CANCELED"
class Order:
...
def cancel(self):
if self.status == OrderStatus.PAID:
raise OrderStateError("paid order cannot be canceled")
self.status = OrderStatus.CANCELED
此時(shí):
狀態(tài)轉(zhuǎn)換路徑被嚴(yán)格限制
非法流程無(wú)法發(fā)生
7. 校驗(yàn)邏輯的分層原則(非常重要)
一個(gè)成熟系統(tǒng)中,校驗(yàn)應(yīng)分為三層:
| 層級(jí) | 校驗(yàn)內(nèi)容 | 示例 |
|---|---|---|
| 接口層 | 格式、必填 | JSON 字段是否存在 |
| 邊界層 | 基本合法性 | 字符串能否轉(zhuǎn) int |
| 領(lǐng)域?qū)?/td> | 業(yè)務(wù)規(guī)則 | 金額、狀態(tài)、約束 |
絕對(duì)禁止:在 Controller 中寫業(yè)務(wù)規(guī)則判斷。
8. 異常如何“向外傳播”
領(lǐng)域異常的傳播路徑應(yīng)當(dāng)是:

Controller 的職責(zé)不是“判斷規(guī)則”,而是:
try:
order.pay(amount)
except DomainError as e:
return error_response(str(e))
9. 工程規(guī)范:失敗是系統(tǒng)能力的一部分
一個(gè)系統(tǒng)是否健壯,不取決于:能否處理正常數(shù)據(jù)
而取決于:能否正確拒絕不合法行為
領(lǐng)域異常的價(jià)值在于:
規(guī)則顯式
行為可追蹤
錯(cuò)誤不可忽略
八、總結(jié)
至此,本篇內(nèi)容對(duì) Python 基礎(chǔ)知識(shí)進(jìn)行了一個(gè)相對(duì)系統(tǒng)的梳理與總結(jié)。
從運(yùn)行環(huán)境與工具選擇,到變量、控制流、函數(shù)、字符串處理,再到列表、字典、集合等核心數(shù)據(jù)結(jié)構(gòu),本博客力求用工程視角 + 可運(yùn)行示例的方式,期望在較短時(shí)間內(nèi)建立起對(duì) Python 編程的整體認(rèn)知和基本實(shí)戰(zhàn)能力。
需要說(shuō)明的是,這份內(nèi)容同樣也是我在學(xué)習(xí) Python 過(guò)程中的階段性整理與沉淀。難免存在理解不夠嚴(yán)謹(jǐn)或表述不夠完善之處,歡迎各位讀者在評(píng)論區(qū)留言指正、交流探討,這對(duì)我和后續(xù)讀者都會(huì)非常有價(jià)值。
如果你能通過(guò)本文:
理解 Python 的核心語(yǔ)法思想
看懂并獨(dú)立編寫基礎(chǔ)程序
對(duì)后續(xù)深入學(xué)習(xí)(如數(shù)據(jù)處理、Web、自動(dòng)化或 AI 方向)不再畏懼
那么這篇總結(jié)就達(dá)到了它應(yīng)有的意義。
Python 是一門上手友好、上限極高的語(yǔ)言,但真正的成長(zhǎng)來(lái)自持續(xù)實(shí)踐,而不是一次性閱讀。希望你在未來(lái)的學(xué)習(xí)過(guò)程中,多動(dòng)手、多思考、多調(diào)試,把“會(huì)寫”逐步變成“寫得好、寫得穩(wěn)”。
以上就是入門初學(xué)者短期內(nèi)快速掌握Python的經(jīng)典全面教程(專家學(xué)習(xí)筆記)的詳細(xì)內(nèi)容,更多關(guān)于Python基礎(chǔ)學(xué)習(xí)總結(jié)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python filecmp.dircmp實(shí)現(xiàn)遞歸比對(duì)兩個(gè)目錄的方法
這篇文章主要介紹了python filecmp.dircmp實(shí)現(xiàn)遞歸比對(duì)兩個(gè)目錄的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
一篇文章帶你深入學(xué)習(xí)Python函數(shù)
這篇文章主要帶大家深入學(xué)習(xí)Python函數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01
Python中ArcPy柵格裁剪柵格(批量對(duì)齊柵格圖像范圍并統(tǒng)一行數(shù)與列數(shù))
本文介紹基于Python中ArcPy模塊,實(shí)現(xiàn)基于柵格圖像批量裁剪柵格圖像,同時(shí)對(duì)齊各個(gè)柵格圖像的空間范圍,統(tǒng)一其各自行數(shù)與列數(shù)的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
Python+OpenCV六種實(shí)時(shí)圖像處理詳細(xì)講解
OpenCV常用的圖像處理為閾值二值化、邊緣檢測(cè)、輪廓檢測(cè)、高斯濾波、色彩轉(zhuǎn)換、調(diào)節(jié)對(duì)比度。本文主要介紹了利用Python和OpenCV對(duì)實(shí)時(shí)圖像進(jìn)行上述六種操作的詳細(xì)講解,感興趣的可以了解一下。2021-11-11
詳解python中靜態(tài)方法staticmethod用法
本文主要介紹了python中靜態(tài)方法staticmethod用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
python Hypothesis生成和執(zhí)行大量的測(cè)試用例
Hypothesis是一個(gè)基于屬性的測(cè)試(property-based testing)庫(kù),它能夠幫助我們生成和執(zhí)行大量的測(cè)試用例,與傳統(tǒng)的單元測(cè)試相比,屬性測(cè)試更加靈活和全面,能夠發(fā)現(xiàn)更多的邊界情況和潛在的錯(cuò)誤2024-01-01
Python 生成短8位唯一id實(shí)戰(zhàn)教程
這篇文章主要介紹了Python 生成短8位唯一id實(shí)戰(zhàn)教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
使用Mixin設(shè)計(jì)模式進(jìn)行Python編程的方法講解
Mixin模式也可以看作是一種組合模式,綜合多個(gè)類的功能來(lái)產(chǎn)生一個(gè)類而不通過(guò)繼承來(lái)實(shí)現(xiàn),下面就來(lái)整理一下使用Mixin設(shè)計(jì)模式進(jìn)行Python編程的方法講解:2016-06-06

