Học bằng cách đọc theo thứ tự

random và secrets — Chọn loại số ngẫu nhiên đúng

Học module random và secrets của Python từ căn bản. Bao quát chuỗi giả ngẫu nhiên có thể tái lập với random.choice / randint / shuffle / sample cùng seed, token an toàn về mặt mật mã với secrets.token_hex / token_urlsafe, và khi nào dùng cái nào — kèm bài tập thực hành chạy được.

Bài này bao quát hai module số ngẫu nhiên trong thư viện chuẩn. random cung cấp giá trị giả ngẫu nhiên cho test, game và mô phỏng (có thể tái lập với seed), và secrets cung cấp giá trị không thể đoán trước cho mật khẩu, token đặt lại và API key (lấy từ nguồn ngẫu nhiên mật mã của OS). Chọn đúng cái cho công việc là điều mà phần còn lại của bài bàn đến.

random và secrets khác nhau như thế nào

Cả hai module đều trả về số ngẫu nhiên, nhưng cơ chế và trường hợp sử dụng hoàn toàn khác nhau. random được xây dựng trên Mersenne Twister (một bộ sinh số giả ngẫu nhiên được sử dụng rộng rãi), và gọi random.seed(giá trị) cho phép bạn tái lập cùng một chuỗi. secrets, ngược lại, lấy từ nguồn ngẫu nhiên mật mã của OS (như /dev/urandom), nên không có khái niệm `seed` và các giá trị không thể đoán mỗi lần.

Với các trường hợp như test hay game — "không sao nếu kết quả bị lộ" — dùng random. Với mật khẩu, token và session ID — "nếu ai đó đoán được giá trị thì đó là sự cố bảo mật" — dùng secrets.

Khi nào dùng random vs secrets
randomGiả ngẫu nhiên tái lập đượccùng chuỗi với seedGame / testmô phỏngsecretsMật mã không thể đoánkhông có seedToken / mật khẩusession ID
random cho bạn giá trị giả ngẫu nhiên có thể tái lập — cho test và game. secrets lấy từ nguồn mật mã của OS các giá trị không thể đoán trước — cho token và sinh mật khẩu. Chọn đúng cái cho công việc.
Khía cạnhrandomsecrets
NguồnMersenne Twister (thuật toán)Nguồn mật mã của OS (/dev/urandom, v.v.)
Khả năng tái lậpCùng chuỗi tái lập được với seedKhông tái lập được (luôn khác)
Tốc độNhanhChậm hơn (không phải vấn đề với mục đích sử dụng điển hình)
Phù hợp nhấtTest, game, mô phỏngToken, mật khẩu, session ID

random — Cơ bản (randint / uniform / choice)

Cơ bản của random là ba thứ: một số nguyên ngẫu nhiên / một float ngẫu nhiên / chọn một phần tử từ danh sách. random.randint(min, max) trả về một số nguyên bao gồm cả hai đầu, random.uniform(min, max) trả về một float trong khoảng đã cho, và random.choice(danh sách) trả về một phần tử từ danh sách.

Hãy bắt đầu mà không dùng seed và xem các hàm cơ bản này hoạt động ra sao.

Các hàm cơ bản của random
random.randint(a, b)→ số nguyên trong [a, b]random.uniform(a, b)→ float trong [a, b]random.choice(danh sách)→ một phần tử
randint là số nguyên ngẫu nhiên trong khoảng (cả hai đầu), uniform là float ngẫu nhiên trong khoảng, và choice lấy một phần tử từ danh sách. Hình dạng đầu vào và đầu ra khác nhau ở mỗi cái, nên chọn theo nhu cầu.
HàmTrả vềSử dụng điển hình
random.randint(a, b)Số nguyên trong [a, b] (cả hai đầu)Tung xúc xắc, ID test
random.uniform(a, b)Float trong [a, b]Nhiễu, mô phỏng xác suất
random.choice(seq)Một phần tử của chuỗiChọn một mục từ menu

Thử ba hàm random cơ bản. Giá trị khác nhau mỗi lần chạy, nên ta xác minh bằng kiểm tra phạm vi và thuộc tập hợp thay vì so sánh chính xác.

① Import random.

② Sinh một số nguyên trong 1〜100 với random.randint và lưu vào n; sinh một float trong 0〜1 với random.uniform và lưu vào f; chọn một phần tử từ ["Apple", "Banana", "Cherry"] với random.choice và lưu vào picked.

③ In ra liệu ntrong 1〜100, ftrong 0〜1, và pickedthuộc danh sách gốc, dạng int range: True / False, float range: True / False, in list: True / False.

(Nếu code chạy đúng, phần giải thích sẽ hiện ra.)

Python Editor

Chạy code để xem đầu ra

