惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
P
Privacy International News Feed
P
Proofpoint News Feed
Security Archives - TechRepublic
Security Archives - TechRepublic
C
CXSECURITY Database RSS Feed - CXSecurity.com
Know Your Adversary
Know Your Adversary
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Attack and Defense Labs
Attack and Defense Labs
NISL@THU
NISL@THU
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
GbyAI
GbyAI
N
News and Events Feed by Topic
N
News | PayPal Newsroom
Y
Y Combinator Blog
C
CERT Recently Published Vulnerability Notes
N
Netflix TechBlog - Medium
S
Security Affairs
Spread Privacy
Spread Privacy
罗磊的独立博客
腾讯CDC
MyScale Blog
MyScale Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
LINUX DO - 热门话题
The Cloudflare Blog
L
LangChain Blog
博客园_首页
H
Hacker News: Front Page
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
SecWiki News
SecWiki News
A
Arctic Wolf
爱范儿
爱范儿
Google Online Security Blog
Google Online Security Blog
T
Threat Research - Cisco Blogs
Hacker News - Newest:
Hacker News - Newest: "LLM"
有赞技术团队
有赞技术团队
The GitHub Blog
The GitHub Blog
Cyberwarzone
Cyberwarzone
博客园 - 叶小钗
V
Visual Studio Blog
V
V2EX
T
Tailwind CSS Blog
Project Zero
Project Zero
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
MongoDB | Blog
MongoDB | Blog
D
Docker

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
14. 练习:登录注册系统 - 少数派
2023-08-11 · via 少数派

Hi,大家好。我是茶桁。

上一节课,我们详细的介绍了文件读写的流程和原理,并用Python进行实际操作了一下。

那么这节课呢,我们利用之前所学的内容,尝试做一个小练习:建立一个登录注册系统。上节课我们在结尾的时候讲练习内容贴了出来,还记得要求吗?

实现功能:
1. 用户输入用户名和密码以及确认密码
2. 用户名不能重复
3. 两次密码要一致
4. 用户用已经注册的账户登录
5. 密码如果错误3次,锁定,无法再登录。

那么这节课呢,因为都是一些讲过的知识点,所以在整个实现过程中我就不详细讲解了,我们重点在于介绍思想和流程。那让我们开始吧。

注册功能

我们先把大的结构写出来,一个注册功能,那首先需要接收两个参数:用户名、密码。

并且,为了防止用户注册时候输错密码导致无法登录,还需要让用户确认一遍密码:

# 先实现注册功能
# 封装一个函数 完成注册功能
def register():

    # 用户输入用户名
    username = input('欢迎注册,请输入用户名:')

    # 用户输入密码
    password = input('请输入您的密码: ')

    # 请确认您的密码
    re_password = input('请再输入一次您的密码: ')

    print(username, password, re_password)


register()

---
du 111111 111111

下一步我们来细化这个框架,我们把用户名先放在一边,来挑一挑密码的刺:

首先,我们需要让密码保持安全性,那么我们对位数,组合就要有要求。这里我们简单点,必须输入6位以上吧。

然后,在确认密码的时候,肯定两次密码要一致才行。

OK,让我们补全这几个逻辑关系:

# 先实现注册功能
# 封装一个函数 完成注册功能
def register():

    # 用户输入用户名
    username = input('欢迎注册,请输入用户名:')

    # 用户名需要检测是否已经存在

    # 用户输入密码
    password = input('请输入您的密码: ')

    # 检测密码长度不能低于6位
    if len(password) >= 6:
        # 请确认您的密码
        re_password = input('请再输入一次您的密码: ')
        # 检测密码和确认密码是否一致
        if re_password == password:
            # 用户名和密码都正确,可以写入文件
            pass
        else:
            print('两次输入的密码不同,请重新输入。', username, password, re_password)

    # 密码长度不够
    else:
        print('密码格式不正确:', username, password)

register()

---
两次输入的密码不同,请重新输入。 du 123456 1234567

