用随机数+字符串操作,做一个帮你生成强密码的小工具
一、你是不是也有这个问题
注册一个新网站,它要求你设置密码:
- 必须8位以上
- 必须包含大写字母
- 必须包含数字
- 不能和以前的密码重复
你想了半天,输入”zhangsan1990″——系统提示:太简单。
改成”Zhangsan1990!”——好像行了,但这个密码你已经用了三个网站了。
一个好密码应该是完全随机的,比如 Kp7#mQx2&Rn9 这种。但人脑不擅长制造随机——你随手敲的”乱码”,往往还是有规律的(键盘上相邻的键、熟悉的拼音、生日年份)。
那谁擅长制造随机?程序。
今天我们做一个密码生成器:程序帮你生成真正随机的强密码。用到的全是学过的东西:随机数、字符串拼接、循环、列表、函数。
二、先搞懂密码是怎么”造”出来的
一个强密码,本质上就是从一大堆字符里随机挑几个,拼在一起。
这个”一大堆字符”包括四类:
- 小写字母:
abcdefghijklmnopqrstuvwxyz - 大写字母:
ABCDEFGHIJKLMNOPQRSTUVWXYZ - 数字:
0123456789 - 特殊符号:
!@#$%^&*
生成一个12位密码,就是从这堆字符里随机抽12次,每次抽出一个字符,连起来。
就像抽奖:把所有字符写成小球扔进箱子,闭着眼睛摸12次,每次摸到什么就记下来。
三、先跟AI聊
你的提示词:
用Python写一个密码生成器。从大小写字母、数字、特殊符号中随机选取字符,生成一个12位的随机密码并打印出来。加上详细的中文注释。
AI会给你一段代码,大概长这样:
import random
import string
# 所有可用的字符:大小写字母+数字+特殊符号
all_chars = string.ascii_letters + string.digits + string.punctuation
# 生成12位密码
password = ""
for i in range(12):
# 从所有字符中随机选一个
char = random.choice(all_chars)
# 拼接到密码后面
password = password + char
print(f"生成的密码是:{password}")
四、逐行理解
第1-2行:两个工具箱
import random
import string
第13篇已经用过random了(生成随机数)。今天多认识一个工具箱:string。
string是Python自带的模块,里面存着一些常用的字符串。你可以把import理解成”打开工具箱盖子”。
第4行:攒一池子字符
all_chars = string.ascii_letters + string.digits + string.punctuation
这里用了字符串拼接(第四篇学的,用+号把字符串连起来):
| 内容 | 实际是什么 |
|---|
|——|———–|
string.ascii_letters |
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'(全部大小写字母) |
|
|---|---|---|
string.digits |
'0123456789' |
|
string.punctuation |
'!"#$%&'()*+,-./:;?@[\]^_{ |
}~’`(各种符号) |
三个字符串用+连起来,存进变量all_chars。现在这个变量里有90多个字符,就是我们的”抽奖箱”。
为什么用现成的变量而不自己写 'abc...XYZ'? 不是不能写,而是容易写漏写错。string模块已经帮你准备好了,程序员的懒惰美德:能用现成的就不自己造。
第6-7行:空字符串和循环
password = ""
for i in range(12):
password = ""——先准备一个空字符串,就像一张白纸,等下往上面”写”密码。
for i in range(12)——循环12次。还记得第八篇的for循环吗?range(12)会生成0到11共12个数字,循环体就执行12遍。
(这里的i其实没用上,纯粹是让循环跑12次。Python不介意。)
第9行:随机抽一个字符
char = random.choice(all_chars)
random.choice()的意思是:从一串东西里随机挑一个。它是random工具箱里的另一个工具(第13篇用的是random.randint(),生成随机整数;这个是随机挑元素)。
每次循环,从90多个字符里随机挑一个,存进char。
第11行:拼接
password = password + char
把刚抽到的字符接到密码末尾。第一次循环后,password变成1个字符;第二次变成2个……12次循环后,就是一个12位的随机密码。
这个写法值得看仔细:password = password + char的意思是”用password加上char,组成新的字符串,再存回password”。就像往一串珠子上不断加新珠子。
五、运行试试
保存为password.py,运行:
python password.py
我运行了三次,得到:
生成的密码是:k3!Qx&m9Rn#p
生成的密码是:7Tz$wL2@jH5c
生成的密码是:Pq8#nX4&bK1m
每次都不一样,而且完全没有规律——这正是强密码该有的样子。复制一个拿去注册,比你自己想的”姓名+生日”安全得多。
六、第一个改进:可选密码长度
12位是写死的。但有时候网站要求8位,有时候你想生成20位的超长密码。
改成让用户输入长度:
import random
import string
all_chars = string.ascii_letters + string.digits + string.punctuation
# 让用户输入密码长度
length_input = input("你想生成几位的密码?(直接回车默认12位):")
# 如果用户直接回车,用默认值12
if length_input == "":
length = 12
else:
length = int(length_input)
password = ""
for i in range(length):
char = random.choice(all_chars)
password = password + char
print(f"生成的{length}位密码是:{password}")
改动点:
- 用
input()问用户要几位 - 如果用户直接按回车(输入空字符串),用默认值12位
- 循环次数从写死的12改成变量
length
注意一个细节:input()返回的是字符串,range()需要整数,所以用int()转换(第13篇讲过)。
这里还有个隐患:如果用户输入”abc”,int("abc")会崩溃。修复方法你已经会了——try...except。这是第13篇做过的练习,留给你自己加(提示词可以这么问AI:”给密码生成器加输入验证,用户输入非数字时用默认长度12″)。
七、第二个改进:可选包含哪些字符
有些网站的密码不允许特殊符号。有些公司规定密码必须全是数字(比如PIN码)。我们让用户自己选:
import random
import string
print("=== 密码生成器 ===")
print("请选择密码包含的字符类型(可多选,用逗号分隔):")
print("1. 小写字母")
print("2. 大写字母")
print("3. 数字")
print("4. 特殊符号")
choice = input("你的选择(如 1,2,3):")
# 先准备一个空的"字符池"
all_chars = ""
# 根据用户的选择,往池子里加对应的字符
if "1" in choice:
all_chars = all_chars + string.ascii_lowercase
if "2" in choice:
all_chars = all_chars + string.ascii_uppercase
if "3" in choice:
all_chars = all_chars + string.digits
if "4" in choice:
all_chars = all_chars + string.punctuation
# 如果用户啥都没选,就全都要
if all_chars == "":
all_chars = string.ascii_letters + string.digits + string.punctuation
print("(没选?那我就全用上)")
length = int(input("密码长度(默认12):") or 12)
password = ""
for i in range(length):
char = random.choice(all_chars)
password = password + char
print(f"你的密码是:{password}")
这里有几个值得拆开看的点:
"1" in choice —— 字符串里的in操作
用户输入的是字符串,比如"1,3"。if "1" in choice:的意思是:这个字符串里有没有包含字符”1″。
in操作你学过:第13篇用if guess in guess_history:检查列表里有没有某数字。它对字符串同样好用——检查”大字符串里包不包含某个小字符串”。
所以用户输入1,3或13或3,1都能被识别。不用in的话,你得处理各种输入格式,麻烦多了。
逐项累加字符池
all_chars = ""
if "1" in choice:
all_chars = all_chars + string.ascii_lowercase
...
一开始是空池子,每满足一个条件就往里加一类字符。四个独立的if(不是if/elif),因为用户可以同时选多项。
input(...) or 12 —— 偷懒的默认值技巧
length = int(input("密码长度(默认12):") or 12)
这行有点trick(技巧)。拆开看:
input(...)让用户输入,返回字符串- 如果用户直接回车,返回空字符串
"" "" or 12—— Python会”挑选”or两边第一个”有内容”的值。空字符串被视为”没有内容”,所以结果是12int(...)把结果转成整数
这样一行就实现了”有输入用输入,没输入用默认”。你看不懂没关系,问AI:”解释一下 x = input() or 12 这种写法的原理”。
运行效果
=== 密码生成器 ===
请选择密码包含的字符类型(可多选,用逗号分隔):
1. 小写字母
2. 大写字母
3. 数字
4. 特殊符号
你的选择(如 1,2,3):1,2,3
密码长度(默认12):16
你的密码是:kT3mQxRn7bWpL2jH
选1,2,3(不要特殊符号),生成的密码里就真的一个符号都没有。需要纯数字PIN码?输入3,长度6,得到847291。
八、第三个改进:用函数重构 + 一次生成多个
现在我们把生成密码的逻辑装进函数(第九篇学的),顺便加一个”一次生成5个密码,你挑一个喜欢的”功能:
import random
import string
def make_password(length=12, use_lower=True, use_upper=True,
use_digit=True, use_symbol=True):
"""生成一个随机密码
参数:
length: 密码长度
use_lower: 是否包含小写字母
use_upper: 是否包含大写字母
use_digit: 是否包含数字
use_symbol: 是否包含特殊符号
"""
# 根据参数组装字符池
all_chars = ""
if use_lower:
all_chars = all_chars + string.ascii_lowercase
if use_upper:
all_chars = all_chars + string.ascii_uppercase
if use_digit:
all_chars = all_chars + string.digits
if use_symbol:
all_chars = all_chars + string.punctuation
# 生成密码
password = ""
for i in range(length):
char = random.choice(all_chars)
password = password + char
# 函数的产出:把生成的密码交出去
return password
# === 主程序 ===
print("=== 密码生成器 ===")
choice = input("包含哪些?(1小写 2大写 3数字 4符号,如1,2,3):")
length_input = input("密码长度(默认12):")
length = int(length_input) if length_input else 12
# 一次生成5个密码
print("n为你生成了5个密码:")
for i in range(5):
pwd = make_password(
length=length,
use_lower="1" in choice,
use_upper="2" in choice,
use_digit="3" in choice,
use_symbol="4" in choice,
)
print(f" {i + 1}. {pwd}")
函数带来了什么
以前的代码是”一整条流水线”,从问用户到生成密码到打印,全混在一起。现在make_password()只负责一件事:给我参数,还你密码。
好处立刻体现出来:
1. 生成5个密码只要一个循环
for i in range(5):
pwd = make_password(length=length, ...)
print(f" {i + 1}. {pwd}")
函数可以被反复调用。要10个密码?把5改成10就行。以前的写法你得把生成代码复制粘贴5遍。
2. 逻辑可以复用
以后你做任何需要随机字符串的东西(生成验证码、生成用户名、生成测试数据),把make_password()搬过去就能用。函数就是”打包好的手艺”。
3. 默认参数值
def make_password(length=12, use_lower=True, ...)
参数后面的=是默认值:调用时不写这个参数,就用默认值。所以make_password()(什么都不传)也能工作,直接给你一个12位全字符密码。
if "1" in choice 搬到了调用处
注意这行:
use_lower="1" in choice,
"1" in choice的结果是True或False(布尔值),正好匹配函数参数。用户输入的解析(”1,2,3″是啥意思)和密码生成的逻辑(怎么挑字符)分开了。这是写代码的一个重要习惯:每块代码只管自己的事。
运行效果
=== 密码生成器 ===
包含哪些?(1小写 2大写 3数字 4符号,如1,2,3):1,2,3
密码长度(默认12):10
为你生成了5个密码:
1. kT3mQxRn7b
2. Wp2LjH8sKx
3. Rn9bQm4TzW
4. jH5cPq7nX2
5. bK1mL2wT9p
挑一个顺眼的用。如果哪个”长得像脏话”或者”看起来不吉利”,换一个就是——反正生成不要钱。
九、回顾一下用到了什么
| 知识点 | 在哪里用的 |
|---|
|——–|———–|
| import模块 | random、string两个工具箱 |
|---|---|
| 变量 | all_chars, password, length |
| 字符串拼接 | all_chars + string.digits,password + char |
| 空字符串 | password = "" 从白纸开始积累 |
| 循环 | for i in range(12) 抽12次 |
| 条件判断 | 根据选择决定加哪类字符 |
| in操作 | "1" in choice 检查输入包含什么 |
| 函数 | make_password 打包生成逻辑 |
| 默认参数 | length=12 不传就用默认 |
| return | 函数把结果交出去 |
| f-string | 格式化输出密码 |
11个知识点,没有一个是新的。 string模块严格说算”新见面”,但它不是新知识——它只是import这个老知识的一次新应用。
十、今日小练习
试着自己加一个功能(可以问AI帮忙):
功能:给密码”评分”
生成密码后,程序顺便告诉你这个密码的强度:
- 包含4类字符 → “很强 💪”
- 包含3类字符 → “较强 👍”
- 包含2类字符 → “一般 😐”
- 只包含1类字符 → “太弱了 ⚠️”
提示:你需要统计all_chars里被加进了几类字符。想想怎么记录”加了几类”?一个计数器变量就够了(每满足一个if就+1)。
先自己想思路,再问AI,然后对比一下谁的方案更简洁。
下一篇:单词统计器 — 读取一篇文章,用字典统计每个词出现了多少次,找出全文最重要的10个词。这是所有”文本分析”工具的雏形。
发表回复