random — Tái lập bằng seed và thao tác trên collection

Bây giờ hãy xem khả năng tái lập test với `random.seed`các hàm thao tác trên collection (shuffle / sample). Sau khi gọi random.seed(N), cùng một seed luôn tạo ra cùng một chuỗi. random.shuffle(danh sách) đảo các phần tử của danh sách tại chỗ, và random.sample(danh sách, k) trả về một danh sách mới gồm k phần tử duy nhất lấy từ nó.

seed / shuffle / sample
random.seed(giá trị)→ cố định trạng thái nội bộcó thể tái lậprandom.shuffle(danh sách)→ đảo tại chỗtrả về Nonerandom.sample(danh sách, k)→ k items duy nhấtdanh sách mới
seed(giá trị) cố định trạng thái nội bộ, nên cùng một chuỗi lặp lại. shuffle sắp xếp lại danh sách gốc tại chỗ (trả về None). sample trả về danh sách mới gồm k phần tử duy nhất (gốc không bị động đến).
HàmTrả vềSử dụng điển hình
random.seed(value)None (khởi tạo trạng thái nội bộ)Test có thể tái lập
random.shuffle(seq)None (đảo danh sách gốc)Ngẫu nhiên hóa thứ tự một danh sách
random.sample(seq, k)Danh sách mới gồm k phần tử duy nhấtBốc thăm k người từ khảo sát

Xác nhận rằng một giá trị tạo ra với seed 42 khớp với một giá trị được tạo lại sau khi đặt lại seed về 42.

① Import random.

② Đặt seed về 42, rồi gọi random.randint(1, 100) một lần và lưu vào n1.

③ Đặt lại seed về 42, rồi gọi random.randint(1, 100) lần nữa và lưu vào n2.

④ In n1 == n2 dạng reproducible: True.

Python Editor

Chạy code để xem đầu ra

Thấy sự khác biệt giữa shuffle (sắp xếp lại bản thân danh sách) và sample (trả về danh sách mới).

① Đặt seed về 7 và chuẩn bị cards = [1, 2, 3, 4, 5].

② Gọi random.shuffle(cards) để sắp xếp lại nội dung của cards tại chỗ. In cards sau khi sắp xếp dạng after shuffle: ◯◯.

③ Đặt lại seed về 7, rồi gọi random.sample([1, 2, 3, 4, 5], 3) để chọn 3 phần tử duy nhất, và in kết quả dạng picked 3: ◯◯.

Python Editor

Chạy code để xem đầu ra

secrets — Ngẫu nhiên mạnh cho bảo mật

Module random ở các phần trước là một bộ sinh giả ngẫu nhiên cho test — nếu trạng thái nội bộ của nó bị lộ, các giá trị tiếp theo có thể bị đoán trước. Với những thứ như token đặt lại mật khẩu, API key và session ID"nếu kẻ tấn công có thể đoán giá trị, bạn có lỗ hổng" — chọn module `secrets`, sử dụng nguồn ngẫu nhiên mật mã của OS.

secrets có API nhỏ hơn nhiều so với random — chủ yếu là các helper biến byte ngẫu nhiên thành chuỗi hex hoặc chuỗi an toàn cho URL.

HàmTrả vềSử dụng điển hình
secrets.token_bytes(n)n byte ngẫu nhiênKhóa mật mã, token nhị phân nội bộ
secrets.token_hex(n)Chuỗi hex độ dài 2*nSession ID đăng nhập
secrets.token_urlsafe(n)Chuỗi an toàn URL (kiểu Base64)Nhúng vào URL của link đặt lại
secrets.choice(seq)Một phần tử từ seq, theo mật mãKhi thứ tự hiển thị không nên đoán được
secrets.compare_digest(a, b)True / False (chống tấn công thời gian)So sánh hash / token (dùng thay cho ==)

Sinh token bảo mật bằng secrets và kiểm tra độ dài và bộ ký tự. Giá trị thực thay đổi mỗi lần chạy, nên ta xác minh các thuộc tính như độ dài.

① Import secrets.

② Sinh một token hex 16 byte, in độ dài dạng hex length: ◯, rồi kiểm tra nó chỉ chứa các ký tự trong 0-9 và a-f dạng hex is hex only: True / False.

③ Sinh một token an toàn URL 16 byte và in độ dài dạng urlsafe length: ◯.

Python Editor

Chạy code để xem đầu ra
QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Module nào bạn nên dùng để sinh một token đặt lại mật khẩu?

Câu 2Sau khi gọi random.seed(42) rồi random.randint(1, 100), điều gì xảy ra nếu bạn làm lại y như vậy?

Câu 3Lựa chọn tốt nhất khi bạn muốn một danh sách được xáo trộn ngẫu nhiên có thể tái lập cho test?