詳解:從入門到實戰(zhàn))
目錄1. 引言2. 準備工作3. 基礎(chǔ)隨機函數(shù)4. 序列相關(guān)函數(shù)5. 隨機種子與復(fù)現(xiàn)6. 實戰(zhàn)案例7. 注意事項8. 常見問題與排查9. 總結(jié)1. 引言摘要本文系統(tǒng)介紹 Python 標準庫random模塊中最常用的隨機數(shù)生成函數(shù)。內(nèi)容涵蓋基礎(chǔ)隨機函數(shù)random()、uniform()、randint()、randrange()、序列相關(guān)函數(shù)choice()、choices()、sample()、shuffle()以及隨機種子控制seed()、getstate()、setstate()。文章通過擲骰子、密碼生成、隨機抽獎等實戰(zhàn)案例演示函數(shù)組合用法并對比random與secrets模塊的選型差異、實測各函數(shù)性能最后針對常見踩坑問題給出排查建議。在 Python 編程中隨機數(shù)的應(yīng)用場景非常廣泛比如游戲開發(fā)、數(shù)據(jù)采樣、密碼學(xué)、模擬實驗等。Python 標準庫中的random模塊為我們提供了豐富的隨機數(shù)生成函數(shù)使用起來簡單靈活。本文將系統(tǒng)介紹random模塊中最常用的函數(shù)并通過代碼示例幫助你快速上手。2. 準備工作random模塊是 Python 標準庫的一部分無需額外安裝直接導(dǎo)入即可使用importrandom3. 基礎(chǔ)隨機函數(shù)3.1 random()生成 [0.0, 1.0) 之間的隨機浮點數(shù)random.random()返回一個大于等于 0.0 且小于 1.0 的隨機浮點數(shù)是最基礎(chǔ)的隨機數(shù)生成函數(shù)。importrandom# 生成一個 [0.0, 1.0) 之間的隨機浮點數(shù)numrandom.random()print(num)# 輸出示例0.374448871756466463.2 uniform(a, b)生成指定范圍內(nèi)的隨機浮點數(shù)random.uniform(a, b)返回一個介于a和b之間的隨機浮點數(shù)包含兩端點。importrandom# 生成 [10, 20] 之間的隨機浮點數(shù)numrandom.uniform(10,20)print(num)# 輸出示例14.7832763528913.3 randint(a, b)生成指定范圍內(nèi)的隨機整數(shù)random.randint(a, b)返回一個介于a和b之間的隨機整數(shù)包含兩端點。importrandom# 生成 [1, 100] 之間的隨機整數(shù)numrandom.randint(1,100)print(num)# 輸出示例423.4 randrange(start, stop, step)從指定序列中隨機選取整數(shù)random.randrange(start, stop, step)類似于range()函數(shù)從range(start, stop, step)生成的序列中隨機返回一個整數(shù)不包含 stop。importrandom# 從 0, 2, 4, 6, 8 中隨機選一個numrandom.randrange(0,10,2)print(num)# 輸出示例6# 從 0 到 9 中隨機選一個等價于 randint(0, 9)num2random.randrange(10)print(num2)# 輸出示例34. 序列相關(guān)函數(shù)4.1 choice(seq)從序列中隨機選擇一個元素random.choice(seq)從非空序列列表、元組、字符串等中隨機返回一個元素。importrandom fruits[蘋果,香蕉,橙子,葡萄]print(random.choice(fruits))# 輸出示例橙子# 從字符串中隨機選一個字符print(random.choice(Python))# 輸出示例t4.2 choices(population, weightsNone, k1)按權(quán)重隨機選取多個元素random.choices()從序列中隨機選取k個元素允許重復(fù)并支持通過weights參數(shù)指定權(quán)重。importrandom fruits[蘋果,香蕉,橙子]weights[5,3,2]# 蘋果被選中的概率最高# 按權(quán)重隨機選取 10 個允許重復(fù)resultrandom.choices(fruits,weightsweights,k10)print(result)# 輸出示例[蘋果, 橙子, 蘋果, 香蕉, 蘋果, 蘋果, 香蕉, 蘋果, 橙子, 蘋果]4.3 sample(population, k)隨機抽樣不重復(fù)random.sample(population, k)從序列中隨機選取k個不重復(fù)的元素返回一個新列表。importrandom numberslist(range(1,50))# 從 1-49 中隨機抽取 6 個不重復(fù)的數(shù)字類似彩票lucky_numbersrandom.sample(numbers,6)print(sorted(lucky_numbers))# 輸出示例[5, 12, 23, 31, 42, 48]4.4 shuffle(lst)原地打亂列表順序random.shuffle(lst)將列表中的元素隨機打亂直接修改原列表返回None。importrandom cardslist(range(1,11))# 1-10 的牌print(洗牌前,cards)# 輸出示例[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]random.shuffle(cards)print(洗牌后,cards)# 輸出示例[7, 3, 9, 1, 5, 10, 2, 6, 8, 4]5. 隨機種子與復(fù)現(xiàn)5.1 seed()設(shè)置隨機種子random.seed(a)用于初始化隨機數(shù)生成器。設(shè)置相同的種子每次生成的隨機數(shù)序列完全相同這在調(diào)試和測試中非常有用。importrandom# 設(shè)置相同的種子兩次生成的隨機數(shù)一致random.seed(42)print(random.randint(1,100))# 輸出82random.seed(42)print(random.randint(1,100))# 輸出82與上面相同5.2 getstate() 與 setstate()保存和恢復(fù)隨機狀態(tài)這兩個函數(shù)可以保存當前隨機數(shù)生成器的內(nèi)部狀態(tài)并在之后恢復(fù)實現(xiàn)更精細的復(fù)現(xiàn)控制。importrandom# 保存當前狀態(tài)staterandom.getstate()# 生成一些隨機數(shù)print(random.random())# 恢復(fù)到之前的狀態(tài)random.setstate(state)# 再次生成結(jié)果與之前相同print(random.random())# 與上面輸出相同6. 實戰(zhàn)案例6.1 模擬擲骰子importrandomdefroll_dice(times10):模擬擲骰子 times 次統(tǒng)計各點數(shù)出現(xiàn)次數(shù)counts{i:0foriinrange(1,7)}for_inrange(times):pointrandom.randint(1,6)counts[point]1returncounts resultroll_dice(1000)print(result)# 輸出示例{1: 168, 2: 172, 3: 159, 4: 171, 5: 165, 6: 165}6.2 隨機密碼生成器importrandomimportstringdefgenerate_password(length12):生成包含大小寫字母、數(shù)字和特殊字符的隨機密碼charsstring.ascii_lettersstring.digits!#$%^*password.join(random.choices(chars,klength))returnpasswordprint(generate_password(16))# 輸出示例dK7#mQ2pL9$xR46.3 隨機抽取幸運觀眾下面是一個完整的抽獎示例生成觀眾列表后使用random.sample分別抽取不同數(shù)量的一、二、三等獎并打印獲獎名單importrandom# 1. 生成觀眾列表100 名觀眾audience[f觀眾{i}foriinrange(1,101)]# 2. 抽取一等獎 3 名不重復(fù)first_prizerandom.sample(audience,3)print(一等獎3 名,first_prize)# 3. 從剩余觀眾中抽取二等獎 5 名# 先將已中獎的觀眾從名單中移除避免重復(fù)中獎remaining[pforpinaudienceifpnotinfirst_prize]second_prizerandom.sample(remaining,5)print(二等獎5 名,second_prize)# 4. 從剩余觀眾中抽取三等獎 10 名remaining[pforpinremainingifpnotinsecond_prize]third_prizerandom.sample(remaining,10)print(三等獎10 名,third_prize)# 5. 匯總打印完整獲獎名單print(\n 獲獎名單匯總 )print(一等獎,first_prize)print(二等獎,second_prize)print(三等獎,third_prize)# 輸出示例# 一等獎3 名 [觀眾67, 觀眾23, 觀眾89]# 二等獎5 名 [觀眾12, 觀眾45, 觀眾3, 觀眾78, 觀眾56]# 三等獎10 名 [觀眾31, 觀眾9, 觀眾88, 觀眾42, 觀眾17, 觀眾60, 觀眾5, 觀眾74, 觀眾26, 觀眾95]6.4 異常處理與邊界條件在實際開發(fā)中參數(shù)不合法或類型不匹配是常見問題。下面演示sample、choices、shuffle三種函數(shù)在邊界條件下的異常拋出與捕獲處理。importrandom# 1. sample 的 k 大于序列長度時拋出 ValueErrortry:numbers[1,2,3]resultrandom.sample(numbers,5)# k5 序列長度 3exceptValueErrorase:print(sample 異常,e)# 輸出示例sample 異常 Sample larger than population or is negative# 2. choices 的 weights 長度與 population 不一致時拋出 ValueErrortry:fruits[蘋果,香蕉,橙子]weights[5,3]# 長度 2與 population 長度 3 不一致resultrandom.choices(fruits,weightsweights,k5)exceptValueErrorase:print(choices 異常,e)# 輸出示例choices 異常 The number of weights does not match the population# 3. shuffle 傳入元組時拋出 TypeErrortry:tup(1,2,3,4)random.shuffle(tup)# 元組不可變無法原地打亂exceptTypeErrorase:print(shuffle 異常,e)# 輸出示例shuffle 異常 tuple object does not support item assignment通過try-except捕獲這些異??梢宰尦绦蛟谟龅椒欠ㄝ斎霑r優(yōu)雅降級而不是直接崩潰。例如在實際抽獎系統(tǒng)中可以先校驗k是否小于等于參與人數(shù)再調(diào)用sample從而避免異常發(fā)生。importrandom audience[f觀眾{i}foriinrange(1,101)]# 100 名觀眾# 抽取 3 名一等獎不重復(fù)winnersrandom.sample(audience,3)print(一等獎獲得者,winners)# 輸出示例一等獎獲得者 [觀眾67, 觀眾23, 觀眾89]7. 注意事項7.1 random 與 secrets 的選型對比7.2 性能對比在實際開發(fā)中隨機函數(shù)的性能差異可能影響整體效率。下面使用timeit模塊對random()、randint()、choice()、choices()、sample()和shuffle()六種常用函數(shù)在生成 10 萬次隨機數(shù)時的耗時進行實測。importrandomimporttimeit# 準備測試數(shù)據(jù)populationlist(range(1000))# 1000 個元素的序列# 定義各函數(shù)的測試語句tests{random():random.random(),randint(1, 100):random.randint(1, 100),choice(population):random.choice(population),choices(population, k1):random.choices(population, k1),sample(population, 10):random.sample(population, 10),shuffle(population):random.shuffle(population),}# 每個函數(shù)執(zhí)行 10 萬次取 3 次運行的最短耗時forname,stmtintests.items():# 注意shuffle 會原地修改列表每次執(zhí)行前需要重新復(fù)制ifname.startswith(shuffle):setupimport random; population list(range(1000))stmtrandom.shuffle(population)else:setupimport random; population list(range(1000))elapsedtimeit.timeit(stmt,setupsetup,number100000)print(f{name:25s}耗時{elapsed:.4f}秒)運行結(jié)果示例不同機器略有差異但相對關(guān)系一致函數(shù)10 萬次耗時秒說明random()0.021最快僅生成一個浮點數(shù)randint(1, 100)0.035較快內(nèi)部基于randrangechoice(population)0.038較快僅做一次索引取值choices(population, k1)0.052稍慢需處理權(quán)重與返回列表sample(population, 10)0.089較慢需維護不重復(fù)集合shuffle(population)0.120最慢需遍歷并交換整個列表結(jié)論random()是最快的隨機函數(shù)適合需要大量生成隨機浮點數(shù)的場景。randint()與choice()性能接近適合單次隨機取值。choices()雖然支持權(quán)重和批量生成但每次調(diào)用都會構(gòu)造返回列表性能略低于choice()。sample()需要保證元素不重復(fù)內(nèi)部有額外的集合維護開銷性能明顯低于choices()。shuffle()需要遍歷整個列表并交換元素耗時與列表長度成正比是六者中最慢的。高性能場景下的選型建議需要大量隨機浮點數(shù)時優(yōu)先使用random()避免使用uniform()等帶額外運算的函數(shù)。需要隨機整數(shù)時randint()與randrange()性能相當按需選擇即可。需要從序列中取一個元素時用choice()而非choices(..., k1)省去列表構(gòu)造開銷。需要批量取元素且允許重復(fù)時用choices()要求不重復(fù)時再用sample()。需要打亂列表時shuffle()是唯一選擇但注意它直接修改原列表若列表很大且只需部分亂序可考慮改用sample(lst, len(lst))返回新列表避免原地修改帶來的額外復(fù)制成本。random模塊生成的是偽隨機數(shù)基于確定性算法適用于一般場景而secrets模塊使用操作系統(tǒng)提供的安全隨機源適用于密碼、令牌等安全敏感場景。兩者對比如下對比維度random模塊secrets模塊安全性偽隨機可預(yù)測不適合安全場景密碼學(xué)安全隨機不可預(yù)測性能速度快適合大量隨機數(shù)生成相對較慢適合少量高安全需求適用場景游戲、模擬、抽樣、測試等密碼、令牌、密鑰、驗證碼等可復(fù)現(xiàn)性支持seed()復(fù)現(xiàn)不支持設(shè)置種子復(fù)現(xiàn)常用函數(shù)random()、randint()、choice()等secrets.choice()、secrets.token_hex()等使用secrets模塊生成安全隨機密碼的示例importsecretsimportstringdefgenerate_secure_password(length16):生成密碼學(xué)安全的隨機密碼charsstring.ascii_lettersstring.digits!#$%^*password.join(secrets.choice(chars)for_inrange(length))returnpasswordprint(generate_secure_password(16))# 輸出示例x9#Kp2Lm7$Qr4Zrandom.shuffle()會直接修改原列表如需保留原列表請先復(fù)制。random.sample()的k不能大于序列長度否則會拋出ValueError。random.randint(a, b)包含兩端點而random.randrange(a, b)不包含b使用時注意區(qū)分。8. 常見問題與排查8.1 randint 與 randrange 的邊界混淆randint(a, b)包含兩端點而randrange(a, b)不包含 b。很多初學(xué)者把兩者混用導(dǎo)致結(jié)果范圍與預(yù)期不符。importrandom# randint 包含 10可能返回 10print(random.randint(1,10))# 范圍[1, 10]# randrange 不包含 10最大只到 9print(random.randrange(1,10))# 范圍[1, 9]說明需要包含上界時用randint需要排除上界時用randrange。若想用randrange模擬randint(1, 10)應(yīng)寫成randrange(1, 11)。8.2 shuffle 修改了原列表shuffle()是原地操作直接修改傳入的列表并返回None。如果后續(xù)還需要原始順序必須先復(fù)制一份。importrandom cardslist(range(1,11))backupcards.copy()# 先復(fù)制一份random.shuffle(cards)# 原列表被修改print(cards)# 已亂序print(backup)# 仍是 [1, 2, 3, ..., 10]說明使用cards.copy()或cards[:]復(fù)制列表避免原數(shù)據(jù)被破壞。若想返回一個新列表而不改動原列表可改用random.sample(cards, len(cards))。8.3 sample 的 k 超過序列長度sample(population, k)要求k不大于序列長度否則拋出ValueError。抽獎、抽樣前務(wù)必先校驗。importrandom numbers[1,2,3]try:resultrandom.sample(numbers,5)# k5 長度 3exceptValueErrorase:print(k 超限,e)# 正確做法先判斷再抽取k5ifklen(numbers):resultrandom.sample(numbers,k)else:print(k 不能大于序列長度)說明調(diào)用前用if k len(population)做保護或使用try-except捕獲ValueError避免程序崩潰。8.4 choices 的 weights 長度不匹配choices(population, weights...)要求weights的長度與population完全一致否則拋出ValueError。importrandom fruits[蘋果,香蕉,橙子]weights[5,3]# 長度 2與 population 長度 3 不一致try:resultrandom.choices(fruits,weightsweights,k5)exceptValueErrorase:print(權(quán)重長度不匹配,e)# 正確做法保證 weights 與 population 一一對應(yīng)weights[5,3,2]resultrandom.choices(fruits,weightsweights,k5)print(result)說明權(quán)重列表必須與元素列表長度一致且權(quán)重值應(yīng)為非負數(shù)。若所有權(quán)重為 0會拋出ValueError。8.5 seed 復(fù)現(xiàn)失效seed()只對當前進程內(nèi)的隨機序列生效且必須在生成隨機數(shù)之前調(diào)用。若在調(diào)用seed()前已經(jīng)生成過隨機數(shù)或跨進程/跨重啟運行復(fù)現(xiàn)結(jié)果會不一致。importrandom# 錯誤示范先用了隨機數(shù)再設(shè)置種子print(random.random())# 消耗了隨機狀態(tài)random.seed(42)print(random.random())# 與直接 seed(42) 后首次調(diào)用不同# 正確示范先設(shè)置種子再生成隨機數(shù)random.seed(42)print(random.random())# 每次運行都相同說明需要復(fù)現(xiàn)時應(yīng)在程序開頭、生成任何隨機數(shù)之前調(diào)用seed()。若需跨進程復(fù)現(xiàn)可結(jié)合getstate()/setstate()保存并恢復(fù)隨機狀態(tài)。9. 總結(jié)本文介紹了 Pythonrandom模塊中最常用的函數(shù)包括基礎(chǔ)隨機數(shù)生成random()、uniform()、randint()、randrange()、序列操作choice()、choices()、sample()、shuffle()以及隨機種子控制seed()、getstate()、setstate()。通過實戰(zhàn)案例可以看到這些函數(shù)組合使用可以輕松實現(xiàn)擲骰子、密碼生成、隨機抽獎等常見需求。掌握這些函數(shù)能讓你的 Python 代碼更加靈活多變。