可以看得出来,我确认密码的时候输入了其他密码,判断逻辑没有问题。

继续来细化,现在的问题是,当我们输入第一次密码的时候判断有问题,或者两次密码输入不一致的时候,我们没有办法跳到一开始让用户重新输入,那么下面我们就来解决这个问题:

# 先实现注册功能
# 封装一个函数 完成注册功能
def register():

    # 用户输入用户名
    username = input('欢迎注册,请输入用户名:')

    # 用户名需要检测是否已经存在

    # 利用循环,都正确的时候结束循环。
    while True:
        # 用户输入密码
        password = input('请输入您的密码: ')

        # 检测密码长度不能低于6位
        if len(password) >= 6:
            # 请确认您的密码
            re_password = input('请再输入一次您的密码: ')
            # 检测密码和确认密码是否一致
            if re_password == password:
                # 用户名和密码都正确,可以写入文件
                print('恭喜你,注册成功', username, password, re_password)
                # 结束循环
                break
            else:
                print('两次输入的密码不同,请重新输入。', username, password, re_password)

        # 密码长度不够
        else:
            print('密码格式不正确:', username, password)

register()

---
密码格式不正确: du 12345
两次输入的密码不同,请重新输入。 du 123456 1234567
恭喜你,注册成功 du 123456 123456

外面套一层while循环,这样就解决了。

只有当密码正确输入的时候,循环才会结束,否则就会跳到循环的最开始,重新进行输入。

接着,我们就来看看当输入正确之后,我们如何写入文件呢?这个是我们上一节课刚讲解的课程,现在让我们来实现一下:

# 先实现注册功能
# 封装一个函数 完成注册功能
def register():
        ...
            if re_password == password:
                # 用户名和密码都正确,可以写入文件
                # 打开文件,写入数据
                with open('./data/user.txt', 'a+', encoding='UTF-8') as fp:
                    fp.write(f'{username}:{password}\n')
                print(f'注册成功,用户名:{username}')
                # 结束循环
                break
        ...
register()

---
注册成功,用户名:du

这样,我们就将用户名和密码以username:password的格式以一行的形式存储到了一个user.txt文件内。并且,因为我们使用的a+的模式,所以每次打开指针都会是放在文件的末尾进行添加。

来,让我们看看是否成功了:

可以看到,没问题。这样,我们在之后读取的时候就可以直接读取单行,并且以key:value的形式拿到我们说要的用户名和密码。用于验证key是否存在,value是否正确。

说到验证key是否存在,似乎我们还没写这一段验证代码,既然文件里已经有数据了,让我们把这段代码补全吧, 我们在register()这个自定义函数外面写一段读取文件的代码,写在外面是因为,我们读取文件这个动作,和整个注册动作不能说毫无关系,只能说是没有关联。

with open('./data/user.txt', 'r', encoding="utf-8") as fp:
    res = fp.readlines()
    print(res)

---
['admin:123456\n', 'du:654321\n']

还记得吗?readlines()这个函数的应用,不记得的,看我教程的上一节内容复习下。

看似我们确实成功的读取到了全部内容,可是我们细想一下,这样想有没有问题?还记得我们上一节课上对r这个模式的说明吗?r是读取已有文件,但是如果文件不存在,那就会报错。

可是我们在整个程序运行过程中,不能因为这个原因就不让用户继续往下走了对吧?那么我们怎么去修改呢?来,我们一起尝试下:

# 读取所有的注册信息
with open('./data/user.txt', 'a+', encoding="utf-8") as fp:
    fp.seek(0,0)
    res = fp.readlines()
    print(res)

---
['admin:123456\n', 'du:654321\n']    

我们利用a+的特性,如果文件不存在则会创建,如果有,这将指针放在文件末尾。这样的话,这一段内容就没有问题了?为什么不用w+? 还不是因为w+太霸道,虽然新人胜旧人,但也不能就直接把旧人干掉吧。

但是a+是将指针放在文件末尾的,我们直接使用的话,什么内容也读不到,所以我们使用可seek(0,0)来将指针放在了文件头的位置。

