Python全棧專案(電子詞典、協程、pdb除錯)

巴黎香榭發表於2018-08-27
什麼是專案?
軟體專案 : 實現一定完整性功能的程式碼
軟體專案的開發流程
需求分析
概要設計
專案規劃
詳細設計
編碼測試 
專案測試
除錯修改 
專案釋出
後期維護更新
需求分析 : 確定使用者的真實需求
1. 確定使用者的真實需求,即專案的基本功能
2. 對專案的難度和可行性進行分析
3. 完成需求分析文件,進行確認
概要設計 : 對專案進行整體分析,初步確定技術方向
1. 整體設計,確定專案架構
2. 確定專案功能模組劃分
3. 確定大的技術方向
4. 編寫專案概要設計文件,開發流程圖
專案規劃 : 確定專案分工,按照專案時限進行規劃
1. 確定開發順序
2. 確定開發的時間軸和里程碑
3. 人員的分配
4. 完成甘特圖和思維導圖指導開發
詳細設計 : 專案具體的開發設計,完成設計手冊
1.根據開發內容,形成詳細設計文件
      思路,  邏輯流程  ,功能說明,技術點說明,資料結構,程式碼說明,注意事項,預期效果,環境約束
編碼測試 : 按照規劃完成編碼,做基本的測試工作
1.寫程式碼
2.程式碼基本測試
3.技術攻關
4.程式碼整合
專案測試 :  對專案整體功能進行測試
1. 跨平臺性,是否符合環境,功能bug,壓力測試
2. 完成測試報告
3. 根據測試結果修改bug
專案釋出
1.將專案提交給使用者,進行釋出使用
2.完成專案使用文件
後期維護 
1.處理使用中出現的問題
2.專案的升級和功能的新增
專案注意事項
1.按時完成專案開發是首要工作
2.專案實施人員的衝突問題
3.無計劃的實施必要帶來後期更大的效率低下
專案開發工具
文件編寫 : 
word  ppt  markdowm   LaTex
專案流程圖 : 
Mindmanager  Xmind  visio
專案管理: 
project
程式碼管理工具:
git   svn
編輯工具:  
pycharm  Webstream  eclipse  
             sublime  vim   vscode  atom
                             電子詞典專案文件
功能說明 : 
1. 使用者可以登入註冊
登入憑藉使用者名稱密碼即可 如果輸入不正確可以重複輸入
註冊 要求使用者有填寫使用者名稱密碼,且使用者名稱不能重複。其他資訊隨意
2. 使用者資訊可以長期儲存在伺服器,保證下次登入可以使    用
3. 能夠滿足多個使用者端程式同時操作的情況
4. 功能分為客戶端和服務端,客戶端執行後即進入第一界    面
    第一介面 : 登入   註冊   退出
5. 登入成功後進入第二介面
    第二介面 : 查詞   檢視歷史記錄  退出
6. 功能說明
    登入 : 
選擇登入功能 輸入使用者名稱密碼,如果成功進入第二介面,不成功保持在第一介面,提示失敗原因
    註冊 : 
選擇註冊功能,填寫資訊,成功後可以保持第一介面或者使用新註冊使用者直接完成登入到第二介面,失敗提示失敗原因
    第一介面退出 : 
直接退出客戶端
第二介面退出 :
第二介面退出相當於登出,即退回到第一介面
    查詞 : 
可以迴圈輸入單詞,顯示出單詞詞義
            輸入q表示查詞結束回到第二介面。如果查詢的詞不存在則有相應提示
*單詞本 : 每一行一個單詞
               單詞和解釋之間一定有空格
       單詞有序排列
             1. 文字查詢  2.資料庫查詢
    歷史記錄:
選擇檢視歷史記錄即列印出使用者的查詢記錄
               可以列印所有記錄也可以列印最近10條。
                name     word     time
專案分析
模組 :  socket 套接字
         pymysql/pymongo
os   multiprocessing   threading   select
1.確定服務端和客戶端分為哪些功能,每個功能要做什麼工作
服務端 :
main()  :  
建立套接字,父子程式,子程式處理客戶端請求,父程式接受新的連線
login  接受客戶端資訊 
        資料庫匹配
        返回結果
register  接受使用者資料
           判斷是否重複
           插入資料庫返回註冊成功
   使用者存在返回註冊失敗
query     接受使用者單詞
           通過資料庫或者檔案查詢單詞
           將單詞結果返回給使用者
   如果沒有查到返回相應資訊
   如果查詞成功則插入歷史記錄
history   接受客戶請求
           查詢資料庫返回歷史記錄
   如果使用者沒有歷史記錄則返回資訊
