Python良好实践指南

221 阅读12分钟

重新审视Python中的一些最佳实践,从项目结构到代码格式,再到一些好的旧代码管理技巧,都有涉及。

照片:Andrei CastanhaonUnsplash

这篇文章是我正在写的关于数据科学的书中的一章,书名是《数据科学的懒人指南》,目前还只是部分完成。要阅读类似的文章,请参考我的博客我的媒体页面。不要忘记在LinkedIn和/或Twitter上打招呼 🖖

简介

编写现在能用的代码很容易。编写明天能用的代码是很难的。编写明天能用的代码,并且足够直观,任何人都能理解和遵循--现在我们遇到了超级困难的事情😀。观察几个和我一起工作的ML工程师和数据科学家,我注意到他们几乎都有自己独特的编码风格。好吧,不要误会我的意思,主观上是一件好事,我认为这就是导致创新的原因。尽管如此,当在一个团队中工作时,甚至在开源合作中,同意一套规则是有帮助的。这就是这篇文章背后的想法,为Python从业者提供一套精心策划的准则,他们可以从中挑选。有了这些,让我们来介绍一些好的做法,这不仅可以帮助我们创建一个可以工作的,而且是一个漂亮的代码😄。为了涵盖这个主题,我们将通过三个部分。

  1. **项目结构化:**关于如何组织你的代码的想法
  2. **代码格式化:**关于如何使你的代码易于理解的想法
  3. **其他提示:**一些对你有长期帮助的东西。

项目的结构化

在这一部分,我们基本上会讨论一些关于如何构建完整的Python项目的良好实践。为此,我们将看一下两种不同的可能性,任何人都可以根据他们的项目的简单或复杂程度来选择。

类型1:经典

  • 这是最基本的格式,但也给人以有组织的结构化的暗示。当我们的项目只由几个模块/脚本组成时,可以采用这种方式。一个示例项目的目录可能看起来像这样。
my_project             # Root directory of the project
  • 从名字上看,代码文件夹包含各个模块(.py文件),输入和输出分别包含输入和输出文件,笔记本包含我们用于实验的.ipynb笔记本文件。最后,config文件夹可以包含yaml或json或ini文件内的参数,可以通过代码模块文件使用[configparser](configparser - 配置文件解析器 - Python 3.7.11 文档)进行访问。
  • requirements.txt 包含了项目所需的所有外部 python 包的列表。维护这个文件的一个好处是,所有这些包都可以使用 pip install -r requirements.txt 命令轻松安装。(不需要手动安装每一个外部包!)。)下面是一个requirements.txt文件的例子_(用_ package_name==package_version格式)
BeautifulSoup==3.2.0
  • 最后,README.MD包含了项目的内容、原因和方法,以及一些关于如何运行项目的假代码和使用案例的例子。

类型2:Kedro

  • Kedro不是一个项目结构化策略,它是QuantumBlack实验室发布的一个python工具,它为你做项目结构化😎。除此之外,他们还提供了大量的功能,使我们的项目组织甚至是代码执行过程变得超级简单,这样我们就可以真正专注于最重要的事情--实验和实施
  • 他们的项目结构如下图所示。另外,我们可以通过运行kedro new命令来创建一个空白项目_(不要忘记先用pip_ install kedro 来安装kedro )。
