软件开发规范

如今包括以前写的一些程序,所谓的'项目',都是在一个py文件下完成的,代码量撑死也就几百。真正的后端开发的项目,系统等,少则几万行代码,多则十几万,几十万行代码,全都放在一个py文件中行么?固然只要能实现功能便可。举个例子,若是你的衣物只有三四件,那么你随便堆在橱柜里,没问题,都能找到,也不显得特别乱,可是若是你的衣物,有三四十件的时候,你在都堆在橱柜里,可想而知,你找你穿过三天的袜子,最终从你的大衣口袋里翻出来了,这是什么感受和心情......python

  软件开发,规范项目目录结构,代码规范,遵循PEP8规范等等,更加清晰滴,合理的开发。git

那么接下来以博客园系统举例,将一个py文件中的全部代码,整合成规范的开发。github

首先看一下,这个是的目录结构(简化版):redis

img

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文件中,那么对于代码结构不清晰,不规范,运行起来效率也会很是低。因此接下来一步一步的修改:后端

  1. 程序配置.

img

项目中全部的有关文件的操做出现几处,都是直接写的register相对路径,若是说这个register注册表路径改变了,或者改变了register注册表的名称,那么相应的这几处都须要一一更改,这样其实就是把代码写死了,要统一相同的路径,也就是统一相同的变量,在文件的最上面写一个变量指向register注册表的路径,代码中若是须要这个路径时,直接引用便可。服务器

img

1.划分文件。app

img

一个项目的函数不能只是这些,只是举个例子,要是一个具体的实际的项目,函数会很是多,因此应该将这些函数进行分类,而后分文件而治。在这里划分了如下几个文件:函数

settings.py: 配置文件,就是放置一些项目中须要的静态参数,好比文件路径,数据库配置,软件的默认设置等等工具

相似于这个:

img

common.py:公共组件文件,这里面放置一些经常使用的公共组件函数,并非核心逻辑的函数,而更像是服务于整个程序中的公用的插件,程序中须要即调用。好比程序中的装饰器auth,有些函数是须要这个装饰器认证的,可是有一些是不须要这个装饰器认证的,它既是何处须要何处调用便可。好比还有密码加密功能,序列化功能,日志功能等这些功能均可以放在这里。

img

src.py:这个文件主要存放的就是核心逻辑功能,须要进行选择的这些核心功能函数,都应该放在这个文件中。

img

start.py:项目启动文件。项目须要有专门的文件启动,而不是在核心逻辑部分进行启动的,有人对这个可能不太理解,为何还要设置一个单独的启动文件呢?生活中使用的全部电器基本都一个单独的启动按钮,汽车,热水器,电视,等等等等,那么为何他们会单独设置一个启动按钮,而不是在一堆线路板或者内部随便找一个地方开启呢? 目的就是放在显眼的位置,方便开启。项目这么多py文件,若是src文件也有不少,那么到底哪一个文件启动整个项目,还得一个一个去寻找,太麻烦了,这样我把它单独拿出来,就是方便开启整个项目。

那么写的项目开启整个项目的代码就是下面这段:

img

把这些放置到一个文件中也能够,可是没有必要,只须要一个命令或者一个开启指令就行,就比如开启电视只须要让人很快的找到那个按钮便可,对于按钮后面的一些复杂的线路板,咱们并不关心,因此要将上面这个段代码整合成一个函数,开启项目的''按钮''就是此函数的执行便可。

img

这个按钮要放到启动文件start.py里面。

除了以上这几个py文件以外还有几个文件,也是很是重要的:

相似于register文件:这个文件文件名不固定,register只是项目中用到的注册表,可是这种文件就是存储数据的文件,相似于文本数据库,那么一些项目中的数据有的是从数据库中获取的,有些数据就是这种文本数据库中获取的,总之,你的项目中有时会遇到将一些数据存储在文件中,与程序交互的状况,因此要单独设置这样的文件。

log文件:log文件顾名思义就是存储log日志的文件。日志主要是供开发人员使用。好比项目中出现一些bug问题,好比开发人员对服务器作的一些操做都会记录到日志中,以便开发者浏览,查询。

至此,将原来的两个文件,合理的划分红了6个文件,可是仍是有问题的,若是项目很大,每个部分相应的你一个文件存不下的,好比src主逻辑文件,函数不少,得分红:src1.py src2.py

文本数据库register这个只是一个注册表,若是还有我的信息表,记录表呢. 若是是这样,你的整个项目也是很是凌乱的:

img

3. 划分具体目录

上面看着就很是乱了, 其实很是简单,这能够整多个文件夹,分别管理不一样的文件,那么标准版本的目录结构就来了:"设计项目目录结构",就和"代码编码风格"同样,属于我的风格问题。对于这种风格上的规范,一直都存在两种态度:

  1. 一类认为,这种我的风格问题"可有可无"。理由是能让程序work就好,风格问题根本不是问题。
  2. 另外一类认为,规范化能更好的控制程序结构,让程序具备更高的可读性。

设计一个层次清晰的目录结构,就是为了达到如下两点:

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

因此,保持一个层次清晰的目录结构是有必要的。更况且组织一个良好的工程目录,实际上是一件很简单的事儿。

img

上面那个图片就是较好的目录结构。

二. 按照项目目录结构,规范博客园系统

1.配置start.py文件

首先要配置启动文件,启动文件很简答就是将项目的启动执行放置start.py文件中,运行start.py文件能够成功启动项目便可。 那么项目的启动就是这个指令run() 把这个run()放置此文件中。

img

这样确定是不能够,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文件,都是能够的了。

  1. 配置settings.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'

img

可是将这个变量放置在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就能够了。

img

这里引起一个问题:为何这样写就能够直接引用settings文件呢?在starts文件中已经说了,刚已启动blog文件时,手动将blog的路径添加到sys.path中了,这就意味着,在整个项目中的任何py文件,均可以引用到blog项目目录下面的任何目录:bin,conf,core,db,lib,log这几个,因此,刚才引用settings文件才是能够的。

  1. 配置common.py文件

接下来,要配置公共组件文件,在这个项目中,装饰器就是公共组件的工具,要把装饰器这个工具配置到common.py文件中。先把装饰器代码剪切到common.py文件中。这样直接粘过来,是有各类问题的:

img

因此要在common.py文件中引入src文件的这两个变量。

img

但是src文件中使用了auth装饰器,此时auth装饰器已经移动位置了,因此要在src文件中引用auth装饰器,这样才可使用上。

img

这样就算是将以前写的模拟博客园登陆按照规范化目录结构合理的完善完成了,最后还有一个关于README文档的书写。

关于README的内容

目的是能简要描述该项目的信息,让读者快速了解这个项目。

它须要说明如下几个事项:

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

有以上几点是比较好的一个README。在软件开发初期,因为开发过程当中以上内容可能不明确或者发生变化,并非必定要在一开始就将全部信息都补全。可是在项目完结的时候,是须要撰写这样的一个文档的。

能够参考Redis源码中Readme的写法,这里面简洁可是清晰的描述了Redis功能和源码结构。

相关文章
相关标签/搜索