客戶端 :
main:  建立套接字 —>  連線 —> 列印一級介面
login   :  輸入使用者名稱密碼
             傳送給服務端
     接受返回結果,如果成功則跳轉到二級介面
     失敗列印結果
register : 輸入使用者名稱密碼
             傳送給服務端
     接受返回結果
query :   迴圈輸入單詞
            傳送單詞給服務端
    接受結果並列印
history :  傳送請求 —> 接受結果列印
2.確定建立什麼樣的資料表,表的結構,將表建立起來
user  : id  name  passwd
create table user (
id int auto_increment primary key,
name varchar(32) not null,
passwd varchar(16) default `000000`);
hist  : id  name  word   time
create table hist (
id int auto_increment primary key,
name varchar(32) not null,
word varchar(64) not null,
time varchar(64));
  
words : id  word  interpret
create table words (
id int auto_increment primary key,
word varchar(64),
interpret text); 
3. 如果要使用資料庫查詞則程式設計將單詞本內容存入資料庫
import pymysql 
import re 

def main():
    # 獲取詞典檔案流物件
    f = open(`dict.txt`)
    # 連結資料庫並建立遊標物件
    db = pymysql.connect
    (`localhost`,`root`,`123456`,`dict`)
    cursor = db.cursor()
    # 迴圈讀取每一行內容
    for line in f:
        try:
            L = re.split("[ ]+",line)
        except Exception:
            pass 
        sql = "insert into words (word,interpret)
         values (`%s`,`%s`)"%(L[0],` `.join(L[1:]))

        # 處理破壞SQL的特殊字元
        try:
            cursor.execute(sql)
            db.commit()
        except Exception:
            db.rollback()
            
    f.close()


if __name__ == "__main__":
    main()

4. 搭建框架,實現通訊 (建立套接字,設定結構,建立併發)
伺服器端:
#!/usr/bin/env python3
#coding=utf-8

```
name : Paris
date : 2018-8-27
email : 1546079656@qq.com
modules: python3.5  mysql  pymysql
This is a dict project for AID
```

from socket import *
import os 
import pymysql
import time
import sys 
import signal 

DICT_TEXT = "./dict.txt"
HOST = `0.0.0.0`
PORT = 8000
ADDR = (HOST,PORT)

#主控制流程
def main():
    #連線資料庫
    db = pymysql.connect
    (`localhost`,`root`,`123456`,`dict`)

    #建立流式套接字
    s = socket()
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(ADDR)
    s.listen(5)
    #或略子程式退出
    signal.signal(signal.SIGCHLD,signal.SIG_IGN)

    while True:
        try:
            c,addr = s.accept()
            print("Connect from",addr)
        except KeyboardInterrupt:
            s.close()
            sys.exit("伺服器退出")
        except Exception as e:
            print(e)
            continue 
        #建立子程式處理客戶端請求
        pid = os.fork() 
        if pid == 0:
            s.close()
            do_child(c,db)
        else:
            c.close()

def do_child(c,db):
    #迴圈接收請求
    while True:
        data = c.recv(128).decode()
        print("Request:",data)
        if (not data) or data[0] == `E`:
            c.close()
            sys.exit(0)
        elif data[0] == `R`:
            do_register(c,db,data) 
        elif data[0] == "L":
            do_login(c,db,data)
        elif data[0] == `Q`:
            do_query(c,db,data)
        elif data[0] == `H`:
            do_history(c,db,data)


def do_register(c,db,data):
    pass


def do_login(c,db,data):
    pass

def do_query(c,db,data):
    pass


def do_history(c,db,data):
    pass

          
if __name__ == "__main__":
    main()

客戶端:

#!/usr/bin/env python3
#coding=utf-8

from socket import *
import sys 
import getpass

def main():
    if len(sys.argv) < 3:
        print("argv is error")
        return
    HOST = sys.argv[1]
    PORT = int(sys.argv[2])
    ADDR = (HOST,PORT)

    s = socket()
    s.connect(ADDR)

    while True:
        print(```

            ===========Welcome=========
            --1.註冊   2.登入  3.退出--
            ===========================
            ```)
        try:
            cmd = int(input("輸入選項>>"))
        except Exception:
            print("輸入命令錯誤")
            continue  

        if cmd not in [1,2,3]:
            print("對不起,沒有該命令")
            sys.stdin.flush() #清除輸入
            continue 
        elif cmd == 1:
            name = do_register(s)
            if name != 1:
                print("註冊成功,直接登入!")
                login(s,name)
            else:
                print("註冊失敗!")
        elif cmd == 2:
            name = do_login(s)
            if name != 1:
                print("登入成功!")
                login(s,name)
            else:
                print("登入失敗!")
        elif cmd == 3:
            s.send(b"E")
            sys.exit("謝謝使用")

