3.2-软件开发规范
软件开发规范
软件开发,规范项目目录结构,代码规范,遵循PEP8规范等,可以更加清晰,合理的开发
status_dic = {
'username': None,
'status': False,
}
flag = True
def login():
i = 0
with open('register', 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['status'] = True
status_dic['username'] = username
return True
else:
print('用户名密码错误,请重新登陆')
i += 1
def register():
with open('register', 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.isalpha():
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', 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
}
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')
此时我们是将所有代码都写到了一个py文件中,如果代码量多且都在一个py文件中,那么对于代码结构不清晰,不规范,运行起来效率也会非常低。
1. 程序配置
项目中所有有关文件的操作,出现了好几次,都是直接写register相对路径,如果这个register注册表路径改变了,或者改变了register注册表名称,那么相应的几处都需要一一更改,这样就把代码写死了。
所以要统一相同的路径,统一相同的变量,在文件的最上main写一个变量指向register注册表的路径,代码中如果需要这个路径时,直接引用即可:
register_path = r'D:\赵贺学习python\register'
......
with open(register_path, encoding='utf-8') as f1:
dic = {i.strip().split('|')[0]: i.strip().split('|')[1] for i in f1}
......
2. 划分文件
每个函数都是一个具体的功能,一个文件中功能越多,代码越冗余,将这些功能分文件而治;
应将函数进行分类,然后分文件:
settings.py 配置文件
放置一些项目中需要的静态参数,比如文件路径,数据库配置,软件的默认设置等;
common.py 公共组件文件
放置常用的公共组件函数(并不是核心逻辑的函数),而是服务于整个程序中的公用的插件,程序中需要即调用。比如程序中的装饰器auth,有些函数需要装饰器认证,有些事不需要的。这就是何处需要何处调用即可。比如密码加密功能,序列化功能,日志功能等这些功能都可以放在这里;
src.py 核心逻辑功能
需要进行选择的这些核心功能函数,都应该放在这个文件中;
start.py 项目启动文件
专门的文件启动,而不是在核心逻辑部分进行启动的。相当于程序启动‘按钮’
类似于register文件
这个文件名不固定,register只是项目中用到的注册表,但是这种文件就是存储数据的文件,类似于文本数据库;我们项目中的数据有的是从数据库总获取的,有些是从这种文本数据库中获取的。
项目中一些数据存储在文件中,与程序交互的情况,需要单独设置这样的文件;
log文件
存储log日志的文件;主要供开发人员使用。比如项目中的bug问题,开发人员对服务器的操作记录。以便浏览,查询;
至此,已经将原来的两个文件,合理的划分成了6个文件。但是还有一个问题,如果项目很大,每一个部分相应的一个文件存不下,比如src主逻辑文件,函数很多,是不是得分开了:
文本数据库register只是一个注册表,但是还有个人信息表,记录表呢。这样的话,整个项目也会非常凌乱;
3. 划分具体目录
设计项目目录结构,就和代码编码风格一样,属于个人风格问题;对于这种风格上的规范,有两种态度
- 个人风格问题无关紧要,能让程序work即可,风格问题不是问题;
- 规范化能更好的控制程序结构,让程序具有更高的可读性;
项目目录结构,也属于可读性和可维护性的范畴。设计一个层次清晰的目录结构,可以达到以下两点:
- 可读性高:不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪,配置文件在哪,可以非常快速的了解这个项目;
- 可维护性高:定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录下。好处是,随着时间的推移,代码 /配置的规模增加,项目结构不会混乱,仍然能够组织良好;
blog
bin
start.py
conf
settings.py
core
src.py
db
register
lib
common.py
log
access.log
README
按照项目目录结构,规范博客园系统
1. 配置start.py文件
首先配置启动文件,把启动执行放置start.py文件中,运行start.py文件可以成功启动项目即可;
项目启动指令run() ,直接放置在此文件不可以的,因为start.py找不到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:\赵贺学习python\blog\core')
from src import run
这样虽然解决了,现在从start文件引用src文件,需要手动将src的工作目录添加到sys.path中,如果引用其他文件呢?比如conf,lib等其他py文件,那么每次引用之前,或者开启项目时,就全部把他们添加到sys.path中么?
sys.path.append(r'D:\赵贺学习python\blog\core')
sys.path.append(r'D:\赵贺学习python\blog\conf')
sys.path.append(r'D:\赵贺学习python\blog\db')
sys.path.append(r'D:\赵贺学习python\blog\lib')
这样也太麻烦了,那到底该怎么办呢?应该把项目的工作路径添加到sys.path中,只要把blog项目的工作目录添加到sys.path中,无论这个项目中的任意一个文件引用项目中哪个文件,就都可以找到:
import sys
sys.path.append(r'D:\赵贺学习python\blog')
from core.src import run
run()
但是这样的话,blog路径就写死了,因为项目不可能只在一台电话上,项目时共同开发的,这时候路径就成了问题。所以这个路径要动态获取,不能写死
import sys
import os
# 获取本文件的绝对路径
print(os.path.dirname(__file__))
# D:/赵贺学习python/blog/bin
# 获取父级目录,也就是blog的绝对路径
BASH_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASH_DIR)
from core.src import run
run()
这个starts文件可以当做脚本文件进行直接启动,如果作为模块,被别人引用的话,按照这个写。也是可以启动整个程序的。但是作为启动文件,是不可以被别人引用启动的,所以用到name
import sys
import os
# 获取本文件的绝对路径
# print(os.path.dirname(__file__))
# D:/赵贺学习python/blog/bin
# 获取父级目录,也就是blog的绝对路径
BASH_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASH_DIR)
from core.src import run
if __name__ == '__main__':
run()
这样,starts启动文件就配置成功了,以后只要通过starts文件启动整个程序,就会先将整个项目的工作目录添加到sys.path中,然后再启动程序,这样整个项目里任何的py文件想要引用项目中的其他py文件,都是可以的了。
2. 配置settings.py文件
将项目中的静态路径,数据库的连接设置等文件放置在settings文件中。
status_dic = {
'username': None,
'status': False,
}
flag = True
register_path = r'D:\赵贺学习python\register'
# 主逻辑src中有这样几个变量;
# 是不是应该把这几个变量都放置在settings文件中呢?
# 不是。settings文件叫做配置文件,也叫作配置静态文件,静态就是不会轻易改变的,但是对于上面的代码status_dic,flag这两个变量,由于在使用这个系统时会时长变化,所以不建议将这两个变量放置在settings配置中,只将register_path放进去就行了
因为主逻辑src找不到这个路径,会报错,所以在src主逻辑中引用settings文件中的register_path就可以了
from conf import settings
为什么可以直接引用settings呢?因为在启动blog时,已经手动将blog路径添加到sys.path中了,这就意味着,整个项目中的任何py文件,都可以引用到blog项目目录下的任何目录。
3. 配置 common.py 文件
配置公共组件文件,在这个项目中,装饰器就是公共组件的工具,要把装饰器放在common.py文件中,先把装饰器放在common.py文件中;
# 会有各种问题
status_dic 与 login 函数找不到,所以要引用
# 在common.py文件中引入src文件的这两个变量
from core import sre
# 可是src文件中使用了auth装饰器,此时auth装饰器已经移动位置了,所以要在src文件中引用auth装饰器
from conf import settings
from lib import common
# 装饰器引用
@common.auth
至此,之前写的博客园登陆规范化目录结构合理的完善成功了;
完善后的项目
# 目录结构
blog
bin
start.py
conf
settings.py
core
src.py
db
register
lib
common.py
log
access.log
README
blog
# bin -- start.py
import sys
import os
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_PATH)
from core.src import run
if __name__ == '__main__':
run()
# conf -- settings.py
register_path = r'D:\赵贺学习python\blog\db\register'
# core -- src.py
from conf import settings
from lib import common
status_dic = {
'username': None,
'status': False,
}
flag = True
def login():
i = 0
with open(settings.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['status'] = True
status_dic['username'] = username
return True
else:
print('用户名密码错误,请重新登陆')
i += 1
def register():
with open(settings.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.isalpha():
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(settings.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 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
@common.auth
def article():
print(f'\033[1;32;0m 欢迎{status_dic["username"]}访问文章页面\033[0m')
@common.auth
def diary():
print(f'\033[1;32;0m 欢迎{status_dic["username"]}访问日记页面\033[0m')
@common.auth
def comment():
print(f'\033[1;32;0m 欢迎{status_dic["username"]}访问评论页面\033[0m')
@common.auth
def enshrine():
print(f'\033[1;32;0m 欢迎{status_dic["username"]}访问收藏页面\033[0m')
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')
# db -- register
zhao|123123
# lib -- common.py
from core import src
def auth(func):
def inner(*args, **kwargs):
if src.status_dic['status']:
ret = func(*args, **kwargs)
return ret
else:
print('\033[1;31;0m 请先进行登陆 \033[0m')
if src.login():
ret = func(*args, **kwargs)
return ret
return inner
# log -- access.log
# README
README内容
每个项目都应该有的一个文件,目的是简要描述该项目的信息,让读者快速了解这个项目;
需要说明以下几个事项:
- 软件定位,软件的基本功能;
- 运行代码的方法:安装环境,启动命令等;
- 简要的使用说明;
- 代码目录结构说明,(软件的基本原理):
- 常见问题说明;
在软件开发初期,由于开发过程中以上内容可能不明确或者发生变化,并不是一定要在一开始就要将所有信息都补齐,但是在项目完结的时候,需要撰写这样一个文档的。可以参考redis源码中readme的写法,简介且清晰的描述redis功能和源码结构;