• 第四章——续3.1


    4.15 软件开发目录规范

    1.为什么要设计好目录结构?

    。"项目目录结构"其实也是属于"可读性和可维护性"的范畴,我们设计一个层次清晰的目录结构,就是为了达到以下两点:

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

    所以,我认为,保持一个层次清晰的目录结构是有必要的。更何况组织一个良好的工程目录,其实是一件很简单的事儿。

    2.目录组织方式:

    假设你的项目名为foo, 我比较建议的最方便快捷目录结构这样就足够了:

    Foo/
    |-- bin/
    |   |-- foo
    |
    |-- foo/
    |   |-- tests/
    |   |   |-- __init__.py
    |   |   |-- test_main.py
    |   |
    |   |-- __init__.py
    |   |-- main.py
    |
    |-- docs/
    |   |-- conf.py
    |   |-- abc.rst
    |
    |-- setup.py
    |-- requirements.txt
    |-- README

    简要解释一下:

    • bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。
    • foo/: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/存放单元测试代码; (3) 程序的入口最好命名为main.py。
    • docs/: 存放一些文档。
    • setup.py: 安装、部署、打包的脚本。
    • requirements.txt: 存放软件依赖的外部Python包列表。
    • README: 项目说明文件。

    3.关于README的内容

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

    它需要说明以下几个事项:

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

    4.关于requirements.txt和setup.py

    setup.py

    一般来说,用setup.py来管理代码的打包、安装、部署问题。业界标准的写法是用Python流行的打包工具setuptools来管理这些事情。这种方式普遍应用于开源项目中。不过这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目一定要有一个安装部署工具,能快速便捷的在一台新机器上将环境装好、代码部署好和将程序运行起来。

    刚开始接触Python写项目的时候,安装环境、部署代码、运行程序这个过程全是手动完成,遇到过以下问题:

    1. 安装环境时经常忘了最近又添加了一个新的Python包,结果一到线上运行,程序就出错了。
    2. Python包的版本依赖问题,有时候我们程序中使用的是一个版本的Python包,但是官方的已经是最新的包了,通过手动安装就可能装错了。
    3. 如果依赖的包很多的话,一个一个安装这些依赖是很费时的事情。
    4. 新同学开始写项目的时候,将程序跑起来非常麻烦,因为可能经常忘了要怎么安装各种依赖。

    5.setup.py可以将这些事情自动化起来,提高效率、减少出错的概率。"复杂的东西自动化,能自动化的东西一定要自动化。"是一个非常好的习惯。

    setuptools的文档比较庞大,刚接触的话,可能不太好找到切入点。学习技术的方式就是看他人是怎么用的,可以参考一下Python的一个Web框架,flask是如何写的: setup.py

    当然,简单点自己写个安装脚本(deploy.sh)替代setup.py也未尝不可。

    6.requirements.txt

    这个文件存在的目的是:

    1. 方便开发者维护软件的包依赖。将开发过程中新增的包添加进这个列表中,避免在 setup.py 安装依赖时漏掉软件包。
    2. 方便读者明确项目使用了哪些Python包。

    这个文件的格式是每一行包含一个包依赖的说明,通常是flask>=0.10这种格式,要求是这个格式能被pip识别,这样就可以简单的通过 pip install -r requirements.txt来把所有Python包依赖都装好了。具体格式说明: 点这里

    7.关于配置文件的使用方法

    注意,在上面的目录结构中,没有将conf.py放在源码目录下,而是放在docs/目录下。

    很多项目对配置文件的使用做法是:

    1. 配置文件写在一个或多个python文件中,比如此处的conf.py。
    2. 项目中哪个模块用到这个配置文件就直接通过import conf这种形式来在代码中使用配置。

    这种做法我不太赞同:

    1. 这让单元测试变得困难(因为模块内部依赖了外部配置)
    2. 另一方面配置文件作为用户控制程序的接口,应当可以由用户自由指定该文件的路径。
    3. 程序组件可复用性太差,因为这种贯穿所有模块的代码硬编码方式,使得大部分模块都依赖conf.py这个文件。

    所以,我认为配置的使用,更好的方式是,

    1. 模块的配置都是可以灵活配置的,不受外部配置文件的影响。
    2. 程序的配置也是可以灵活控制的。

    能够佐证这个思想的是,用过nginx和mysql的同学都知道,nginx、mysql这些程序都可以自由的指定用户配置。

    所以,不应当在代码中直接import conf来使用配置文件。上面目录结构中的conf.py,是给出的一个配置样例,不是在写死在程序中直接引用的配置文件。可以通过给main.py启动参数指定配置路径的方式来让程序读取配置内容。当然,这里的conf.py你可以换个类似的名字,比如settings.py。或者你也可以使用其他格式的内容来编写配置文件,比如settings.yaml之类的。

    4.16本章小结:

    logging模块有几个日志级别?

    答案
     logging模块共5个级别,它们分别是:
     DEBUG INFO WARNING ERROR CRITICAL

    请配置logging模块,使其在屏幕和文件里同时打印以下格式的日志

    2017-10-18 15:56:26,613 - access - ERROR - account [1234] too many login attempts

    json、pickle、shelve三个区别是什么?

    答案
     首先,这三个模块都是序列化工具。
     1. json是所有语言的序列化工具,优点跨语言、体积小.只能序列化一些基本的数据类型。intstrlist	upledict
     pickle是python语言特有序列化工具,所有数据都能序列化。只能在python中使用,存储数据占空间大.
     shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式。
     2. 使用方式,json和pickle用法一样,shelve是f = shelve.open('shelve_test')

    json的作用是什么?

    答案:
     序列化是指把内存里的数据类型转变成字符串,以使其能存储到硬盘或通过网络传输到远程,因为硬盘或网络传输时只能接受bytes

    subprocess执行命令方法有几种?

    答案吧:有三种方法,他们分别是
     run()方法
     call()方法
     Popen()方法

    为什么要设计好目录结构?

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

    打印出命令行的第一个参数。例如:

    python argument.py luffy
    打印出 luffy
    
    答案:
    import sys
    print(sys.argv[1])

    代码如下:

    '''
    Linux当前目录/usr/local/nginx/html/
    文件名:index.html
    '''
    import os
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath('index.html')))
    print(BASE_DIR)

    打印的内容是什么?

    答案
    /usr/local/nginx

    os.path.dirname和os.path.abspath含义是什么?

    答案
    os.path.dirname:指定文件的目录
    os.path.abspath:指定文件的绝对路径

    通过configparser模块完成以下功能

    文件名my.cnf

    [DEFAULT]
    
     [client]
     port = 3306
     socket = /data/mysql_3306/mysql.sock
    
     [mysqld]
     explicit_defaults_for_timestamp = true
     port = 3306
     socket = /data/mysql_3306/mysql.sock
     back_log = 80
     basedir = /usr/local/mysql
     tmpdir = /tmp
     datadir = /data/mysql_3306
     default-time-zone = '+8:00'

    修改时区 default-time-zone = '+8:00' 为 校准的全球时间 +00:00、

    答案
    import configparser
    config = configparser.ConfigParser()
    config.read('my.cnf')
    config.set('mysqld','default-time-zone','+00:00')
    config.write(open('my.cnf', "w"))
    print(config['mysqld']['default-time-zone'] )

    删除 explicit_defaults_for_timestamp = true

    import configparser
    config = configparser.ConfigParser()
    config.read('my.cnf')
    config.remove_option('mysqld','explicit_defaults_for_timestamp')
    config.write(open('my.cnf', "w"))

    为DEFAULT增加一条 character-set-server = utf8

    答案:
    import configparser
    config = configparser.ConfigParser()
    config.read('my.cnf')
    config.set('DEFAULT','character-set-server','utf8')
    config.write(open('my.cnf', "w"))
  • 相关阅读:
    Quartz 多个触发器
    Java获取一个路径下指定后缀名的所有文件
    Dom4J对XML的创建、修改、删除等操作
    struts2的json-default和struts-default的区别
    在JSP页面中输出JSON格式数据
    jbpm4.4 demo3
    jbpm4.4 demo2
    jbpm4.4 demo1
    十六进制字符串操作
    如何使用C#操作WinAPI
  • 原文地址:https://www.cnblogs.com/cnlogs1/p/9601195.html
Copyright © 2020-2023  润新知