如今包括以前寫的一些程序,所謂的'項目',都是在一個py文件下完成的,代碼量撐死也就幾百。真正的後端開發的項目,系統等,少則幾萬行代碼,多則十幾萬,幾十萬行代碼,全都放在一個py文件中行麼?固然只要能實現功能便可。舉個例子,若是你的衣物只有三四件,那麼你隨便堆在櫥櫃裏,沒問題,都能找到,也不顯得特別亂,可是若是你的衣物,有三四十件的時候,你在都堆在櫥櫃裏,可想而知,你找你穿過三天的襪子,最終從你的大衣口袋裏翻出來了,這是什麼感受和心情......python
軟件開發,規範項目目錄結構,代碼規範,遵循PEP8規範等等,更加清晰滴,合理的開發。git
那麼接下來以博客園系統舉例,將一個py文件中的全部代碼,整合成規範的開發。github
首先看一下,這個是的目錄結構(簡化版):redis
py文件的具體代碼以下:數據庫
status_dic = { 'username': None, 'status': False, } flag = True REGISTER_PATH = r'E:\s24\模擬博客園代碼\register' def login(): i = 0 with open(REGISTER_PATH, encoding='utf-8') as f1: dic = {i.strip().split('|')[0]: i.strip().split('|')[1] for i in f1} while i < 3: username = input('請輸入用戶名:').strip() password = input('請輸入密碼:').strip() if username in dic and dic[username] == password: print('登陸成功') status_dic["username"] = username status_dic["status"] = True return True else: print('用戶名密碼錯誤,請從新登陸') i += 1 def register(): with open(REGISTER_PATH, encoding='utf-8') as f1: dic = {i.strip().split('|')[0]: i.strip().split('|')[1] for i in f1} while 1: print('\033[1;45m 歡迎來到註冊頁面 \033[0m') username = input('請輸入用戶名:').strip() if not username.isalnum(): print('\033[1;31;0m 用戶名有非法字符,請從新輸入 \033[0m') continue if username in dic: print('\033[1;31;0m 用戶名已經存在,請從新輸入 \033[0m') continue password = input('請輸入密碼:').strip() if 6 <= len(password) <= 14: with open(REGISTER_PATH, encoding='utf-8', mode='a') as f1: f1.write(f'\n{username}|{password}') status_dic['username'] = str(username) status_dic['status'] = True print('\033[1;32;0m 恭喜您,註冊成功!已幫您成功登陸~ \033[0m') return True else: print('\033[1;31;0m 密碼長度超出範圍,請從新輸入 \033[0m') def auth(func): def inner(*args, **kwargs): if status_dic['status']: ret = func(*args, **kwargs) return ret else: print('\033[1;31;0m 請先進行登陸 \033[0m') if login(): ret = func(*args, **kwargs) return ret return inner @auth def article(): print(f'\033[1;32;0m 歡迎{status_dic["username"]}訪問文章頁面\033[0m') @auth def diary(): print(f'\033[1;32;0m 歡迎{status_dic["username"]}訪問日記頁面\033[0m') @auth def comment(): print(f'\033[1;32;0m 歡迎{status_dic["username"]}訪問評論頁面\033[0m') @auth def enshrine(): print(f'\033[1;32;0m 歡迎{status_dic["username"]}訪問收藏頁面\033[0m') def login_out(): status_dic['username'] = None status_dic['status'] = False print('\033[1;32;0m 註銷成功 \033[0m') def exit_program(): global flag flag = False return flag choice_dict = { 1: login, 2: register, 3: article, 4: diary, 5: comment, 6: enshrine, 7: login_out, 8: exit_program, } def run(): while flag: print(''' 歡迎來到博客園首頁 1:請登陸 2:請註冊 3:文章頁面 4:日記頁面 5:評論頁面 6:收藏頁面 7:註銷 8:退出程序''') choice = input('請輸入您選擇的序號:').strip() if choice.isdigit(): choice = int(choice) if 0 < choice <= len(choice_dict): choice_dict[choice]() else: print('\033[1;31;0m 您輸入的超出範圍,請從新輸入 \033[0m') else: print('\033[1;31;0m 您您輸入的選項有非法字符,請從新輸入 \033[0m') run()
此時是將全部的代碼都寫到了一個py文件中,若是代碼量多且都在一個py文件中,那麼對於代碼結構不清晰,不規範,運行起來效率也會很是低。因此接下來一步一步的修改:後端
項目中全部的有關文件的操做出現幾處,都是直接寫的register相對路徑,若是說這個register註冊表路徑改變了,或者改變了register註冊表的名稱,那麼相應的這幾處都須要一一更改,這樣其實就是把代碼寫死了,要統一相同的路徑,也就是統一相同的變量,在文件的最上面寫一個變量指向register註冊表的路徑,代碼中若是須要這個路徑時,直接引用便可。服務器
1.劃分文件。app
一個項目的函數不能只是這些,只是舉個例子,要是一個具體的實際的項目,函數會很是多,因此應該將這些函數進行分類,而後分文件而治。在這裏劃分瞭如下幾個文件:函數
settings.py: 配置文件,就是放置一些項目中須要的靜態參數,好比文件路徑,數據庫配置,軟件的默認設置等等工具
相似於這個:
common.py:公共組件文件,這裏面放置一些經常使用的公共組件函數,並非核心邏輯的函數,而更像是服務於整個程序中的公用的插件,程序中須要即調用。好比程序中的裝飾器auth,有些函數是須要這個裝飾器認證的,可是有一些是不須要這個裝飾器認證的,它既是何處須要何處調用便可。好比還有密碼加密功能,序列化功能,日誌功能等這些功能均可以放在這裏。
src.py:這個文件主要存放的就是核心邏輯功能,須要進行選擇的這些核心功能函數,都應該放在這個文件中。
start.py:項目啓動文件。項目須要有專門的文件啓動,而不是在覈心邏輯部分進行啓動的,有人對這個可能不太理解,爲何還要設置一個單獨的啓動文件呢?生活中使用的全部電器基本都一個單獨的啓動按鈕,汽車,熱水器,電視,等等等等,那麼爲何他們會單獨設置一個啓動按鈕,而不是在一堆線路板或者內部隨便找一個地方開啓呢? 目的就是放在顯眼的位置,方便開啓。項目這麼多py文件,若是src文件也有不少,那麼到底哪一個文件啓動整個項目,還得一個一個去尋找,太麻煩了,這樣我把它單獨拿出來,就是方便開啓整個項目。
那麼寫的項目開啓整個項目的代碼就是下面這段:
把這些放置到一個文件中也能夠,可是沒有必要,只須要一個命令或者一個開啓指令就行,就比如開啓電視只須要讓人很快的找到那個按鈕便可,對於按鈕後面的一些複雜的線路板,咱們並不關心,因此要將上面這個段代碼整合成一個函數,開啓項目的''按鈕''就是此函數的執行便可。
這個按鈕要放到啓動文件start.py裏面。
除了以上這幾個py文件以外還有幾個文件,也是很是重要的:
相似於register文件:這個文件文件名不固定,register只是項目中用到的註冊表,可是這種文件就是存儲數據的文件,相似於文本數據庫,那麼一些項目中的數據有的是從數據庫中獲取的,有些數據就是這種文本數據庫中獲取的,總之,你的項目中有時會遇到將一些數據存儲在文件中,與程序交互的狀況,因此要單獨設置這樣的文件。
log文件:log文件顧名思義就是存儲log日誌的文件。日誌主要是供開發人員使用。好比項目中出現一些bug問題,好比開發人員對服務器作的一些操做都會記錄到日誌中,以便開發者瀏覽,查詢。
至此,將原來的兩個文件,合理的劃分紅了6個文件,可是仍是有問題的,若是項目很大,每個部分相應的你一個文件存不下的,好比src主邏輯文件,函數不少,得分紅:src1.py src2.py
文本數據庫register這個只是一個註冊表,若是還有我的信息表,記錄表呢. 若是是這樣,你的整個項目也是很是凌亂的:
3. 劃分具體目錄
上面看着就很是亂了, 其實很是簡單,這能夠整多個文件夾,分別管理不一樣的文件,那麼標準版本的目錄結構就來了:"設計項目目錄結構",就和"代碼編碼風格"同樣,屬於我的風格問題。對於這種風格上的規範,一直都存在兩種態度:
設計一個層次清晰的目錄結構,就是爲了達到如下兩點:
因此,保持一個層次清晰的目錄結構是有必要的。更況且組織一個良好的工程目錄,實際上是一件很簡單的事兒。
上面那個圖片就是較好的目錄結構。
1.配置start.py文件
首先要配置啓動文件,啓動文件很簡答就是將項目的啓動執行放置start.py文件中,運行start.py文件能夠成功啓動項目便可。 那麼項目的啓動就是這個指令run() 把這個run()放置此文件中。
這樣確定是不能夠,starts.py根本就找不到run這個變量,確定是會報錯的。
NameError: name 'run' is not defined 本文件確定是找不到run這個變量也就是函數名的,要使用模塊, 另個一文件的內容咱們能夠引用過來。可是import run 或者 from src import run 都是報錯的。模塊之因此能夠引用,那是由於模塊確定在這個三個地方:內存,內置,sys.path裏面,那麼core在內存中確定是沒有的,也不是內置,並且sys.path也不可能有,由於sys.path只會將當前的目錄(bin)加載到內存,因此剛纔那麼引用確定是有問題的,那麼如何解決?內存,內置是左右不了的,只能將core的路徑添加到sys.path中,這樣就能夠了。
import sys sys.path.append(r'D:\lnh.python\py project\teaching_show\blog\core') from src import run run()
這樣雖然解決了,如今從這個start文件須要引用src文件,那麼須要手動的將src的工做目錄添加到sys.path中,那麼有沒有可能會引用到其餘的文件?好比項目中可能須要引用conf,lib等其餘py文件,那麼在每次引用以前,或者是開啓項目時,所有把他們添加到sys.path中麼?
sys.path.append(r'D:\lnh.python\py project\teaching_show\blog\core') sys.path.append(r'D:\lnh.python\py project\teaching_show\blog\conf') sys.path.append(r'D:\lnh.python\py project\teaching_show\blog\db') sys.path.append(r'D:\lnh.python\py project\teaching_show\blog\lib')
應該把項目的工做路徑添加到sys.path中,只要將這個blog項目的工做目錄添加到sys.path中,這樣不管這個項目中的任意一個文件引用項目中哪一個文件,就均可以找到了。因此:
import sys sys.path.append(r'D:\lnh.python\py project\teaching_show\blog') from core.src import run run()
這樣寫blog的路徑就寫死了,項目不可能只在你的電腦上,項目是共同開發的,項目確定會出如今別人電腦上,那麼路徑就是問題了,在你的電腦上你的blog項目的路徑是上面所寫的,若是移植到別人電腦上,他的路徑不可能與你的路徑相同, 這樣就會報錯了,因此咱們這個路徑要動態獲取,不能寫死,因此這樣就解決了:
import sys import os # sys.path.append(r'D:\lnh.python\py project\teaching_show\blog') print(os.path.dirname(__file__)) # 獲取本文件的絕對路徑 # D:/lnh.python/py project/teaching_show/blog/bin print(os.path.dirname(os.path.dirname(__file__))) # 獲取父級目錄也就是blog的絕對路徑 # D:/lnh.python/py project/teaching_show/blog BATH_DIR = os.path.dirname(os.path.dirname(__file__)) sys.path.append(BATH_DIR) from core.src import run run()
那麼還差一個小問題,這個starts文件能夠當作腳本文件進行直接啓動,若是是做爲模塊,被別人引用的話,按照這麼寫,也是能夠啓動整個程序的,這樣是不合理的,做爲啓動文件,是不能夠被別人引用啓動的。此時要想到 __name__
了:
import sys import os # sys.path.append(r'D:\lnh.python\py project\teaching_show\blog') # print(os.path.dirname(__file__)) # 獲取本文件的絕對路徑 # D:/lnh.python/py project/teaching_show/blog/bin # print(os.path.dirname(os.path.dirname(__file__))) # 獲取父級目錄也就是blog的絕對路徑 # D:/lnh.python/py project/teaching_show/blog BATH_DIR = os.path.dirname(os.path.dirname(__file__)) sys.path.append(BATH_DIR) from core.src import run if __name__ == '__main__': run()
這樣starts啓動文件就已經配置成功了。之後只要經過starts文件啓動整個程序,它會先將整個項目的工做目錄添加到sys.path中,而後在啓動程序,這樣整個項目裏面的任何的py文件想引用項目中的其餘py文件,都是能夠的了。
接下來,就會將項目中的靜態路徑,數據庫的鏈接設置等等文件放置在settings文件中。
status_dic = { 'username': None, 'status': False, } flag = True register_path = r'D:\lnh.python\py project\teaching_show\blog\register'
setttings文件叫作配置文件,其實也叫作配置靜態文件, 靜態就是通常不會輕易改變的,可是對於上面的代碼status_dic ,flag這兩個變量,因爲在使用這個系統時會時長變化,因此不建議將這個兩個變量放置在settings配置文件中,只須要將register_path放置進去就能夠。
register_path = r'D:\lnh.python\py project\teaching_show\blog\register'
可是將這個變量放置在settings.py以後,程序啓動起來是有問題的。
with open(register_path, encoding='utf-8') as f1: NameError: name 'register_path' is not defined
由於主邏輯src中找不到register_path這個路徑了,因此會報錯,那麼解決方式就是在src主邏輯中引用settings.py文件中的register_path就能夠了。
這裏引起一個問題:爲何這樣寫就能夠直接引用settings文件呢?在starts文件中已經說了,剛已啓動blog文件時,手動將blog的路徑添加到sys.path中了,這就意味着,在整個項目中的任何py文件,均可以引用到blog項目目錄下面的任何目錄:bin,conf,core,db,lib,log這幾個,因此,剛纔引用settings文件纔是能夠的。
接下來,要配置公共組件文件,在這個項目中,裝飾器就是公共組件的工具,要把裝飾器這個工具配置到common.py文件中。先把裝飾器代碼剪切到common.py文件中。這樣直接粘過來,是有各類問題的:
因此要在common.py文件中引入src文件的這兩個變量。
但是src文件中使用了auth裝飾器,此時auth裝飾器已經移動位置了,因此要在src文件中引用auth裝飾器,這樣纔可使用上。
這樣就算是將以前寫的模擬博客園登陸按照規範化目錄結構合理的完善完成了,最後還有一個關於README文檔的書寫。
它須要說明如下幾個事項:
有以上幾點是比較好的一個README
。在軟件開發初期,因爲開發過程當中以上內容可能不明確或者發生變化,並非必定要在一開始就將全部信息都補全。可是在項目完結的時候,是須要撰寫這樣的一個文檔的。
能夠參考Redis源碼中Readme的寫法,這裏面簡潔可是清晰的描述了Redis功能和源碼結構。