你的开发环境是影响你使用一门语言时的工作效率的主要因素。你不应该满足于一个简单的默认shell,而应该为任何一个生产级别的项目准备一个开发环境。
一个好的Python开发环境通常包括语言解释器、pip包管理器、虚拟环境、一个面向Python的代码编辑器,以及一个或多个静态分析器来检查你的代码是否有错误和问题。我将在本章介绍Python开发环境的各个组成部分。我也将介绍Python中常见的风格约定,以及最常见的Python集成开发环境(Integrated Development Environment,IDE)。
在你开始其余的工作之前,你必须安装Python本身,以及一些必要的工具。正如你从第1章中所了解的,Python是一门解释型语言,所以你需要安装它的解释器。你还必须安装pip,即Python包管理器,这样你就可以安装额外的Python工具和库。安装的具体步骤取决于你的平台,这里介绍在主要的平台上安装Python的步骤。
在本书中,我使用的是Python 3.9。你在阅读本书时只需使用Python 3的最新稳定版本,所有的指令应该都是一样的。你只需要在命令行中运行命令时显式地替换版本号。
这是一个简短的安装指南。完整的官方指南包括更多情况和高级选项,请参见Python文档的“Python安装和使用”部分。
在Windows系统中,Python通常不会默认安装,所以你需要自行从Python官网下载并运行安装程序。在安装Python的过程中,确保你勾选了Install the launcher for all users和Add Python to PATH选项
。
同时,使用者也可以通过Windows应用商店来安装Python。但是到目前为止,这种安装方式仍然被官方认为是不稳定的。我建议你下载官方安装程序。
在macOS系统中,你可以使用MacPorts或Homebrew来安装Python和pip。
请使用下面的命令来利用MacPorts安装Python和pip,将38替换为你想要下载的版本即可(去掉版本号中的小数点):
sudo port install python38 py38-pip sudo port select --set python python38 sudo port select --set pip py38-pip
或者,你也可以使用下面的命令来一步安装Python和pip:
brew install python
请只使用上面两种方法中的一种
。
如果你正在运行Linux操作系统,那么很可能已经默认安装好了Python(Python 3)
,但是你所需要的其余工具可能没有在发行版中默认安装。(以防万一,我会告诉你如何安装Python。)
在Ubuntu、Debian或相关Linux发行版中安装Python和pip,请运行下面的命令
:
sudo apt install python3 python3-pip python3-venv
在Fedora、RHEL或CentOS中,你可以运行下面的命令:
sudo dnf python3 python3-pip
在Arch Linux中,运行下面的命令:
sudo pacman -S python python-pip
对于其余Linux发行版,你需要自行搜索Python 3和pip的安装方法。
如果你正在使用类UNIX系统,而且你的系统中的Python 3版本过旧或者缺少包管理器,那么你可以通过源代码构建Python。这是我通常安装最新版Python的方式。
在macOS系统中,安装Python的构建依赖项有一些相对复杂的考虑因素。你应该查阅Python官方文档。
在大多数Linux系统中,你需要确保你已经安装了Python所依赖的几个库的开发文件。这些库的安装方式取决于你的系统,更具体地说,取决于你使用的包管理器。
如果你正在使用诸如Ubuntu、Pop!_OS、Debian或Linux Mint等基于APT包管理器的Linux发行版,那么你应该在软件源或软件更新设置中勾选“启用源代码”选项,或者确保你的sources.list文件中包含了源代码。(具体方法取决于你的系统,这个主题超出了本书的讨论范围。)
然后,运行下面的命令:
sudo apt-get update sudo apt-get build-dep python3.9
如果你收到“Unable to find a source package for python3.9”的错误信息,请将9改为较小(或较大)的数字,直到找到一个可用的数字为止。Python 3的依赖关系在次要版本
之间并没有太大的变化
。
如果你使用的是诸如Fedora、RHEL或CentOS等基于DNF包管理器的Linux发行版,运行下面的命令:
sudo dnf install dnf-plugins-core sudo dnf builddep python3
如果你使用的是旧版本的基于yum包管理器的Fedora或RHEL,运行下面的命令:
sudo yum install yum-utils sudo yum-builddep python3
如果你使用的是SUSE Linux,则需要一个一个地安装依赖项,包括所需的库。表2-1列出了这些依赖项。如果你使用的是其他基于UNIX的系统,这个列表会很有用,尽管你可能需要更改包的名称或通过源代码构建依赖项。
表2-1 在SUSE Linux系统中安装的Python 3.9的依赖项
你可以从Python官网下载以压缩文件(.tgz)形式发布的Python源代码。我通常喜欢将这个压缩文件移动到专用目录中,尤其是当我同时拥有多个版本的Python时。在该目录中,使用命令tar -xzvf Python-3.x.x.tgz解压缩这个文件
,并将Python-3.x.x.tgz替换为你下载的压缩文件的名称。
接下来,在解压好的目录中,运行下面的命令,请确保每条命令运行成功后再运行下一条命令:
./configure --enable-optimizations make make altinstall
上面的命令将为通用场景配置Python,确保它不会在当前环境中遇到任何错误,然后将其与任何现有的Python一起安装。
陷阱警告: 如果已经安装了其他版本的Python,则应该使用make altinstall命令安装新的Python。否则,已有的Python版本可能会被覆盖或隐藏,从而导致系统出现问题。如果你非常确定这是在当前系统中安装的第一个Python,那么你可以使用make install命令。
一旦安装完成,你就可以使用Python了。
现在,你已经安装好了Python解释器,你可以运行Python脚本和项目了。
解释器的 交互式会话 允许你实时输入和运行代码,并查看结果。你可以使用下面的命令在命令行中启动交互式会话:
python3
陷阱警告: 你应该养成使用python2或python3命令的习惯,而不是使用python命令,因为后者可能会引用错误的版本(即使在今天,许多系统中仍然预装了Python 2)。你可以使用 --version标志来检查这3个命令中的任何一个命令调用的确切版本,例如运行命令python3 --version。
虽然上面的命令在Windows系统中也可以正常工作,但Python文档建议在Windows系统中使用下面的替代命令:
py.exe -3
为了保持跨系统的一致性,我将在后文中使用python3作为启动命令。
启动交互式会话后,你应该会看到类似下面这样的内容:
Python 3.10.2 (default) Type "help", "copyright", "credits" or "license" for more information. >
在提示符 > 后输入任何可运行的Python代码,按Enter键,解释器将立即运行它。你甚至可以输入多行语句,例如条件语句,解释器将在运行代码之前知道更多的行是预期的。当解释器正在等待用户输入后续内容时,你将看到提示符“...”。完成输入后按Enter键,解释器将运行整个代码块:
> spam = True > if spam: ... print("Spam, spam, spam, spam...") ... Spam, spam, spam, spam...
如果想要退出交互式会话,请运行下面的命令:
> exit()
交互式会话对于在Python中测试东西非常有用,但除此之外没有什么其他用途。你应该知道它的存在,但我不会在本书中大量使用它。请使用一个合适的代码编辑器。
你可以在文本或代码编辑器中编写脚本和程序。我将在2.11节介绍几个代码编辑器和IDE,与此同时,你也可以使用自己喜欢的文本编辑器来编写代码。
Python代码将被写入 .py文件。要运行Python文件(例如myfile.py),你可以在命令行(而不是解释器)中使用下面的命令:
python3 myfile.py
一个
包
指的是一组代码,这与大多数其他编程语言中的库类似。Python以“内置电池”
而闻名,因为大多数事情可以通过简单的import语句来完成。但是,如果你需要做一些超出基本功能的事情,例如创建一个漂亮的用户界面,则通常需要安装一个包。
幸运的是,安装大多数第三方库很容易。库的作者已经将他们的库打包,这些包可以使用我们之前安装的易用的pip包管理工具来安装。稍后我会介绍这个工具。
使用多个第三方包需要一些技巧。一些包需要先安装其他包,某些包与其他包存在冲突。你还可以安装特定版本的包,具体取决于你需要什么。因为某些应用程序和操作系统组件依赖于某些Python包,所以我们需要构建虚拟环境
。
一个 虚拟环境 是一个沙盒,你可以在其中安装特定项目所需的Python包,从而避免这些包与其他项目(或系统)的包发生冲突的风险。为每个项目创建不同的沙盒,并且只在其中安装需要的包,一切都井井有条。这实际上从未改变Python包在系统中的安装情况,因此可以避免破坏与项目无关的重要内容。
你甚至可以创建与特定项目无关的虚拟环境。例如,我有一个专用的虚拟环境,用于在Python 3.10中运行随机代码文件,其中包含一组用于查找问题的工具。
每个虚拟环境都位于专用目录中。通常,相应文件夹命名为env或venv。
对于每个项目,我通常会在项目文件夹内创建一个专用的虚拟环境。Python提供了一个名为venv的工具来实现这一点。
如果你选择使用包含诸如Git的版本控制系统(Version Control System,VCS)来对你的代码进行版本控制,那么稍后我将介绍一个额外的设置步骤。
请执行以下命令,在命令行中创建名为venv的虚拟环境,该虚拟环境位于当前工作目录中:
python3 -m ❶ venv ❷ venv
上面命令中的❶venv是创建虚拟环境的命令,❷venv是虚拟环境的路径。在这种情况下,venv只是一个相对路径,它在当前工作目录中创建了一个venv目录。但是你也可以使用绝对路径,并随意命名。例如,你可以在UNIX系统的 /opt目录中创建一个名为myvirtualenv的虚拟环境,如下所示:
python3 -m venv /opt/myvirtualenv
请注意,我在这里指定了python3,尽管我也可以使用任何Python版本来运行它,例如通过命令python3.9 -m venv venv。
如果你使用的是Python 3.3之前的版本,请确保安装了系统的virtualenv包,然后运行以下命令:
virtualenv -p python3 venv
现在,如果你查看工作目录,你会注意到venv目录已经创建好了。
为了使用虚拟环境,你需要激活它。
在类UNIX系统中,运行以下命令:
$ source venv/bin/activate
在Windows系统中,运行以下命令:
> venv\Scripts\activate.bat
或者,如果你在Windows系统中使用PowerShell,则运行以下命令:
> venv\Scripts\activate.ps1
一些PowerShell用户必须首先运行命令set-executionpolicy RemoteSigned,以便在Windows PowerShell上使用虚拟环境。如果你遇到问题,请尝试使用这个命令。
就像魔法一样,你现在正在使用你的虚拟环境!你应该能在命令行提示符的开头(而不是末尾)看到venv,这表示你正在使用名为venv的虚拟环境。
陷阱警告: 如果你同时打开了多个shell(通常是终端窗口),你应该意识到虚拟环境只对你明确激活的那个shell有效!在shell中查找venv标记以确保你正在使用虚拟环境。
当你处在虚拟环境中时,你仍然可以访问系统中虚拟环境之外的所有文件,但是你的环境路径将被虚拟环境覆盖。实际上,你在虚拟环境中安装的任何包都只能在虚拟环境中使用,并且除非你显式指定包的引入路径,否则在虚拟环境中无法访问系统范围的包。
如果你想要在虚拟环境中也能使用系统范围的包,你可以利用一个特殊的标志来实现,这个标志必须在你第一次创建虚拟环境时设置,且不能在创建虚拟环境之后更改。命令如下:
python3 -m venv --system-site-packages venv
为了退出虚拟环境回到系统环境,你需要运行一个简单的命令。
已经准备好了吗,UNIX用户?只需运行以下这个命令:
$ deactivate
就这么简单。Windows PowerShell用户也是如此。
不过在Windows命令行上,命令就稍微有点复杂了:
> venv\Scripts\deactivate.bat
但还是比较简单的。记住,就像激活虚拟环境时所做的一样,如果你给虚拟环境起了一个别名,你就必须相应地改变那一行中的venv。
我们大多数人对Python的包系统有很高的期望。Python的包管理器是pip,它通常使包的安装变得轻而易举,特别是在虚拟环境中。
请记住,在进行任何Python开发工作时,你都应该在虚拟环境中工作。这将确保你始终使用正确的包来工作,而不会搞乱系统中其他程序的包。如果你确定自己想在系统范围的Python环境中安装包,你也可以使用pip来做。首先,确保你不在虚拟环境中工作,然后使用如下命令:
python3 -m pip command
将上面命令中的command替换为对应的pip命令,稍后我将详细介绍。
为了安装一个包,需要运行命令pip install package。例如,要在激活的虚拟环境中安装PySide6,你可以使用下面的命令:
pip install PySide6
如果你想安装特定版本的包,可以在包名的后面加上两个等号(==),然后跟上想要的版本号(不要有空格):
pip install PySide6==6.1.2
顺带一提,你还可以使用运算符 >= 来表示“至少这个版本或更高版本”,这叫作需求规范。命令如下所示:
pip install PySide6>=6.1.2
上面这行命令将安装最新版本的PySide6,它至少是6.1.2版本。如果你既想安装最新版本的包,又想确保至少安装最低版本的包(你可能并未安装),这就非常有用了。如果无法安装满足要求的包,pip将显示一个错误消息。
如果你在使用类UNIX系统,你可能需要使用命令pip install "PySide6>=6.1.2",因为 > 在shell中有另外的含义。
你可以通过配置requirements.txt文件为你的项目开发节省更多的时间。这个文件列出了你的项目所需要的包。在创建一个虚拟环境时,通过这个文件,你和其他用户可以使用一个命令安装所有需要的包。
在创建这个文件时,将一个包的名称和版本(如果需要的话)写在同一行。例如,我的一个项目有一个requirements.txt文件,如清单2-1所示。
PySide2>=5.11.1 appdirs
现在,任何人都可以使用下面的命令一次性安装所有这些包:
pip install -r requirements.txt
我将在第18章再次介绍requirements.txt,那时我会介绍打包和分发。
你也可以使用pip更新已安装的包。例如,要将PySide6更新到最新版本,可以运行下面的命令:
pip install --upgrade PySide6
如果你有一个requirements.txt文件,你也可以一次性升级所有需要的包:
pip install --upgrade -r requirements.txt
你可以使用下面的命令卸载包:
pip uninstall package
将命令中的package替换成对应的包名即可。
这里有个小问题。安装一个包时,它所依赖的包也会被安装,我们称之为依赖项。卸载一个包时,它的依赖项不会被卸载,所以你可能需要手动卸载它们。这可能会变得棘手,因为多个包可能共享依赖项,所以你可能会破坏另一个包。
在这里,虚拟环境的优势就体现出来了。一旦你陷入这种困境,你可以删除虚拟环境,再创建一个新的虚拟环境,然后只安装所需要的包即可。
好了,现在你可以安装、升级和卸载包。你怎么知道pip有哪些包可用呢?
有两种方法可以找到答案。第一种是使用pip自身来进行搜索。假设你想要一个用于网络爬虫的包,运行下面的命令:
pip search web scraping
上面的命令会给你一大堆结果,当你忘记包的名称时,它会很有用
。
如果你想要获取更多的信息,可参考PyPI官网提供的官方Python包索引。
除非你是相关领域的专家,否则不要在类UNIX系统中使用sudo pip!它会对你的系统安装做很多坏事——这些事情是你的系统包管理器无法纠正的——如果你决定使用它,你可能会在以后使用系统时感到后悔。
通常,当你认为需要使用sudo pip时,实际上应该使用命令python3 -m pip或pip install –user把包安装到本地用户目录中。大多数其他问题可以通过虚拟环境来解决。
陷阱警告: 除非你是一个专家,完全理解你在做什么以及如何在出现问题后回滚操作,否则不要使用sudo pip!
与虚拟环境和版本控制系统(如Git)一起工作可能会很棘手。虚拟环境目录中的内容是你用pip安装的实际包,这会使你的版本控制系统中充斥着大量不必要的文件,而且你也不能从一台计算机复制虚拟环境文件夹到另一台计算机,并寄希望于它能正常工作
。
因此,你不想在版本控制系统中跟踪这些文件。有以下两种解决方案:
1. 在你的仓库之外创建虚拟环境;
2. 不要将虚拟环境目录纳入版本控制系统的控制范围内。
以上两种解决方案各有优点,具体使用哪一种则取决于你的项目、环境和特定需求。
如果你使用的是Git,创建或编辑一个名为 .gitignore的文件,将其放在你的仓库的根目录下,并在其中添加清单2-2所示的这一行。
venv/
如果你使用的是其他的虚拟环境名称,修改这一行以匹配。如果你使用的是其他的版本控制系统,比如Subversion或Mercurial,请查看对应的文档以了解如何忽略类似venv的目录。
通常,每个克隆
你的仓库的开发者都会构建自己的虚拟环境,并且可能会使用你提供的requirements.txt文件。
即便你计划将你的虚拟环境放在仓库之外,使用 .gitignore文件也是个好主意,这样可以提供一些额外的保障。最佳的版本控制实践是手动选择要提交的文件,但是错误不可避免。因为venv是虚拟环境目录的最常见的名称之一,所以将它添加到 .gitignore文件中至少可以防止一些意外的提交。如果你的团队有其他标准的虚拟环境名称,你也可以考虑将它们添加进去。
许多用户及开发者可能会运行你的代码,但是如果你的Python文件的第一行不正确,这一切都会很快崩溃。
shebang是Python文件顶部的一个特殊命令,通过它你可以直接执行Python文件,如清单2-3所示。
❶ #!/usr/bin/env python3 print("Hello, world!")
shebang(又称hashbang,在代码中的形式为 #!)❶提供了Python解释器的路径。虽然它是可选的,但我强烈建议你在代码中包含它,因为这意味着你可以将文件标记为可执行并直接执行,如下所示:
./hello_world.py
这非常有用,但是正如我之前提到的,你必须小心使用shebang。shebang告诉计算机在哪里找到要使用的确切的Python解释器,所以错误的shebang可以跳出虚拟环境的限制,甚至指向一个没有安装的解释器版本。
你可能已经在实践中看到过清单2-4所示的shebang。
#!/usr/bin/python
这行代码完全不正确,因为它强制计算机使用特定的系统范围内的Python副本。再次强调,这完全忽略了虚拟环境的目的。
探究笔记: 你可能想知道#!/usr/bin/python在Windows系统中是怎么成为一个有效的路径的。它确实是有效的,这要归功于PEP 397中概述的一些技巧(但你仍然应该避免使用它)。
对于任何只能运行在Python 3中的Python文件,你应该始终使用清单2-5所示的shebang。
#!/usr/bin/env python3
如果你有一个脚本既可以在Python 2中运行,也可以在Python 3中运行,那么请使用清单2-6所示的shebang。
#!/usr/bin/env python
关于shebang以及如何处理它们的规则,PEP 394(针对类UNIX系统)和PEP 397(针对Windows系统)中有正式的说明。无论你使用的是哪个操作系统,了解UNIX和Windows系统中shebang的含义都是很好的。
自Python 3.1开始,所有的Python文件都使用UTF-8编码,以允许解释器使用Unicode中的所有字符(在该版本之前,Python使用的默认编码系统是旧的ASCII编码)。
如果你需要使用一个不同的编码系统,而不是默认的UTF-8编码,你需要告诉Python解释器。
比如,要在Python文件中使用Latin-1编码,你需要将以下这行代码放在文件的顶部,紧跟在shebang之后。为了使其正常工作,它必须在第一行或第二行——这就是解释器查找此信息的地方:
# -*- coding: latin-1 -*-
如果你想使用其他编码系统,用相应的名称替换latin-1即可。如果你指定了Python无法识别的编码,它将抛出一个错误。上面的这种方式是指定编码的常规方式,此外还有其他两种形式。你可以使用下面这种形式:
# coding: latin-1
或者使用下面这种更长但更易于理解的形式:
# This Python file uses the following encoding: latin-1
不管你使用哪种形式,都必须与此处介绍的命令完全相同(除了将latin-1替换为你想要的其他编码的名称)。因此,首选第一种或第二种形式。
请参阅PEP 263,以了解更多信息。
大多数时候,你还是可以使用默认的UTF-8编码;如果你需要使用其他的编码系统,相信你现在已经知道如何通知解释器了。
当你习惯了使用虚拟环境和pip后,你会学会用一些额外的技巧和工具来简化整个过程。以下是一些比较流行的技巧。
你可以在不激活虚拟环境的情况下使用虚拟环境的二进制文件。例如,你可以执行venv/bin/python来运行虚拟环境自己的Python实例,或者执行venv/bin/pip来运行虚拟环境自己的pip实例。这样做的效果与激活虚拟环境后的效果相同。
例如,假设我的虚拟环境是venv,我可以在终端这样做:
venv/bin/pip install pylint venv/bin/python > import pylint
完美运行!但是import pylint命令仍然不会在系统范围内的Python交互式shell中工作(当然,除非你在系统中安装了它)。
在本书中,我将使用pip和venv,因为它们是现代Python的默认工具。但是还有一些其他的解决方案值得一看。
有不少Python开发人员都在使用Pipenv,它将pip和venv结合在一起而成为一个整体的工具,具有许多额外的功能。
由于工作流程有很大的不同,因此我不会在这里介绍Pipenv。如果你对它感兴趣,我建议你阅读其出色的官方文档。你可以在那里找到全面的设置和使用说明,以及对Pipenv所提供的优势的更详细解释。
有许多的pip任务可以通过pip-tools简化,包括自动更新、编写requirements.txt的辅助工具等。
如果你使用pip-tools,你应该只在虚拟环境中安装它。它专为此用例而设计。
更多的信息可以参考PyPI官网的pip-tools 7.3.0部分。
一些Python开发者非常讨厌整个pip工作流程。其中一个开发者创建了poetry作为替代的包管理器。我不会在本书中使用它,因为它的行为非常不同,但我不应该不提及它。
你可以在poetry网站上找到更多信息,如下载方式(创建者不建议使用pip安装它)及官方文档。
许多语言的代码风格完全由相应社区决定,而Python却有一个官方的代码风格指南,发布为PEP 8。尽管该指南中的约定主要用于标准库代码,但许多Python开发人员选择将其作为规则遵守。
这并不意味着PEP 8具有强制性:如果你有一个合适的理由在项目中使用不同的代码风格,那没有什么问题,不过你应该确保项目中代码风格的一致性。
PEP 8本身从一开始就明确了这一点,如下所示。
本指南是关于一致性的。与本指南保持一致很重要,项目内的一致性更重要,一个模块或函数内的一致性最重要。
你要知道什么时候应该不一致——有时候,本指南的建议是不适用的。当你不确定的时候,请相信你自己的判断。看看其他的例子,以决定什么样的代码风格是最好的。另外,不要害怕提出疑惑!
在实践中,你可能会发现很少有理由违背PEP 8。它并不是包罗成象的,但它提供了足够的空间,同时明确了什么代码风格是好的,以及什么代码风格是坏的。
PEP 8推荐的行宽限制为79或80个字符,但这个话题有很多争议。一些Python开发者遵守这个规则,而另一些则更喜欢每行限制为100或120个字符。该怎么办呢?
最常见的关于行宽限制的论点是,现代显示器更宽、分辨率更高,79或80个字符的限制是历史遗留问题。是吗?绝对不是!坚持使用常见的行宽限制有多个原因,例如:
● 视力障碍者,他们必须使用更大的字体或放大的界面;
● 查看文件中的不同提交之间的差异;
● 编辑器分屏,同时显示多个文件;
● 垂直显示器;
● 笔记本计算机显示器上的并排窗口,编辑器只有通常空间的一半;
● 使用老式显示器的人,他们无法升级到1080p大屏幕;
● 在移动设备上查看代码。
在这些情况下,79或80个字符的行宽限制背后的原因变得显而易见:每行的水平空间根本不够显示120个或更多的字符。软文本换行(即截断的行的后半部分显示在下一行,没有行号)确实解决了一些问题。但是它可能很难阅读,许多被迫经常依赖它的人会证实这一点。
当然,这并不意味着你必须严格遵守79或80个字符的最大限制。也有例外情况。首先,可读性和一致性是目标。许多开发人员接受80/100规则:在大多数情况下尽量遵守80个字符的“软”限制;而将100个字符作为“硬”限制,以应对80个字符的限制会对可读性产生负面影响的情况。
关于使用空格还是制表符的争论,许多程序员之间的友谊因此而受到考验。大多数程序员对这个话题有着强烈的共情。
PEP 8推荐使用空格而不是制表符,但从技术上讲,两者都可以使用。重要的是,永远不要混合使用两者。使用空格或制表符,然后在整个项目中坚持使用。
如果使用空格,那么就会有关于使用多少个空格的争论。PEP 8也回答了这个问题:每个缩进级别使用4个空格。任何少于这个数量的空格都会对代码可读性产生负面影响,特别是对于视力障碍者或某些特定的阅读障碍者。
顺带一提,大多数代码编辑器能在你按下Tab键时自动输入4个空格,因此输入时不需要重复按空格键。
在任何开发者的工具箱中,最有用的工具之一是一个可靠的、可以读取你的代码并查找潜在错误的静态分析器。如果你之前没有使用过类似的工具,那么现在可以去体验一下。其中一种通用类型的静态分析器称为linter,它可以检查代码中存在的常见错误、潜在隐患,以及代码风格的不一致性。在Python社区中,最受欢迎的两个linter是Pylint和PyFlakes。
Python在社区中有非常多的静态分析器,包括静态类型检查器(如Mypy)和复杂度分析器(如mccabe)。
接下来将介绍如何安装部分工具,以及如何使用它们。建议只安装上述两个linter之一,并安装其余的静态分析器。
Pylint可能是Python中最通用的静态分析器。它在默认情况下工作得很好,允许你自定义想要查找和忽略的内容。
你可以使用pip安装Pylint包,建议在虚拟环境中安装这个包。安装完成后,你可以向Pylint传递你想要分析的文件的名称,如下所示:
pylint filetocheck.py
你也可以分析整个包或模块(我将在第4章介绍模块和包)。例如,如果你想让Pylint分析当前工作目录中名为myawesomeproject的包,可以运行以下命令:
pylint myawesomeproject
Pylint将会扫描文件并在命令行中显示警告和建议。然后,你可以编辑文件并进行必要的更改。
比如,考虑清单2-7所示的Python文件。
def cooking(): ham = True print(eggs) return order
在系统命令行中运行linter,如下所示:
pylint cooking.py
Pylint提供以下反馈:
************* Module cooking cooking.py:1:0: C0111: Missing module docstring (missing-docstring) cooking.py:1:0: C0111: Missing function docstring (missing-docstring) cooking.py:3:10: E0602: Undefined variable 'eggs' (undefined-variable) cooking.py:4:11: E0602: Undefined variable 'order' (undefined-variable) cooking.py:2:4: W0612: Unused variable 'ham' (unused-variable) ----------------------------------------------------------------------------------------------- Your code has been rated at -22.50/10
linter发现了代码中的5个错误:模块和函数都缺少docstring(请参阅第3章);试图使用变量eggs和order,但它们都不存在;为变量ham分配了一个值,但从未在任何地方使用过该值。
如果Pylint对你认为应该保留的代码行感到不满意,你可以告诉静态分析器忽略它并继续。你可以使用特殊的注释来做到这一点,注释可以在行内或块的顶部,如清单2-8所示。
# pylint: disable=missing-docstring def cooking(): # pylint: disable=missing-docstring ham = True print(eggs) return order
通过第一条命令,我告诉Pylint不要提醒我模块中缺少docstring,这将影响整个代码块。下一行的行内注释将抑制关于函数中缺少docstring的警告,它只会影响该行。如果再次运行linter,我只会看到另外3个linter错误:
************* Module cooking cooking.py:5:10: E0602: Undefined variable 'eggs' (undefined-variable) cooking.py:6:11: E0602: Undefined variable 'order' (undefined-variable) cooking.py:4:4: W0612: Unused variable 'ham' (unused-variable) ---------------------------------------------------------------------------------------------- Your code has been rated at -17.50/10 (previous run: -22.50/10, +5.00)
这时候,我会编辑我的代码并实际修复剩下的问题(除非我不想修复这些问题)。
你也可以通过在项目的根目录中创建一个pylintrc文件来控制Pylint在项目范围内的行为。要这样做,请运行以下命令:
pylint --generate-rcfile > pylintrc
找到这个文件,打开它,然后编辑它以打开和关闭不同的警告、忽略文件和定义其他设置。你可以从文件的注释中了解不同选项的作用。
当你运行Pylint时,它将在当前工作目录中查找pylintrc(或 .pylintrc)文件。或者,你也可以通过在调用Pylint时传递文件名(例如myrcfile)给--rcfile选项,从而指定一个不同的文件名,让Pylint从中读取设置,命令如下:
pylint --rcfile=myrcfile filetocheck.py
Pylint用户喜欢在他们的主目录中创建 .pylintrc或 .config/pylintrc(仅适用于类UNIX系统)。如果Pylint找不到另一个配置文件,它将使用主目录中的那个。
尽管Pylint文档不是很全面,但它仍然是有用的。
Flake8工具实际上是以下3个静态分析器的组合。
● PyFlakes是一个linter,与Pylint相似,旨在更快地工作并避免误报(这两点都是Pylint常被吐槽的地方)。此外,它还忽略了由下一个工具处理的样式规则。
● pycodestyle是一个风格检查器,它可以帮助确保你编写符合PEP 8的代码。(这个工具以前叫作pep8,但是它被重命名了,以避免与实际的风格指南混淆。)
● mccabe用于检查代码的McCabe(或Cyclomatic)复杂性。如果你不知道这是什么,不要担心——它的作用实际上只是在你的代码结构变得太复杂时警告你。
你可以使用pip安装Flake8包,我通常在虚拟环境中这样做。
为了扫描文件、模块或包,请将其传递给flake8命令行。例如,要扫描之前的cooking.py文件(见清单2-8),可以使用以下命令:
flake8 cooking.py
输出的内容如下:
cooking.py:2:5: F841 local variable 'ham' is assigned to but never used cooking.py:3:11: F821 undefined name 'eggs' cooking.py:4:12: F821 undefined name 'order'
你会注意到Flake8并没有报告缺少文档字符串,这在该工具中默认是禁用的。
默认情况下,命令flake8只运行PyFlakes和pycodestyle。如果要分析代码的复杂性,则还需要传递参数--max-complexity,参数名后跟一个表示代码复杂程度的数字。大于10的数字表示代码十分复杂,但是如果你理解McCabe复杂性,你也可以根据需要更改它。因此,如果要检查cooking.py文件的复杂性,你可以运行以下命令:
flake8 --max-complexity 10 cooking.py
然而,无论你如何运行Flake8,你都会得到代码中所有错误和警告的详细列表。
如果需要告诉Flake8忽略它认为是问题的某些内容,你可以使用 # noqa注释,后跟要忽略的错误代码。这个注释应该是行内注释,放在错误发生的那一行。如果你省略了错误代码,那么 # noqa将导致Flake8忽略该行的所有错误。
在我的示例代码中,如果我想忽略收到的错误,则代码可能看起来如清单2-9所示。
def cooking(): ham = True # noqa F841 print(eggs) # noqa F821, F841 return order # noqa
这里,你会看到3种不同的情况。首先,我只忽略警告F841。其次,我忽略了两个错误(其中一个实际上并不会被触发,仅作为示例)。最后,我忽略了所有可能的错误。
Flake8还支持配置文件。在项目目录中,你可以创建一个 .flake8文件。该文件中的内容以 [flake8]开头,然后是你想要定义的所有Flake8设置。
Flake8也接受名为tox.ini或setup.cfg的项目范围的配置文件,只要它们内部有一个 [flake8]部分即可。
比如,如果你想要在每次调用Flake8时自动运行mccabe,而不是每次都指定--max-complexity,你可以定义一个 .flake8文件,如清单2-10所示。
[flake8] max-complexity = 10
一些开发者喜欢为Flake8定义一个系统范围的配置文件,你可以在类UNIX系统中这样做(也只能在类UNIX系统中这样做)。在你的主文件夹中,创建名为 .flake8或 .config/flake8的配置文件。
Flake8相较于Pylint更重要的优势之一是文档。Flake8有警告、错误、选项等的完整列表。更多信息可以查看Flake8官方文档。
Mypy是一个不同寻常的静态分析器,因为它完全专注于类型注解(见第6章)。因为涉及我还没有介绍的许多概念,所以我不会在这里深入介绍。
就像到目前为止的所有包一样,你可以通过pip安装mypy包。一旦安装完毕,你就可以通过向其传递文件、包或模块来使用Mypy,命令如下:
mypy filetocheck.py
Mypy只会尝试检查带有类型注解的文件,而忽略其他文件。
另一种你可能会发现有用的工具是自动格式化工具(autoformatter),它可以自动更改你的Python代码——空格、缩进和首选的等效表达式(例如使用!=而不使用< >)——以符合PEP 8。此处推荐的自动格式化工具有两个:autopep8和Black。
autopep8基于pycodestyle(Flake8的一部分),其甚至使用与该工具相同的配置文件来确定最终遵循或忽略哪些样式规则。
和之前一样,你可以使用pip安装autopep8。
默认情况下,autopep8只会修复空格,但是如果你传递--aggressive参数给它,它就会做出更多的改变。实际上,如果你传递这个参数两次,它做出的改变会更多。限于篇幅,请在PyPI官网查看autopep8 2.0.4项目以了解更多信息。
为了修复Python代码文件中的大多数PEP 8问题,且在原地更改(而不是创建副本,这是默认行为),请运行以下命令:
autopep8 --in-place --aggressive --aggressive filetochange.py
直接修改文件听起来有点冒险,但实际上风险不大。样式更改只会改变样式,而不会影响代码的实际行为。
Black相较于autopep8更加简单:它假设你想要完全遵循PEP 8,因此它不会提供太多的选项而让你感到困惑。
就像autopep8一样,你可以使用pip安装Black,尽管它需要Python 3.6或更高版本。要使用它格式化文件,请传递文件名,命令如下:
black filetochange.py
可通过执行命令black --help来获取完整的Black命令行选项。
测试框架对任何良好的开发工作流程来说都是重要组成部分,但我不会在本章详细介绍它们。Python有3个主要的测试框架:Pytest、nose2和unittest。还有一个很有前途的新项目叫作ward。所有这些都可以使用pip来安装。
因为必须了解更多的Python知识才能有效地研究测试框架这个主题,所以我将在第20章重新对此进行介绍。
你已经有了Python解释器、虚拟环境、静态分析器和其他工具。现在你已经准备好写代码了。
你可以利用任何普通文本编辑器来编写Python代码,就像你使用任何其他编程语言一样。但是,合适的代码编辑器可以让你更轻松地编写生产质量的代码。
在结束本章之前,我想带你了解几个受欢迎的Python代码编辑器和可用的集成开发环境(IDE)。此处仅作示例推荐,除此之外还有许多其他选择。如果你已经知道自己想要使用什么代码编辑器或IDE,可以跳过这一节。
Python有自己的IDE,名为IDLE,它随标准的Python发行版一起被安装。它是一个相当简单的IDE,有两个组件:一个编辑器和一个交互式shell的界面。如果你现在不想安装其他编辑器,使用IDLE也不错。但我建议你还是好好考虑一下,因为大多数编辑器和IDE有许多有用的功能,而IDLE没有。
如果你是纯粹的“黑客”,那么你应该会很高兴知道Emacs和Vim都有很好的Python支持。设置这两个编辑器不是那么容易,所以我不会在这里介绍它们的任何内容。
如果你已经是Emacs或Vim的爱好者(或者两者你都喜欢),你可以在Real Python上找到关于它们的优秀教程。
对于Emacs,请查阅Real Python网站中的文章“Emacs: The Best Python Editor?”(“Emacs:最好的Python编辑器”)。
对于Vim,请查阅Real Python网站中的文章“VIM and Python-A Match Made in Heaven”(“VIM和Python——天作之合”)。
根据JetBrains的“开发者生态系统2021”(The State of Developer Ecosystem 2021)开发者调查,JetBrains的PyCharm IDE是Python编程方面最受欢迎的选择。它有两种类型:免费的PyCharm Community Edition(社区版)和付费的PyCharm Professional Edition(专业版)。
两个版本的PyCharm都提供专门的Python代码编辑器,具有自动完成、重构、调试和测试工具。PyCharm可以轻松管理和使用虚拟环境,并与你的版本控制软件集成,它甚至可以执行静态分析(使用它自己的工具)。专业版增加了用于数据、科学开发和Web开发的工具。
如果你熟悉其他JetBrains IDE,比如IntelliJ IDEA或CLion,你会发现PyCharm就是一个很好的Python IDE。它需要比许多代码编辑器更多的计算机资源,但如果你有一台性能不错的计算机,这不会成为问题。如果你之前没有使用过JetBrains IDE,请在购买付费版本之前尝试社区版。
你可以在PyCharm官网查看更多信息和下载方式。
Visual Studio Code有着非常棒的Python支持。根据2021 JetBrains调查,它是第二受欢迎的Python代码编辑器。它是免费且开源的,几乎可以在所有平台上运行。安装完来自Microsoft的官方Python扩展,你就可以开始了!
Visual Studio Code支持自动完成、重构、调试和虚拟环境切换,以及通常的版本控制集成。它可以与Pylint、Flake8和Mypy等流行的静态分析器集成,甚至可以与最常见的Python单元测试工具一起使用。
请从Visual Studio Code官网下载安装包。
Sublime Text是另外一个流行的多语言代码编辑器。它因速度快和简单而受到欢迎,并且可以通过扩展和配置文件轻松定制。Sublime Text可以免费试用,但如果你发现自己喜欢上了它并希望继续使用,则建议付费购买。
Anaconda插件将Sublime Text转换成了一个Python IDE,它具有Sublime Text的所有功能:自动完成、导航、静态分析、自动格式化、测试运行,甚至还有文档浏览器。与其他代码编辑器相比,它需要更多的手动配置,特别是当你想要使用虚拟环境时。如果Sublime Text符合你的要求,那么安装Anaconda插件就是值得的。
可以从Sublime Text官网下载Sublime Text,并从GitHub的DamnWidget/anaconda项目中下载Anaconda插件(其中还提供了在Sublime Text中安装Anaconda插件的说明)。
如果你的关注点是科学编程或数据分析,或者你熟悉MATLAB的界面,那么你在Spyder中会感到非常舒适,这是一个免费且开源的Python IDE,也是用Python编写的。
除了通常的功能(专用的Python代码编辑器、调试器、与静态分析器的集成和文档查看器),Spyder还包括与许多常见的Python库的集成,用于数据分析和科学计算。它集成了完整的代码分析器和变量资源管理器。其插件支持单元测试、自动格式化和编辑Jupyter笔记本等其他功能。
你可以从Spyder官网下载Spyder。
Eclipse已经失去了与新编辑器竞争的地位,但它仍然拥有忠实的用户群。虽然是针对Java、C++、PHP和JavaScript等语言而设计的,但Eclipse也可以通过PyDev插件成为Python IDE。
如果你已经安装了Eclipse(它是完全免费的),那么只需要从Eclipse Marketplace安装PyDev插件即可。
或者你也可以安装LiClipse,它将Eclipse、PyDev和其他有用的工具捆绑在了一起。你可以在30天内免费使用LiClipse,之后你必须购买许可证。你可以从LiClipse官网下载LiClipse。
PyDev提供自动完成、重构、对类型提示和静态分析的支持、调试、单元测试集成以及许多其他功能。你可以在PyDev官网找到有关PyDev的更多信息。
Eric可能是这里列举的最古老的IDE,但它仍然像以前一样可靠。Eric是用Python编写的免费且开源的IDE。
Eric提供了编写Python代码所需的一切功能:自动完成、调试、重构、静态分析、测试集成、文档工具、虚拟环境管理等。
你可以在Eric Python IDE的官网找到关于Eric的信息并下载它。
在设置好开发环境、项目和IDE之后,你现在可以专注于把你的代码写得尽可能好。
此刻,你应该已经拥有一个适用于任何生产级项目的Python开发工作台,或者至少应该已经安装了Python解释器、pip、venv、一个或多个静态分析器,以及一个Python代码编辑器。
现在,为了在阅读本书时进行实验,请在你的代码编辑器或IDE中,创建一个FiringRange项目。为了确保一切正常,你可以在该项目中创建一个Python文件,如清单2-11所示。
#!/usr/bin/env python3 print("Hello, world!")
执行以下命令:
python3 hello_world.py
你将会看到如下输出:
Hello, world!
我将在第4章介绍Python项目的正确结构,但是在FiringRange项目中编写和运行Python文件的前提是必须满足第3章的要求。
如果你是新手,花几分钟时间熟悉一下你选择的IDE。你尤其应该确保自己知道如何导航和运行代码、管理文件、使用虚拟环境、访问交互式控制台,以及使用静态分析器。