get-started         # Parent directory of the template
  • 虽然大部分的目录与其他类型的目录相似,但有几点需要注意。Kedro对不同模块进行分组的方式是创建不同的 "管道"。这些管道存在于 src文件夹 ,而src文件夹又包含模块文件。此外,它们对执行的单个函数有明确的隔离--这些 函数 存储在 nodes.py文件 ,这些函数随后与 pipeline.py文件__中的输入和输出相连 *(都在单个管道文件夹中)。Kedro还将代码和参数分开,将参数存储在conf文件夹中。
  • 除了帮助组织项目之外,他们还提供了顺序或平行执行的选项。我们可以执行单个函数_(在_ nodes.py ,或单个管道_(这是一个函数的组合)_,或一次性执行整个项目。我们还可以创建完整项目的文档,或者将项目编译并打包成一个python .whl文件,只需运行一个命令。更多的细节,相信我,我们才刚刚接触到表面,请参考他们的官方文档

代码格式化

  • 用自上而下的方法,让我们先看看一段_整齐_的代码。我们将在后面更详细地讨论代码的个别方面。现在,只是假设如果有人要求你做一些脚本,一个理想的代码文件应该是什么样子。
  • 下面的代码取自csv_column_operations.py模块文件。它是根据提示生成的:"编写一个以CSV文件为输入并返回一列之和的函数"。

Some might argue why do such an overkill for a simple piece of code. Note, it's a dummy example. In real life, you will develop more complex pieces of codes and hence it become quite important that we understand the gist.
  • 现在让我们深入了解一下上述代码的各个环节。

模块结构

  • 模块是一个以.py为扩展名的python文件,它包含可执行代码或函数或类等。
  • 通常,我们以模块定义开始模块,这是一个提供模块的一些基本细节的区域。我们可以用下面的模板来做_(而且可以很容易地与上图所示的真实代码相比较)_。
"""<Short description>
<Long description>
Author: <Name> <email>
Created: <date>
  • 接下来,我们应该用注释线将模块的各个部分,如导入、代码区等清楚地分隔开来。
  • 最后,在底部,我们可以包括一些关于如何运行代码的例子。在 if __name__ == '__main__' 内包括这些脚本:确保它们只在文件被直接执行时运行(比如 python _csv_column_operations.py)。_所以当你在另一个脚本中说导入该模块时,这些代码不会运行。

函数结构

  • 函数是执行特定任务的基本代码块。一个模块由几个函数组成。为了告诉用户一个特定的代码块是做什么的,我们用一个函数定义来开始这个函数。下面提供了一个示例模板。
"""Description
Paramters
Returns
  • 在这之后,我们可以开始添加相关的代码行。请确保在函数中使用注释来分隔不同的逻辑代码块。
  • 在编码部分开始时要处理的一件重要事情是检查参数和输入数据是否有一些数据类型或数据内容相关的基本问题。大部分的代码断裂都是由于愚蠢的错误,比如有人提供了错误的输入,在这种情况下,我们应该打印或记录警告信息并优雅地退出。上述相同的代码在第1步中包含了两个这样的初步但重要的检查。

命名惯例

我们可以遵循几种格式约定,如Camel Case、SnakeCase等。这是很主观的,取决于开发者。下面是一些命名python代码中不同实体的例子_(取自PIP8惯例--有一些修改)_😇。

  • **模块名称:**模块应该有简短的、全小写的名称(例如:csv_column_operations.py)。
  • **函数或方法名:**函数名应该是小写的,必要时用下划线隔开单词,以提高可读性。另外,别忘了添加你的动词(例如: perform_column_sum())
  • **变量名:**与函数名类似,但不要加动词(例如: list_of_news)
  • **类名:**类名通常应使用CapWords惯例。(例如:FindMax)
  • **常量名称:**常量通常定义在模块层面,用大写字母书写,用下划线隔开单词。(例如:MAX_OVERFLOW和TOTAL)。)

添加注释