好了,继续。我们只将文件读取出来没用,需要将其中的数据放在一个变量里供我们检测,所以下一步,我们就需要创建一个变量,并且接收文件中的所有数据:

# 先实现注册功能

# 专门定义数据变量,存放已经注册的用户信息
userlist = []
pwdlist = []

# 读取所有的注册信息
with open('./data/user.txt', 'a+', encoding="utf-8") as fp:
    fp.seek(0,0) # 调整指针到文件头
    res = fp.readlines() # 按照每一行读取所有用户数据
    for i in res: # 循环读取每一行数据
        r = i.strip() # 处理每一行尾部的换行符
        mydict = r.split(':') # 分隔用户名和密码
        userlist.append(mydict[0])
        pwdlist.append(mydict[1])
    print(userlist, pwdlist)

---
['admin', 'du'] ['123456', '654321']

这样,我们拿到用户名和密码,并且分别放入了两个list变量中。

接下来,我们对于注册那一步就需要进行判断了。

# 封装一个函数 完成注册功能
def register():
    ...
    # 用户输入用户名
    username = input('欢迎注册,请输入用户名:')

    # 用户名需要检测是否已经存在
    if username in userlist:
        print('当前用户已经存在,请更换用户名。')
    else:
        # 利用循环,都正确的时候结束循环。
        while True:
            # 用户输入密码
            password = input('请输入您的密码: ')
        ...

# register()

在代码中,我们判断用户名是否存在于列表中,如果存在,打印已经存在,如果不存在,则继续往下执行。将之前输入密码的while循环放入了else判断逻辑中。

看似问题解决了,不过新的问题又来了。虽然我们现在判断了用户是否存在,并且用户不存在的话可以继续往下执行,可是如果存在呢?我们需要的是让用户重新输入用户名,但是现在是打印完之后就结束了。所以,我们还得继续修改代码,这回简单了,和password的处理用一样的方法就可以了。

def register():
    # 执行循环, 用户名操作
    while True:
        # 用户输入用户名
        username = input('欢迎注册,请输入用户名:')

        # 用户名需要检测是否已经存在
        if username in userlist:
            print('当前用户已经存在,请更换用户名。')
        else:
            # 利用循环,都正确的时候结束循环。
            while True:
               ...
                if len(password) >= 6:
                    ...
                    if re_password == password:
                        ...
                        # 结束循环
                        break
                    else:
                      ...
# register()

我们将register()内的所有判断代码放入了一个循环中,那么在if之后,没有向下执行else内的逻辑的时候,就会跳转到最开始的while循环重新执行。

不过这样似乎还是不行,那就是几遍else逻辑全部执行完毕,内部的break也是跳出里面那层while循环,外层循环只能无限的执行下去了。

我们目前所要做的,就是在所有的代码顺利执行到密码正确确认的时候,整个函数执行就全部终止了。那有什么办法吗?

我们看到了while True, 然后开启了无限循环。那是不是说,如果True那里不为真,就无法进入循环了?

嗯,既然这样,我们设定一个变量,在需要终止函数的时候,设定变量为Flase就行了。

# 封装一个函数 完成注册功能
def register():
    # 定义一个变量,用于控制外循环
    site = True
    # 执行循环, 用户名操作
    while site:
        ...
        if username in userlist:
            print('当前用户已经存在,请更换用户名。')
        else:
            # 利用循环,都正确的时候结束循环。
            while True:
                ...
                if len(password) >= 6:
                    ...
                    if re_password == password:
                        ...
                        # 结束循环
                        # 结束外循环
                        site = False
                        # 结束内循环
                        break
                    else:
                        ...

# register()

这样,在密码确认成功之后,site设定为falsewhile循环判断条件为假,无法再次进入循环。执行代码,输出注册成功:用户名:du2, 并且跳出了循环不再继续向下执行。说明我们这一步已经没问题了。

