Python环境搭建&Flask项目架构&代码风格
Python 最佳实践
Python 日常使用的最佳实践,包括:
- Python环境搭建
- 项目工程的架构
- 单文件的结构
- pythonic代码风格
- 优质项目代码
- 文档
- 测试
- 日志记录
- 配置文件和数据
安装Python
- 从Python的官方网站下载Python 3.7对应的或32位安装程序
- 然后,运行下载的exe安装包。特别要注意勾上
Add Python 3.7 to PATH
,然后点“Install Now”即可完成安装。
底层虚拟环境 virtualenv
virtualenv 是一个 Python 项目依赖管理工具
建议在开发项目时使用virtualenv做依赖隔离,便于使用pip freeze自动生成requirements文件
通过 pip 安装 virtualenv :
1 |
|
为项目创建一个虚拟环境,名叫my_project
:
1 |
|
virtualenv 会创建一个文件夹,其中包含使用 Python 项目所有所需的可执行文件。
开始使用虚拟环境前,需要先激活:
1 |
|
安装包的话就与往常一样,如:
1 |
|
如果你在虚拟环境中暂时完成了工作,可以这样停用它:
1 |
|
为了保持环境的一致性,“冻结” 当前环境包的状态是正确的选择:
1 |
|
该命令将创建一个 requirements.txt 文件,里面包含有当前环境所有包的简单列表及对应的版本,这样就能完全搭建出与之前一致的环境了:
1 |
|
这样有助于在跨设备,跨部署,跨人员的情况下保证环境的一致性。
最后,记得将虚拟环境文件夹从源代码控制中排除,也就是将其添加到 ignore 列表中 ( 详见 Version Control Ignores).
写出优雅的 Python 代码
结构化您的工程
工程化的项目目录具备让项目跑起来的所有基本内容。它里边会包含你的项目文件布局、自动化测试代码,模组,以及安装脚本。当你建立一个新项目的时候,只要把这个目录复制过去,改改目录的名字,再编辑里边的文件就行了。
“结构化”意味着通过编写简洁的代码,并且正如文件系统中文件和目录的组织一样, 代码应该具有逻辑和依赖清晰。
目的:为了防止各个模块的依赖混乱,一般通过模块划分,对Python项目进行结构化。之后,就只剩下架构性的工作,包括设计、实现项目各个模块,并整理清他们之间 的交互关系。
项目架构&仓库
简单的仓库结构模板: 可以在GitHub上找到 。
布局 | 作用 |
---|---|
README.rst | 项目介绍 |
LICENSE | 许可证. 法律相关 |
setup.py | 安装、部署、打包的脚本-分发管理 使用 python setup.py install 安装 |
requirements.txt | 项目所需的依赖库 |
sample/__init__.py sample/core.py sample/helpers.py sample/setting.py sample/configs/ sample/data/ sample/static/ sample/templates/ |
初始化应用并组合所有其它组件 核心代码/具体代码 helpers 工具模块 setting 用来作变量和常量的初始化 configs 配置文件包 data 数据文件包 包括了公共CSS, Javascript等 放置应用的Jinja2模板 |
docs/conf.py docs/index.rst |
项目的参考文档 |
tests/ | 包的集合和单元测试 上下文环境的文件 context.py -方便测试导包 |
Makefile | 通用的管理任务 |
Makefile 模板:
1 |
|
setup.py 模板:
1 |
|
Python中global的用法
global 是python中的一个关键字,作用在变量上,该关键字通常放在函数块中,用来声明该变量为全局变量。
例如下面变量a,定义在函数外面的是全局变量a,定义在fun函数里面的a是另一个a,是局部变量a,两者没有任何关系。好比这个地区有个叫张三的人,公办室里有个另一个叫张三的人。他们是两个不同的人。
1 |
|
如果想要函数里面的那个a就代表外面的全局变量a,那么就要将函数里面的a 用关键字 global 声明为全局变量
1 |
|
单个文件结构)
包
Python提供非常简单的包管理系统,即简单地将模块管理机制扩展到一个目录上(目录扩 展为包)。
任意包含 __init__.py
文件的目录都被认为是一个Python包。导入一个包里不同 模块的方式和普通的导入模块方式相似,特别的地方是 __init__.py
文件将集合 所有包范围内的定义。
模块
Python模块对应的是一个.py
文件,是最主要的抽象层之一。抽象层允许将代码分为 不同部分,每个部分包含相关的数据与功能。
例如在项目中,一层控制用户操作 相关接口,另一层处理底层数据 操作。最自然分开这两 层的方式是,在一份文件里重组所有功能接口,并将所有底层操作封装到另一个文件中。这种情况下,接口文件需要导入封装底层操作的文件,可通过 import
和 from ... import
语句完成。一旦您使用 import 语句,就可以使用这个模块。
类
包含函数、变量。类中有属性和方法。一个对象就是一个类的实例。
可变和不可变类型
Python提供两种内置或用户定义的类型。数字、字符串、元组是不可变的,列表、字典是可变的。
对不可变类型的变量重新赋值,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象(如果没有其他变量引用原有对象的话(即引用计数为0),原有对象就会被回收)。
字符串是不可变类型。这意味着当需要组合一个 字符串时,将每一部分放到一个可变列表里,使用字符串时再组合 (‘join’) 起来的做法更高效。
差
1 |
|
好
1 |
|
函数的参数
函数的参数可以使用四种不同的方式传递给函数。
- 必选参数 是没有默认值的必填的参数
point(x, y)
- 关键字参数 是非强制的,且有默认值
point(x, y, z=None)
- 任意参数列表 如果函数的参数数量是动态的,该函数可以被定义成
*args
的结构send(message, *args)
- 任意关键字参数字典 如果函数要求一系列待定的命名参数,我们可以使用
**kwargs
的结构。在函数体中, kwargs 是一个字典,它包含所有传递给函数但没有被其他关键字参数捕捉的命名参数
返回值
当一个函数在其正常运行过程中有多个主要出口点时,它会变得难以调试其返回结果,所以保持单个出口点可能会更好。
1 |
|
pythonic风格
代码风格:
每个缩进层级使用4个空格
每行最多79个字符
顶层函数或类的定义之间空两行(特别容易漏,漏的话,是报E302 expected 2 blank lines, found 1)
采用ASCII或者UTF-8编码文件
每条import导入一个模块,导入放在代码顶端,导入顺序是先标准库,第三方库,本地库
小括号,大括号,中括号之间的逗号没有额外的空格
类命名采用骆驼命名法,CamelCase;函数用小写字符
函数命名使用小写字符,例如xxx_xxx_xxx; 用下划线开头定义私有的属性或方法,如_xxx
命名风格:
类名使用 UpperCamelCase 风格,必须遵从驼峰形式,如:XmlService
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式:localValue / getHttpMessage()
常量命名全部大写,单词间用下划线隔开
包名统一使用小写、单数、_
命名时,使用尽量完整的单词组合来表达其意
方法命名规约:
1) 获取单个对象的方法用 get 做前缀。 2) 获取多个对象的方法用 list 做前缀。 3) 获取统计值的方法用 count 做前缀。 4) 插入的方法用 save/insert 做前缀。 5) 删除的方法用 remove/delete 做前缀。 6) 修改的方法用 update 做前缀。
异常处理
对大段代码进行 try-catch,这是不负责任的表现。
捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之
finally 块必须对资源对象、流对象进行关闭
日志文件推荐至少保存 15 天单元测试
保证测试粒度足够小,方法级别
必须使用 assert 来验证
编写单元测试代码遵守 BCDE 原则,以保证被测试模块的交付质量1
2
3
4B: Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
C: Correct,正确的输入,并得到预期的结果。
D: Design,与设计文档相结合,来编写单元测试。
E: Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得到预期的结果。MySQL 数据库
表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint
表名、字段名必须使用小写字母或数字,如:aliyun_admin
小数类型为 decimal,禁止使用 float 和 double
varchar 是可变长字符串,不预先分配存储空间
表必备三字段: id, gmt_create, gmt_modified
表的命名最好是加上“业务名称_表的作用”
阅读优质的代码
- Howdoi Howdoi 使用 Python 实现的代码搜索工具。
- Flask Flask 是基于 Werkzeug and Jinja2 的 Python 微框架。 它的目的是快速入门并开发实现你头脑中的好主意。
- Diamond Diamond 是使用 python 实现的用于收集监控数据的工具,主要收集 metrics 类型的数据,并将其发布到 Graphite 或其他后台。它能够收集 cpu , 内存, 网络, i/o ,负载和磁盘 metrics 数据。此外,它还提供 API 用以实现自定义收集器从任意来源中收集指标数据。
- Werkzeug Werkzeug 最初是 WSGI 应用程序的各种实用工具的简单集合,并已成为最先进的 WSGI 实用程序模块之一。它包括强大的调试器、功能齐全的请求和响应对象、处理实体标记的 HTTP 实用程序、缓存控制头、HTTP 日期、cookie 处理、文件上传、强大的 URL 路由系统和一群社区贡献的插件模块。
- Requests Requests 是一个用 Python 实现的 Apache2 授权的 HTTP 库供大家使用。
- Tablib 是用 Python 实现的无格式的表格数据集库。
项目文档
建议提供相关函数的更多信息,包括它是做什么的, 所抛的任何异常,返回的内容或参数的相关细节。
通常称为 Numpy style Docstrings
代码测试
py.test 测试工具功能完备,并且可扩展,语法简单
1 |
|
日志记录
日志记录一般有两个目的:
- 诊断日志 记录与应用程序操作相关的日志。例如,当用户遇到程序报错时, 可通过搜索诊断日志以获得上下文信息。
- 审计日志 为商业分析而记录的日志。从审计日志中,可提取用户的交易信息, 并结合其他用户资料构成用户报告,或者用来作为优化商业目标的数据支撑。
Python的logging模块提供了通用的日志系统,包括logger,handler,filter,formatter这四个方法:
- logger提供日志接口,供应用代码使用。
logger最长用的操作有两类:配置和发送日志消息。可以通过logging.getLogger(name)获取logger对象,如果不指定name则返回root对象,多次使用相同的name调用getLogger方法返回同一个logger对象。 - handler
将日志记录(log record)发送到合适的目的地(destination),比如文件,socket等。一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。 - filter
提供一种优雅的方式决定一个日志记录是否发送到handler。 - formatter
指定日志记录输出的具体格式。formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。
总的一个逻辑是这样的,我们需要创建一个logger,这样在代码执行中,可以使用logger.debug/info/warning/error等方法,将日志输出到指定的位置(可以是控制台、文件,可多选);创建logger之后,需要给logger添加处理句柄addHandler(),每个句柄就对应一种输出,比如StreamHandler表示输出到控制台(当然我这里简单化了),FileHandler输出到指定文件。然后给句柄设置格式Format,这就是你想要看见的格式。
1. logger的定义
Logging.Logger:Logger是Logging模块的主体,进行以下三项工作:
- 为程序提供记录日志的接口
- 判断日志所处级别,并判断是否要过滤
- 根据其日志级别将该条日志分发给不同handler
常用函数有:
Logger.setLevel()
设置日志级别
Logger.addHandler()
和Logger.removeHandler()
添加和删除一个Handler
Logger.addFilter()
添加一个Filter,过滤作用
2. handler的使用
所有的handler汇总,按需自取
1 |
|
3. Formater的使用
format:指定输出的格式和内容,format可以输出很多有用的信息
1 |
|
举个例子
1 |
|
4. Level级别
level:设置日志级别,默认为logging.WARNNING;
可以给logger和handler设置不同的级别,比如logger设置为debug,然后handler1设置为error,这样这个输出只会记录error以上的日志级别信息,另一个handler2设置为info,记录info级别以上的信息。有下面这几种可以设置
1 |
|
示例代码
1 |
|
配置文件和数据
- 如框架无特殊规定,配置文件应放置于项目根目录下的
config
文件夹中 - 配置文件在部署、预发布、生产环境、开发环境等环境中会有很大差异,因此请不要将配置文件在上传到git、svn等版本库中, 而是建议在版本库中上传一个配置的示例文件(如:config.example)
- 上传到版本库中配置示例文件不允许出现密码、证书、token等敏感信息
- 数据和程序应该尽量分离,不要将数据写在代码中,需要持久化存储数据必须使用数据库