學習記錄:【Python項目實戰】Python+MySQL開發新聞管理系統全集_嗶哩嗶哩_bilibilihttps://www.bilibili.com/video/BV1Qb4y1b75q?p=2&spm_id_from=pageDriver
目錄
一、項目介紹
1、項目結構
2、系統部分功能提前展示
3、項目流程圖
4、項目設計的數據庫以及數據表信息
?二、環境配置
三、項目實戰數據庫模塊db編寫
1、數據庫連接池
2、數據庫數據訪問接口(DAO)
四、項目實戰業務處理模塊service編寫
1、用戶數據操作業務代碼編寫
五、APP程序控制臺輸入輸出模塊
1、模塊介紹
2、代碼
一、項目介紹
1、項目結構
db:數據庫相關信息:mysql_db.py——定義數據庫連接等事宜;user_dao.py——定義數據表增刪改查相關操作
service:業務邏輯,將數據庫資源和用戶操作界面分開
app.py:編寫業務邏輯等代碼
2、系統部分功能提前展示
?
3、項目流程圖
?
4、項目設計的數據庫以及數據表信息
數據庫:vega
數據表:
1)用戶表t_user
2)角色表t_role
?二、環境配置
mysql數據庫安裝MySQL :: MySQL Community Downloads
mysql驅動程序
連接mysql, 需要mysql connector,
pip install mysql-connector
pip install pymysql
windown10
pycharm
python3.7
三、項目實戰數據庫模塊db編寫
1、數據庫連接池
為什么要使用數據庫連接池 、好處是什么_yangniceyangyang的博客-CSDN博客_數據庫連接池的作用
https://blog.csdn.net/yangniceyangyang/article/details/87183013
1.為什么要使用數據庫連接池 、好處是什么
? ? ? ?對于一個簡單的數據庫應用,由于對于數據庫的訪問不是很頻繁。這時可以簡單地在需要訪問數據庫時,就新創建一個連接,用完后就關閉它,這樣做也不會帶來什么明顯的性能上的開銷。但是對于一個復雜的數據庫應用,情況就完全不同了。頻繁的建立、關閉連接,會極大的減低系統的性能,因為對于連接的使用成了系統性能的瓶頸。
連接復用。通過建立一個數據庫連接池以及一套連接使用管理策略,使得一個數據庫連接可以得到高效、安全的復用,避免了數據庫連接頻繁建立、關閉的開銷。
對于共享資源,有一個很著名的設計模式:資源池。該模式正是為了解決資源頻繁分配、釋放所造成的問題的。把該模式應用到數據庫連接管理領域,就是建立一個數據庫連接池,提供一套高效的連接分配、使用策略,最終目標是實現連接的高效、安全的復用。
數據庫連接池的基本原理是在內部對象池中維護一定數量的數據庫連接,并對外暴露數據庫連接獲取和返回方法。如:
外部使用者可通過getConnection 方法獲取連接,使用完畢后再通過releaseConnection方法將連接返回,注意此時連接并沒有關閉,而是由連接池管理器回收,并為下一次使用做好準備。2.數據庫連接池技術帶來的優勢:
1. 資源重用
? ? ? ?由于數據庫連接得到重用,避免了頻繁創建、釋放連接引起的大量性能開銷。在減少系統消耗的基礎上,另一方面也增進了系統運行環境的平穩性(減少內存碎片以及數據庫臨時進程/線程的數量)。
2. 更快的系統響應速度
? ? ? ? 數據庫連接池在初始化過程中,往往已經創建了若干數據庫連接置于池中備用。此時連接的初始化工作均已完成。對于業務請求處理而言,直接利用現有可用連接,避免了數據庫連接初始化和釋放過程的時間開銷,從而縮減了系統整體響應時間。
3. 新的資源分配手段
? ? ? ?對于多應用共享同一數據庫的系統而言,可在應用層通過數據庫連接的配置,實現數據庫連接池技術,幾年錢也許還是個新鮮話題,對于目前的業務系統而言,如果設計中還沒有考慮到連接池的應用,那么…….快在設計文檔中加上這部分的內容吧。某一應用最大可用數據庫連接數的限制,避免某一應用獨占所有數據庫資源。
4. 統一的連接管理,避免數據庫連接泄漏
? ? ? ?在較為完備的數據庫連接池實現中,可根據預先的連接占用超時設定,強制收回被占用連接。從而避免了常規數據庫連接操作中可能出現的資源泄漏。一個最小化的數據庫連接池實現:
補充:這里的數據庫連接指的是:
conn = pymysql.connect(host="localhost",user="root",passwd="123456",database="vega")
上面這個語句就是創建了一個數據庫連接,在對數據庫訪問不頻繁的時候,通常就會在使用數據庫的時候,創建一個數據庫連接,但是頻繁訪問數據庫的時候,臨時創建數據庫連接的話,會帶來極大的資源浪費,因此數據庫連接池會事先維護一定的數據庫連接,等需要的時候直接拿一個連接對數據庫連接進行操作即可,一般一個系統只有一個全局連接池。
Authentication plugin 'caching_sha2_password' is not supported問題
添加參數:
"auth_plugin":'mysql_native_password'
mysql_db.py
# @Time : 2021/12/16 22:09
# @Author : @linlianqin
# @Site :
# @File : mysql_db.py
# @Software: PyCharm
# @description:'''
創建連接池,這樣可以避免頻繁訪問數據庫的時候臨時創建連接,使得資源浪費
'''import mysql.connector.pooling# 定義連接需要的參數,用字典封存,私有參數
__config = {"host":"localhost","port":3306,"user":"root","password":"","database":"vega","auth_plugin":'mysql_native_password'
}
import pymysql
# 創建連接池,定義最大連接數
try:pool = mysql.connector.pooling.MySQLConnectionPool(**__config,pool_size=10)
except Exception as e:print("創建連接池出現異常:",e)
2、數據庫數據訪問接口(DAO)
DAO:data access object
這部分代碼主要是為了將對數據的操作和業務分開
這部分寫在代碼user_dao.py中,主要含登錄功能和角色查找功能
涉及到的知識點:密碼加密解密(這里沒有使用)、數據庫查找操作、表連接等
可參考:【數據庫學習】——從零學習SQL語句(含SQL數據類型、SQL語句實例操作)_python_AI_fans的博客-CSDN博客https://blog.csdn.net/qq_45769063/article/details/121994889
基本步驟:
1)從連接池中取出一個連接;
2)創建cursor游標對象
3)SQL語句執行
4)邏輯代碼執行
5)異常處理
6)返回數據庫連接(這里雖然是close,但是不是將連接關閉,而是將其返回給了數據連接池)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 11:19
# @Author :
# @Site :
# @File : user_dao.py
# @Software: PyCharm
# @description:'''
定義數據庫操作,即一個數據訪問接口,將數據庫操作和業務邏輯封存起來
'''from mysql_db import poolclass userDAO:# 登錄數據庫def login(self,username,password):try:# 從連接池中取出一個連接conn = pool.get_connection()cor = conn.cursor()# 根據用戶名和密碼從用戶表t_user中選擇信息記錄,若記錄數為1則登錄成功,否則不成功sql = "select count(*) from t_user where username=%s and password=%s;"cor.execute(sql,(username,password))# 因為只有一條記錄,取出第一個字段count = cor.fetchone()[0]if count == 1:return Trueelse:return Falseexcept Exception as e:print("login failed:",e)finally:# 當連接屬性在當前目錄中,關閉連接if "conn" in dir():conn.close()# 根據用戶名獲取角色身份,因為客戶和管理員身份看到的界面是不一樣的# 這里主要是通過用戶表的角色id在身份表t_role中進行查找對應的身份def search_user_role(self,username):try:conn = pool.get_connection()cor = conn.cursor()sql = "select r.role from t_user u join t_role r on u.role_id=r.role_id where u.username=%s"cor.execute(sql,(username))role = cor.fetchone()[0]return roleexcept Exception as e:print(e)finally:if "conn" in dir():conn.close()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/22 13:47
# @Author : @linlianqin
# @Site :
# @File : role_dao.py
# @Software: PyCharm
# @description:from db.mysql_db import poolclass roleDAO:# 查詢所有身份def search_role(self):try:conn = pool.get_connection()cor = conn.cursor()sql = "select * from t_role"cor.execute(sql)role = cor.fetchall()return roleexcept Exception as e:print(e)finally:if "conn" in dir():conn.close()
?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 14:45
# @Author : @linlianqin
# @Site :
# @File : news_dao.py
# @Software: PyCharm
# @description:'''
定義新聞數據庫操作,即一個新聞數據訪問接口,將新聞數據庫操作和業務邏輯封存起來
'''from db.mysql_db import poolclass newDAO:# 查找待審批的新聞,并且顯示指定頁數的記錄def search_unreview_list(self,page):try:# 從連接池中取出一個連接conn = pool.get_connection()cor = conn.cursor()# 這里是按照編輯降序的方式排序,并且限制每頁顯示10個記錄sql = "select n.id,n.title,n.state,t.type,u.username from t_news n join t_type t on n.type_id=t.id join t_user u on n.editor_id=u.user_id " \"where n.state=%s order by n.create_time desc limit %s,%s"cor.execute(sql,("待審批",(page-1)*5,5))res = cor.fetchall()return resexcept Exception as e:print(e)finally:# 當連接屬性在當前目錄中,關閉連接if "conn" in dir():conn.close()# 計算待審批新聞按照每頁10條記錄進行顯示的話總共多少頁def count_page_unreview_list(self):try:# 從連接池中取出一個連接conn = pool.get_connection()cor = conn.cursor()sql = "select ceil(count(*)/5) from t_news n where n.state=%s"cor.execute(sql,["待審批"])res = cor.fetchone()[0]return resexcept Exception as e:print(e)finally:# 當連接屬性在當前目錄中,關閉連接if "conn" in dir():conn.close()# 更新新聞狀態,審批新聞,即將新聞的狀態更新為已審批def update_unreview_news_state(self,new_id):try:# 從連接池中取出一個連接conn = pool.get_connection()cor = conn.cursor()sql = "update t_news set state=%s where id = %s"cor.execute(sql,("已審批",new_id))conn.commit()except Exception as e:print(e)if "conn" in dir():# 事務回滾conn.rollback()finally:# 當連接屬性在當前目錄中,關閉連接if "conn" in dir():conn.close()# 查詢所有的新聞def search_news_list(self,page):try:# 從連接池中取出一個連接conn = pool.get_connection()cor = conn.cursor()# 這里是按照編輯降序的方式排序,并且限制每頁顯示10個記錄sql = "select n.id,n.title,n.state,t.type,u.username from t_news n join t_type t on n.type_id=t.id join t_user u on n.editor_id=u.user_id " \"order by n.create_time desc limit %s,%s"cor.execute(sql,((page-1)*5,5))res = cor.fetchall()return resexcept Exception as e:print(e)finally:# 當連接屬性在當前目錄中,關閉連接if "conn" in dir():conn.close()# 計算新聞按照每頁10條記錄進行顯示的話總共多少頁def count_page_list(self):try:# 從連接池中取出一個連接conn = pool.get_connection()cor = conn.cursor()sql = "select ceil(count(*)/5) from t_news"cor.execute(sql)res = cor.fetchone()[0]return resexcept Exception as e:print(e)finally:# 當連接屬性在當前目錄中,關閉連接if "conn" in dir():conn.close()# 根據新聞id刪除新聞def delete_new_by_id(self,new_id):try:# 從連接池中取出一個連接conn = pool.get_connection()cor = conn.cursor()sql = "delete from t_news where id = %s"cor.execute(sql,[new_id])conn.commit()except Exception as e:print(e)if "conn" in dir():# 事務回滾conn.rollback()finally:# 當連接屬性在當前目錄中,關閉連接if "conn" in dir():conn.close()if __name__ == '__main__':l = newDAO()print(l.search_unreview_list(1))
四、項目實戰業務處理模塊service編寫
1、用戶數據操作業務代碼編寫
user_service.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 11:57
# @Author : @linlianqin
# @Site :
# @File : user_service.py
# @Software: PyCharm
# @description:'''
用于處理業務邏輯,比如調用多個數據庫的dao,獲取多個數據庫的數據和保存數據到多個數據庫當中
'''from db.user_dao import userDAOclass userService:__user_dao = userDAO() # 私有屬性# 登錄數據庫def login(self,username,password):flag = self.__user_dao.login(username,password)return flag# 根據用戶名獲取角色身份,因為客戶和管理員身份看到的界面是不一樣的# 這里主要是通過用戶表的角色id在身份表t_role中進行查找對應的身份def search_user_role(self,username):role = self.search_user_role(username)return role
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/22 13:48
# @Author : @linlianqin
# @Site :
# @File : role_service.py
# @Software: PyCharm
# @description:from db.role_dao import roleDAOclass roleService:__role_dao = roleDAO()# 查詢所有身份def search_role(self):res = self.__role_dao.search_role()return res
?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 16:59
# @Author : @linlianqin
# @Site :
# @File : news_service.py
# @Software: PyCharm
# @description:from db.news_dao import newDAOclass newsService:__news_dao = newDAO()# 查找待審批的新聞,并且顯示指定頁數def search_unreview_list(self,page):res = self.__news_dao.search_unreview_list(page)return res# 計算待審批新聞按照每頁10條記錄進行顯示的話總共多少頁def count_page_unreview_list(self):count_page = self.__news_dao.count_page_unreview_list()return count_page# 更新新聞狀態,審批新聞,即將新聞的狀態更新為已審批def update_unreview_news_state(self,new_id):self.__news_dao.update_unreview_news_state(new_id)# 查詢所有的新聞def search_news_list(self,page):res = self.__news_dao.search_news_list(page)return res# 計算新聞按照每頁10條記錄進行顯示的話總共多少頁def count_page_list(self):count_page = self.__news_dao.count_page_list()return count_page# 根據新聞id刪除新聞def delete_new_by_id(self,new_id):self.__news_dao.delete_new_by_id(new_id)
?
五、APP程序控制臺輸入輸出模塊
因為一個系統不是運行一次就結束了,而是可以反復的循環運行,即比如登錄界面,點擊登錄,退出后又可以登錄,因此需要些一個可以進行循環運行的程序
app.py
1、模塊介紹
getpass:用于掩蓋密碼
os:用于清空控制臺——os.system("cls")
colorama:控制控制臺打印內容的前背景色
2、代碼
上圖可以看出系統有三級菜單:
登錄菜單
管理事項選擇菜單:新聞管理、用戶管理
具體管理操作菜單:審批新聞、刪除新聞等
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 13:34
# @Author : @linlianqin
# @Site :
# @File : app.py
# @Software: PyCharm
# @description:'''
用于處理控制臺的輸入輸出,以及跟用戶交互邏輯處理
'''
from colorama import Fore,Style
from getpass import getpass
import os
import sys
import timefrom service.user_service import userService
from service.news_service import newsService
from service.role_service import roleService__userService = userService()
__newsService = newsService()
__roleService = roleService()# 登錄系統輪詢
while True:# 每一輪的輪詢后清空控制臺os.system("cls")print(Fore.LIGHTBLUE_EX,"\n\t==================")print(Fore.LIGHTBLUE_EX,"\n\t歡迎使用新聞管理系統")print(Fore.LIGHTBLUE_EX, "\n\t==================")print(Fore.LIGHTGREEN_EX, "\n\t1.登錄系統")print(Fore.LIGHTGREEN_EX, "\n\t2.退出系統")print(Style.RESET_ALL)opt = input("\n\t輸入操作編號:")# 登錄系統if opt == "1":username = input("\n\t用戶名:")password = getpass("\n\t密碼:") # 掩藏密碼# 登錄result = __userService.login(username,password)# 登錄成功if result:# 獲取角色role = __userService.search_user_role(username)# 根據不同角色進入不同的界面# 界面輪詢while True:if role=="新聞編輯":print("test")elif role == "管理員":os.system("cls")print(Fore.LIGHTGREEN_EX, "\n\t1.新聞管理")print(Fore.LIGHTGREEN_EX, "\n\t2.用戶管理")print(Fore.LIGHTRED_EX, "\n\tback.退出登錄")print(Fore.LIGHTRED_EX, "\n\texit.退出系統")print(Style.RESET_ALL)opt = input("\n\t輸入操作編號:")# 新聞管理if opt == "1":while True:os.system("cls")print(Fore.LIGHTGREEN_EX, "\n\t1.審批新聞")print(Fore.LIGHTGREEN_EX, "\n\t2.刪除新聞")print(Fore.LIGHTRED_EX, "\n\tback.返回上一層")print(Style.RESET_ALL)opt = input("\n\t輸入操作編號:")# 審批新聞if opt == "1":page = 1 # 當前頁碼數while True:os.system("cls")total_page = __newsService.count_page_unreview_list() # 總頁數cur_page_result = __newsService.search_unreview_list(page) # 當前頁記錄# 逐條打印當前頁記錄for index in range(len(cur_page_result)):new = cur_page_result[index]print(Fore.LIGHTBLUE_EX, "\n\t%d\t%s\t%s\t%s\t%s"%(index+1,new[1],new[2],new[3],new[4]))print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")print(Fore.LIGHTBLUE_EX, "\n\t%d/%d"%(page,total_page))print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")print(Fore.LIGHTRED_EX, "\n\tback.返回上一層")print(Fore.LIGHTRED_EX, "\n\tprev.上一頁")print(Fore.LIGHTRED_EX, "\n\tnext.下一頁")print(Style.RESET_ALL)opt = input("\n\t輸入操作指令(輸入數字表示審批對應記錄的新聞):")if opt == "back":breakelif opt == "prev" and page > 1:page -= 1elif opt == "next" and page < total_page:page += 1elif int(opt) >= 1 and int(opt) <= 5:# 獲取新聞在數據表中的主鍵IDnew_id = cur_page_result[int(opt)-1][0]__newsService.update_unreview_news_state(new_id)# 刪除新聞elif opt == "2":page = 1 # 當前頁碼數while True:os.system("cls")total_page = __newsService.count_page_list() # 總頁數cur_page_result = __newsService.search_news_list(page) # 當前頁記錄# 逐條打印當前頁記錄for index in range(len(cur_page_result)):new = cur_page_result[index]print(Fore.LIGHTBLUE_EX,"\n\t%d\t%s\t%s\t%s\t%s" % (index + 1, new[1], new[2], new[3], new[4]))print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")print(Fore.LIGHTBLUE_EX, "\n\t%d/%d" % (page, total_page))print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")print(Fore.LIGHTRED_EX, "\n\tback.返回上一層")print(Fore.LIGHTRED_EX, "\n\tprev.上一頁")print(Fore.LIGHTRED_EX, "\n\tnext.下一頁")print(Style.RESET_ALL)opt = input("\n\t輸入操作指令(輸入數字表示刪除對應記錄的新聞):")if opt == "back":breakelif opt == "prev" and page > 1:page -= 1elif opt == "next" and page < total_page:page += 1elif int(opt) >= 1 and int(opt) <= 5:# 獲取新聞在數據表中的主鍵IDnew_id = cur_page_result[int(opt) - 1][0]__newsService.delete_new_by_id(new_id)# 返回上一層elif opt == "back":break# 用戶管理elif opt == "2":while True:os.system("cls")print(Fore.LIGHTGREEN_EX, "\n\t1.添加用戶")print(Fore.LIGHTGREEN_EX, "\n\t2.修改用戶")print(Fore.LIGHTGREEN_EX, "\n\t3.刪除用戶")print(Fore.LIGHTRED_EX, "\n\tback.返回上一層")print(Style.RESET_ALL)opt = input("\n\t輸入操作編號:")# 返回上一層if opt == "back":break# 添加用戶elif opt == "1":os.system("cls")username = input("\n\t用戶名:")password = getpass("\n\t密碼:")repassword = getpass("\n\t重復密碼:")if password != repassword:print(Style.RESET_ALL)print("\n\t兩次輸入的密碼不一樣(3s后返回)")time.sleep(3)continueemail = input("\n\t郵箱:")role_result = __roleService.search_role()for index in range(len(role_result)):one = role_result[index]print(Fore.LIGHTBLUE_EX, "\n\t%d\t%s"%(index+1,one[1]))print(Style.RESET_ALL)opt = input("\n\t角色編號:")role_id =role_result[int(opt)-1][0]__userService.insert_new_user(username,password,role_id,email)print("\n\t保存成功(3s自動返回)")time.sleep(3)# 修改用戶elif opt == "2":page = 1 # 當前頁碼數while True:os.system("cls")total_page = __userService.count_user_page() # 總頁數cur_page_result = __userService.search_all_users(page) # 當前頁記錄# 逐條打印當前頁記錄for index in range(len(cur_page_result)):new = cur_page_result[index]print(Fore.LIGHTBLUE_EX,"\n\t%d\t%s\t%s\t%s\t%s" % (index + 1, new[1], new[2], new[3], new[4]))print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")print(Fore.LIGHTBLUE_EX, "\n\t%d/%d" % (page, total_page))print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")print(Fore.LIGHTRED_EX, "\n\tback.返回上一層")print(Fore.LIGHTRED_EX, "\n\tprev.上一頁")print(Fore.LIGHTRED_EX, "\n\tnext.下一頁")print(Style.RESET_ALL)opt = input("\n\t輸入操作指令(輸入數字表示要修改的用戶名id):")if opt == "back":breakelif opt == "prev" and page > 1:page -= 1elif opt == "next" and page < total_page:page += 1elif int(opt) >= 1 and int(opt) <= 5:os.system("cls")username = input("\n\t新用戶名:")password = getpass("\n\t新密碼:")repassword = getpass("\n\t重復密碼:")if password != repassword:print(Style.RESET_ALL)print("\n\t兩次輸入的密碼不一樣(3s后返回)")time.sleep(3)continueemail = input("\n\t新郵箱:")role_result = __roleService.search_role()for index in range(len(role_result)):one = role_result[index]print(Fore.LIGHTBLUE_EX, "\n\t%d\t%s" % (index + 1, one[1]))print(Style.RESET_ALL)opt = input("\n\t新角色編號:")role_id = role_result[int(opt) - 1][0]user_id = cur_page_result[int(opt)-1][0]opt = input("\n\t是否保存:Y/N?")if opt == "Y" or opt == "y":__userService.update_user_mess(user_id,username,password,role_id,email)print("\n\t保存成功(3s后返回)")elif opt == "N":continue__userService.insert_new_user(username, password, role_id, email)print("\n\t保存成功(3s自動返回)")time.sleep(3)# 刪除用戶elif opt == "3":page = 1 # 當前頁碼數while True:os.system("cls")total_page = __userService.count_user_page() # 總頁數cur_page_result = __userService.search_all_users(page) # 當前頁記錄# 逐條打印當前頁記錄for index in range(len(cur_page_result)):new = cur_page_result[index]print(Fore.LIGHTBLUE_EX,"\n\t%d\t%s\t%s\t%s\t%s" % (index + 1, new[1], new[2], new[3], new[4]))print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")print(Fore.LIGHTBLUE_EX, "\n\t%d/%d" % (page, total_page))print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")print(Fore.LIGHTRED_EX, "\n\tback.返回上一層")print(Fore.LIGHTRED_EX, "\n\tprev.上一頁")print(Fore.LIGHTRED_EX, "\n\tnext.下一頁")print(Style.RESET_ALL)opt = input("\n\t輸入操作指令(輸入數字表示要刪除的用戶id):")if opt == "back":breakelif opt == "prev" and page > 1:page -= 1elif opt == "next" and page < total_page:page += 1elif int(opt) >= 1 and int(opt) <= 5:os.system("cls")user_id = cur_page_result[int(opt) - 1][0]__userService.delete_by_id(user_id)print("\n\t刪除成功(3s自動返回)")time.sleep(3)# 退出登錄elif opt == "back":break# 退出系統elif opt == "exit":sys.exit(0)# 登錄失敗else:print("\n\t登錄失敗(3s后自動返回)")time.sleep(3)# 退出系統elif opt == "2":sys.exit(0)
?