那到这里,我们一个简易的可运行的注册方法就完成了,当然,这段代码中其实还有很多可以完善的空间,比如说,判断用户名是否存在之后,还要判断用户名是否合法,密码是否合法等等。还有就是我们一般接收账户密码的变量应该使用key:value形式的字典,不过大多数时候,我们还要考虑效率问题。分别保存成两份是不错的选择,那么这个时候,我们就需要用到获取当前字典中所有key值的方法了。以上代码在关键部位可以这样改:

# 专门定义数据变量,存放已经注册的用户信息
userdict = {}
userlist = []

# 读取所有的注册信息
with open('./data/user.txt', 'a+', encoding="utf-8") as fp:
    fp.seek(0,0) # 调整指针到文件头
    res = fp.readlines() # 按照每一行读取所有用户数据
    for i in res: # 循环读取每一行数据
        r = i.strip() # 处理每一行尾部的换行符
        mydict = r.split(':')
        userdict.update({mydict[0]:mydict[1]})
        userlist = userdict.keys()

# 封装一个函数 完成注册功能
def register():
    # 定义一个变量,用于控制外循环
    site = True
    # 执行循环, 用户名操作
    while site:
        # 用户输入用户名
        username = input('欢迎注册,请输入用户名:')

        # 用户名需要检测是否已经存在
        if username in userlist:
            print('当前用户已经存在,请更换用户名。')
        else:
            ...
register()

总之,代码的实现是需要一步一步细细琢磨的,而且实现的方式也不是只有一种。要考虑逻辑,效率等等因素。文章最后会给到我的源码文件,现在大家先一步步的跟着我往下走吧。

登录功能

在实现登录功能之前,我们来分析一下这个功能要做的事情:

  1. 需要使用已经注册的用户信息登录
  2. 密码输入错误3次之后,锁定账户信息(不能再使用这个账户进行登录操作)

这是两个最基本的功能,既然要核对用户信息和密码,那和注册一样,我们还是需要读取user.txt文件,拿到里面的信息后传给相应的变量,用于检测。那好,我们先把注册那边实现的部分功能拿过来,就无需自己再写一遍了:

# 搞定登录功能
# 专门定义数据变量,存放已经注册的用户信息
userdict = {}
userlist = []

# 读取所有的注册信息
with open('./data/user.txt', 'a+', encoding="utf-8") as fp:
    fp.seek(0,0) # 调整指针到文件头
    res = fp.readlines() # 按照每一行读取所有用户数据
    for i in res: # 循环读取每一行数据
        r = i.strip() # 处理每一行尾部的换行符
        mydict = r.split(':')
        userdict.update({mydict[0]:mydict[1]})
        userlist = userdict.keys()
    print(f'userdict:{userdict},\nuserlist:{userlist}')

---
userdict:{'admin': '123456', 'du': '654321', 'du2': '123456', 'zhang': '123456', 'wang': '123456'},
userlist:dict_keys(['admin', 'du', 'du2', 'zhang', 'wang']),

执行过后可以看到,三个变量已经分别存储了一个字典和两个列表,也就是字典存储了健值对的用户密码,以及将用户和密码再分别存储到两个列表内。

数据准备好了,接下来,我们开始定义函数吧:

# 封装登录函数
def login():

    # 获取用户登录时输入的用户名
    username = input('欢迎登录,请输入您的用户名:')

    # 检测当前用户名是否存在
    if username in userlist:
        # 让用户输入密码
        pass
    else:
        # 用户名不存在
        print('用户名错误,亲重新输入')
    # 检测用户是否属于锁定状态

那么大的框架就定义好了。现在让我们逐步开始细化这些代码。

先不看让用户继续输入密码的逻辑,我们先来看看用户名不存在的逻辑该怎么做。

当用户名不存在的时候,我们肯定是先要告知提示用户,接下来应该是让用户重新输入一次用户名。那怎么做呢?之前实现注册那边实际上已经有经验了,用while循环呗,接下来,让我们完善一下:

# 封装登录函数
def login():
    # 自定义变量, 控制登录外循环
    isLogin = True 

    # 创建循环
    while isLogin:

        # 获取用户登录时输入的用户名
        username = input('欢迎登录,请输入您的用户名:')

        # 检测当前用户名是否存在
        if username in userlist:
            while True:
                # 让用户输入密码
                pwd = input('请输入您的密码:')
                # 检测用户输入的密码是否正确
                if pwd == userdict[username]:
                    print(pwd)
                    isLogin = False # 结束外循环
                    break # 结束内循环
                else:
                    print('您的密码输入有误。')
        else:
            # 用户名不存在
            print('用户名错误,亲重新输入')

    # 检测用户是否属于锁定状态

login()

在细化的代码中,我们用了和注册函数一样的方法,创建了一个外循环和一个内循环。原因就是我们分别需要判断用户名和密码,都需要返回来重新输入。

之前注册的循环已经讲的比较详细了,所以这里我们就不细致的讲解了,小伙伴们自己好好琢磨一下逻辑关系。

那到这里,我们还缺什么呢?看看需求,我们似乎还需要判断用户输入密码错误的次数对吧?

好的,让我们继续,从定义变量开始:

# 封装登录函数
def login():

    ...
    # 定义变量,用来记录用户输入密码错误次数
    errorNum = 3

    # 创建循环
    while isLogin:
        ...
        if username in userlist:
            while True:
                ...
                if pwd == userdict[username]:
                    ...
                else:
                    # 密码错误,修改变量次数
                    errorNum -= 1
                    # 判断当前密码错误次数
                    if errorNum == 0:
                        print('给你机会你不中用啊细狗。账户已锁定,请联系管理人员并上供品。')
                        isLogin = False
                        break
                    else:
                        print(f'您的密码输入有误, 您还能再尝试{errorNum}次。')
        else:
            ...

    # 检测用户是否属于锁定状态

login()

我们定义了一个errorNum的变量,因为我们只能尝试最多三次,所以我们给这个变量设定了一个3的值。

在之后的循环中,没尝试错误一次,我们就让这个变量-1操作,直到变为0为止。

然后,就是我们需要定义一个黑名单把用户名写入了。

# 封装登录函数
def login():
    ...
    # 创建循环
    while isLogin:
        if username in userlist:
            while True:
                ...
                if pwd == userdict[username]:
                    ...
                else:
                    # 密码错误,修改变量次数
                    errorNum -= 1
                    # 判断当前密码错误次数
                    if errorNum == 0:
                        print('给你机会你不中用啊细狗。账户已锁定,请联系管理人员并上供品。')
                        # 锁定当前账户,把锁定的用户拉入黑名单
                        with open('./data/black.txt', 'a+', encoding='UTF-8') as fp:
                            fp.write(username+'\n')
                        isLogin = False
                        break
                    else:
                        print(f'您的密码输入有误, 您还能再尝试{errorNum}次。')
        else:
            ...

login()

---
您的密码输入有误, 您还能再尝试2次
您的密码输入有误, 您还能再尝试1次
给你机会你不中用啊细狗。账户已锁定,请联系管理人员并上供品。

这样,当我们的账户被输入错误三次之后,就会得到神的审判:关入小黑屋了。需要上供才行。(谁家这么设计产品,估计死的会很快吧?)

好了,继续往后,我们只写入了黑名单还不行,这样在执行的时候,还是无法判断用户是否输入的是锁定的账户。接下来需要做的事情,就是读取这个黑名单的文件,然后把里面的用户名全部以列表形式传到这个变量中。

再然后,我们只要在用户输入用户名的时候判断此用户是否被关小黑屋了就行了:

# 搞定登录功能
# 专门定义数据变量,存放已经注册的用户信息
...
blackUserList = [] # 定义一个小黑屋专用变量

# 读取所有的注册信息
with open('./data/user.txt', 'a+', encoding="utf-8") as fp:
      ...

# 读取所有黑名单用户
with open('./data/black.txt', 'a+', encoding='utf-8') as fp:
    fp.seek(0,0)
    res = fp.readlines()
    for i in res:
        r = i.strip() #
        blackUserList.append(r)

