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. 划分具体目录

设计项目目录结构,就和代码编码风格一样,属于个人风格问题;对于这种风格上的规范,有两种态度

  1. 个人风格问题无关紧要,能让程序work即可,风格问题不是问题;
  2. 规范化能更好的控制程序结构,让程序具有更高的可读性;

项目目录结构,也属于可读性和可维护性的范畴。设计一个层次清晰的目录结构,可以达到以下两点:

  1. 可读性高:不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪,配置文件在哪,可以非常快速的了解这个项目;
  2. 可维护性高:定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录下。好处是,随着时间的推移,代码 /配置的规模增加,项目结构不会混乱,仍然能够组织良好;
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内容

每个项目都应该有的一个文件,目的是简要描述该项目的信息,让读者快速了解这个项目;

需要说明以下几个事项:

  1. 软件定位,软件的基本功能;
  2. 运行代码的方法:安装环境,启动命令等;
  3. 简要的使用说明;
  4. 代码目录结构说明,(软件的基本原理):
  5. 常见问题说明;

在软件开发初期,由于开发过程中以上内容可能不明确或者发生变化,并不是一定要在一开始就要将所有信息都补齐,但是在项目完结的时候,需要撰写这样一个文档的。可以参考redis源码中readme的写法,简介且清晰的描述redis功能和源码结构;

posted @ 2020-12-03 18:54  黑色利穆  阅读(130)  评论(0编辑  收藏  举报