PEP-8定义了三种类型的注释。

  • **区块注释:**这是为单个或一组代码行编写的。当你想解释一组代码或只是想隔离代码时,都可以这样做。在上面的例子中,你可以看到# Step {1, 2, 3}被用作隔离注释,# run when file is directly executed被用来解释一组代码行。
  • **内联注释:**与代码添加在同一行。例如,请看#处理csv文件是如何用来说明pandas包导入的。PEP-8建议尽量少用内联注释。
  • **文档字符串:**这些是用于模块、函数或类的文档。PEP-257建议对docstring使用多行注释_(使用""")。上面的示例代码中提供了一个模块和函数文档字符串(简称为文档_字符串)的例子。

我们应该在我们的注释中尽可能地进行描述。尽量把代码的功能部分分开,为复杂的代码行提供解释,提供关于函数输入/输出的细节,等等。你怎么知道你有足够的注释?- 如果你认为拥有你一半专业知识的人能够理解代码而不在半夜给你打电话的话!😤

缩进 - Tabs vs Spaces

  • 坦率地说,我只打算用一根长棍子🧹来触及这个话题。已经有几篇文章reddit线程、甚至电视连续剧(硅谷📺)对这个话题进行了大量的讨论!你想知道吗?
  • 想要我的2分钱吗?选择任何现代IDE_(如VSCode、Sublime等)_,将缩进设置为制表符,并设置1个制表符=4个空格。完成了 😏

其他提示

到目前为止,我们已经讨论了如何构建项目或格式化代码。接下来,我们将介绍一套通用的良好做法,这将为你的工作节省一些痛苦😬。

记录

  • 与其在控制台中临时打印语句(做一个cls,然后就不见了💨),更好的办法是将这些语句保存在一个单独的文件中,你可以随时返回并参考。这就是日志📜
  • Python 提供了一个内建的日志功能。通过参考官方的方法,将日志记录到一个文件中是超级容易的。
  • 注意,日志有一个分层的等级,以隔离不同严重程度的日志。在上面的例子中,级别参数表示被追踪的最小级别,因此被保存到文件中。根据官方指南,这些是不同的日志级别,以及关于何时使用这些级别的一些细节_(按严重程度递增)_。

Python中不同级别的日志记录。来源。链接

文档

  • 如果你打算维护代码或在可预见的将来把它交给别人,那么代码的文档是绝对必须的。只要问问任何一个开发者,当他们发现他们打算使用的软件包有现成的、精心整理过的文档时,他们会有多么兴奋!这就是所谓的 "文档"。另一方面,自己创建一个文档看起来相当困难,不是吗?我的意思是,看看sklearnpandas的漂亮文档吧。😮
  • 好吧,抱歉吓到你了,但实际上这很简单😉。还记得我们之前遵循的所有函数和模块的文档串和格式吗?事实证明,我们可以利用许多开源工具,如pydocsphinx来创建成熟的HTML文档探讨实际的细节不在本文的范围之内,但按照这两个软件包的 "如何 "步骤,让你的文档准备好是相当容易的。
  • 最后一件事,如果你使用Kedro,这个过程就更简单了。你所要做的就是运行一个命令--kedro build-docs --open来创建文档,并在你的默认浏览器中自动打开它!

虚拟环境

虚拟环境(VE)可以被认为是专门为一个项目创建的python环境的本地副本。这个本地副本就像一块白板,因为任何需要的包都可以在这里单独安装。为任何新项目创建一个新的虚拟环境是非常重要的,因为。

  • 每个项目都有自己的依赖树_(一个特定版本的软件包需要另一个软件包来运行,并有自己的特定版本)_。
  • 在开发一个项目时,我们可能需要降级或升级一个软件包,如果在基本的Python环境中这样做,将会影响你的系统
  • 因此,一个单独的python副本(VE),在那里安装你想要的东西,似乎是最合理的解决方案。

使用VE基本上需要两个步骤。

  • **创建一个VE:**这可以通过在项目根目录下运行命令python3 -m venv tutorial-env来完成。(注意, tutorial-env是VE的名称,你可以将其重命名为任何东西)。)
  • **激活VE:**这可以通过在Windows上运行tutorial-env\Scripts\activate.bat命令和在Unix或MacOS上运行tutorial-env/bin/activate源代码来完成。

这就是了!安装、卸载、升级或降级是你想要的。

Remember to switch to another VE when you start working on another project or/and to deactivate the VE when you want to move to base VE.

参考资料

干杯。


Python良好实践指南》最初发表在Medium上的《Towards Data Science》上,在那里人们通过强调和回应这个故事继续对话。