# 封装登录函数
def login():
    ...
    # 创建循环
    while isLogin:

        # 获取用户登录时输入的用户名
        username = input('欢迎登录,请输入您的用户名:')

        # 检测当前用户名是否存在
        if username in blackUserList:
            print('您的账户已经被锁定,并且还未给管理员上供品。')
            isLogin = False # 结束外循环
        elif username in userlist:
            while True:
                ...
        else:
            # 用户名不存在
            ...

    # 检测用户是否属于锁定状态

login()

功能合并

现在,我们把注册和登录功能都分别完成了,那么我们现在就剩下最后一步,将两个功能合并在一起。

注册和登录的函数是现成的,那么现在我们要做的事情就是:

  1. 将读取数据的两个with方法封装到一个函数内
  2. 定义一个主函数,进入进程后就执行
  3. 告知用户选择登录还是注册
  4. 在执行登录注册函数之前,初始化数据(加载读取数据的函数)

OK,让我们来实现吧:

首先是封装读取数据的函数:

# 定义一个读取所有用户数据的函数
def readAllUsers():
    # 读取所有的注册信息
    with open('./data/user.txt', 'a+', encoding="utf-8") as fp:
        global userdict
        global userlist
        fp.seek(0,0) # 调整指针到文件头
        res = fp.readlines() # 按照每一行读取所有用户数据
        for i in res: # 循环读取每一行数据
            r = i.strip() # 处理每一行尾部的换行符
            mydict = r.split(':')
            userdict.update({mydict[0]:mydict[1]})
            userlist = userdict.keys()

    # 读取所有黑名单用户
    with open('./data/black.txt', 'a+', encoding='utf-8') as fp:
        global blackUserList
        fp.seek(0,0)
        res = fp.readlines()
        for i in res:
            r = i.strip() #
            blackUserList.append(r)

注意我们这里用了global, 因为我们需要使用外部定义的变量,所以必须要讲变量改成全局变量,改动才会有效。

注册登录函数不用改动,直接原封不动的粘贴过来就可以了。再来我们就需要直接进入主进程进行选择:

# 判断当前脚本是否作为一个主进程脚本在执行
if __name__ == '__main__':
    '''
    这里的代码,只有在使用Python解释器直接运行时才会执行
    如果当前脚本作为了模块被其他文件导入后使用,那么这个地方的代码不会执行
    因此这个地方的代码,适合写当前脚本中的一些测试,这样不会影响其他脚本
    '''
    # 调用初始化方法,加载数据
    readAllUsers()

    isBegin = True
    while isBegin:
        myStr = '''
        ======================
        ** 登录(0) 注册(1)**
        ======================
        '''
        print(myStr)

        # 让用户选择对应的操作
        num = input('请输入对应的序号,体验功能:')
        if num == '0':
            login()
            isBegin = False
        elif num == '1':
            register()
            isBegin = False
        else:
            print('后续功能还在开发中。')
            isBegin = False

这样当我们输入python3 file.py的时候,就会直接进入这个主程序内。然后执行加载数据的函数,打印用户选择内容,然后等待用户选择并执行后续操作,如图:

而这其中我们设定一个变量isBegin来控制循环,每次执行完一次函数就会结束循环,然后需要重新执行。主要原因就是因为加载数据的方法我们写在了while循环外面,所以当我们注册之后再去登录,新创建的用户并不在用户列表内,无法完成登录。所以干脆结束掉之后重新加载数据,就可以执行登录了。

能不能改善呢?可以。只是今天的课程目的已经完毕了,所以当作留给大家的练习题吧。如何优化整个程序让其更合理高效,大家试试看。然后记得在评论区给我留言。

好了,这节课到这里就结束了,相关代码可以在我的仓库里去找到。

https://github.com/hivandu/AI_Cheats/tree/main/Python

下一节课中,我们会讲解模块,系统的内置模块。我们下次见。