def do_register(s):
    pass


def do_login(s):
    pass


def login(s,name):
    pass


def do_query(s,name):
    pass


def do_history(s,name):
    pass


if __name__ == "__main__":
    main()
5. 實現具體框架優化和具體功能
完整程式碼:
#!/usr/bin/env python3
#coding=utf-8

```
name : Paris
date : 2018-8-27
email : 1546079656@qq.com
modules: python3.5  mysql  pymysql
This is a dict project for AID
```

from socket import *
import os 
import pymysql
import time
import sys 
import signal 


DICT_TEXT = "./dict.txt"
HOST = `0.0.0.0`
PORT = 8000
ADDR = (HOST, PORT)


# 主控制流程
def main():
    # 連線資料庫
    db = pymysql.connect
    (`localhost`, `root`, `123456`, `dict`)

    # 建立流式套接字
    s = socket()
    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    s.bind(ADDR)
    s.listen(5)
    # 或略子程式退出
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)

    while True:
        try:
            c,addr = s.accept()
            print("Connect from", addr)
        except KeyboardInterrupt:
            s.close()
            sys.exit("伺服器退出")
        except Exception as e:
            print(e)
            continue 
        # 建立子程式處理客戶端請求
        pid = os.fork() 
        if pid == 0:
            s.close()
            do_child(c,db)
        else:
            c.close()


def do_child(c,db):
    # 迴圈接收請求
    while True:
        data = c.recv(128).decode()
        print("Request:", data)
        if (not data) or data[0] == `E`:
            c.close()
            sys.exit(0)
        elif data[0] == `R`:
            do_register(c, db, data) 
        elif data[0] == "L":
            do_login(c, db, data)
        elif data[0] == `Q`:
            do_query(c, db, data)
        elif data[0] == `H`:
            do_history(c, db, data)


def do_register(c, db, data):
    l = data.split(` `)
    name = l[1]
    passwd = l[2]

    cursor = db.cursor()
    sql = 
    "select * from user where name=`%s`" % name
    cursor.execute(sql)
    r = cursor.fetchone()
    if r != None:
        c.send(b`EXISTS`)
        return 

    sql = "insert into  user (name, passwd)
     values (`%s`, `%s`)" % (name, passwd)
    try:
        cursor.execute(sql)
        db.commit()
        c.send(b`OK`)
    except:
        db.rollback()
        c.send(b`FALL`)
        return
    else:
        print("%s註冊成功" % name)


def do_login(c, db, data):
    l = data.split(` `)
    name = l[1]
    passwd = l[2]
    cursor = db.cursor()

    sql = "select * from user where 
    name=`%s` and passwd=`%s`" % (name, passwd)

    cursor.execute(sql)
    r = cursor.fetchone()
    if r == None:
        c.send(`使用者名稱或密碼不正確`.encode())
    else:
        c.send(b`OK`)

def do_query(c, db, data):
    l = data.split(` `)
    name = l[1]
    word = l[2]
    cursor = db.cursor()

    def insert_history():
        tm = time.ctime()
        sql = "insert into hist (name, word, time)
         values (`%s`, `%s`, `%s`)" % (name, word, tm)
        try:
            cursor.execute(sql)
            db.commit()
        except:
            db.rollback()
            return


    try:
        f = open(DICT_TEXT, `rb`)
    except:
        c.send("500 服務端異常".encode())
        return
    while True:
        line = f.readline().decode()
        w = line.split(` `)[0]
        if (not line) or w > word:
            c.send("沒找到該單詞".encode())
            break 
        elif w == word:
            c.send(b`OK`)
            time.sleep(0.1)
            c.send(line.encode())
            insert_history()
            break
    f.close()


def do_history(c, db, data):
    name = data.split(` `)[1]
    cursor = db.cursor()

    try:
        sql = "select * from hist 
        where name=`%s`" % name
        cursor.execute(sql)
        r = cursor.fetchall()
        if not r:
            c.send(`沒有歷史記錄`.encode())
            return
        else:
            c.send(b`OK`)
    except:
        c.send("資料庫查詢錯誤".encode())
        return
    n = 0
    for i in r:
        n += 1
        #最多顯示10條
        if n > 10:
            break
        time.sleep(0.1)
        msg = "%s   %s   %s" % (i[1], i[2], i[3])
        c.send(msg.encode())
    time.sleep(0.1)
    c.send(b`##`)

          
if __name__ == "__main__":
    main()

客戶端:

#!/usr/bin/env python3
#coding=utf-8

from socket import *
import sys
import getpass


def main():
    if len(sys.argv) < 3:
        print("argv is error")
        return
    HOST = sys.argv[1]
    PORT = int(sys.argv[2])
    ADDR = (HOST, PORT)

    s = socket()
    s.connect(ADDR)

    while True:
        print(```

            ===========Welcome=========
            --1.註冊   2.登入   3.退出--
            ===========================
            ```)
        try:
            cmd = int(input("輸入選項>>"))
        except Exception:
            print("輸入命令錯誤")
            continue  

        if cmd not in [1, 2, 3]:
            print("對不起,沒有該命令")
            sys.stdin.flush()  # 清除輸入
            continue
        elif cmd == 1:
            name = do_register(s)
            if name != 1:
                print("註冊成功,直接登入!")
                login(s, name)
            else:
                print("註冊失敗!")
        elif cmd == 2:
            name = do_login(s)
            if name != 1:
                print("登入成功!")
                login(s, name)
            else:
                print("登入失敗!")
        elif cmd == 3:
            s.send(b"E")
            sys.exit("謝謝使用")


def do_register(s):
    while True:
        name = input("使用者名稱:")
        passwd = getpass.getpass("密 碼:")
        passwd1 = getpass.getpass("確認密碼:")

        if (` ` in name) or (` ` in passwd):
            print("使用者名稱密碼不允許空格")
            continue
        if passwd != passwd1:
            print("兩次密碼不一致")
            continue

        msg = "R {} {}".format(name, passwd)
        # 傳送請求
        s.send(msg.encode())
        # 接收回復
        data = s.recv(128).decode()

        if data == "OK":
            return name
        elif data == `EXISTS`:
            print("該使用者已存在")
            return 1
        else:
            return 1


def do_login(s):
    name = input("使用者名稱:")
    passwd = getpass.getpass("密 碼:")
    msg = "L {} {}".format(name, passwd)
    s.send(msg.encode())
    data = s.recv(128).decode()

    if data == `OK`:
        return name
    else:
        print(data)
        return 1


def login(s, name):
    while True:
        print(```

            ===========查詢介面============
            1.查詞     2.歷史記錄   3.登出
            =============================
            ```)
        try:
            cmd = int(input("輸入選項>>"))
        except Exception:
            print("命令錯誤")
            continue
        if cmd not in [1,2,3]:
            print("對不起,沒有該命令")
            sys.stdin.flush()  # 清除輸入
            continue 
        elif cmd == 1:
            do_query(s, name)
        elif cmd == 2:
            do_history(s, name)
        elif cmd == 3:
            return


def do_query(s, name):
    while True:
        word = input("單詞:")
        if word == "q":
            break 
        msg = "Q {} {}".format(name, word)
        s.send(msg.encode())
        data = s.recv(128).decode()
        if data == `OK`:
            data = s.recv(2048).decode()
            print(data)
        else:
            print(data)


def do_history(s, name):
    msg = "H {}".format(name)
    s.send(msg.encode())
    data = s.recv(128).decode()
    if data == `OK`:
        while True:
            data = s.recv(1024).decode()
            if data == "##":
                break
            print(data)
    else:
        print(data)


if __name__ == "__main__":
    main()
import getpass
passwd = getpass.getpass()
功能 : 隱藏密碼輸入
pdb除錯:
通過pdb模組完成除錯功能
功能 : 斷點設定,單步執行,函式檢視,程式碼段檢視,變數值檢視等…
break ,  b   設定斷點
continue , c   繼續執行
list ,  l   檢視當前程式碼段
next,  n   單步執行 
step,   s   進入函式單步執行
pp   列印變數值
help   幫助
pdb.set_trace()
功能 : 設定初始斷點,開始進入pdb除錯模式
以pdb除錯模式執行
python3 -m pdb dict_client.py
協程:
定義 : 纖程,微執行緒。協程本質只是一個單執行緒程式
工作原理 : 
通過應用層層程式,記錄上下文的執行棧。實現程式在執行過程中的跳躍執行,
選擇可以不能阻塞的部分執行,這樣就可以大大提高IO執行的效率。
yield 是 python實現協程的基本關鍵字
安裝第三方模組:
sudo  pip3 install greenlet
sudo pip3 install gevent
greenlet模組:
greenlet.greenlet()  生成協程物件
gr.switch()   選擇要執行的協程事件
gevent模組:
1. 將要執行的事件封裝為函式
2. 生成協程物件
    gevent.spawn(func,argv)
    功能 : 
將事件變為協程
    引數:
func  繫結的協程函式
            argv  給函式傳遞引數
    返回值 : 
協程物件
3. 回收協程
    gevent.joinall([obj1,obj2…..])(obj:協程物件)
4. 協程阻塞
    gevent.sleep(n)


相關文章