PyCharm-应用开发实用指南-全-
PyCharm 应用开发实用指南(全)
原文:
zh.annas-archive.org/md5/32117da21b20b2d3bae9d0e710aaee68
译者:飞龙
前言
欢迎来到 PyCharm 的 Python 编程世界!在这本书中,我们开始了一段通过 PyCharm 集成开发环境所赋予的灵活和动态的 Python 开发领域的旅程。无论您是刚开始编码冒险的新手程序员,还是希望提升 Python 技能的资深开发者,这本书都是为了成为您的可靠伴侣而设计的。
Python 已成为最受欢迎和多功能性的编程语言之一,以其简洁性和可读性著称。凭借其丰富的库和框架生态系统,Python 被广泛应用于各种应用,从 Web 开发、数据分析到人工智能和科学计算。由 JetBrains 开发的 PyCharm 是领先的 Python IDE,它为程序员提供了一套强大的工具和功能,以实现高效的代码开发、调试和协作。
在接下来的章节中,我们将探讨 PyCharm 的基础知识,深入研究高级配置功能,并利用 PyCharm 的专业版来简化您的编码工作流程。无论您是希望构建 Web 应用、自动化任务、分析数据还是开发机器学习模型,这本书都将为您提供将想法变为现实的知识和技能。
我们的目的是让您的 Python 编程之旅不仅具有教育意义,而且充满乐趣。在整个书中,我们提供了实用的示例、动手练习和真实世界的项目,以加强您的理解并激发您的创造力。当您翻到最后一页时,您将拥有信心和专业知识,使用 PyCharm 作为您的单一首选工具来应对各种规模和复杂性的 Python 项目。
因此,让我们共同踏上这场激动人心的冒险之旅,一起揭示 Python 编程的美丽,利用 PyCharm 的力量将您的编码愿望转化为实际成果。祝您编码愉快!
这本书面向的对象
这本书是为对 Python 编程感兴趣并希望利用 PyCharm 集成开发环境(IDE)来提升编码体验的多元化受众设计的。以下是这本书主要针对的人群:
-
初学者程序员:如果您是编程新手或编码经验有限,这本书提供了对 Python 和 PyCharm 的温和介绍。PyCharm 本身通过提供大量帮助,如设置项目模板、提供自动完成和自动 PEP-8 格式化代码等功能,使得 Python 更容易学习。
-
中级 Python 开发者:如果你已经有一些 Python 经验,但想深化你的知识和技能,这本书以及 PyCharm 可以帮助你。PyCharm 的可配置代码检查器和代码分析技术将你的工作保持在最高标准。重构,这种常常被忽视的实践,因为 PyCharm 的索引和重构工具而变得简单。你将学会使用集成测试、覆盖率分析和性能分析工具,以确保你的代码运行迅速且可靠。
-
其他语言经验丰富的开发者:如果你是其他语言的经验丰富的程序员,并希望过渡到 Python 或将其纳入你的技能组合,这本书将帮助你弥合差距,并使用 PyCharm 掌握 Python 编程。如果你使用过其他 PyCharm IDE,这一点尤其正确。如果你通常使用 IntelliJ Idea、WebStorm、Rider 或 PHP Storm,你将感到宾至如归,因为你将使用相同的键盘快捷键。如果你使用 Visual Studio,你可以轻松配置 PyCharm 以使用你习惯的键盘快捷键,我们认为你会发现 PyCharm 中的常见工作流程,如与 Git 一起工作,更优越且更直观。
-
学生和教育工作者:Python 是教学和学习编程的流行语言。本书可以作为学生作为课程学习 Python 的有价值资源,也可以作为寻找有效使用 PyCharm 教授 Python 的全面指南的教育工作者的重要资源。
-
数据科学家和分析师:Python 在数据分析领域和机器学习领域被广泛使用。本书涵盖了数据探索、操作、清洗和分析的高级库和工具,对于寻求在 PyCharm 环境中提高 Python 技能的数据专业人士来说非常有价值。PyCharm 包含了一套强大的工具,用于处理关系型和非关系型数据库,本书对此进行了全面介绍。
-
Web 开发者:如果你对使用 Python 进行 Web 开发感兴趣,本书涵盖了 Web 框架和工具,使你能够使用 PyCharm 作为开发工具创建动态 Web 应用。你将学会在流行的 Web 开发框架如 Flask、FastAPI 和 Django 中创建项目。大多数人没有意识到,PyCharm 专业版包含一个针对 JavaScript 和 HTML 开发的完整 IDE。本书对此进行了详细说明。
-
对 Python 和 PyCharm 感兴趣的任何人:如果你对编程、技术,特别是 Python 有一般兴趣,这本书提供了对 Python 功能和 PyCharm 特性的引人入胜的探索,使其对广泛的读者既易于理解又富有信息量。
无论你的背景或专业水平如何,本书旨在为任何渴望学习 Python 编程并利用 PyCharm 的力量编写高效、可读性和可维护的代码的人提供宝贵的资源。
本书涵盖内容
第一章, PyCharm 入门,最受欢迎的 Python IDE:在本章中,我们将讨论未来的道路。
第二章, 安装和配置:本章介绍了安装过程,并提供了有关根据您的特定开发风格自定义 PyCharm 的说明。
第三章, 自定义解释器和虚拟环境:Python 生态系统的一个非常有用的特性是能够沙盒化您的项目。PyCharm 提供了一个以项目为中心的图形工具来管理您的项目和相关的解释器和虚拟环境。
第四章, 在 PyCharm 中轻松编辑和格式化:任何优秀 IDE 的核心是其编辑器。本章提供了一个坚实的基础。
第五章, 在 PyCharm 中使用 Git 进行版本控制:你可以在 IDE 中图形化地完成通常在命令行上进行的所有操作。本章将向你展示如何操作。
第六章, 无缝测试、调试和性能分析:PyCharm 直接在 IDE 中支持多种单元测试框架。你将学习如何在 PyCharm 中编写测试并可视化结果。
第七章, 使用 JavaScript、HTML 和 CSS 进行 Web 开发:PyCharm 是一个完整的全栈开发环境。因此,你将学习在 PyCharm 中开发 HTML、JavaScript 和 CSS。我们将简要介绍一些前端框架,如 HTML 模板、Bootstrap 和 React。
第八章, 使用 Flask 构建动态 Web 应用:Flask 是一个无意见的框架,用于构建能够提供动态内容的 Web 应用。PyCharm 使这变得非常简单。
第九章, 使用 FastAPI 创建 RESTful API:在本章中,你将学习如何使用 FastAPI 创建 RESTful API。你还将学习如何使用 PyCharm 内置的 HTTP 请求和测试框架来测试 API。
第十章, 更多全栈框架:Django 和 Pyramid:PyCharm 包含针对 Django 的专用工具,它是 Python 中最受欢迎的 Web 框架之一。我们还将简要介绍 Pyramid 框架,它旨在比 Django 更简单,但比 Flask 更完整。
第十一章, 理解 PyCharm 中的数据库管理:PyCharm 包含一个功能齐全的数据库 IDE,可帮助您与数十种关系型和非关系型(NoSQL)数据平台协同工作。
第十二章, 开启科学模式:你将学习 PyCharm 科学模式的基础,这是其数据科学工具的核心。
第十三章,使用 SciView 和 Jupyter 动态查看数据:您将学习如何利用在多阶段数据管道的每个步骤中查看数据的能力,这在评估数据时非常有价值。PyCharm 支持一个高级查看器,可以渲染 NumPy 和 Pandas 数据结构。
第十四章,在 PyCharm 中构建数据管道:PyCharm 拥有您进行高级科学数据分析所需的一切。在本章中,我们分析了一项旨在预测早期阿尔茨海默病的科学研究。
第十五章,使用 PyCharm 插件拓展更多可能性:JetBrains IDEs 中的许多功能都是通过插件实现的。JetBrains 市场允许您通过更多专业化的功能来增强您的 PyCharm 安装。
第十六章,未来发展方向:JetBrains 并未停滞不前。PyCharm 正在快速发展。本章向您展示了在撰写本书时正在积极开发的一些功能。
为了充分利用本书
我假设您已经理解了基本的 Python 编程以及您喜欢的操作系统的基本命令行技能。在整个过程中要记住的一点是,我们在这里介绍的是 PyCharm,而不是第几章中提到的各种框架的深度开发教程。例如,第八章介绍了为 Flask 开发设计的 PyCharm 功能。它并不是一个完整的 Flask 教程。
本书涵盖的软件/硬件 | 操作系统要求 |
---|---|
Python 3 | Windows、macOS 或 Linux |
PyCharm 专业版 | Windows、macOS 或 Linux |
Docker 桌面版 | Windows、macOS 或 Linux |
Git | Windows、macOS 或 Linux |
本书的大部分内容需要 PyCharm 的专业版。前六章可以使用社区版,但之后您必须拥有专业版。
如果您正在使用本书的数字版,我们建议您亲自输入代码或从本书的 GitHub 仓库(下一节中提供链接)获取代码。这样做将帮助您避免与代码复制粘贴相关的任何潜在错误。
下载示例代码文件
您可以从 GitHub 下载本书的示例代码文件github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition
。如果代码有更新,它将在 GitHub 仓库中更新。第二章介绍了使用 PyCharm 的集成 Git 客户端克隆仓库。
我们还提供了来自我们丰富的书籍和视频目录中的其他代码包,可在github.com/PacktPublishing/
找到。查看它们吧!
使用约定
本书使用了多种文本约定。
文本中的代码
:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“我们使用corr()
方法计算此数据集的相关矩阵。”
代码块设置如下:
# Compute and show correlation matrix
corr_mat = df.corr()
plt.matshow(corr_mat)
plt.show()
任何命令行输入或输出都如下所示:
$ mkdir css
$ cd css
粗体:表示新术语、重要单词或您在屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“如果您点击查看数组链接,也可以通过右键单击变量激活,您可以在数据面板中看到一个类似电子表格的表格。”
小贴士或重要提示
看起来是这样的。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果您对本书的任何方面有疑问,请通过电子邮件发送给我们 customercare@packtpub.com,并在邮件主题中提及书名。
勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在此书中发现错误,我们将不胜感激,如果您能向我们报告此错误。请访问www.packtpub.com/support/errata并填写表格。
盗版:如果您在互联网上以任何形式遇到我们作品的非法副本,如果您能提供位置地址或网站名称,我们将不胜感激。请通过copyright@packt.com与我们联系,并提供材料的链接。
如果您想成为一名作者:如果您在某个领域有专业知识,并且对撰写或参与一本书籍感兴趣,请访问authors.packtpub.com。
分享您的想法
一旦您阅读了《动手使用 PyCharm 进行应用开发》,我们很乐意听听您的想法!请点击此处直接进入此书的亚马逊评论页面并分享您的反馈。
您的评论对我们和科技社区都很重要,并将帮助我们确保我们提供高质量的内容。
下载此书的免费 PDF 副本
感谢您购买此书!
您喜欢在移动中阅读,但无法携带您的印刷书籍到处走吗?
您的电子书购买是否与您选择的设备不兼容?
别担心,现在每购买一本 Packt 书籍,您都可以免费获得该书的 DRM 免费 PDF 版本。
在任何地方、任何设备上阅读。直接从您最喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。
优惠不会就此结束,您还可以获得独家折扣、时事通讯和每日免费内容的每日电子邮件。
按照以下简单步骤获取好处:
- 扫描下面的二维码或访问以下链接
packt.link/free-ebook/9781837632350
-
提交您的购买证明
-
就这些!我们将直接将您的免费 PDF 和其他福利发送到您的电子邮件
第一部分:PyCharm 的基础知识
本部分向读者介绍 PyCharm,并提供详细的操作指南,说明如何下载、安装以及开始使用 PyCharm 进行 Python 项目开发。
本部分包含以下章节:
-
第一章,PyCharm 简介,最受欢迎的 Python IDE
-
第二章,安装和配置
第一章:PyCharm 简介 – 最受欢迎的 Python 集成开发环境
欢迎来到《PyCharm 实战应用开发》的第二版!大多数程序员的目标是构建健壮、高质量的软件,这些软件能够经受时间的考验。实现这一目标最重要的步骤是选择正确的语言。在这么多语言中,哪一种是最好的选择呢?一位杰出的程序员会考虑很多关于语言的因素。在编程语言中需要考虑的最重要方面之一是开发阶段所需的工具支持。据说 Python 编程语言与其他许多语言相比,能够极大地提高生产力。Python 著名的 batteries included 哲学通过捆绑一个强大的标准库、一个代码编辑器和调试器来体现这一理念。所有这些都被整合到了语言的正常安装程序中,可以从 www.python.org
获取。但至少对我来说,有一个小问题——微软。
我知道你们在想什么。你们已经在心理上准备好听一位那些 Unix/Linux 的人抱怨微软这个大坏蛋了。但我不打算这么做,因为我并不确定自己是不是一个 Linux 的人。我的意思是,我确实有很多条卡其裤在衣柜里。我无法控制自己。它们真是太宽松了,你可以携带所有东西而不需要拖着包走来走去。我还要承认,我有很多带有徽章、标志或可能只有我遇到的 5%的人能理解的说法的 T 恤。这些 T 恤非常有趣,但我得到的唯一笑容都是来自我的同事。越想越觉得,我并不是一个 Linux 的人。对我来说,Linux 只是一个工具。有时候,它是正确的选择,但有时候,它并不是。既然我不是一个 Linux 迷,那么这不能成为我声称微软是问题的原因。真正的理由正好相反。大约 30 年前,微软做了一件非常正确的事情。他们创造了第一个真正好的商业化的 集成开发 环境 (IDE)。
事实上,这可能是 30 多年前的事情了,也许在此之前就有其他类似的产品。然而,今天软件行业的许多“资深开发者”都是从微软的Visual Basic(VB)产品开始起步的。好吧;这就是那些语言精英们开始嘲笑并皱起鼻子,好像刚刚被端上了一盘卷心菜,或者可能是一块脏尿布,但让我们回到正题。30 年前,那些拥有家用电脑的人只有少数几个选择。所有用途符号指令代码(BASIC)几乎与 1978 年以后的每台计算机一起发货。那是一个连苹果公司都没有在其操作系统(OS)上提供图形用户界面(GUI)的时代。这种情况直到 1983 年苹果发布 Lisa 才改变。我们有了鼠标,可以创建能够与指针设备协同工作的程序,但操作系统没有窗口系统。当时,计算机一次只能运行一个程序,所以不需要窗口系统。
为那些缺乏 Windows 操作系统级别支持的计算机编写桌面软件是件困难的事情。没有软件开发工具包(SDK)或应用程序编程接口(API)来处理任何繁重的工作。编写软件几乎是一项单调乏味的练习。你必须在几乎和记事本一样糟糕的工具中编写数百行框绘制样板代码。然而,就在 1991 年,也就是我从俄克拉荷马大学毕业后的一年,一切都变了。
微软发布了一个版本的 BASIC,它允许在开发环境中直接创建桌面 GUI。他们称之为Visual Basic。最初版本在微软的磁盘操作系统(MS-DOS)上运行,但后来我们得到了对 Windows、Windows 2 和 Windows 3.1 的支持。Windows 3.1 的重要性在于,那时如果我们的 PC 配备了 80386 处理器,我们就能得到真正的多任务处理。PC 不再受限于一次只能运行一个程序,Windows 操作系统使得鼠标驱动的交互变得无处不在。
在 VB(Visual Basic)时代,事情变得有趣起来。你不再需要编写接口代码,而是直接绘制界面。IDE(集成开发环境)中包含了一系列组件和窗口。你可以在窗口上直接绘制按钮、文本框以及其他你需要的东西。绘制完成后,你再用事件处理器将它们“连接”起来。你绘制的内容就是程序运行时显示的内容。VB 的用户界面(UI)最终被整合进了微软的 Visual Studio。即使今天,Visual Studio 2022 仍然保留了 1991 年那些开创性的功能。图 1.1展示了用于绘制 Windows 可视化 UI 的工具集:
图 1.1:Visual Studio IDE 起源于 1991 年的 Visual Basic 产品。它定义了良好 IDE 应该具备的标准
那个开启我职业生涯的 VB3 IDE 引入了更多革命性的特性,我的那些自负、穿着工装裤的 Unix 同事只能梦想拥有。他们还在争论 vi 和 Emacs 谁更优越,或者反过来,这取决于你问的是谁。与此同时,VB3 拥有彩色语法高亮、支持编辑多个文件、用于绘制按钮和其他屏幕小部件的图形界面编辑器,以及将代码、事件和 GUI 元素结合在一起的视觉编程工具。它有一个调试系统,你可以通过简单地点击行号来使用。这样做会在代码中创建一个红色圆点——一个在测试运行期间执行会停止的断点,允许开发者检查正在运行的程序的状态。这是一个纯粹的程序员乌托邦!无论你喜欢还是讨厌,微软的 VB IDE 定义了今天 IDE 应该是什么样子。任何使用微软 IDE 学习编码的人,无论是传统语言还是现代语言,都不愿意接受低于那种体验的东西。
自从我开始学习每一种语言以来,我总是首先寻找提供我无法离开的特性的最佳 IDE。大约六年前,当我开始使用 Python 3 时,我发现了 PyCharm。我用它对一个复杂的软件即服务(SaaS)产品进行了全面的重写,这个过程花费了我大约 18 个月的时间。这是一次真正的火炼。在这本书中,我打算分享我所学到的知识,包括所有的经验教训。
在整本书中,我们将学习 PyCharm IDE 的一般界面,以及自定义和扩展,以帮助您将工具适应您将使用 Python 进行的类型的工作。第一章讨论了 IDE 的一般优点。我将提供最常用于 Python 开发的工具的比较。其中一些非常好,而其他一些,尽管被广泛使用,但相当原始。
在本章中,我们将涵盖以下主要主题:
-
PyCharm 作为 Python IDE 的目的以及其开发公司 JetBrains 的一些显著细节
-
PyCharm 在社区中的使用情况以及哪些职业倾向于最频繁地使用 PyCharm
-
一份关于使用 PyCharm 相对于其他 Python 编辑器/IDE 的优缺点全面概述
-
PyCharm 专业版和社区版之间的区别以及付费版提供的额外功能
另一方面,如果你已经决定 PyCharm 是你需要的 Python IDE,那么请随意跳转到第二章,安装和配置 PyCharm,开始安装和注册过程。如果你已经下载并在你的系统上成功设置了 PyCharm,你可能想从本书的第二部分开始,从第三章,自定义解释器和虚拟环境开始。
技术要求
本章是入门性的,所以我们不会编写代码,技术要求为零。这是第一章,我知道你们都热情洋溢,准备出发,零是无聊的。所以,让我们开始吧!
首先,这是你成功使用本书所需的东西:
-
一台电脑。我知道!这很明显,但我以我的全面性而自豪,不留任何余地!
-
操作系统。如果它已经安装在你的电脑上,这将是最有效的,因为我们不会在这本书中介绍如何安装它。Windows、macOS、Linux——就本书而言,它们都是一样的,因为 PyCharm 在这三个环境中都能运行,并且每个环境的 UI 几乎相同。
-
安装 Python。在这本书中,我们将仅使用 Python 3。Python 3 有几个不同的“风味”,但大部分情况下,从
www.python.org
下载的普通 Python 3 就足够了。我们将在第三章,自定义解释器和虚拟环境中讨论这些“风味”。如果修订号让你感到安心,我在写这本书时的最新版本是 3.11.1。我之前提到的那个生产 SaaS 应用中使用的 Python 版本是 3.8。如果你的 Python 3 安装版本比这更旧,你应该更新它。 -
在某个时候,拥有一个GitHub账户可能会很有用,因为我会使用Git仓库分享书中的代码。
Python 的持续成功
在本书的第一版中,作者将这一节标题为Python 的近期兴起。时间已经过去,我将继续他留下的部分。我认为指出近期的兴起自本书第一版出版以来大致持续至今是很重要的。Python 继续成为最受欢迎和广泛采用的语言之一,有非常好的理由。其中之一是 Python 强调可读性并使用简单的语法。这使得语言的新手,以及软件开发领域的初学者,能够快速成功。与此相对比的是,之前通常的做法是强迫大学生和大学生学习 C 或 C++作为他们的第一语言。这些语言简洁而复杂,通常在开发者生产力方面有着糟糕的记录。当然,C 和 C++是强大的语言,可以产生最高性能的软件。然而,根据我的经验,一种可以在短时间内从“Hello World”到能够快速产生有用软件的语言,在所有但最极端的情况下,都胜过性能的提升。Python 的创造者吉多·范罗苏姆在他的论文OMG-DARPA-MCC 关于组合软件架构的工作坊中将 Python 的快速与其他语言进行了比较。在论文中,范罗苏姆表示,Python 的开发速度估计比 Java 快 3-5 倍,比 C/C++快 5-10 倍。考虑到这个差异,我们可以很容易地理解为什么 Python 被如此广泛地采用。毕竟,时间就是金钱。你可以在这里找到吉多·范罗苏姆的完整文章:www.python.org/doc/essays/omg-darpa-mcc-position/
。
将 Python 与 Java 或 C/C++进行比较是微不足道的,因为这些语言是为不同的应用程序设计和使用的。当需要非常高的性能时,会使用 C 和 C++。大多数操作系统都是用 C++编写的,包括你在特斯拉汽车或现代航天器中找到的实时系统。比较 Python 和 C++的具体生产力并不一定公平,因为它们不是用来制作相同类型的应用程序的。
Java,另一方面,被用于开发你可能使用 Python 来开发的相同类型的应用程序:企业级和 Web 应用程序。然而,Java 需要大量的模板代码。这意味着开发者必须编写大量的代码和结构,仅仅是为了支持应用程序的存在,他们才能开始考虑为该应用程序本身编写代码。Python 中几乎没有这种模板代码。此外,Java 依赖于一个非常严格、静态的面向对象范式。相比之下,Python 要灵活得多,提供了一种动态的编程模型。尽管这两种语言都用于制作相同类型的应用程序,但 Python 由于其更灵活的范式,为你提供了一些严重的捷径。
这些构成 Python 强大优势的因素,以及许多其他因素,共同凝聚成了一种非常易于使用的开发语言,得到了一群狂热粉丝的支持。这个社区仍在通过将编程引入与我们历史上仅专注于传统应用开发截然不同的众多领域和职业中不断壮大。在撰写本文时,TIOBE 指数,一个用于编程语言流行度的排名系统,将 Python 排名为第一语言,如图 1.2所示:
图 1.2:TIOBE 排名显示 Python 是最受欢迎的语言
Python 拥有一个庞大的标准库,提供了你构建任何类型软件可能需要的任何东西。如果这个声明对你特定的项目不成立,那么有一个庞大的第三方和主要开源的生态系统,由数十万个库组成,你可以在此基础上构建。你可以在pypi.org
找到这些库的目录。将所有这些因素综合考虑,一个新软件开发者可以从零 Python 经验到生产应用程序非常快。这个过程可以通过一个好的 IDE 大大加速。
IDEs 的哲学
在我年轻时,情况是不同的。当然,除非我们同龄,那样的话一切都会相同。我们没有互联网。当我们想要学习新的编程语言和技术,或者理解我们手艺的历史时,我们必须进行一次神圣的朝圣之旅。有一年,我偷带了一台宝丽来相机。你可以在图 1.3中看到我拍的照片。你应该明白,我即将告诉你的所有内容都是真实的,同时也是被严格保守的行业秘密。为了明确起见,你没有从我这里听到这些。
在某个神秘的山脉深处,寻求伟大编程智慧的寻道者会在满月的光辉下攀登万级台阶,以寻找大师。这段旅程并不容易,所获得的智慧也必须是通过辛勤努力获得的。正是在这样的远征中,我学会了为什么好的 IDE 如此重要。大师说:“如果你懂得语言,又懂得 IDE,你就不必担心一百次部署的结果。”
图 1.3:在神圣的山脉之巅,沿着万级台阶,坐落着我学习编程的寺庙
大师经常用谜语说话,所以让我来解释一下。部署指的是软件的发布迭代或增量。在大多数专业情况下,目标是发布软件。如果我们的目标是发布,下一个难点是我们必须知道一种编程语言。我假设你对 Python 编程至少有一个默许的理解。那么,只剩下大师对 IDE 的引用了。
开发者可能会使用几种工具来开发 Python 代码。Python 语言可以被认为是一种解释型语言。我们可以争论,当它运行时,一些代码被优化成 C 代码并缓存,但在这个阶段,我们并不关心这个层面的细节。重点是 Python 程序以简单的纯文本文件的形式存在,并且可以以这种形式执行。这与静态编译语言如 C、C++、C#、Java 或 Go 形成对比。这些以及许多其他语言要求文本文件中的代码经过编译阶段,生成新的可执行文件。在 C#中,你不能简单地执行一个.cs
文件。你需要将其编译成二进制文件,然后执行该二进制文件。由于 Python 通过 Python 解释器直接执行其代码,因此用于 Python 的工具级别可以非常简单。基本上,任何文本编辑器都可以。可以选择三种编辑器功能级别。
第一种是简单的文本编辑器。简单的文本编辑器通常限于打开、编辑和保存文本。它们是通用工具,旨在与任何类型的文本文件一起工作,从购物清单到systemd
配置文件。在 Windows 上,你可能知道它叫做记事本。在 Mac 上,你可能使用TextPad,如果你在使用像systemd
配置这样的 Linux 桌面,别担心;它是一个 Linux 的系统管理文件。我只是需要一个听起来很复杂的东西来描述文本文件范围更复杂的一端。
编程编辑器的第二次进化被称为增强型编辑器。这些编辑器专门设计用于处理技术文件。以下是一些流行的例子:
-
Visual Studio Code
-
Atom
-
Notepad++
-
UltraEdit
-
Sublime Text
-
JetBrains Fleet
-
Bluefish 编辑器
-
IDLE(随 Python 一起提供的编辑器)
这些工具旨在与各种编程语言一起工作,并且通常可以轻松定制以添加对新兴语言的支持。增强型编辑器提供了一些使开发者的生活更加舒适的常见功能,例如以下这些:
-
语法高亮,它将代码中的关键字和其他语义元素着色。
-
宏,允许开发者记录和回放常见的快捷键
-
项目和文件组织,以便于在构成项目的多个文件之间轻松切换
-
基本的代码补全,以减少编写代码时所需的输入量
-
插件支持其他小工具,如代码检查器、拼写检查器、代码预览等更多功能
随着时间的推移,一些增强型编辑器已经变得非常健壮,因为你可以自定义和扩展它们的功能。当你考虑这些工具作为它们直接从盒子里出来时,它们比通用文本编辑器更有用和专业化,但它们不足以成为 IDE。
在代码编辑器的食物链顶端是IDE。如果你要从一战时期的战斗机驾驶舱内部看,你会看到一些简单的控制装置,再无其他。如果那是一个文本编辑器,IDE 就像是波音 747 飞机的驾驶舱。每个开发者可能需要的或渴望的工具都被塞进了一个相对复杂的用户界面中。IDE 包含了增强文本编辑器的所有功能,但通常还提供以下附加增强:
-
一些直接从编辑器运行代码的简单方法。
-
帮助管理你的源代码仓库的工具,例如Git或Subversion。
-
一个集成、易于使用的调试器,它允许你暂停正在运行的程序并检查或修改其当前状态。
-
帮助你编写自动化测试,如单元测试,并运行和可视化结果的工具。
-
复杂的代码补全基于项目代码的反射或索引。在现代 IDE 中,这通过使用人工 智能(AI)得到增强。
-
帮助你找到执行瓶颈的剖析工具。
-
集成工具,帮助管理辅助系统,如数据库。
-
从 IDE 中直接部署你的代码到服务器或云环境的工具。
一些 IDE 的流行例子包括以下:
-
Visual Studio(这与 Visual Studio Code 不同)
-
PyCharm
-
IntelliJ IDEA
-
NetBeans
-
Apple Xcode
-
Xamarin Studio
-
Eclipse
如你所见,IDE 是你编码武器库中最强大的武器。使用最好的 IDE 对你来说非常重要。如果你是软件开发的新手,或者甚至不是那么新手,你可能想知道为什么增强型编辑器如此受欢迎。在撰写本文时,大约 50%的开发者使用 Visual Studio Code,但这不在我的 IDE 列表中。
许多开发者更喜欢一个更“轻量级”的开发环境。这对于前端 Web 开发者来说尤其如此,他们发誓使用 Sublime Text 和 Visual Studio Code。事实上,他们需要 IDE 的所有功能,他们也确实在使用它们,但这些功能分散在他们每天使用的不同工具中。前端开发者依赖于在浏览器中运行的剖析器和调试器,他们不需要在 IDE 中这些工具。相反,他们可以获取一个更简单的编辑器,这个编辑器下载快速,安装简单,点击操作系统中的图标即可立即运行。
我认为,如果你在进行全栈 Web 开发或移动开发,或者需要处理服务器或容器,IDE 是一个更好的选择。
存在着一种特定的软件开发者,他们发誓你永远不应该使用除了最简单可能的工具以外的任何东西。他们认为,依赖工具来完成编码和相关的艰苦工作会减少被认为是熟练所需的总体掌握和成就。我完全不同意。有一年,在修道院的时候,大师给我讲了一个关于日本一位伟大的剑客宫本武藏的故事。在他那个时代,每个武士都知道宫本武藏是当时最伟大的剑客,所有的武士都想要尝试击败他。当时,决斗通常是生死之战。有一天,一个决斗挑战者在宫本武藏下船时遇到了他。宫本武藏没有带武器。挑战者等到宫本武藏从船的桅杆上制作出一把木剑,即bokken,他打算用它来进行决斗。据传说,宫本武藏让那个挑战者出丑,并且让他活着离开,这对挑战者来说是一种耻辱。大师说,宫本武藏是有史以来最优秀的战士,他的剑术至今无人能敌。然而,如果目标仅仅是击败他,我可以用机关枪轻易做到。
在我看来,因为对自己能力的自豪或缺乏自信而限制使用的工具是愚蠢的。软件开发者的目标是交付软件,通常是在一个不容宽恕的截止日期之前。这并不是为了试图证明自己符合别人的标准,除非,当然,你是寻求学位的学生。我相信这本书的读者中有一小部分人。玩好这个游戏,按照你的教授们说的去做。你应该意识到,一旦你毕业,一切都会改变。你将需要快速、准确、一致地编写代码。这最好通过一个好的集成开发环境(IDE)中可用的自动化来实现。你应该为手头的任务选择最好的工具。我发现 PyCharm 帮助我在学习 Python 语言时变得高效。当你开始时,如果你没有使用一个可以纠正你的行间距和缩进的编辑器,你将会犯很多愚蠢的错误。这很令人沮丧。我会想,“如果我现在用 C#,我现在就完成了。”我甚至想放弃 Python 和 PyCharm,去选择更舒适的东西。然而,那并不是我想做的。
PyCharm 会为你自动标记所有那些愚蠢的错误,并一键纠正!在我看到这些错误被反复标记后,我学会了在使用没有代码检查功能的编辑器时应该怎么做。如今,当我用其他语言工作时,我仍然使用 Python 规则。在 PyCharm 的帮助下学习 Python,我能够更快地交付、更快地学习,并在其他语言和工具中改进我的代码。请帮个忙,永远不要让任何人告诉你你不是真正的开发者,因为你没有按照他们的方式做事。如果他们坚持,告诉他们 nano 比 vi 或 Emacs 更好,然后离开。这样的声明可能会让他们的大脑炸裂。
我还想对 Visual Studio Code 说一点。这个编辑器通过插件的发展已经到了可以与功能齐全的 IDE 竞争的地步。然而,与 PyCharm 这样的专业开发 IDE 相比,这需要付出代价。要在 Visual Studio Code 中获得 PyCharm 中找到的相同功能,你需要安装大量的插件。这些插件都是由社区编写的,这意味着它们都是独立开发的工作。这些插件永远不会像 PyCharm 这样的 IDE 中的基础功能那样协同工作。当比较 Visual Studio 与 Visual Studio Code 时,这一点同样适用。尝试在 Visual Studio 和 Visual Studio Code 中创建 C# 项目,你会发现 Visual Studio 的过程梦幻般顺畅。另一方面,Visual Studio Code 需要进行大量的命令行操作和奇怪的插件安装。体验完全不同。对于其他编辑器,如可以高度定制的 vim,也有同样的观察。你可能会花一周的时间摆弄插件和开源脚本,以实现与 IDE 开箱即用功能的部分相似性。
PyCharm 作为 Python IDE
虽然谈论与其他语言通用的工具比较很好,但我们并不关心那个,对吧?我们想知道 Python 的选项!最好的 IDE 通常都是专业的。PyCharm 专注于与 Python 一起工作。仅此而已。当你使用 PyCharm 创建新项目时,你会看到 Python 项目的选项,没有其他选项。将这种体验与 Visual Studio 进行对比。在我看来,Visual Studio 是 Python 项目中 PyCharm 的唯一接近的竞争对手。在 Visual Studio 中创建项目时,你很可能会花上好几分钟试图浏览众多选项。该 IDE 支持数十种语言,并且由于有网页、桌面和其他十几种项目类型,这种支持被放大了。Visual Studio 正在试图成为所有开发者的万能工具。PyCharm 只想与 Python 开发者玩耍。
PyCharm 本身是在几个设计目标下创建的:
-
智能编码辅助
-
流程化编程工具
-
网页开发选项
-
科学计算支持
-
可视调试和性能分析
我们将依次查看这些设计目标,但首先,我需要指出一点。在我撰写这篇文章的时候,PyCharm 即将经历一次重大变革。JetBrains 正在开发全新的用户体验。到这本书出版时,有很大可能性这个新用户界面将成为默认界面。如果你是 PyCharm 的新用户,你应该知道你将暂时以两种不同的方式看到它。经典用户界面将在产品中继续存在一段时间,让我们能够逐渐适应新体验。鉴于第一版和这一版之间相隔几年,我决定我将接受新用户界面。话虽如此,值得一提的是,你将看到经典用户界面与新用户界面并排存在,直到可能到 2024 年晚些时候,当旧用户界面不再维护时。它将变得过时,并有一天会像雪莱传说中的奥西曼迪亚斯雕像一样消失在时间的沙尘中:
图 1.4:新用户界面(顶部)与经典用户界面(底部)的比较
图 1**.4 展示了两个用户界面并排。新用户界面的设计目标是减少界面上的杂乱。在这方面他们并没有错。随着工具这些年的发展,越来越多的功能被塞进了菜单,使得界面对于新用户来说有点令人畏惧。最重要的是要意识到,你会在菜单中找到的大部分内容仍然存在,但菜单系统本身隐藏在屏幕左上角的汉堡图标下面。别担心,我会在稍后详细说明这一点。在我撰写这篇文章的时候,有一个设置我们将在 第二章,安装和配置 PyCharm 中进行回顾,它允许你在经典用户界面和新用户界面之间切换。
我想现在就指出这一点,因为你们即将看到一些截图,如果你已经看到了旧用户界面,你可能会认为你拿错了书。你没有。恰恰相反。如果我能准确把握时间,你将是唯一拥有正确书籍的人。
智能编码辅助
我要告诉你我妻子经常说的一句话。我非常懒惰。等等。我说错了。她是在说我懒惰。我并不是说她是在说她自己懒惰。哎呀。写作太难了!我差点在那里挖了个坑,不是吗?
她并没有说错。作为一个开发者,我本质上非常懒惰。我拒绝花几个小时甚至几分钟去做一些事情。希腊有一个关于名叫西西弗斯的男人的传说,他被诅咒推一块石头上陡峭的山坡。一旦他到达山顶,石头就会滚回山脚下。西西弗斯被困在一个没有 Cmd/Ctrl + C 选项的无限循环中。
这里有一件事我知道:西西弗斯不是软件开发者!任何软件开发者都会把那块石头推上两次,然后他们会花费永恒的时间设计一套由物联网设备控制的滑轮和起重机系统。当然,微控制器会运行 Python 脚本。我跑题了。
我称之为懒惰的某些人(我在心里对着我那毫无戒心的妻子说),我称之为高效!作为一个开发者,我想在每一件事上都用最少的努力创造最大的效果。写代码是复杂的。你正在为最固执、最不智能的对象编写指令。编码比试图教一个两岁的孩子系鞋带还要糟糕。相信我,我两者都尝试过!编码者必须在对任何他们想要执行的操作的解释中非常具体和详尽。此外,事情变得更糟,因为一些开发者倾向于强迫用户编写大量的样板代码。我指的是与你想写的代码或你想解决的问题无关的冗余代码。Python 通常避免这种情况,所以让我给你举一个可能是最糟糕的例子:Java。
在我小时候,Java 可是风靡一时。有一群与 Java 相关的企业程序员,他们想出了叫做企业 JavaBeans(EJBs)的东西。EJBs 本应成为模块化编程和可重用对象的典范。实现起来相当费劲。你不仅要创建一个简单的类,这是你唯一需要的,还要创建一个特殊的文件结构,包括各种文件夹和清单文件,以暴露 bean 中的内容,然后所有内容都要编译成一种特殊格式。结果发现,这种特殊格式不过是一个 ZIP 文件。仅仅为了创建一个 EJB 就需要做很多工作,这意味着开发者必须创建大量的文件和编写大量的代码,才能开始实现他们需要表达的功能。这就是我们所说的样板代码。样板代码通常是无用的,但又是必要的,因为没有它,代码就无法工作。
所有 IDE 都是因为这个现象而演变的。PyCharm 是从 JetBrains 的 Java IDE IntelliJ 演变而来的。Python 通常不需要很多样板代码来使代码工作,但确实会出现。样板代码有两种。使老式的 EJBs 工作的样板代码是坏的那一种。作为启动项目的一种手段而生成的样板代码是好的那一种。正如我们将看到的,PyCharm,就像大多数 IDE 一样,会生成一个文件夹结构,一组文件和一些基本代码,以帮助你开始。这可以被认为是样板代码。但在这个例子中,这些代码并没有保留。它们被你的项目的真实代码所取代。IDE 生成的代码只是给你一个心理提示,让你开始。它防止你必须手动创建项目的起点。
所有这些都很棒,但样板代码生成并不是我们通常在听到“智能编码辅助”时想到的。我们通常想到的是微软开创的名为IntelliSense的功能。如果你允许我暂时将 IDE 拟人化,这个功能会观察你输入代码的过程。在此期间,IDE 会思考你试图做什么。当它看到可以帮助你的方式,比如自动为你完成一个单词或一行,它会将这个选项呈现出来。我有一个智能的人帮我完成所有句子:她就是我的妻子。当她帮我完成句子时,它们通常比我自己完成时更有组织和智慧。(这可能是她认为我懒惰的另一个原因。)
我想指出,并非所有具有类似 IntelliSense 功能的工具都是同等创建的。当你在一个增强编辑器中看到这个功能时,它通常与在 IDE 中的工作方式不同。在增强编辑器中,它们使用关键字列表来突出显示和自动完成语言的元素。真正优秀的增强编辑器可能会索引你的代码,识别变量和函数名称,并使用统计信息来首先提供最可能的完成选项。这个选项通常后面跟着一个长长的列表,包含给定完成选项的每一个可能性。随着 AI 工具的引入,代码完成变得越来越高级,这至少在这个问题上使得 IDE 和增强编辑器之间的区别变得有些模糊。像 GitHub 的 Copilot 这样的工具不仅可以自动完成变量名和关键字,还可以自动编写你代码的整个部分。
至少在我写这篇文章的时候,重要的是要记住,这些 AI 功能不是 IDE 或增强编辑器的一部分。它们作为插件实现。既然这是真的,我会继续宣扬 IDE 的优点,尤其是 PyCharm 的优点,仅基于软件本身的优点。我们将在第十六章中讨论插件,PyCharm 插件带来的更多可能性。
虽然增强编辑器可能会为你提供代码完成的可能性的长列表,但 PyCharm 可以分析你的代码并执行更智能的自动完成。你还可以获得代码分析,例如重复代码警告。软件开发中的一个常见反模式是在项目之间或甚至在同一项目内部复制和粘贴代码。看到这种糟糕但常见的事情是令人沮丧的。PyCharm 会检测到重复的代码并为你标记,这样你就可以被提醒将重复的代码重构为一个函数或模块,该函数或模块可以在一个位置重用和维护。
PyCharm 还可以对你的代码执行静态分析。这种分析是在寻找代码本身中的反模式;例如,PyCharm 将检测如图 1**.5中所示的死代码。关于 Python 开发,PyCharm 将自动格式化你的缩进,并对你代码是否符合PEP-8约定提供关键反馈,这些是必须满足的样式要求,以被认为是pythonic(这是一件好事)。
例如,如果你要在 PyCharm 的新文件中输入以下代码,你会看到 PyCharm 的警告,指出你在第 13 行创建了不可达的代码。该行的文本被突出显示。将鼠标悬停在突出显示的行上会显示错误信息:
def print_hi(name):
print(f'Hi, {name}')
for x in range(25):
print(str(x))
if x == 12:
return
print("You'll never make it here")
if __name__ == '__main__':
print_hi('PyCharm')
print_hi
函数最初是无害的,它将name
参数中传递给函数的任何内容打印到控制台。之后,我们创建一个循环,它将运行 25 次。在每次循环运行中,我们打印出包含当前迭代的x
。当计数器变量x
达到 12 时,通过return
函数退出循环,幸运的是,这个函数就在第 12 行。我向你保证,这纯粹是巧合。由于循环在第 12 行返回,第 13 行的代码将永远不会被执行:
图 1.5:PyCharm 将突出显示许多常见的编码错误,例如这个错误。第 13 行的代码不可达,当你将鼠标悬停在突出显示的代码上时,会有所指示
PyCharm 还允许你通过帮助你找到函数、变量和类的定义位置以及它们的使用位置,在复杂的项目中导航文件。随着时间的推移,你会学会一套快捷键,让你可以在项目中任意移动,而无需离开键盘。
从本质上讲,PyCharm 的智能编码辅助让你可以少担心错误,更多地关注你的逻辑和需求,这让你可以更快地完成代码,错误更少。
流程简化的编程工具
编写代码是开发者每天为了追求项目截止日期而执行的一项活动。伟大的开发者也会花时间调试、测试和性能分析他们的产品,以产生最佳结果。我们还需要处理将代码推送到测试服务器、重构(别人的)糟糕代码、与数据库合作以及处理容器。PyCharm 为这些过程以及更多提供了工具。当我使用 PyCharm 编写复杂的 Web 应用程序时,我通常只打开 PyCharm 和一个网络浏览器:每个工具都在自己的显示器上。
PyCharm 调试器
我最喜欢的功能,也是我第一次使用 PyCharm 时感到兴奋的功能是调试器。PyCharm 的调试器很棒。它比 Python 本身提供的标准调试器要好得多。Python 附带一个名为Python Debugger(pdb)的调试器。据我拙见,我宁愿从人行道上吃虫子也不愿使用这个工具。我在本章前面提到了这一点。我从小使用 Microsoft 调试器,其他任何东西都无法替代。PyCharm 的调试器工作方式完全符合我的预期。在您想要程序停止执行的那一行点击鼠标以设置断点,然后在 IDE 中点击调试按钮,程序就会运行并在指定的行停止。您将得到一个屏幕,您可以检查堆栈的状态以及终端输出。它非常简单易用,我将在第六章,“无缝测试、调试和性能分析”中向您展示如何使用它。
使用图形测试运行器运行测试
测试工具以测试运行器的形式集成。PyCharm 支持所有主要的测试框架,包括pytest、nose以及标准库中的常规单元测试功能。再次强调,我来自一些非常优秀的 IDE 的使用经验,在这种情况下,我正在回忆 Eclipse 和 Visual Studio,它们都包括图形测试运行器。谚语“如果条形是绿色的,代码就是干净的”在 PyCharm 中得到了视觉上的实现。您可以在图 1**.6中看到一个示例。您可以运行您的测试,并看到一个显示通过和失败的列表,尽管它是一个列表而不是条形图。然后您可以重新运行失败的测试,直到它们通过。
我会给你一个简单的例子。在本章源代码的main.py
中,我有一个名为main.py
的文件,还有一个名为test_main.py
的文件。main.py
的内容是一个简单的函数,用于将两个数字相加:
def add_two_numbers(a: int, b: int) -> int:
return a + b
在test_main.py
文件中,有一个简单的单元测试:
from unittest import TestCase
from main import add_two_numbers
class Test(TestCase):
def test_add_two_numbers(self):
self.assertTrue(add_two_numbers(a=5, b=6) == 11, \
"Should be 11")
def test_show_a_fail(self):
self.fail()
Test
类包含两个测试:一个会通过,另一个会自动失败。我通常先创建一个自动失败的测试,以确保我的测试类设置正确。然后,稍后,由于我的多巴胺成瘾,我会移除失败,因为只有测试运行器中的绿色勾选才能满足我,如图图 1**.6所示。如果我在test_main.py
上右键点击,如图图 1**.5所示,我会得到一个选项来运行文件中的测试:
图 1.6:右键点击 test_main.py 文件,然后点击“在 test…中运行 Python 测试”来运行文件中的单元测试
看一下左下角的图 1.7,它显示了测试运行的完成情况,你会看到一个通过或失败的测试列表,用绿色勾号或黄色 X 表示失败。像这本书中的所有图一样,它们都是用黑白打印的,所以你看不到颜色。彩色墨水很贵,你父亲是对的,钱不是从树上长出来的:
图 1.7:PyCharm 内置的测试运行器显示传统的通过/失败列表(左下窗格)以指示通过和失败的测试
PyCharm 的剖析工具
类似地,代码剖析是内置的,并且易于使用。你可以点击剖析按钮来运行代码。当程序退出时,你将得到每个函数调用的图表,以及调用消耗的时间和资源。这使得很容易发现有关执行速度和资源消耗的未实现改进机会。
考虑这样一种可能性,即你的程序中可能有一个算法,它的表现可能并不如你所期望。我知道,我知道,这种情况绝不会发生在你身上,所以假设你刚刚被雇佣,而被解雇的人编写了这个表现糟糕的算法。也许假装现在是 1956 年,你新雇主纽约人寿保险公司解雇的那个人是爱德华·哈里·弗莱德。弗莱德写了一篇题为《电子计算机系统中的排序》的论文,这可能是我们今天所知的冒泡排序算法首次公开发表的实例。如果弗莱德用 Python 3 编写他的算法,它可能看起来有点像这样:
def bubble_sort(input_array):
length_of_array = len(input_array)
弗莱德刚刚创建了一个接受列表作为参数的函数,在我们的例子中,这将是一个整数数组。目标是排序这些数字。为了做到这一点,让我们创建两个循环,一个嵌套在另一个里面:
for i in range(length_of_array):
for j in range(0, length_of_array - i - 1):
if input_array[j] > input_array[j + 1]:
input_array[j], input_array[j + 1] = \
input_array[j + 1], input_array[j]
在这些循环中,每个数字都会与它前面的数字进行比较。如果确定这两个数字顺序颠倒,它们就会被交换。在循环的下一轮运行中,这种情况会再次发生在下一个两个数字上,并且这个过程会一直持续到列表的末尾。
如果你研究过算法,你可能听说过冒泡排序,并且已经被警告过为什么它不被使用。它非常慢。我们在一个for
循环内部又嵌套了一个for
循环,如果未排序的列表大小很小,这是可以的。但是随着数字列表的增长,这个算法以对数速度减慢。算法性能是用大 O 符号来衡量的。我不想把这个变成一本算法书,所以我只会告诉你,在一个循环内部嵌套另一个循环在性能方面会表现不佳。在大 O 符号中,我们将这个算法归类为O(n2)。这很糟糕。
用简单的话说,这意味着如果你将需要排序的数字数量(n)加倍,那么你的算法处理时间将延长 22 或 4 倍。如果你将计数的大小乘以 5,那么它将变慢 52 或 25 倍。列表越大,排序越慢。
为了展示性能工具,我们将给这个测试运行一个包含 100,000 个数字的列表进行排序。现在是一个指出我正在运行英特尔 i9 处理器的好时机。如果你是一个学生或预算受限的消费者,使用的是 i3 处理器(或更差),如果你想要尝试这个,你可能需要将数字列表的大小减少几个零。在我的 i9 上,这需要相当长的时间:
test_array = []
for x in range(100000):
test_array.append(random.randint(1, 10000))
让我们通过调用函数并打印结果来完成测试代码:
bubble_sort(test_array)
print("The result of the sort is:")
for i in range(len(test_array)):
print(test_array[i])
我们将在第六章中详细介绍性能分析工具,无缝测试、调试和性能分析,但现在,让我们只是运行这段代码并查看结果。要在 performance.py
文件上运行性能分析器,只需右键单击文件,然后点击更多运行/调试,然后分析‘性能’,如图 图 1.8 所示:
图 1.8:右键单击你想要分析性能的文件,然后点击更多运行/调试 | 分析‘性能’以查看性能分析
记住,如果你使用和我一样的代码,这将需要很长时间才能运行,尤其是在较慢的计算机上。如果你运行时间过长,请随意调整列表的大小。结果是一个 .pstat
文件,在 PyCharm 中显示为表格。我们将在第六章中更详细地介绍这一点,无缝测试、调试和性能分析。你可以在 图 1.9 中查看性能报告:
图 1.9:PyCharm 的资源分析器显示了运行程序中的性能瓶颈
如你所见,程序 84.4% 的时间都花在了 bubble_sort
函数中,这是瓶颈。PyCharm 已经告诉你应该在哪里集中重构努力来提高你程序的性能。
从 IDE 发布
当你需要将代码发布到测试服务器,而你又没有使用持续集成系统时,你可以使用 PyCharm。为了明确起见,你应该使用持续集成系统,但我在项目初期,在持续集成系统运行之前,经常使用 PyCharm 的功能来将代码上传给利益相关者进行测试。你可以使用文件传输协议(FTP)或安全文件传输协议(SFTP)进行部署,或者直接复制到网络共享,以便快速轻松地与任何可能想要审查的人分享你的进度。
重构工具
PyCharm 拥有您从合适的 IDE 中期望的强大重构工具。如果您想更改变量名,或者甚至更改函数上的方法签名,右键单击并选择重构工具。请放心,您所做的更改将应用到项目中所有相关的实例,而不仅仅是您正在编辑的文件中。图 1.10展示了这一功能的示例:
图 1.10:PyCharm 提供了完整的重构工具选择
除了重命名变量或函数之外,您还可以执行其他操作,例如更改方法签名和更改面向对象类的结构。
在 PyCharm 中与数据库协同工作
如果您与数据库工作,PyCharm 的专业版包括图形化表格编辑器和对数十种流行数据库的 SQL 支持。请参阅图 1.11。我将在本章稍后更详细地介绍 PyCharm 的专业版;我们将在第十一章“使用 PyCharm 理解数据库管理”中有一个专门章节介绍数据库功能:
图 1.11:PyCharm 提供了一套强大且完整的工具,用于处理关系型数据库,如 Oracle、SQL Server、Postgres 以及更多
如您所见,许多关系型和非关系型数据库都直接得到支持。
远程开发、容器和虚拟机
最后,但并非详尽无遗,PyCharm 还提供了通过 SSH 与远程系统协同工作的功能,使用 HashiCorp 的 Vagrant 进行本地虚拟机,以及对 Docker 容器的广泛支持。
这并不是 PyCharm 可以为你做的所有事情的详尽列表,但到现在,你应该明白了。你可能需要的每一个工具都集成到了开发环境中。这可能是他们称之为集成开发环境的原因。
网络开发选项
我敢打赌,超过一半在 Python 中工作的开发者,在某个时刻都需要一个网络项目。无论你是像我一样,正在制作一个作为完全实现网络应用的 SaaS 产品,还是你在进行火箭科学,需要一种方式来可视化和交互式地分享你最新的关于深空无线电发射数据的快速傅里叶变换,网络项目通常是不可避免的。我不是科学家,我编造了那句话。如果最后一句对任何人有意义,那纯粹是巧合。
与 Web 项目一起工作提供了一层新的、独立的复杂性。大多数使用三层设计,通常用 模型-视图-控制器(MVC)模式表示。如果你不确定这是什么意思,请继续关注,因为本书有一个专门介绍 Web 开发的部分。现在,这意味着应用程序有一个用户可以与之交互的前端,一个包含连接逻辑的中层,以及一个用于结构化数据存储和检索的数据库层。只有中层是用 Python 实现的。我们将在后面的章节中广泛介绍 Web 开发,但现在,我想告诉你 PyCharm 提供的工具水平。
制作 PyCharm 的公司 JetBrains 制作了一系列针对不同语言的 IDE。他们中的一个 IDE 专门针对 Web 开发。它被称为 WebStorm。我之前说过,好的 IDE 针对一种语言。WebStorm 针对的是 JavaScript;具体来说,我们谈论的是全栈 JavaScript。现代 JavaScript 执行发生在两个地方。传统上,JavaScript 总是在浏览器中执行。大约 10 年前,Node.js 发布了,JavaScript 脱离了浏览器窗口的束缚,并允许在后台运行。
之前,我提到了 PyCharm 中用于数据库工作的强大功能集。JetBrains 还有一个针对 SQL 数据库开发者的 IDE,名为 DataGrip。事实上,PyCharm 的专业版包含了 WebStorm 和 DataGrip 中可用的全部功能集。当你购买专业版时,你将获得一个包含三个 JetBrains 产品的套餐:PyCharm、WebStorm 和 DataGrip。当你使用 PyCharm 处理 Web 项目时,你需要所有这三个功能集,它们在专业版中为你准备好了。
科学计算支持
数据科学领域的发展在 Python 自身的发展中发挥了重要作用,Python 现在是科学项目中使用的最常见编程工具(甚至比 R 更常见)。PyCharm 中包含的显著功能,有助于数据科学工作,包括 IPython、Jupyter 笔记本和交互式控制台。PyCharm 中对科学计算的支持在本书的第四章中详细说明,从 第十三章,开启科学模式 开始。PyCharm 还提供了一个名为 SciView 的定制视图,它以最佳方式组织科学项目中的工作空间,如图 1**.12 所示:
图 1.12:SciView 在 PyCharm 中通过简洁的界面提供对科学可视化工具的访问
理解专业版、社区版和教育版
PyCharm 有三个版本。我已经提到了两个,因为第三个是一个特殊版本,仅适用于教师,而本书的重点是应用开发,而不是软件开发教学。我会告诉你每个版本的情况,但我知道你想要的是一个功能比较图表。你可以在图 1.13中找到它:
PyCharm 专业版 | PyCharm 社区版 | |
---|---|---|
价格 | 付费 | 免费 |
智能 Python 编辑器 | ![]() |
![]() |
图形调试器和测试运行器 | ![]() |
![]() |
代码导航和重构工具 | ![]() |
![]() |
代码检查 | ![]() |
![]() |
Git、Subversion 和其他源代码控制工具 | ![]() |
![]() |
科学工具 | ![]() |
|
使用 HTML、JavaScript、CSS 等进行 Web 开发 | ![]() |
|
Python Web 框架支持 | ![]() |
|
性能分析 | ![]() |
|
远程开发、容器等 | ![]() |
|
数据库和 SQL 支持 | ![]() |
图 1.13:一个功能比较图表,显示了免费社区版与付费专业版所包含的功能
社区版是免费的,但与专业版相比,只提供有限的功能集。它非常适合仅涉及 Python 工作的项目。我正在开发的产品有一套 Python 脚本,用于批量处理大量数据。所有操作都在 Python 中进行,这正是社区版的完美用例。如果你只需要一个出色的 Python IDE,请使用免费版本。如果你只是在编写自动化脚本,例如 3D 计算机图形的图形管线或一般 IT 任务自动化,社区版也非常合适。
专业版拥有免费版的所有功能,并增加了网页开发、数据库、远程开发、容器化和科学项目类型。这是针对那些制作可发布软件项目的专业人士。虽然它不是免费的,但 JetBrains 通过提供多种定价选项,使其保持可负担性,具体取决于您如何使用该工具。独立开发者可能以比企业开发者更低的价位获得许可证。还有方法可以免费获得专业版,例如证明您正在使用 PyCharm 进行完全开源软件(FOSS)项目。初创公司可能有资格获得 50%的折扣,如果您在代码训练营或大学环境中教学,您也可能有资格获得免费的专业许可证。由于这些信息可能会随时间变化,您应检查 JetBrains 网站上的完整详情,网址为www.jetbrains.com/pycharm/
。
我之前提到 PyCharm 有三个版本,我们只介绍了两个。教育版旨在为教师和大学教授开发用于教授 Python 的课程。这个版本可以直接在 IDE 中创建和回放交互式课程。它只对教师、讲师和内容创作者有价值。
在这本书中,我将专注于社区版和专业版中存在的功能。
摘要
在本章中,我们介绍了 Python 语言本身,以及 Python IDEs 的一般背景和 PyCharm 的具体情况。
我们还讨论了 PyCharm 对 Python 程序员的可用性。具体来说,为了能够充分利用 PyCharm 提供的所有功能和功能,而不过度依赖 IDE,程序员应首先掌握 Python 语言及其核心语法的基础知识。我们还探讨了 PyCharm 本身与各种其他 Python 编辑器/IDE 的比较以及为什么 PyCharm 被认为是所有开发环境中的最佳。
最后,我们比较了两种可下载的 PyCharm 版本:付费的专业版和免费的社区版。如果您正在处理大型、复杂的项目,包括数据库管理、网页开发语言和科学报告中可查看性,那么您很可能将从使用专业版中受益。
在下一章中,您将学习如何下载 PyCharm,在您的系统上设置它,并为其 Python 项目配置其环境。这将是开始使用 PyCharm 的第一步,之后我们将开始讨论本书涵盖的 PyCharm 提供的特定功能。
问题
回答以下问题以测试您对本章知识的掌握:
-
程序员通常使用编辑器或集成开发环境(IDE)来编写代码。两者之间有什么区别,PyCharm 又是哪一种?
-
为什么有些人可能会认为 Python 开发的 IDE 可能不合适或不必要?
-
PyCharm 的一些关键特性是什么?在这些特性中,哪些使 PyCharm 在其他编辑器/IDE 中具有优势?
-
PyCharm 相比于可以配置以执行 PyCharm 提供的许多相同功能的编辑器(如 Visual Studio Code 或 vim)有哪些优势?
-
PyCharm 有哪三个版本?它们之间有哪些关键区别?
进一步阅读
一定要查看这本书的配套网站pycharm-book.com
。要了解更多关于本章所涉及的主题,请查看以下资源:
-
Friend, Edward H. 《电子计算机系统中的排序》。《ACM 期刊 (JACM)》3.3 (1956): 134-168。
-
Nguyen, Quan. 《使用 PyCharm 进行实战应用开发:利用 PyCharm 中的实用编码技巧加速 Python 应用程序开发》。Packt Publishing Ltd, 2019。
-
Shelley, Percy B. 《奥西曼提亚斯》。
www.poetryfoundation.org/poems/46565/ozymandias
。 -
维基百科贡献者。 (2022, 12 月 19 日)。冒泡排序。维基百科。
en.wikipedia.org/wiki/Bubble_sort
。
第二章:安装和配置 PyCharm
在上一章中,我们探讨了 PyCharm 最流行的功能,并不仅考虑了是什么让 PyCharm 成为一个伟大的 IDE,还考虑了是什么让任何 IDE 在历史上都变得伟大。我们作为开发者需要一套基本的功能才能真正提高生产力。在本章中,我们将关注 PyCharm 的安装。你可能认为你只需下载并安装它。你可以这样做,但安装 PyCharm 有不同方式,你可能更喜欢其中的一种。根据你的操作系统,也有不同的选项。
除了简单地下载安装程序、运行它并按下一个按钮直到安装程序的对话框消失之外,还有其他考虑因素以确保工具正确安装并运行。PyCharm 高度可定制,当你第一次运行程序时,你会看到一些定制选项。其中一些选项很有趣,如果你在过程中随意选择每个选项和每个定制,其中一些可能会带来麻烦。
在本章中,你可以期待以下内容:
-
下载 JetBrains Toolbox 并使用它来安装和管理 PyCharm。这是我推荐的安装方法,因为你可以轻松处理升级和卸载。如果你需要,你甚至可以安装和管理多个版本的 PyCharm。
-
我们将第一次运行 PyCharm,并了解软件在首次运行时提供的定制选项。当然,你可以随时更改这些选项,我们也会介绍这一点。
-
我们将使用 PyCharm 的集成版本控制系统(VCS)工具从 GitHub 克隆这本书的仓库。
技术要求
要在本章中取得成功,你需要以下条件:
-
一台电脑。以防你错过了第一章中的这个特别玩笑,我自豪于我的完整性,不留任何余地!电脑应满足以下 PyCharm 的系统要求:
-
64 位版本的 Microsoft Windows 8 或更高版本、macOS 10.14 或更高版本,或运行 GNOME 或 KDE 桌面的 Linux。
-
官方系统要求列表中将 4 GB RAM 作为最低要求,8 GB 作为推荐。如果你打算进行非平凡的工作,并且你在考虑新电脑的配置,我不会购买少于 16 GB RAM 的电脑,我更倾向于 32 GB。较轻的配置只是运行 PyCharm 所需的配置。大多数开发者运行的配置要高于 IDE。
-
2.5 GB 硬盘空间;推荐使用 SSD。再次强调,这是 PyCharm 的低端推荐。如果你在购物,请选择 NVMe 驱动器而不是 SSD。性能通常高 10 倍,而且成本容易承受。
-
至少 1,024 X 768 的屏幕分辨率。这是低端规格,这只是一个笑话。在这么大的屏幕上,你几乎什么也做不了,而现在的廉价电脑很容易就能支持 1,920 X 1,080。对于专业工作,如果可能的话,你真的希望有 4K,或者至少两个(或更多)1,920 X 1,080 的显示器。你拥有的屏幕空间越多,你使用任何 IDE 的生产力就越高。在 4K 监视器上,我可以让 PyCharm 显示我的项目资源管理器,两个并排打开的代码窗口,数据库资源管理器,以及一个终端会话。
-
-
一个操作系统。如果它已经安装在你的电脑上,这将是最有效的方法,因为在这本书中我们不涉及如何安装它。Windows、macOS、Linux——就这本书而言,它们都是一样的,因为 PyCharm 在这三个环境中都能运行,并且每个环境中的用户界面几乎相同。
-
一个互联网连接。
-
安装 Python 3。在这本书中,我们将仅使用 Python 3。Python 3 有几种不同的“风味”,但就大部分情况而言,来自
www.python.org
的普通 Python 3 就足够了。当我们开始讨论虚拟环境时,我们会在 第三章,自定义解释器和虚拟环境中详细介绍这些“风味”。如果你对修订号感到舒适,我在写作时的最新版本是 3.11.1。我在提到的那个生产 SaaS 应用程序中使用的 Python 修订版是 3.8。如果你的 Python 3 安装版本比这更旧,你应该更新它。 -
在某个时候,一个 GitHub 账户可能会变得很有用,因为我会使用 Git 仓库分享书中的代码。由于你将克隆一些代码,但不会推送,所以登录并不是严格必要的——也就是说,除非你想登录,查看书籍的仓库,并给它加星。那将是极好的,并向世界证明你是一个正直的人。
以传统方式下载 PyCharm
首先,我将展示安装 PyCharm 最简单、最直接、最常见的方法。在你购买这本书之前,你很可能已经这样做过了,这没什么问题。在介绍完这个之后,我会展示我首选的安装方法,即使用来自 JetBrains 的免费应用程序 Toolbox。你可以选择你喜欢的任何安装方法,知道你的选择不会影响这本书中我们做的任何事情。
注意,专业版提供 30 天免费试用,如果你想试用一下。30 天后,你将不得不付费或降级到社区版。此外,不要过于纠结于 图 2**.1 中显示的版本号。JetBrains 频繁地为 PyCharm 发布更新,这个数字在本书上架之前可能会改变几次。选择最新版本通常不会出错。
要下载和安装 PyCharm,请将你的浏览器指向 www.jetbrains.com/pycharm
。网站将检测你的操作系统并尝试向你展示正确的下载选项。
下载页面有三个主要部分,如图 图 2.1 所示:
-
你可以选择操作系统(Windows、macOS 或 Linux)。如果你想使用 Linux,请确保你正在运行 GNOME 或 KDE,因为它们是受支持的窗口管理器。
-
你可以在专业版或社区版之间进行选择。
-
无论前两个选项如何,请确保查看安装程序的下拉菜单。Windows 和 Linux 允许你在 Intel 和 ARM 之间进行选择。macOS 允许你在 Intel 和 Apple Silicon 之间进行选择:
图 2.1:JetBrains 网站的下载窗口;请确保选择了正确的操作系统和处理器
下载了正确的版本后,请按照你操作系统的常规安装程序进行操作。如果你选择了 Linux 版本,你并不是在下载安装程序——你是在下载一个 gzipped .tar
文件。你只需提取它,然后从生成的文件夹中运行 PyCharm。
JetBrains Toolbox
我刚刚介绍了人们最常用来将软件(包括 PyCharm)安装到他们的开发计算机上的最常见方法。还有一种不同的方法,我更喜欢,涉及安装一个名为 JetBrains Toolbox 的独立产品。如果你像我一样拥有多个 JetBrains 产品,Toolbox 特别有用。我订阅了他们所有的工具,并且经常使用其中许多。我首选的 C# IDE 是 JetBrains Rider,我在 Packt Publishing 出版的书籍 Real-World Implementation of Design Patterns in C# 中独家使用它。
即使你不需要使用多个 JetBrains 产品,Toolbox 也提供了一些有用的功能,例如提供一种简单的方式来安装、卸载和更新你的 PyCharm 安装。如果你需要,你甚至可以使用它来安装多个版本的 PyCharm,包括 早期访问计划(EAP)版本。EAP 版本让你在它们普遍可用之前就能访问 JetBrains 的最新功能。作为一名开发主管,我喜欢在我向我的开发团队发出“绿灯”之前,先对最新的 IDE 进行测试。Toolbox 让这变得非常简单。
Toolbox 是一个独立的下载,并且是免费的。让我们首先回顾一下 PyCharm 的下载页面。在底部,你会找到一个指向 JetBrains Toolbox 的链接,如图 图 2.2 所示:
图 2.2:跳过我们之前提到的常规下载链接,而是点击 Toolbox 链接
有可能 JetBrains 会重新安排其网站。如果没有在底部找到链接,你可以简单地搜索工具箱并找到最新的下载。你将进入一个像图 2.3中显示的页面:
图 2.3:工具箱下载屏幕
就像我们之前审查的正常 PyCharm 下载一样,这个网页将检测你正在使用的操作系统。选项稍微简单一些,但如果你使用的是 Mac,务必点击格式按钮以选择 Intel 与 Apple Silicon,就像图 2.3中显示的那样。
在 Windows 中安装工具箱
安装工具箱的过程很简单。这是一个你运行安装程序并一直点击下一步按钮直到对话框消失的情况。
注意,当工具箱运行时,你可以在 Windows 的系统托盘找到它。
在 macOS 中安装工具箱
就像所有 Mac 产品一样,macOS 的安装非常简单。确认你下载了正确的.dmg
文件版本(Intel 与 Apple Silicon)后,在你的home
文件夹中的Downloads
文件夹中找到下载的.dmg
文件。双击打开.dmg
文件。将工具箱图标拖到Applications
文件夹。完成!
使用工具箱安装 PyCharm
无论你是如何到达这里的——无论是 Mac、Windows 还是 Linux——你现在应该已经有工具箱在运行了。在这个阶段,体验几乎是通用的。在 macOS 上,工具箱就像其他任何常规应用程序一样。但在 Windows 上,它运行在系统托盘上。
第一次运行工具箱会带来一个相当标准的最终用户许可协议(EULA)。你知道流程。阅读它并确保 JetBrains 没有要求你放弃你的第一个孩子,然后选择同意或不同意 EULA。当然,如果你不同意,我们的合作就结束了,除非你是偶尔来听一些爸爸笑话的。我将假设你同意了 EULA。
在安装了工具箱后,你可以安装 IDE。你将看到一个像图 2.4中显示的屏幕,列出了所有可用的 JetBrains 产品:
图 2.4:JetBrains 工具箱在 Windows 系统托盘运行
自然,我们只对 PyCharm 感兴趣。图 2.4显示了列表中的 PyCharm Professional 和 PyCharm Community。如果你对 PyCharm 教育版感兴趣,它在列表中更靠下。
要安装 IDE,点击安装按钮,等待工具箱下载并安装 IDE。安装完成后,工具箱菜单会略有变化,以显示你已安装的工具。你可以在我安装了 PyCharm Professional 后的图 2.5中看到我的:
图 2.5:安装 PyCharm Professional 后的工具箱应用程序
您会注意到 Toolbox 将您安装的应用程序列在列表的顶部,而可安装的则位于下方。Toolbox 相对于正常安装的优势在于,您可以启动、更新和卸载您的 IDE,以及轻松尝试不同的版本。
使用 Toolbox 启动 PyCharm
在您安装 PyCharm 之后,您可以通过简单地点击菜单中的条目来启动它或任何已安装的 IDE。在我们离开 Toolbox 之前,让我向您展示一些更有趣的功能。
安装备用版本或卸载
在列表中每个已安装的 IDE 旁边,您将看到三个点,如图图 2.6所示:
图 2.6:每个应用程序旁边的三个点代表一个菜单
这些点代表一个菜单。如果您点击它们,您将看到一些选项。您可以访问 PyCharm 的设置。我们将在 PyCharm 内部稍后进行此操作,所以现在我们不需要这么做。有一个选项,通过点击什么是新功能来查看最新新闻。在其下方,有一个选项,可以安装除最新版本之外的 PyCharm 的不同版本。如果您遇到最新版本的问题,可以轻松回滚到最后一个安装版本。最后,在分隔符下方,以非常令人畏惧的红色字体显示的是卸载选项。它甚至有一个令人毛骨悚然且神秘的省略号跟在菜单选项之后。将鼠标悬停在选项上通常会让人感到一种不祥的预感。您已经被警告了。
使用 Toolbox 更新 PyCharm
Toolbox 将帮助您保持 PyCharm 以及您使用的任何其他 IDE 的最新版本。Toolbox 本身有自己的设置和更新机制。
要访问这些设置,请点击 Toolbox 屏幕右上角的六边形图标,如图图 2.7所示:
图 2.7:使用 Toolbox 顶部的六边形图标来访问其更新选项
登录选项允许您将 Toolbox 连接到您的 JetBrains 账户。关于和常见问题解答选项显示有关产品的信息。后者将您带到维护产品常见问题解答(FAQ)的网站。退出选项将关闭 Toolbox 程序。
启动并注册 PyCharm
不论您如何启动 PyCharm,它都会显示一个启动屏幕,然后带您进入一组典型的首次启动屏幕。如果您安装了专业版,您首先会看到图图 2.8所示的注册屏幕:
图 2.8:PyCharm Professional 的许可屏幕在首次运行时出现
在这里进行操作的最常见方式是登录到你的 JetBrains 账户。点击标有登录到 JetBrains 账户…的按钮将启动你的浏览器。你可以登录或创建一个账户。如果你已经购买了许可证,登录将使你的副本与所购买的许可证关联。
如果你为拥有许多许可证的公司工作,你可能需要登录到 JetBrains 许可证服务器。还有一个使用注册码进行注册的选项。你可以在你的商店账户中找到这个代码。如果你没有良好的互联网接入,这可能很有用。
你有多台电脑吗?
注意,只要你不使用同一许可证同时运行两个副本,在多台计算机上安装是合法的。IDE 会检测到这一点并要求你关闭一个副本。
如果你没有许可证并且还没有准备好提交,你可以选择开始试用选项。你仍然需要登录到 JetBrains 账户以激活你的试用。
设置 PyCharm
当你第一次启动 PyCharm,并且通过了许可和注册,你接下来看到的就是一个表示 PyCharm 没有加载项目的较小窗口。你可以在 图 2.9 中看到这个屏幕的浅色方案版本:
图 2.9:未加载项目的 PyCharm
从这个窗口,有几个明显的选项。我称它们为明显,因为它们就在窗口中间一个很大的开放空间中。你可以创建一个项目,打开一个现有项目,或者从 Git 或 Subversion(SVN)等 VCS 中克隆一个项目。然而,我的第一个目的地是在屏幕左侧的灰色区域,在那里你可以找到自定义菜单选项。让我们来回顾一下自定义 PyCharm 以适应你的工作风格的选项。点击自定义会带你到偏好设置屏幕,如图 图 2.10 所示:
图 2.10:PyCharm 中设置一些重要首选项的小窗口
这个屏幕允许我们更改最常访问的设置。我们可以更改我们的颜色主题、IDE 字体和我们的快捷键映射。
在底部,我们可以导入我们的设置。当你的老板最终为你买下那台新笔记本电脑,而你不想花大量时间重新定制你的 IDE 时,这很有用。众所周知,一个配置良好、个性化的 IDE 就像你的旧沙发。任何试图坐在上面的人都会感到极不舒服。而且,它还有味道。你的朋友们不会告诉你,但它确实有。对你来说,它通过可能多年的久坐满足感,完美地适应了你的形状。那就是你的 IDE。你可能花了几年的时间来调整它以达到完美,但每次你得到一台新机器时,都必须从头开始。今天不是,朋友们。不是用 PyCharm。我们可以导出、导入,甚至共享我们的设置,并轻松地将它们带入新的安装。
我喜欢大字体,但不知道为什么!
你可能已经注意到,在这些截图里我的字体大小都非常大。我来自德克萨斯州,在德克萨斯州,一切事物都更大。然而,在这种情况下,我是为你这么做的。使用夸张的字体大小,截图更容易阅读,所以你会在本书的偏好设置中看到一些疯狂的设置。
你可能此时会想,PyCharm 的定制选项相当有限。你会错的。这个看似简单的窗口旨在通过展示最常更改的设置来让你轻松入门。许多用户会在这里停止。但不是你。不,不是你。你是维京人!其他开发者看到屏幕底部的那个看似微不足道的所有设置…按钮,会想这里可能有龙!而你看到的是一个充满冒险的丰富机会!所以,如果你敢,点击所有设置…按钮,我们将一起探索这个勇敢的新世界。
外观和行为
点击所有设置…按钮会带你到图 2.11所示的屏幕。我将仁慈地不尝试涵盖每个可用的设置,因为可能性的列表详尽到令人厌烦。当你通过定制设置时,你可以大致定制运行 IDE 生成的每个像素:
图 2.11:PyCharm 的设置屏幕允许你更改 PyCharm 中用户体验的各个方面
可能最实用的开始方式是指出设置类别列表的顶部有一个搜索框。通常,搜索你想要的设置比翻遍所有屏幕试图找到它更实际。
在我们继续前进之前,请注意图 2**.11中的箭头。它指向屏幕上的两个项目:顶部的是外观(我们稍后会讨论)以及一个新 UI设置。随着本书的编写,PyCharm 的用户体验正在进行全面的重构。随着应用程序的增长,菜单和工具栏变得越来越拥挤。为了解决这个问题,JetBrains 正在开发一个更简单的 UI 布局,希望这将使工具更容易学习和采用。经典的 UI 可能会在 2024 年之前仍然可用,但新的 UI 将在 2023 年底成为标准,这应该与本书付印的时间相吻合。本书中展示的所有内容都将使用新 UI设置。甚至可能是在 PyCharm 的后续版本中,这个选项甚至可能不会出现在屏幕上。
大多数开发者使用新 IDE 的第一件事就是调整外观。我们已经找到了颜色主题和字体大小的设置,但还有其他您想要查看的设置。让我们先点击图 2**.11中显示的外观,这将打开左侧的菜单并显示外观设置屏幕。
外观
打开外观设置后,您将看到一个类似于图 2**.12所示的窗口:
图 2.12:PyCharm 的外观设置
我们之前已经看到过一些这些设置。我们可以设置主题,就像我们在之前看到的简单设置对话框中做的那样。然而,此窗口提供了一个标签为获取更多主题的链接,这将允许您在 JetBrains Marketplace 中探索 PyCharm 安装中标准问题之外的主题。我们将在本书的末尾介绍插件,但如果您想找到一些更独特的东西,看看也无妨。
等一下!
这不是您设置代码编辑器中字体的地方。此设置控制 UI 本身的字体。它将影响按钮、菜单项、窗口标题等,而不是编辑器,这可能是您想要更改的。
别担心——我们很快就会定制编辑器字体。
编辑设置
与设置窗口中的大多数部分一样,有许多自定义选项。我只会强调一些重点。随着我们浏览屏幕,您将看到可以进行的自定义程度。如果是在屏幕上由 PyCharm 渲染的像素,您很可能会以某种方式对其进行自定义!图 2**.13显示了设置窗口中的编辑器设置部分:
图 2.13:PyCharm 设置窗口中的编辑器设置
如果你喜欢,可以浏览左侧的选项,看看是否有任何适合你更改的内容。颜色方案部分让你可以自定义 IDE 着色语法的方式。我将跳到我认为最有用的设置修改部分。点击字体部分,就像我在图 2.14中做的那样:
图 2.14:PyCharm 中编辑器的字体设置
这个屏幕只做一件事:它控制 IDE 编辑窗口中文本的外观。我们之前看到的其他字体设置通常适用于整个 IDE。对于我们这些工具用户来说,这个设置是最重要的。这是你整天都会盯着看的工具部分。你可以设置字体、大小和行高。我把我字体的大小设置得很大,因为我需要漂亮的、大号的字体来显示所有这些截图,但说实话,我在工作笔记本电脑上的日常设置是 16 或 18 号。字体大小越大,根据你的显示器距离越远,阅读起来就越容易。理想情况下,这不应该超过手臂的长度。如果你的字体太小,你可能会不知不觉地更频繁地眯着眼睛看显示器,这可能会导致疲劳。
这里有一个设置可以启用字体连字符。这些是对标准字体的增强,允许我们代码中使用的符号以更多优雅的方式显示。例如,启用字体连字符后,一个带有不等于符号的条件语句if a != b
将渲染成图 2.15中所示的样子:
图 2.15:开启字体连字符可以让你表达某些通常不可用的符号,例如第 5 行的不等号
为了让这个功能正常工作,你必须选择一个支持连字符的字体。PyCharm 附带Fira Code字体,专门因为它是一个很好的 IDE 字体,支持连字符。如果你想探索更多支持连字符的字体,请将你的浏览器指向nerdfonts.com
。它有许多适合编辑器、终端和控台的字体,其中许多支持连字符。
在编辑器设置中的字体设置下方是颜色方案。你之前见过这个,但知道它的位置是好的,因为上次我们看到它时,它是在设置窗口中。
如果你想要更多颜色方案的自定义选项,向下旋转颜色方案菜单项旁边的箭头,你就会看到,就像我在图 2.16中做的那样,有更多选项:
图 2.16:您可以使用颜色方案设置菜单中的选项更改 PyCharm 中几乎所有内容的颜色
您可以控制应用程序中每个窗口在所有使用上下文中的颜色。例如,您可以为 SQL 文件、JavaScript 文件和 Python 文件设置不同的颜色方案。
在这些设置中,最有用的可能就是能够自定义 PyCharm 集成终端窗口中显示的字体。您可以在图 2中看到这一点。17*:
图 2.17:您可以在 PyCharm 的集成终端窗口中自定义字体
默认情况下,控制台使用与编辑器相同的字体。如果您想更改它,您当然可以。您还可以使用控制台颜色设置进一步自定义控制台的外观,如图 2所示。18*:
图 2.18:PyCharm 中的控制台颜色设置
个人来说,我在终端上花费了很多时间,所以我很欣赏可用的自定义程度。
关闭颜色方案菜单,让我们跳转到代码样式菜单。您可以在图 2中看到该菜单。19*:
图 2.19:PyCharm 设置窗口中的代码样式菜单
这一点我觉得有点令人困惑。PEP 8标准规定,Python 代码的任何一行都不应超过 79 个字符,然而编辑器的硬换行设置为 120。幸运的是,我们可以解决这个问题。正如我所说的,PEP 8规定代码行应限制在 79 列或更少。该标准还建议注释行和文档字符串应限制在 72 列。为了确保我遵循这些规则,我更改了我的代码样式设置,如图 2所示。20*:
图 2.20:这是我推荐的折行和指南设置,它尊重 PEP 8
我更改了79
,并勾选了复选框来检查折行。然后,我将72
和79
添加到视觉指南设置中,这样我就能在右侧的页边空白处看到线条。良好的 Python 代码始终与标准保持一致。设置这些视觉指南有助于我避免那些讨厌的红波浪线。我讨厌那些。
如果您不明白我的意思,Python 有一个内置的代码检查器,当您违反PEP 8规则时,它会警告您。过了一段时间,它就像一个烦人的丈母娘坐在后座上一样。以这种方式设置代码样式设置可以避免代码愤怒,这在实际的后座司机,甚至可能是所有丈母娘中都是不可避免的。
应用这些设置会改变编辑器的外观,如图 2所示。21*。我们可以看到右侧有两行,分别显示我们的 72 列和 79 列的指南:
图 2.21:代码样式已应用,我们可以在编辑器的右侧看到我们两条浅灰色线条
如果你记得,我们在第 79 行也开启了一个硬换行。为了演示这一点,你会注意到代码中的第 10 行,在我更改设置之前输入的,明显超过了 79 列的指南。为了测试硬换行设置,我在第 11 行下面立即重新输入了这一行。一旦达到硬换行限制,PyCharm 插入了一个行续符(\
),并将我带到下一行,并适当地缩进。现在,我无法输入违反PEP 8标准的代码行。
注意,更改设置不会自动重新格式化你的代码。有一个工具可以做到这一点。我们将在第四章中学习如何自动格式化和重新格式化代码。
其他设置
在设置窗口中还有更多可见的设置,包括远程开发和插件。这些将在后面的章节中介绍。
导出自定义设置
自定义设置非常棒!但当你必须切换到新电脑时,这总是很麻烦。你肯定不愿意重新做所有的自定义设置。或者你可能想与你团队中的其他开发者分享你的设置。PyCharm 让这变得很容易。只需点击文件菜单,找到管理 IDE 设置选项,如图2.22所示。22*:
图 2.22:PyCharm 让你可以导出和导入你的设置
你可以按需导入、导出和重置你的设置。
与项目一起工作
现在我们已经看到了所有的配置选项,并且调整了一些更受欢迎的设置,让我们快速浏览一下 PyCharm。我们将创建一个快速的 Python 项目,并学习如何在 UI 中运行我们的主脚本。
到这一点,你需要在你的电脑上安装 Python 3。Python 文档在docs.python.org/3/using/index.html
中解释了安装过程。我发现这份文档很完整,但你必须浏览很多才能找到好的部分。为了使初学者更容易,我将在本章末尾的进一步阅读部分包含一些额外的链接。
创建新项目
你可能开始意识到 PyCharm 是一个非常强大且灵活的工具。有几种方法可以配置它,应用程序中有几个地方可以开始配置过程。在创建项目方面也有类似的灵活性。
如果你之前从未在 PyCharm 中创建过项目,现在很可能就是这样,你会看到一个像图2.23中所示的窗口。这个窗口提供了一个创建新项目的按钮:
图 2.23:PyCharm 的启动屏幕有一个用于创建新项目的按钮
如果你已经在 PyCharm 中创建了一个项目,PyCharm 窗口将打开到最后加载的项目。这就是我在图 2.24中展示的情况。在这里,我有我们迄今为止使用的示例项目。在本章稍后,我将向你展示如何使用 PyCharm 的集成 Git 客户端克隆此代码,但现在让我们回到创建新项目。在图 2.24中,我已经打开了一个项目。在这种情况下,你可以使用文件菜单来创建一个新项目。点击文件 | 新项目…:
图 2.24:你可以通过点击文件菜单中的“文件 | 新项目…”来创建一个新项目
无论你选择哪个向量,结果都是一样的。你将得到如图2.25所示的新项目窗口:
图 2.25:PyCharm 专业版中的新项目窗口
这里你可以注意到专业版和专业版之间的一个重大差异。在图 2.26中,你会看到专业版(左侧)和社区版(右侧)的新项目窗口。请将注意力集中在专业版窗口的左侧。专业版提供了更多项目类型,而免费版只有一个选项。社区版只创建纯 Python 项目。它甚至不提供选项,因为它只能做到这一点:
图 2.26:PyCharm 专业版(左侧)和社区版(右侧)的项目创建选项
重要的是要注意,在 PyCharm 社区版中创建任何类型的项目并非不可能。例如,如果我想创建一个 Flask 项目来制作一个网络应用,我绝对可以在社区版中做到这一点。然而,我必须从头开始手动完成。专业版通过更好的工具加快了速度,但它不会阻止你在任何其他编辑器中做任何你不能做的事情,无论是免费的还是其他类型的。
我将选择两个版本都有的一个项目:一个纯 Python 项目。这是两个版本中的默认设置。
屏幕顶部的第一个选项允许你设置项目的名称和位置。我将保留默认的pythonProject
。在其下方,有一系列用于创建 Python 虚拟环境的选项。这是第三章的主题,所以我将它们保留为默认设置。
注意
如果你还没有安装 Python 3,或者 PyCharm 无法检测到你的 Python 3 安装,你可能会被提示自动安装 Python 3。请注意,PyCharm 可能不会安装 Python 3 的最新版本,也不会给你任何控制安装位置的能力。它还会显著减慢创建第一个项目所需的时间。
确保勾选名为创建 main.py 欢迎脚本的复选框,然后点击创建按钮。如果你在开始创建新项目的过程中已经打开了一个项目,你会看到一个对话框询问你想要如何处理当前打开的项目,如图2.27 所示:
图 2.27:如果你已经打开了一个项目,在创建新项目时,你有选择如何处理它的选项
现在,让我们选择此窗口按钮,这将关闭你打开的项目,并用你的新项目替换它。当然,你的旧项目仍然完好无损,可以通过文件 | 打开菜单选项重新打开。
结果是一个包含单个文件的新的 Python 3 项目。该文件将包含一种常见的Hello World习语变体,这样我就不必在本书的源代码中包含它了。看?我们都赢了!让我们在 IDE 中查看我们的新项目,如图2.28 所示:
图 2.28:新项目已创建;现在,让我们四处看看!
如你所见,PyCharm 在我们的项目资源管理器中为我们创建了一个项目结构(1)。项目资源管理器允许你查看和交互你的项目中的所有文件。你可以看到它为虚拟环境创建了一些文件夹。我们将在第三章中深入探讨这一点。
编辑器清晰地位于中间(2)。我们将在这里花费很多时间,所以让我们指出,PyCharm,就像大多数 IDE 和编辑器一样,使用的是标签界面(3)。有一个工具栏(4),可以运行、调试、测试和性能分析你的程序,我们很快就会用到它。
在 PyCharm 的右下角(\t
)字符上,除非你自定义了它。但你在世界上为什么要做这样的事情呢?
小贴士
如果 PyCharm 无响应,请向下看窗口底部的区域 6附近,你可能会看到一个进度条,表明 PyCharm 正在执行某些操作,如项目文件索引。你可能必须等待它完成,你的 IDE 才能完全响应。
运行 PyCharm 项目
我感觉自己有点像那个不停谈论汽车特点的烦人的汽车销售员。你只想开车而已。让我给你那些钥匙,并告诉我的不存在的经理我们钓到了一条鱼。我马上回来。
在 PyCharm 的情况下,键位于窗口的顶部:区域 4 如 图 2**.28 所示。您可以通过找到带有箭头的蓝色按钮轻松找到它。我会在 图 2**.29 中向您展示确切的按钮。您必须相信我关于颜色的描述。它是蓝色的。这是 运行 按钮。点击它:
图 2.29:点击蓝色箭头以运行主脚本
现在,大声喊一声大大的德克萨斯 YEEEE-HAW!或者,您也可以做您所在地区认为的欢呼。您已经运行了程序。我们朝着掌握新 IDE 的目标迈出了重要一步。IDE 窗口发生变化,如 图 2**.30 所示:
图 2.30:当您运行程序时,您可以在控制台中看到运行结果
除了运行窗口本身外,请注意,选项卡上还有按钮,允许您重新运行程序或停止当前正在运行的程序。
在下一节中,您将学习如何从 GitHub 克隆代码项目。在我们这样做之前,让我们关闭当前项目。定位 文件 菜单,然后点击 文件 | 关闭项目,如 图 2**.31 所示:
图 2.31:从文件菜单关闭项目
太棒了!您已经准备好开始下一场冒险了!让我们从 GitHub 获取本书的示例源代码。
从 GitHub 克隆本书的代码
PyCharm 为处理 VCS(如 Git、SVN、Mercurial 和 Perforce)提供了一套强大的功能。在本书中,我们将使用 Git 和 GitHub,因为它们已成为行业中的事实标准。如果您使用其他支持的 VCS,过程大致相同。实际上,用户体验大致相同,只是在某些 VCS 的操作方式上有所不同。例如,Git 使用四步过程来提交和推送文件:
-
阶段您的本地更改 (
git add
)。 -
在本地提交您的更改 (
git commit
)。 -
从 GitHub 或您的中央仓库拉取最新更改,并解决任何冲突 (
git pull
)。 -
将您的更改推送到中央仓库 (
git push
)。
相比之下,SVN 只有两个步骤:
-
拉取最新更改并解决任何冲突 (
svn update
)。 -
将您的本地更改提交到中央仓库 (
svn commit
)。
我的观点是,不同的 VCS 可能会有不同的工作流程,这可能会改变 PyCharm 中的用户体验。对于本书,我们只会使用 Git。
由于您在上一个部分的末尾关闭了项目,您应该看到一个类似于 图 2**.32 所示的窗口:
图 2.32:定位“从 VCS 获取”按钮
在本节中,我们将完成两件事。首先,我们将在 PyCharm 中设置您的 GitHub 账户。如果您目前没有账户,这并不是一个严格的要求,但迟早您会需要一个 GitHub 账户。您不妨直接面对现实。我不会在这里介绍如何创建 GitHub 账户。您可以在github.com/signup
找到创建账户的说明。
定位并点击从版本控制获取按钮。您将看到一个类似于图 2.33所示的屏幕。33*:
图 2.33:PyCharm 中的从版本控制获取窗口
设置您的 GitHub 账户
注意,在窗口的右侧,您可以查看您在 GitHub、GitHub Enterprise 和 JetBrains Space 上的所有账户。我将继续设置我的 GitHub 账户。点击左侧菜单中的GitHub项。您将看到一个类似于图 2.34所示的屏幕。34*:
图 2.34:登录 GitHub 简单且只需进行一次
定位并点击通过 GitHub 登录按钮。在您点击它之前,我建议您使用您喜欢的浏览器登录到github.com/login
。如果您已经登录并清除了任何双因素认证(2FA)的障碍,这个过程将更加无缝。
一旦授权,您可以关闭浏览器并返回 PyCharm。几分钟后,您将在窗口中看到所有您的 GitHub 仓库。这使得处理您自己的仓库变得非常容易。然而,我们要克隆的仓库不在您的列表中,所以我们还有更多的工作要做。
克隆书籍的仓库
要克隆您不拥有的仓库,请回到左侧菜单中的仓库 URL选项,如图图 2.35所示。35*:
图 2.35:要克隆您不拥有的仓库,请点击左侧菜单中的仓库 URL 选项
在PyCharmProjects
下的home
文件夹中。我将其留在了默认位置。点击标有克隆的按钮。
一旦克隆,您将看到一个在当今软件开发工具中普遍存在的典型偏执对话框,如图图 2.36所示:
图 2.36:您信任我吗?
我知道我们刚刚见面,但如果你真的想要这本书的源代码,你应该点击信任项目按钮。一旦你这样做,你就准备好了。你现在拥有了书中展示的所有示例代码。
摘要
本章主要介绍了准备工作。我们在您的电脑上安装并配置了 PyCharm 的新实例。您有多种方式可以安装 PyCharm。您可以走传统的路线,只下载安装程序。我更倾向于安装 JetBrains Toolbox 应用程序,并使用它来安装和管理 PyCharm,以及我可能需要或想要的任何其他 JetBrains 工具。使用 Toolbox 应用程序使我们能够轻松地更新 PyCharm,甚至在需要时执行干净的卸载。
我们学习了 PyCharm 三个版本之间的区别。免费的社区版在 PyCharm 支持的项目类型和集成工具方面有限制。当我们创建新项目时,我们没有获得任何项目模板选项。只支持“纯 Python”项目。这并不意味着我们不能创建任何类型的项目;这仅仅意味着设置不同类型项目的繁琐部分不是由 IDE 为您处理的。
专业版支持更广泛的工具集,包括完整的项目模板和扩展的 Web 开发和科学工作负载工具。虽然专业版不是免费的,但为个人和企业开发者提供了许可选项。还有通过申请程序获得免费专业许可的选项。例如,微软 MVP 和其他认可的专业人士、开源开发者以及大学教授。在 JetBrains 网站上经常有针对初创公司、教育机构和其他人的折扣优惠。不幸的是,书籍作者并未列出。
PyCharm 的第三个版本是教育版。这是一个特殊版本,允许在 IDE 中创建和播放交互式课程。如果您使用 PyCharm 教授 Python,您可能不需要这个版本。您应该使用社区版或专业版,并利用 JetBrains 为教师提供的免费软件优惠。
一旦我们安装了 PyCharm,我们就开始配置应用程序。突出了一些重要的配置选项。我们可以几乎自定义 PyCharm 工作的各个方面。流行的设置包括设置 IDE 的颜色主题、字体大小和代码开发设置。在我们的案例中,我们设置了编辑器使用 PyCharm 附带的带有连字符的字体 Fira Code。我们还配置了代码风格设置,在 79 个字符处进行硬换行,以保持与PEP 8标准的合规性。
在一切设置完成后,我们通过创建一个简单的纯 Python 项目并使用 IDE 的运行按钮运行它来检验一下。
最后,我们使用 GitHub 账户设置了 PyCharm,并克隆了本书的示例代码仓库。我们在很短的时间内完成了很多事情。到目前为止,您已经准备好使用 PyCharm 开发软件了,而我们才刚刚进入第二章!
随着我深入探讨下一章中与 Python 虚拟环境一起工作的内容,请继续跟随我。虚拟环境被认为是最佳实践,它们提供了一种在本地开发环境中隔离项目需求的方法。正如您将看到的,PyCharm 消除了记住五六条命令以及安装额外库来快速启动和运行项目的需求。
问题
-
使用 JetBrains Toolbox 应用程序安装和管理 PyCharm 安装有什么好处?
-
PyCharm 社区版和专业版之间主要有哪些区别?
-
PyCharm 社区版的主要限制是什么?
-
如果你使用的是 PyCharm 社区版,你还能不能使用 Flask 等框架开发项目,这些框架似乎只在专业版中支持?
-
将您的 GitHub 账户链接到 PyCharm 有什么好处?
-
你已经去书的仓库给它点过星了吗?如果你分叉了仓库,如果我有任何更改,你会收到通知。我怀疑这种情况不会发生,因为像你的代码一样,我的代码通常第一次就能完美运行。不,我做不到。我尽力了。
进一步阅读
一定要查看这本书的配套网站:pycharm-book.com
。
设置 Python 的说明:www.maddevskilz.com/pages/python-landing-page
PEP 8 – Python 代码风格指南: peps.python.org/pep-0008/#maximum-line-length
第二部分:提高生产力
这一部分涵盖了从入门到高级的概念和技术,这些概念和技术有助于在 PyCharm 中处理 Python 项目时的生产力。读者将能够了解项目、Python 解释器和虚拟环境管理的动态选项,以及如何进行良好的编程实践,如版本控制、测试、调试和性能分析,以及 PyCharm 如何简化这些流程。
这一部分包含以下章节:
-
第三章, 自定义解释器和虚拟环境
-
第四章, 在 PyCharm 中轻松编辑和格式化
-
第五章, 在 PyCharm 中使用 Git 进行版本控制
-
第六章, 无缝测试、调试和性能分析
第三章:自定义解释器和虚拟环境
上一章重点介绍了配置选项。配置选项旨在帮助您根据项目定制工作环境,同时也允许根据个人喜好进行定制。这些选项是 PyCharm 成为一款优秀工具的许多原因之一。PyCharm 的另一个优秀特性是解释器和虚拟环境的定制。
本章将涵盖以下主题:
-
在 Python 中使用虚拟环境的重要性
-
如何使用
virtualenv
手动创建虚拟环境 -
如何使用 PyCharm 创建虚拟环境
-
如何在 PyCharm 中处理现有的虚拟环境
-
如何使用 PyCharm 添加和删除第三方库
-
如何导入 PyCharm 外创建的项目
-
如何在运行配置中处理虚拟环境设置
到目前为止,你们中的许多人至少安装了一个 Python 解释器。它可能随系统一起提供,例如在 macOS 或 Linux 中,或者你自己在 Windows 上安装了它。Python 是一种解释型语言——这意味着编写的代码只有在运行时才会真正被评估。让我们花一点时间来描述这意味着什么,通过将 Python 与其他一些语言进行比较,以及它们是如何执行的。
在你的计算机上执行编写好的代码有三种常见方式,如下所示:
-
编译
-
解释
-
带运行时的中间编译
在我们深入探讨这些之前,让我们短暂地回顾一下历史。我知道这是历史,但请跟我来。在 1522 年,整个西欧占主导地位的宗教是罗马天主教。这是该教会历史上动荡不安的时期,但我们将暂时将其放在一边。相反,我们将关注一位名叫马丁·路德的德国牧师。在当时,德国人民完全依赖于教会机构来满足他们的精神需求。在 1522 年,《圣经》只提供希腊语和拉丁语版本。当时被监禁的路德将旧约从希腊语翻译成了德语的通俗拉丁语。这对德国人来说是一个转折点。当时的印刷商抢购了它,并印刷了数百份,以便每个家庭都能阅读。我将用它做一个类比。
执行书面代码的第一种方法是编译。这类似于马丁·路德将希腊语翻译成德语的过程。大多数无法编写代码的人看到编程语言时会挠头,心想:“对我来说这是希腊语!”同样的事情也发生在计算机上,因为计算机也无法理解你的代码语言。计算机不会说 C 或 C++或 Java。相反,程序员用一种与人类语言相比简单且易于翻译的语言编写了某些内容。编译指的是将你的文本代码文件翻译成一次性的二进制格式,这种格式是计算机可以理解的。编译语言的例子包括 C 和 C++。代码文件会通过编译器运行,编译器将代码翻译成一种新的格式,只进行一次。这个过程产生了一个新文件,与代码分开,对计算机来说是有用的。为了完成我们的类比,在将希腊语翻译成德语之后,印刷的书籍就是编译步骤的结果。阅读圣经的能力对德国人来说是有用的。
与第二种代码执行形式相对比的是解释型语言。在路德翻译之前,圣经在教堂中被即时翻译。牧师打开圣经,在朗读的同时将文本翻译成德语。这就是解释型语言,包括 Python 的工作方式。当你运行python main.py
时,Python 解释器会打开代码文件,读取代码,将其翻译(编译),并在逐行读取的同时执行指令。
对于那些对编译理论非常熟悉的人来说,这可能是一种过于简化的说法,但对于我们大多数人来说,这个类比是成立的。Python 在过程中通过缓存和优化一些翻译工作,以便可以再次使用。总的来说,每次程序运行都呈现一个新的解释。解释型语言的例子包括 Python、JavaScript 和 Lua。
最后一种代码执行类别介于其他两种之间。例如 C#和 Java 这样的语言使用编译步骤。与常规编译器不同,结果并不是一个可以在电脑裸机上独立运行的文件。相反,它产生了一种中间格式,可以被运行时读取。
回想一下你在学校的时候。一个常见的做法是教授或老师向全班授课,而学生们做笔记。一个好的学生可以从笔记中复述讲座的内容。这些笔记是讲座的简略形式。在这个类比中,老师是程序员,学生是中间编译器。编译器的输出,希望是一套出色的笔记,这将使学生能够在考试中取得好成绩。你的代码文件(讲座)被转换成代码的紧凑、二进制版本(你的笔记)。当这个中间文件执行时,运行时基本上“查看笔记”,并使代码能够在计算机上运行。这种方法的好处是中间编译可以在各种平台上运行,而无需重新编译。用 C 语言编写的程序可以在各种平台上运行,从英特尔和 ARM 到旧平台如大型机。然而,为了实现这一点,你必须在该平台上重新编译程序。可执行文件只能在编译它们的平台上运行。在运行时系统中,运行时被转换并使其能够在不同的平台上运行。中间编译随后在任何支持运行时的平台上运行。你可以编译一次,然后分发到任何地方。
对于我们的目的,Python 是一种解释型语言,我们需要一个解释器。此外,PyCharm 需要了解一些关于解释器的情况。本章全部关于你如何进行介绍的方法。PyCharm,认识解释器。解释器,认识 PyCharm。
技术要求
为了继续阅读本章,以及本书的其余部分,你需要以下内容:
-
安装并运行良好的 Python 解释器。我将使用来自
python.org
的最新版本。 -
安装了
pip
和virtualenv
。当你在 Windows 上安装 Python 时,这些会自动安装,macOS 系统上默认包含它们。如果你使用 Linux,你需要单独安装包管理器,如pip
,以及虚拟环境工具,如virtualenv
。我们的示例将使用pip
和virtualenv
。 -
安装并运行良好的 PyCharm 副本。安装过程在第二章,“安装和配置”中已有介绍,以防你在书中间开始阅读。
-
本书从 GitHub 提供的示例源代码。我们在第二章,“安装和配置”中介绍了如何克隆代码。你可以在
github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-03
找到本章的相关代码。
Python 有多种版本。我使用并参考了来自www.python.org
的 Python 3。记住——因为它是一个开源项目,所以有可能创建 Python 的替代版本。这已经多次尝试过,并且成功程度各不相同。以下是一些这些变体的例子:
-
安装中包含
numpy
、matplotlib
和pandas
。缺点是这种更大的安装会消耗你驱动器上的空间,但考虑到现在的存储价格,这可能不是什么大问题。如果你打算进行数据科学工作,这些库可能是需要考虑的最重要变体。 -
IronPython——这是一个为在 Microsoft .NET 运行时中运行而设计的 Python 变体。其影响超出了本书的范围。然而,IronPython 很有趣,因为它除了在.NET 环境中运行外,这个实现并没有受到全局解释器锁(GIL)的限制。GIL 阻止了代码中的高效多线程。如果你不了解 Python 中的这个限制,我将在本章的进一步阅读部分留下一个链接。
-
Jython——这个变体允许你的 Python 代码在 Java 虚拟机(JVM)中执行。
-
MicroPython——这个变体用于在物联网(IoT)和潜在的嵌入式项目中运行 Python 代码。
-
ActiveState ActivePython——这是一个商业支持的 Python 实现,特别关注 Windows 兼容性和执行。传统上,Python 的设计假设它将在 Unix 或 Linux 环境中运行。如果你打算在 Windows 服务器上运行你的 Python 代码,你可能需要考虑这个变体。
你可以在www.python.org/download/alternatives/
上官方找到这些文档。虽然这些变体中的任何一个都应该在 PyCharm 中工作,但大多数使用 IDE 的开发者都在使用纯 Python(来自python.org)或 Anaconda。随着你在 Python 中使用虚拟环境,你无疑会看到这些替代实现的不同选项。一般来说,我总是使用 Python 3 的纯版本。
虚拟环境
我们在第二章,“安装和配置”中定位了项目创建;然而,我们只是简要地提到了创建或设置matplotlib
的细节,它只在 Mac 上的 Python 3.6 上运行,但在 Windows 上,它在 Python 3.9 上稳定。当这个项目在进行时,另一个需要 Python 3.10 的项目开始了。
如果你严格使用 Python 的全局或系统级安装,在两个项目之间切换会非常困难。你将不得不调整环境变量并修复你电脑的 PATH
环境变量。然后,你还得尝试记住哪些第三方库是全局安装的,并希望这些库的要求不会在独立项目之间发生冲突。真糟糕。
虚拟环境允许你轻松地管理这些问题。虽然使用它们完全是可选的,但为每个项目创建一个虚拟环境被认为是最佳实践。在我们回到 PyCharm 之前,我想手动创建一个虚拟环境,这会很有趣。如果你想跳过这个步骤,也可以,但如果你之前没有这样做过,我认为这将让你对 PyCharm 在新项目开始时为你分担的一些工作有所欣赏。
手动创建虚拟环境
打开你的电脑终端,在你的驱动器上找到一个可以进行一些临时工作的地方。我们不会保留这个文件夹以供以后使用;我们只是会走一遍这个过程。然后我们将切换到 PyCharm,以便我们可以看到同一工作流程的更自动化版本。
我使用的是 Windows,并在我的主文件夹中有一个专门存放项目的位置;一个简单地叫做 Projects 的文件夹。打开电脑的终端程序后,在这个例子中是运行 PowerShell 的 Windows Terminal,我可以输入这个实验的命令。你不必使用 Windows,也不必使用 PowerShell。macOS 或 Linux 上的正常 zshell (zsh) 终端提示符或 Bourne Again Shell (Bash) 提示符工作方式相同。大多数命令在所有终端和操作系统上都是相同的。我将首先创建一个新文件夹,如下所示:
mkdir python-environment-demo
这将创建一个名为 python-environment-demo
的新文件夹。接下来,我需要更改目录(cd
)进入它,如下所示:
cd python-environment-demo
现在我已经在这个文件夹里了,我将创建我的虚拟环境。如果你使用的是 macOS 或 Linux,你很可能同时安装了 Python 2 和 3,而我们想确保基于 Python 3 创建虚拟环境。为了区分,你需要输入以下内容:
python3 -m venv venv
如果你使用的是 Windows,你可能只安装了 Python 3,所以命令是这样的:
python -m venv venv
这里,我们运行 python3
命令,并传递一个开关(-m
),这将执行 venv
包来在名为 venv
的新文件夹中创建一个基于 Python 3 的新虚拟环境。一旦命令执行完毕,我可以用以下方式确保它在 Windows 上工作:
dir
或者,在 macOS/Linux 上,我可以使用以下命令:
ls -a
你应该能看到你系统的输出。我使用的是 Windows,所以我的输出如 图 3**.1 所示:
图 3.1:检查我的虚拟环境创建是否成功的终端输出
你的虚拟环境不需要与项目的其余部分放在同一个文件夹中,但我通常这样组织是为了让虚拟环境以后更容易找到。
如果我打算使用 Git 或其他版本控制系统,我最好让版本控制系统忽略这个文件夹。你不应该将这个文件夹检入到你的仓库中。
使用虚拟环境的最后一步是激活它。在 Windows、macOS 和 Linux 中,命令略有不同。在 Windows 中激活虚拟环境的命令如下:
.\venv\Scripts\activate
在 macOS 和 Linux 中,它会是这样的:
source ./venv/bin/activate
如果你成功激活了虚拟环境,提示符应该会改变以显示当前激活的虚拟环境名称。请注意,如果你已经自定义了终端以防止这种情况发生,它可能不会工作。你可以在图 3.2中看到,对我来说一切正常,因为我的提示符前出现了(venv)
。上面的例子显示了 Windows 11 中的激活,而下面的例子显示了 Linux 中的激活命令,它与 macOS 中的相同:
图 3.2:我的虚拟环境已激活
当我准备好停止在虚拟环境中工作时,我可以在终端中输入以下命令来停用它:
deactivate
这需要一点工作。如果你已经这样做了一段时间,这并不是太糟糕——可能只是几分钟。然而,如果你不经常这样做,你将不得不查找所有内容,这可能会花费时间。你实际上只在新的项目开始时做这件事。有些人可能一年只做几次。现在,让我们回到 PyCharm,看看这一步是如何集成到新项目创建工作流程中的。
在 PyCharm 中创建项目(重新审视)
如果我们回到 PyCharm 并创建一个新的纯 Python 项目,你会看到创建虚拟环境的过程。在 PyCharm 中,让我们通过点击文件 | 新建项目…来创建一个新项目,如图 3.3 所示:
图 3.3:在 PyCharm 中创建新项目
这将是一个纯 Python 项目。不需要专业版来跟随。新项目对话框是我们之前在第二章,“安装和配置”中看到的,但这次我们将关注一些我们跳过的细节。图 3.4显示了 PyCharm 专业版的新项目窗口在左侧。PyCharm 社区版缺少项目类型菜单,因为它只能创建“纯 Python”项目:
图 3.4:专业版(左)和社区版(右)的新项目窗口的并排比较
我希望您关注的新项目对话框部分在图 3.5中被突出显示。这是屏幕上的这部分允许您设置您的虚拟环境。在第二章,安装和配置中,我们迅速跳过这部分并接受了默认设置。实际上,默认设置几乎与我们在上一节中完成的手动过程相匹配:
图 3.5:为说明目的而列举的 PyCharm 的虚拟环境设置
让我们考虑图 3.5中的编号标签:
-
这是新项目窗口中的 Python 解释器设置部分。如果您喜欢,可以通过旋转部分标签旁边的三角形来隐藏它,尽管如果我们这样做,我们就无法继续谈论接下来会发生什么。
-
这里有两个选项在起作用。您可以选择创建一个新的虚拟环境或指向您已经创建的一个。让我们专注于创建一个新的。
virtualenv
虚拟环境功能。通过下拉列表更改机制将更改屏幕内容以匹配您选择的虚拟环境脚本的设置。目前,让我们将其保留在Virtualenv,因为这是最古老的可能也是使用最广泛的。 -
项目文件夹内的
venv
。您可以在计算机上的任何位置设置位置;它不必在项目文件夹中。如果您有多个项目共享虚拟环境,因为它们共享依赖项,那么创建一个中央位置来保存环境是有意义的。大多数时候,我更喜欢将虚拟环境与项目文件夹放在同一位置,这样就不会对其位置产生疑问。 -
基本解释器字段允许您选择用于创建虚拟环境的 Python 安装。如果您在计算机上安装了多个 Python 版本,PyCharm 可能已经自动找到它。选择以 PyCharm 找到 Python 安装的位置的下拉列表形式呈现。如果它遗漏了某个安装,您可以点击省略号按钮(…)并导航到您想要使用的 Python 安装。如果您这样做,您需要导航到 Python 可执行文件并双击它。
-
继承全局站点包复选框处理您可能全局安装的任何第三方库。勾选此复选框将它们复制到您的虚拟环境中,以便在您的项目中本地使用。
-
对所有项目可用复选框允许您轻松在其他项目中找到并重用此虚拟环境。
使用现有的虚拟环境
有时候,你可能需要使用与另一个项目完全相同的精确要求来制作一个项目。在 PyCharm 中,你可以轻松地共享或重用虚拟环境。图 3**.5 展示了 PyCharm 中的 新建项目 对话框。
要使用现有的虚拟环境,你需要将默认设置从 新建环境 更改为 先前配置的解释器,如 图 3**.6 所示:
图 3.6:你可以通过更改项目的设置来将项目指向现有的虚拟环境
一旦你这样做,选择现有环境的选项就会变得活跃。有一个下拉列表可供选择解释器。它的工作方式与我们之前在创建新虚拟环境时看到的 基本解释器 下拉列表相同。如果你在 PyCharm 中创建了现有环境,IDE 会记住它。在这种情况下,我在创建 第一章 的演示项目代码时,使用了 PyCharm 创建了一个虚拟环境,《PyCharm 简介 – 最受欢迎的 Python IDE》。PyCharm 会记住使用 PyCharm 创建的虚拟环境,并将它们列出来。
如果你使用的是手动方法或其他工具,你需要使用 添加解释器 按钮将解释器添加到列表中。当你点击按钮时,你会注意到一些可能性,如 图 3**.7 所示:
图 3.7:当你点击添加解释器时,你有很多选择来添加你想要添加的环境的来源
到目前为止,我们严格限制讨论的范围仅限于添加存在于你本地计算机上的虚拟环境。有可能添加远程计算机、虚拟机(VM)、Windows Subsystem for Linux(WSL,它是一个虚拟机)或 Docker 容器上的环境。我们将在本书的后面部分详细讨论这些选项。
一旦你选择了包含 Python 可执行文件的 bin
文件夹。在 Windows 中,虚拟环境有一个 Scripts
文件夹,它将包含一个指向 Python 可执行文件的文件。在任何情况下,你的目标都是选择可执行文件:
图 3.8:你需要选择你想要使用的虚拟环境文件夹内的 Python 可执行文件
更改项目的解释器
当我处理一个持续了 6 个月或更长时间的项目时,我会使用的一个技巧是创建一个带有完全更新包的新虚拟环境。这样,我可以在不破坏我的生产就绪虚拟环境的情况下测试带有更新依赖关系的程序。我们将在本章稍后介绍包管理。现在,我想向您展示您的项目解释器设置在哪里独立于项目创建过程。我发现这有点不直观。它在 设置 中。您用于在所有项目中全局配置 IDE 的相同 设置 选项也用于设置特定于项目的设置,例如解释器设置。
无论您的理由是什么,您都可以更改解释器,以及由此扩展的项目中使用的虚拟环境。您可以通过点击 UI 左上角的齿轮图标找到项目设置,如图 图 3.9 所示:
图 3.9:通过点击齿轮图标,您可以找到全局和项目设置
一旦您选择 设置,您将被带到我们在 第二章 中研究的全局设置屏幕,即 安装和配置。项目属性也存储在这里,如图 图 3.10 所示:
图 3.10:项目设置位于列表中间
在屏幕左侧选择解释器设置,并使用我们第一次设置虚拟环境时使用的相同机制更改解释器。您可以创建一个新的环境或浏览到一个新的环境。别忘了,当您浏览到一个新的环境时,您需要选择 Python 可执行文件,而不是 Python 安装所在的文件夹。我有时会忘记这一点,然后我会 wonder 为什么选择对话框不会消失。它正在寻找一个文件,即 Python 可执行文件,而不是一个文件夹。
激活 virtualenv
在本章前面的手动练习中,我们使用 virtualenv
从头开始创建了一个虚拟环境。一旦环境创建完成,您必须激活它才能使用它。一个可能的问题可能是“我该如何激活环境?”这是一个美丽的答案:您不必这样做。当您在 IDE 中时,环境设置始终存在,并且始终得到尊重。当您从 Python 包索引(PyPI)添加新的第三方包,我们将在稍后介绍,它们会根据您的环境设置自动放置到正确的位置。
当我们运行我们的程序时,我们可以覆盖 运行配置,但默认情况下,它使用项目的环境设置。坦白说,在运行配置中覆盖的情况很少有用。话虽如此,我将在本章后面向您展示如何覆盖运行配置。
使用集成终端
另一个需要担心虚拟环境是否激活的地方是在终端内部。诚然,PyCharm 无法帮助你处理系统终端。然而,PyCharm 确实有自己的终端窗口标签页。如果你使用 PyCharm 的终端,项目的解释器设置会自动应用。你不需要手动激活任何内容。既然我们提到了它,让我们看看 PyCharm 中的集成终端。你可以在视图菜单中找到显示终端的菜单项,如图图 3.11所示。我个人在终端上花费了很多时间,所以我将键盘快捷键记在了肌肉记忆中:
图 3.11:PyCharm 的集成终端可以在视图菜单中找到
调用菜单选项会弹出终端,正如承诺的那样,你可以在图 3.12中看到它尊重我们项目的虚拟环境设置:
图 3.12:你可以看到终端已经激活了你的虚拟环境
如果提示符中(venv)
的存在不足以证明它已经工作,我在图 3.12中提供了更多内容。如果你使用 macOS 或 Linux,你可以使用which
命令找到任何可执行文件的路径。抱歉,Windows 用户:在您的操作系统上没有等效的命令。which
命令的结果是,它显示当我运行python3
时,它使用的是指定路径上的那个,而不是系统范围内的安装,通常在/usr/bin/python3
。
在控制窗口中与 REPL 一起工作
PyCharm 中一个可能让你怀疑环境设置是否被尊重的最终工具是单独的python3
。PyCharm 为你提供了一个集成工具,因此你不必这样做。要进入控制台,使用你用来进入终端的相同菜单。我在图 3.13中向你展示了它。
图 3.13:可以通过视图菜单调用 Python 控制台
你的控制台不仅了解你的解释器设置,而且还了解你项目的内容。你可以在控制台中直接导入和测试项目中的任何内容。我发现这在追踪我们有时遇到的可怕的“模块未找到?你什么意思它没找到?!它就在那里!我正看着它!”问题时很有用。它也适用于实验 Python 中我们发现的一些更简洁的语法,例如正则表达式(regexes)或列表推导式。图 3.14中有一个小例子:
图 3.14:正在使用的控制窗口来测试一个简单的正则表达式
我在图 3**.14中尝试的代码不会在谷歌的 Python 面试中给人留下深刻印象:
import re
title = "Hands on Application Development with PyCharm"
test = re.search("Hands", title)
我将每一行都输入到控制台中。记住——这是一个实时编码视图,而不是一个你可以执行的文件。每次我按下Enter,我刚才输入的表达式就会被评估,并且当适当的时候,结果会出现在右侧的调试窗口中。把它想象成一个调试会话,其中你有一个右侧的监视窗口,而你输入到>>>
提示符旁边的每一行都成为了一个即时断点。
在评估了第三行后按下Enter,我可以看到test
变量有一个re.Match
对象作为其值。多看一会儿窗口,你可以看到字符位于位置(0,5),或者字符 0 到 5 表示为一个单一的元组(regs
)。
在控制台中尝试复杂的代码片段比通常的过程节省时间,通常的过程包括运行程序、阅读print
语句、进行更改,然后再次尝试,这占用了我们作为开发者的大部分时间。然后,你忘记移除print
语句,你应该这样做,因为它们可能会严重减慢长时间运行的脚本。
控制台立即执行所有操作并将结果反馈给你。它比print
语句详细得多,因为你可以使用调试窗口查看任何对象的内部结构,而不仅仅是看到字符串。在控制台窗口中做一些调整后,你可以回到你的代码中并直接使用那些有效的工作。
与第三方包库一起工作
Python 以其“内置电池”哲学而闻名,这与许多其他语言相矛盾。Python 的创造者 Guido van Rossum 认为,一个强大而完整的标准库非常重要,并且语言应该能够几乎完成任何任务,而无需使用任何第三方依赖。
我所说的第三方依赖是指那些设计用于执行 Python “开箱即用”未实现的专业功能的、外部于 Python 的库。换句话说,Python 为自己设定了一个非常崇高的目标。它应该能够仅使用所谓的 Python 标准库就完成任何事情。
自然,这个目标永远无法完全实现。Python 的标准库非常完整。然而,最终你会发现标准库要么无法完成某些功能,要么其实现方式不如预期那么易于使用。让我们看看几个广泛使用的第三方库的快速示例,并讨论为什么你可能想要使用它们。
让我们从requests
库开始。这是一个你可以在 PyPI 上找到的第三方库,网址是pypi.org/project/requests/
。每种现代语言都有类似的东西。JavaScript 有Node 包管理器(NPM),网址是npmjs.com
。PHP使用Composer从packagist.org/
安装包。.NET 语言使用NuGet,包注册在nuget.org
。Python 有pypi.org
。这是一个集中列出的第三方模块和库,你可以将其添加到项目中,通常免费。从比喻的角度来说,这个想法是为了防止我们每次想要建造一辆新车时都要重新发明轮子。由于我在我的日常工作是从事软件即服务(SaaS)项目,我发现自己需要一种简单的方法来发送超文本传输协议(HTTP)请求。我的 Python 脚本能够调用某个网络服务并处理结果的能力至关重要,这是一个非常常见的任务。
Python 的标准库提供了一些完成此任务的方法。我认为最明显的是标准库中的urllib
。以下是从urllib2
文档中的示例代码docs.python.org/3/howto/urllib2.html
:
import urllib.parse
import urllib.request
url = 'http://www.someserver.com/cgi-bin/register.cgi'
values = {'name' : 'Michael Ford',
'location' : 'Northampton',
'language' : 'Python' }
data = urllib.parse.urlencode(values)
data = data.encode('ascii') # data should be bytes
req = urllib.request.Request(url, data)
with urllib.request.urlopen(req) as response:
the_page = response.read()
首先,我们导入urllib
包的两个部分,记住它实际上是urllib2
。然后,我们创建一个url
变量。如果你在样本中想知道cgi
引用是什么,那是在地址中的cgi-bin
。这种开发风格很久以前就被PHP、经典活动服务器页面(ASP)、Java 服务器页面(JSP)和 ColdFusion 等所取代。即使是这些,也随后演变成了使用更复杂但更容易编码的路由库的现代系统,这些库非常灵活。我们将在本书后面讨论 PyCharm Professional 中的 Web 应用程序开发功能时介绍这些内容。
一旦我们建立了url
变量,我们需要一些数据。在这个例子中,我们使用 HTTP POST
将数据发送到我们的 Web 服务器进行处理。这是通过values
变量完成的,它包含一个字典,模拟了你在超文本标记语言(HTML)表单中可能找到的字段和值。
一旦我们设置好,我们必须将那个字典解析成适合传输的格式。这是通过urllib.parse.urlencode
完成的。接下来,我们需要进一步将数据编码为美国信息交换标准代码(ASCII)。这是文档中的另一个时代错误。现代系统使用 8 位通用文本格式(UTF-8),因为 ASCII 只编码来自罗曼语系的语言,如英语、法语、德语、意大利语或葡萄牙语。世界上其他地方的人就不那么幸运了。UTF-8 处理今天全球普遍使用的所有字母表;这样,只有古埃及人被忽视,因为 UTF-8 不支持象形文字——至少,据我所知。
在我们对文本进行编码后,我们需要创建一个request
对象。我们将url
变量和数据传递给urllib.request.Request
构造函数。只有在那时,我们才准备好在示例的最后两行发送请求。
我们已经确定在 Python 的标准库中存在一种发送POST
请求的方法。然而,到目前为止,我们只处理了一个非常基本的需求。如果我需要发送一个经过身份验证的请求怎么办?我该如何处理会话或 cookies?我们能否使这个过程更加简单?我们可以使用requests
库。以下是一些示例代码:
import requests
requests.post('https://httpbin.org/post', data={'key':'value'})
我们之前的urllib
代码可以用requests
库表达为一个单行代码!像会话、cookies 等更高级的需求同样简单。文档是现代的,并包含有用的示例。像requests
这样的流行 PyPI 库有很好的文档,例如在realpython.com
网站上。我从realpython.com/python-requests/
借用了前面的示例,这是关于使用这个强大库的庞大而完整的教程的一部分。
所有这些的目的是指出,在 Python 的背景下,一个第三方代码库系统是必要的,或者至少是有用的——Python 是一种力求包含你所需一切的语言,但永远不会包含你想要的一切。标准库可能很接近,但永远不会包含我可能想要的每一件事。
另一个必要的第三方库的重要例子是关于科学和金融工作。Python 中的标准数学功能与其他任何语言相比大致相同。大多数语言在处理浮点计算方面并不非常精确。它们在处理数值数据的矩阵方面也不太细腻,而这是一种常见的需求。幸运的是,像numpy
和pandas
这样的第三方库存在,以填补这一空白。这些库为 Python 开发者打开了新的可能性,也是 Python 继续吸引新用户的主要原因之一。
在 PyCharm 中添加第三方库
PyCharm 包含一个 UI 屏幕,允许您管理当前项目的包。然而,如果您没有使用 IDE,您可以使用包管理器手动完成。例如,要将 numpy
安装到您的项目中,您可以使用以下命令的 pip
包管理器:
pip install numpy
您应该确保已激活项目的虚拟环境,以便正确执行此操作。否则,numpy
将全局安装。pip
安装程序并非唯一的安装程序。还有 easy_install
、pipenv
、poetry
,当然还有 conda
。我不会就它们的相对优点进行辩论,因为这取决于个人喜好。PyCharm 支持它们所有。
PyCharm 的包管理器调用您环境设置中设置的包管理器。它提供了一个图形界面,您可以在其中搜索包,查看它们的描述,并在项目中安装、更新和卸载任何包。让我们看看。
回到 PyCharm,让我们回到我们的项目设置。点击Python 解释器,如图 3.15 所示:
图 3.15:解释器设置不仅显示了您的解释器,还显示了可用于该解释器的包列表
屏幕显示了当前项目正在使用的解释器。我们之前看到我们可以随时更改此环境。现在,我们将关注占据屏幕大部分空间的空包列表。这就是我们在安装包后能够与之交互的地方。
让我们安装 numpy
包。首先点击 numpy
。随着您输入,您将看到来自 PyPI 的满足您搜索条件的包列表。点击 numpy
条目并查看描述。这是一个很好的主意,仔细检查描述,因为如果您拼写搜索词错误,您可能会得到错误的包。这确实是 numpy
库,所以我会通过点击对话框底部的安装包按钮来安装它。稍等片刻,我会看到一个消息,如图 3.16(位于灰色安装包按钮上方),表明包已成功安装:
图 3.16:底部的显示指示包已成功安装
继续关闭对话框以返回到项目设置。
在 PyCharm 中移除第三方库
在图 3.17 中的箭头旁边,numpy
包旁边有几个按钮:
图 3.17:管理您的包安装的工具
您可能可以猜到它们的作用。pip
可以升级到版本 23.0.1。点击向上箭头即可完成此操作。眼睛按钮会显示预发布包版本,如果您想尝试包的最新版本。
现在我们已经了解了包管理 UI 的功能,我想暂时偏离一下话题,谈谈它做得不好的地方。在我看来,PyCharm 当前版本没有很好的方法来处理requirements.txt
文件。
使用 requirements.txt 文件
在使用 Python 项目时,一个最佳实践是使用名为requirements.txt
的文件来列出项目的第三方库依赖。如果您是从零开始创建项目,您可以使用终端中的pip
生成requirements.txt
文件,命令如下:
pip freeze > requirements.txt
图 3.18显示了刚刚生成的requirements.txt
文件的内容示例。您可能需要仔细查看才能看到它,因为它只有一行长。您可以看到包名以及numpy==1.24.2
的版本要求。如果您使用的是不同的包管理器,请查看生成您的requirements.txt
文件的具体过程:
图 3.18:显示我们项目第三方依赖关系的示例 requirements.txt 文件内容
如果您已克隆现有项目并且它包含requirements.txt
文件,当您打开项目时,PyCharm 将识别它,并建议将内容安装到您的虚拟环境中。PyCharm 还会在您工作时监控您的项目。如果您添加了新的依赖项,无论是通过我们讨论的 GUI 还是从终端手动添加,PyCharm 都会自动建议将这些依赖项添加到您的requirements.txt
文件中。
新的 Python 包窗口
对于使用最新版本 PyCharm(该版本使用新的 UI)的用户来说,新增了一个Python 包窗口。它在不同的位置和布局中添加了与解释器设置窗口类似的功能。显然,他们在我之前提到 Python 环境设置在通用设置窗口中不够直观时,是在我的肩膀后面阅读的。虽然我怀疑我无法因此获得认可,但您可以在更多工具窗口的省略号中找到新的Python 包窗口,如图3.19所示:
图 3.19:更多工具菜单包含新的 Python 包窗口
当您点击Python 包图标时,将出现一个新窗口,如图3.20所示:
图 3.20:PyCharm 中的新 Python 包窗口
与“环境设置”窗口类似,“Python 包”窗口显示了在虚拟环境中安装的包列表,以及相应的版本号。有一个“添加包”按钮,允许您从 PyPI 或从存储库添加包。您可以使用搜索对话框搜索包,该对话框显示匹配的包列表。点击其中一个将显示该包的文档。这比“环境设置”窗口更进了一步,因为该窗口显示了项目的 Markdown 文档。您可以在图 3.21中看到一个示例:
图 3.21:在新的 Python 包管理器窗口中搜索 numpy 包
如您所见,我搜索了一个在 PyPI 上有 184 个匹配项的包。我选择了 numpy
包,PyCharm 显示了该项目的文档。有一个漂亮的“安装包”按钮和一个选择器,可以挑选我想要安装的版本。
我可以通过点击列表中的已安装包来删除已安装的包。版本号会与包的文档一起显示。在已安装版本号旁边的省略号下是“删除包”按钮,如图 3.22所示:
图 3.22:在 Python 包管理器窗口中,可以通过点击版本号旁边的省略号菜单选项来删除包
当我写这篇文章的时候,这个功能是如此新,以至于它几乎没能在这一章中体现出来。那天我在工作时偶然注意到了它。在我看来,它提供了一个不那么令人困惑且更方便的方式来处理 Python 包。
对虚拟环境重要的专业功能
PyCharm 专业版的一个主要特点是项目创建的便捷性,与更手动的方法相比。考虑一下 PyCharm 专业版的新项目窗口,它包含一系列项目类型,如图 3.23所示:
图 3.23:在新建项目对话框中,专业版列出了许多项目模板
Python 社区版没有这些项目选项。您只能创建纯 Python 项目,但公平地说,您仍然可以使用社区版创建 Flask 项目或其他列出的项目类型。然而,集成开发环境(IDE)不会帮助您完成这项工作。
让我们看看创建 Flask 项目的步骤。Flask 是一个库,它使我们能够轻松地创建动态网络应用程序。我们将在 第八章 中对其进行广泛介绍,使用 Flask 构建动态网络应用程序。现在,我们将比较在没有专业版(即没有任何 IDE)的情况下完成这项工作所需的工作量。
这些是手动设置项目的步骤:
-
为你的项目创建一个新的文件夹。
-
为你的项目创建一个新的虚拟环境。
-
激活虚拟环境。
-
使用你喜欢的包管理器安装 Flask 库及其依赖项。
-
创建一个 Python 文件来保存你的起始代码。
-
在网络上进行研究,找到一个简单的“Hello world”示例,并将其放入你的 Python 文件中。
-
创建一个脚本,可以设置任何必要的环境变量,例如
PYTHONPATH
,然后运行你的程序。
我敢打赌,你们中的许多人可以在 20-30 分钟内完成这项工作。我的问题是:你在什么时候对你的新项目想法最兴奋?是在项目的开始阶段!你准备好大干一场,但随后必须停下来花 30 分钟做这个繁琐的设置。这不是很多时间,但它是一个干扰。有可能遇到一些意外的障碍,这可能会耗尽你的热情。在极端情况下,有时,意外的障碍会延迟你几个小时或几天。PyCharm 自动化整个流程。你只需通过几个对话框屏幕,通常接受默认设置,你的项目在 30 秒内就准备好了。你可以立即通过修改 IDE 生成的起始模板来开始用代码实现你的想法。
专业版将帮助你在几乎不费吹灰之力的情况下生成更复杂的项目。IDE 将创建你的文件夹结构,一组基本的文件,带有编辑提示以开始,以及一个虚拟环境。PyCharm 甚至会为你安装需求。
将项目导入 PyCharm
可能不用说,但我还是要说。你可以将使用手动创建或使用其他 IDE 创建的现有项目导入 PyCharm。这看起来很明显,因为毕竟,这只是打开你电脑上的一个文件夹的问题。导入过程实际上就像在 PyCharm 中打开一个文件夹一样简单。然而,故事还有更多。当涉及到在没有 PyCharm 的情况下在其他机器上运行在 PyCharm 中开始或完全编写的项目时,PyCharm 是你的智能盟友。让我们看看这个实际操作。
在本书末尾克隆的源代码中,在 第二章 安装和配置 和 chapter-03
文件夹中,有一个我完全从命令行创建的项目。你可以在 图 3**.24 中看到我使用的流程:
图 3.24:我用来在 PyCharm 之外完全创建 Python 项目的终端窗口
我遵循的过程在这里描述得很清楚:
-
在一个名为
manually-created
的新项目文件夹中,我使用python3 -m venv
命令创建了一个新的虚拟环境。 -
我使用
source venv/bin/activate
命令激活了虚拟环境。在这种情况下,我在 Linux 上工作。这个命令在 Mac 上同样适用。如果我在使用 Windows,它将是.\venv\Scripts\activate
。 -
接下来,我安装了一个名为
tqdm
的第三方模块。你可以在pypi.org/project/tqdm/
了解更多信息。这个模块的简短版本用于轻松格式化程序的终端输出,包括一个看起来很棒的进度条。你可以在图 3.24中看到我运行程序后稍低处的一个块状进度条。我有点跑题了。 -
我使用
nano
向一个名为main.py
的新文件中添加了一些代码。我稍后会展示其内容,但我所做的只是从之前链接的pypi.org
页面上的tqdm
文档中复制粘贴了一个示例。如果你想知道-l
开关的作用,它会在nano
编辑器中显示行号,这使得nano
成为一个快速、易于使用的代码编辑器。 -
我使用
python main.py
命令运行了程序。 -
在确认一切工作正常后,我创建了一个
requirements.txt
文件来记录我的程序对tqdm
模块的依赖。pip freeze
命令会将依赖关系图输出到屏幕上。为了将其转换成我可以包含在 Git 仓库中的文本文件,我使用>
将输出重定向到requirements.txt
,使得完整的命令看起来是这样的:pip freeze > requirements.txt.
这种命令行设置工作对我们来说微不足道。事实上,我建议你通过为自己买一条新的工装裤并带着新的自信姿态来庆祝这一点!如果你自认为是非工装裤穿着的有感知的生物,我希望你能发挥想象力来翻译我的意图。转念一想,为了避免泛化,你可能不应该从我这儿得到关于着装或社交行为的建议。传统上,着装建议并不是我的强项。
警告
如果你是我写的第一本书的读者,我坚持的一个基本规则是,所有的双关语都是有意为之。
幸运的是,使用 PyCharm 之外创建的项目是 PyCharm 的一个强项。由于项目已经在 PyCharm 之外创建,让我们看看 PyCharm 对此有何看法。让我们在 PyCharm 中打开chapter-03/manually-created
文件夹。为了使事情更加清晰,我在图 3.25中展示了项目的打开对话框。25*:
图 3.25:打开在 PyCharm 之外创建的手动创建的项目
对于这个项目,我做了我通常不会做的事情。虚拟环境(venv
)文件夹包含在 git 存储库中。通常情况下,它不应该在那里,但为了您能看到接下来的小魔法,它需要在那里。
打开项目文件夹并观察会发生什么。如果您在寻找软件赋能的震撼展示,可能会觉得有点令人失望。甚至不值得截图。项目打开了,它就静静地在那里。这就是酷的地方。PyCharm 可以看到您的虚拟环境文件夹,因此它会自动为您设置项目。您可以在项目设置中验证所有这些,如图 3**.26所示:
图 3.26:导入的项目无需我们任何努力就具有正确的解释器设置
从存储库导入克隆的项目
我们之前的示例展示了手动在电脑上创建的项目导入。更有可能的是,您将导入从 GitHub 或其他存储库克隆的项目。这些克隆的项目将不会包含虚拟环境文件夹。因此,您需要做更多的工作来设置项目。
我将重用同一个项目,但我会删除一些文件,这样 PyCharm 就不会记住关于项目的任何信息。
首先,如图 3**.27所示,在 PyCharm 中关闭项目:
图 3.27:关闭项目
接下来,使用您的操作系统删除图 3**.28中显示的文件夹。
图 3.28:删除 venv 和 .idea 文件夹以模拟从版本控制系统(VCS)克隆的项目
我们将删除 venv
文件夹和 .idea
文件夹。后者在 macOS 和 Linux 系统上将被隐藏,所以请确保您打开了查看隐藏文件夹的能力。.idea
文件夹是 PyCharm 项目文件夹,当您打开项目文件夹时自动创建。这两个文件夹删除后,PyCharm 现在几乎不知道曾经打开过项目。文件夹仍然会出现在您的最近项目文件夹中,但这没关系。再次用 PyCharm 打开文件夹。在 PyCharm 中打开项目的结果如图 3**.29所示:
图 3.29:导入 PyCharm 外部创建的项目会提示创建虚拟环境
PyCharm 看到了requirements.txt
文件,但它找不到合适的虚拟环境,因为项目中没有包含 Python 可执行文件的子文件夹。PyCharm 会提示你为项目创建一个环境。在我的情况下,默认是本地文件夹venv
。它将使用在/usr/bin/python3.10
找到的 Python 3.10 可执行文件作为虚拟环境的基础,并且它看到了requirements.txt
文件。如果点击确定,PyCharm 将设置虚拟环境并为我安装依赖项。
处理无效的解释器
没有 IDE 是完美的。有时,PyCharm 可能会对使用哪个虚拟环境感到困惑。这通常只在你使用 PyCharm 未创建的代码设置新项目时发生。这是一个边缘情况,但迟早你会遇到它,所以我想要花一分钟来谈谈这个场景。也许你正在准备写一本关于 PyCharm 的书。好吧——这很可能主要发生在我身上。假设你正在准备一个演示。也许你正在设置一个培训课程,并且你对事情做了一些调整或者调整了太多次。如果你像我一样,你喜欢事情完美。你已经努力设置了一个项目,你看到了无效环境的列表,例如图 3.30中的:
图 3.30:无效解释器错误会发生在我们所有人身上
太糟糕了!有一个难看的红色消息说基本解释器无效。我们需要解决这个问题。好消息是,你知道该怎么做!首先,让我们解决你的 immediate 问题。你需要设置基本解释器,以便 PyCharm 可以完成导入你的项目。首先尝试的是基本解释器的下拉列表。在所有可能性中,你的全局 Python 安装将出现在列表中。如果它确实如此,选择它然后点击确定,PyCharm 会做它的事情。
如果找不到,你仍然可以创建一个有效的环境。你只需以我们从头开始创建项目时的相同方式创建一个虚拟环境。你需要取消你看到的图 3.32中的对话框,并使用项目设置添加一个新环境。
点击文件 | 设置进入设置对话框,找到项目设置,如图图 3.31所示:
图 3.31:最坏的情况(这种情况不常发生,但确实会发生)
这是最坏的情况!列表没有显示基本解释器,唯一的选择是我们的无效虚拟环境。诚然,你必须努力工作才能让 PyCharm 这样做,但这种情况通常在你需要 PyCharm 在很多人面前工作时正好 5 分钟前发生。我在这里为你服务。
在下拉菜单的底部是显示所有…选项。这将允许我们修复一切!点击它!您将看到一个非常有用的对话框,如图 3.32所示:
图 3.32:Python 解释器对话框允许您在一个地方管理所有 Python 环境
注意图 3.32中的箭头。您可以使用+和–图标来添加和移除环境。您可以选择无效的环境,并用–图标将其移除。接下来,您可以使用+图标添加一个新的、有效的环境。添加新解释器的流程与我们在创建新项目时从头开始创建的流程相同。我们已经覆盖了该过程,所以不会再重复了。
使用运行配置
PyCharm 中的运行配置允许您从 IDE 中设置不同的程序运行方式。运行配置的核心是在主窗口顶部的一组蓝色按钮。按照图 3.33中的箭头操作:
图 3.33:屏幕顶部有几个运行按钮,以及一个下拉列表,允许您处理各种运行配置
由于我们已经在之前打开了手动创建
的项目,为什么不继续使用该项目进行探索呢?我在图 3.33中指出的那一组按钮包括一个运行按钮、一个调试按钮和一个下拉菜单,允许您设置和管理运行配置。当我们导入项目时,它生成了一个名为当前文件的运行配置。
在下拉菜单中设置此选项后,当您点击运行或调试按钮时,聚焦标签页中的文件将被执行:
-
这是因为当前聚焦的标签页中是
main.py
脚本。 -
这是运行(调试)按钮。点击此按钮将运行您选择的运行配置,并附加调试器。我们将在第六章中看到这一功能,无缝测试、调试和性能分析。
-
省略号按钮包含更多运行分析器和检查测试覆盖率的选项。我们将在后面的章节中介绍这些内容。
-
运行配置下拉菜单允许您选择运行配置。您可以选择创建和编辑现有配置以及创建新的配置。
现在,让我们专注于运行配置本身,因为这些配置有自己的环境设置,与项目中的其他部分独立。
点击显示当前文件的下拉菜单,然后点击编辑配置…,如图3.34所示。这将弹出一个用于管理您的运行配置的对话框。您可以在这里添加、编辑和删除您的配置。请注意,专业版在此窗口中提供的工具将比社区版更多。我将专注于专业版:
图 3.34:为手动创建的项目添加新的运行配置
对话框中有很多提示,允许您创建新的运行配置。让我们忽略那些,因为一旦添加一个,那些选项就会消失。如图3.35所示,点击对话框顶部的+图标:
图 3.35:点击+按钮添加新的运行配置(显示的列表为专业版,您需要向下滚动以找到显示的 Python 项)
如果您正在运行专业版,您将看到一个长长的选项列表,如图3.35所示。社区版的列表要短得多,因为它只为纯 Python 项目提供工具。
我将选择纯 Python 项目类型,因为这是我们都可以使用的。每个选项都可能根据您的选择显示不同的对话框,并具有不同的设置。纯 Python 选项看起来如图3.36所示:
图 3.36:使用纯 Python 模板的运行配置选项
这里有很多设置。让我们根据图3.36中提供的数字来回顾最重要的设置:
-
您应该给运行配置起一个名字。如果您不这样做,它将默认为脚本的名称。您可以使用一个用户友好的名称在这里;它不必以任何方式与运行文件相关。
-
这个设置可能是最明显的。它允许您选择要运行的脚本。您可以浏览到它或输入路径。您还可以通过点击脚本路径文本输入框旁边的三角形将此设置从脚本文件更改为模块名称。如果您将设置更改为运行模块,文件夹浏览按钮将变为省略号,您将能够浏览程序中的模块以找到您想要运行的模块。
-
argparse
,您可以将它们传递到这里。这个文本框相当交互式。注意+图标。点击它允许您调用工具中可用的某些宏。例如,如果您需要当前目录的路径,您将在宏列表中找到一个选项。 -
这些是环境设置。默认情况下使用项目设置。如果您需要在不同环境中测试运行软件,可以为您的运行配置设置不同的虚拟环境。您还可以在此处设置环境变量,而无需跳转到操作系统。如果您依赖环境变量来设置程序设置,这将很有用。如果您使用操作系统级别的环境变量来存储敏感数据,如密码或连接字符串,我建议不要在此处设置它们。这些设置可能会出现在项目配置文件中,这意味着它们可能会进入您的源代码库。没有什么比意识到您的黑客马拉松项目文件包含您的个人密码或您最喜欢的云提供商的访问令牌更糟糕的了。
SuperH4xr1337@some-evil-hacker-org.darkweb.net
(注意:希望这不是一个真实的地址)喜欢您这样做。有一些机器人会在 GitHub 上搜索这类东西,所以请小心。我个人使用配置文件来处理这个问题,使用许多env
库中的一个。如果您对此感兴趣,我将在进一步阅读部分提供链接。 -
如果您的脚本中的任何部分依赖于相对文件路径,您可以设置一个工作目录。
-
此复选框控制当您运行程序时,您的项目内容根是否注入到
PYTHONPATH
环境变量中。PYTHONPATH
环境变量控制 Python 解释器搜索模块的位置。内容根指的是项目结构设置,您在其中定义内容文件夹。这通常仅用于 Web 项目。内容文件夹可能包含图像或其他静态内容。PYTHONPATH
环境变量。我在微服务架构项目中工作过,那里这个功能很有用。有时,您可能有一个在其他项目中需要利用的模块,但它属于它自己的独立项目,而不是直接在当前项目中管理。您可以设置源根以包含具有依赖关系的其他项目,并勾选此框将使这些项目对您的运行程序可用。
如您所见,PyCharm 提供了许多选项和方式,您可以自定义程序运行的配置。如果您的应用程序有多个可执行脚本,您可以分别为每个脚本设置其自己的运行配置。
PyCharm 的项目文件
大多数集成开发环境(IDE)都有某种项目文件,用于包含项目级别的设置。如果你曾经使用过微软的任何 IDE,你可能还记得 Visual Studio Code 中的 .vscode
文件夹或 Visual Studio 项目中的 .vs
文件夹。NetBeans 和 Eclipse 等 Java IDE 也使用一组文件来包含它们的项目设置。PyCharm 同样在每个项目的文件夹中存储一组文件,称为 .idea
。这个名字可能看起来有些奇怪,直到你记得 JetBrains 是从只有一个 IDE 项目,即 IntelliJ IDEA 开始的。IntelliJ IDEA 以其作为 Java 开发的最佳 IDE 而闻名,无与伦比。它如此之好,以至于谷歌与 JetBrains 签订合同创建 Android Studio;鉴于 Android 应用程序完全用 Java 编写,这是一个自然的匹配。JetBrains 的所有 IDE 都有相同的血统。它们都是 IntelliJ IDEA 的后代,这就是为什么项目文件夹被称为 .idea
。记住——Windows 用户会清楚地看到这个文件夹,而在 Linux 和 Mac 上,任何以点(.
)开头的文件夹名都是隐藏的。
默认情况下,如果你试图编辑这些文件夹,里面并没有什么特别有趣的东西。事实上,如果你删除它们,PyCharm 会在你重新打开项目时简单地重新创建它们。
话虽如此,PyCharm 中有一些选项允许你将这些运行配置存储在这些文件中,如图 图 3.37 所示:
图 3.37:你可以选择将运行配置作为项目文件的一部分存储在 .idea 文件夹中
如果你选择这样做,你就有能力将 .idea
文件检入你的版本控制系统(VCS)仓库,并与你的团队共享。这意味着每个人都会有相同的配置,所以你可能想要召开一个会议,标准化你的项目结构,这样你就不会相互干扰。
摘要
本章全部关于为 Python 项目设置虚拟环境。每个项目通常都有自己的虚拟环境。我们使用虚拟环境来隔离我们的项目,防止其他项目的需求影响,并防止我们的全局 Python 安装被仅用于一个项目的许多全局包污染。
虚拟环境是 Python 解释器的副本,以及所有支持工具,例如包管理器和项目所需的第三方库。PyCharm 内置了创建和管理虚拟环境的功能。
我们第一次看到这些工具是在创建新项目时。PyCharm 会提示我们每次都创建一个新的虚拟环境。如果合适,我们也可以选择现有的环境。PyCharm 给我们提供了在任何时候更改项目虚拟环境的能力。
我们的虚拟环境还包含了我们项目所需的所有第三方库。你可以在pypi.org
找到数千个现成的模块。这些模块可以通过包管理器安装并用于你的项目中。最常用的包管理器是pip
,但还有其他,如conda
、pipenv
和poetry
。PyCharm 支持所有这些工具,当你创建虚拟环境时,你可以选择你喜欢的工具。
在选择了你的包管理器后,PyCharm 有一个 GUI,允许你查看、添加、删除和更新你的 PyPI 库依赖项。你可以在 PyCharm 本身的一般设置中找到这个 GUI,它包含在你的项目设置中。
PyCharm 可以轻松导入在 PyCharm 外部创建的项目。你需要做的就是打开代码所在的文件夹。PyCharm 会在该文件夹内创建一组名为.idea
的项目文件,然后提示你设置解释器,如果它无法在项目文件夹内找到解释器。PyCharm 还会寻找requirements.txt
文件,并为你安装依赖项。
当你准备好在 IDE 中运行一些代码时,PyCharm 提供了一个强大的运行系统,该系统使用在 GUI 中创建的运行配置。PyCharm Professional 附带许多项目类型,每种类型都有自己的运行配置设置。我们介绍了纯 Python 项目中发现的广泛设置,因为这是社区版用户可用的。运行配置可以管理——在某些情况下可以模拟——你通常需要在操作系统级别手动设置的广泛设置。
总的来说,PyCharm 为你提供了一套非常完整的工具,用于管理虚拟环境以进行开发和运行你的代码。在下一章中,我们将把重点转向你每天都会使用的核心工具,用于管理、编写和编辑你的代码。
问题
-
虚拟环境是什么?为什么它们很重要?
-
Python 社区使用哪些工具来创建虚拟环境?
-
PyCharm 支持哪些虚拟环境工具?
-
运行配置可以使用与主项目不同的虚拟环境吗?这可能会怎样有用?
-
PyCharm 将项目配置文件保存在哪里?
进一步阅读
-
替代 Python 实现:
www.python.org/download/alternatives/
-
The env
库:pypi.org/project/env/
-
Ajitsaria, A. 什么是 Python 的全局解释器锁(GIL)?:
realpython.com/python-gil/
-
一定要查看这本书的配套网站
www.pycharm-book.com.
第四章:在 PyCharm 中轻松编辑和格式化
列奥纳多·达·芬奇,这位伟大的画家和雕塑家,曾沉思他的雕塑在看到大理石之前就已经在采石场的石块中完全成型。达·芬奇解释说,他所做的只是移除那些对于形状不需要的大理石块。换句话说,他的杰作是在雕刻刀触及粗糙的石头之前,在他的脑海中完成的。在许多方面,你就是达·芬奇。你心中有一个完整的项目,渴望向世界展示你的杰作。你不用锤子和雕刻刀来编写代码,而是使用 PyCharm。在前几章中,我们完成了 PyCharm 的安装过程和配置。我们还为你的项目设置了解释器。接下来:探索你将用来打造杰作的主要工具,这些工具主要位于编辑器中。
到目前为止,你已经发现了编辑器中许多明显的功能。我们知道它自动处理许多 PEP-8 语法规则。我们知道我们得到了彩色编码的语法。我们还观察到,集成开发环境(IDE)将在编码的多个不同领域提供建议,从代码风格规则检查到自动补全。
本章将重点关注编辑器中不那么明显的功能。产品的文档提供了键盘快捷键和编辑器基础知识,这些内容在本章中不会讨论。相反,以下主题将被涵盖:
-
实时代码检查和自动修复,这允许你专注于开发目标,而不是 Python 编码的规则。
-
PyCharm 中的各种代码补全支持功能以及如何利用它们。通过使用这些功能,你将能够更快、更准确地编写代码。我们只会关注 PyCharm 自带而非需要插件如 Kite 或 GitHub Copilot 的第三方人工智能(AI)增强工具。这些将在第十五章中介绍。
-
重构工具,允许你通过耐心、纪律和良好的工具将你的代码打磨成杰作。
-
文档工具,将带你从“优秀开发者”的水平提升到“大师开发者”的水平。发明一个惊人的代码作品是一回事。记录它,让其他人从中受益,则将你的工作提升到了另一个层次。
技术要求
为了继续阅读本章和本书的其余部分,你需要以下内容:
-
一个已安装并正常工作的 Python 解释器。我将使用来自
python.org
的最新版本。 -
安装好的
pip
和virtualenv
副本。当您在 Windows 上安装 Python 时,这些会自动获得,而 MacOS 则将它们包含在每一个系统中。如果您使用 Linux,您需要单独安装包管理器,如pip
,以及虚拟环境工具,如virtualenv
。我们的示例将使用pip
和virtualenv
。 -
安装并运行 PyCharm 的副本。安装过程在第二章中已有介绍。
-
您可以在 GitHub 上找到这本书的示例源代码,地址为
github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-04
。我们已经在第二章中介绍了代码的克隆,安装和配置。
代码分析、检查和建议
智能代码补全对于任何编程工具的采用都是必不可少的。一个好的代码补全引擎的定义是它能够了解编程的高级方面,包括语言语法的具体细节。引擎还必须了解你所编写的程序的底层具体细节。许多增强型文本编辑器支持代码补全,但缺乏这种高级的复杂性。PyCharm 作为一个异常复杂的代码编辑器脱颖而出,它包含了代码编辑器的历史和现代方面,并且在智能代码补全方面超越了其他许多增强型文本编辑器。
代码补全最常见的形式是在您输入时匹配的单词的大列表。随着更多字母的输入,可能性的列表会缩小。Notepad++是一个被开发者广泛使用的增强型文本编辑器。当我太没有耐心等待 IDE 完全加载时,我认为它是快速轻松编辑的必备工具。图 4.1显示了我在输入一些 Python 代码的会话:
图 4.1:Notepad++使用一个非常简单的机制进行代码高亮和补全
工具并不能直观地知道正在输入什么;因此,我必须告诉它我正在用 Python 编写代码。在设置完语言后,它将尝试自动补全一切,尽管这样的练习显然是徒劳的。在图 4.1中,我打算输入以下内容:
import time
def test_code():
pass
这显然不会为我赢得任何 Jolt 奖项。您可以从截图看到,列表正在过滤一个已知的单词列表。它的唯一上下文参考点是知道该文件是一个 Python 文件。这并不非常有效,但总比没有好。
这样的系统几乎只是拼写检查器。虽然经验丰富的专业人士可能会嘲笑这样一个简单系统的有用性,但最早显示这种魔法般功能的 IDE 开创了一个我们现在不愿意没有的功能:代码完成。
PyCharm 在复杂度方面处于光谱的另一端。就像 Notepad++一样,PyCharm 能够识别构成 PyCharm 的关键词。然而,PyCharm 还能够洞察组成标准库的对象结构。“图 4.2”展示了我在 PyCharm 中输入一些代码,为这个示例创建了一个简单的文件:
图 4.2:PyCharm 根据其对时间库的理解进行自动完成
在这种情况下,我像在“图 4.1”中的 Notepad++示例中那样导入了time
库。我已经进行了一步,定义了我的函数,接下来我将简单地使用已经导入的time
库打印当前本地时间。正如您在“图 4.2”中可以看到的,PyCharm 正在根据对时间库的理解提供自动完成建议。
完成自动完成
一旦自动完成列表出现,您可以按Tab或Enter键选择高亮选项。您可以使用上箭头和下箭头键在列表中移动,或者用鼠标点击列表中的任何项。不过,如果您保持手指在键盘上,您的工作速度会更快。
到现在为止,您已经开始欣赏 PyCharm 提供的代码完成系统。让我们进一步探索这个重要工具的功能。
它 duzunt assewm yew cna spel
我们之前从 Notepad++中的简单单词列表示例已经与 PyCharm 自动完成引擎的更复杂功能形成了鲜明对比。让我们进一步挖掘。如果您的工具依赖于单词列表,那么一旦您的拼写出错,建议列表就会枯竭。实际上,单词列表方法要求您知道您要找的是什么,同时要求拼写正确。
在“图 4.3”中,您将看到一些不同之处:
图 4.3:PyCharm 找到包含您已输入文本的所有可能性
PyCharm 被设计成根据输入的字母提供建议。换句话说,当您输入cl
时,单词clock
可能会出现。如果您输入lo
、ck
或任何包含在匹配关键词列表中的连续字母,clock
单词也会出现。不需要拼写正确。只要接近正确,您要找的单词很可能会出现。
它理解您的代码
良好的代码补全功能能够根据所使用的语言和库来理解和自动补全。在我们的案例中,那就是 Python,与其他语言相比,Python 拥有庞大的标准库。Python 的设计理念是“内置电池”。与之相比,在 Node.js 中实现的 JavaScript,你只能获得文件和 HTTP 库。.NET 语言给你的是一个小的核心。对于 Golang 也可以这么说。大多数语言都需要你使用包管理器。PyCharm 能够做到这一点本身就非常出色。
PyCharm 作为一个优秀的 IDE,也能理解您所编写的代码。图 4.4 显示了autocomplete_demo.py
文件,我在其中添加了对no_problemo.py
文件的导入。no_problemo.py
文件中有一个名为perfection()
的函数。正如您所看到的,PyCharm 能够查看文件内部并提供关于我所编写代码的自动补全,而不仅仅是基于语言词汇表的自动补全:
图 4.4:PyCharm 在您所编写的代码上提供自动补全建议,以及标准 Python 语言和标准库
我得到了关于函数名的自动补全,以及关于方法签名的提示。如果您不熟悉“方法签名”这个术语,它仅仅是指函数或方法的名称、参数列表和返回值。如果您已经包含了类型提示,PyCharm 会提醒您函数或方法需要的参数名称和类型。这同样适用于模块以及类,如果您正在使用面向对象的编程(OOP)。
后缀代码补全
在 PyCharm 中,传统的代码补全已经被提升到了一个新的水平,但我们还没有完成。通常,在键盘上按住句号()键会触发一个列表出现。我们已经习惯了这个列表包含点之后可能出现的代码。然而,如果 PyCharm 能够给出点之前可能出现的代码建议呢?在图 4.5 中,我们看到一个后缀代码补全的例子,您可以在第四章*示例代码中的postfix_example.py
文件中找到:
图 4.5:PyCharm 中的后缀补全可以建议在点之前可能出现的代码,而不仅仅是点之后可能出现的代码
如果您看到.if
(if expr)作为紧跟一个明显是字符串类型的变量的可能选项而感到困惑,我并不怪您。.if
建议不是 Python 的一部分。它是一个后缀建议。如果您完成这个建议,您的代码将被转换。以下代码不是有效的 Python 语法:
b.if
因此,它被转换为以下内容:
if b:
想象一下所有可能的情况!然而,如果你不是那种富有想象力的人,请查看图 4**.6,它显示了 PyCharm 的后缀完成配置选项:
图 4.6:PyCharm 的后缀完成配置选项
自然,这些选项是完全可配置的。你甚至可以添加自己的!模板不仅限于 Python。你可以在列表中看到,有 TypeScript、JavaScript 和结构化查询语言(SQL)的配置。这是因为应用程序开发很少仅限于 Python 语言的范围。
嘿皮士完成
嘿,伙计!你想看看一些绝对超凡脱俗的东西吗?它被称为循环词扩展。不过,只有真正的老派人才会这样叫它。如果你想显得嬉皮士,你会称它为嬉皮士完成!
嘿皮士完成是通过按Alt + /(Windows 和 Linux)或⌥ + /在 macOS 上触发的。一旦触发,PyCharm 将索引你当前打开的所有文件,并根据该上下文中的单词提供自动完成建议。实际上,你正在使用最简单的自动完成形式;一个单词列表。单词列表是实时从你打开的文件中的单词生成的。它们不必是代码。纯文本文件、标记、Markdown 或任何文本都会根据简单的正向匹配出现在建议列表中。随着你输入,列表会缩小。你可以在图 4**.7中看到一个例子:
图 4.7:嬉皮士完成绝对超凡脱俗!
右侧的文本文件包含了我从fungenerators.com/lorem-ipsum/hippie/
生成的单词列表。不幸的是,结果发现,美国嬉皮士一代的许多标志性词汇无法在这个级别的书中印刷。我们的标准是最高的!因此,我编辑了列表,这就是剩下的所有内容。这些单词是随机的,所以请不要试图从它们中推断出任何含义,尽管有关于披头士专辑上存在秘密反向信息的所有指控。右侧的代码可以使用嬉皮士完成从该列表中扩展。为了使这起作用,我输入了print("pat
然后按了Alt + /。神奇地出现了单词patchouli!我希望这个例子对你来说是有价值的。我可能永远无法从我的键盘上消除那种气味!
你可以使用Alt + Shift + /或⌥ + Shift + /来启用反向循环词扩展,它从单词的末尾向后匹配到开头。如果当权者问我(他们没有,因为他们从不这样做)我会称这个为#GingerRogersCompletion
并引用这本书,它可能会流行起来!
嘿皮士小贴士
重复按 Alt + /(Windows 和 Linux)或 ⌥ + /(macOS)会在列表中循环。你可以一直按这个键组合,直到出现你想要的单词。
索引
在为 PyCharm 的各种代码补全技术提供动力方面,有各种引擎在发挥作用。你可能想知道它是如何工作的。我向你保证,这并不是黑暗魔法。理解它的关键是注意 PyCharm 加载项目时的行为。图 4.8 引导你的注意力到 PyCharm 应用程序窗口的底部。PyCharm 启动几个后台进程,这些进程会遍历你的代码并对每个字符进行索引。然后,索引被转换成一个内存数据库,该数据库被各种引擎使用:
图 4.8:注意 PyCharm 窗口的底部,以了解索引等后台进程何时运行
通常,我并不真的关心魔法是如何工作的,但这是值得提及的,因为有时 PyCharm 可能会显得缓慢或无响应。当 PyCharm 显得缓慢或自动补全功能不起作用时,检查 图 4.8 中指示的屏幕区域,看看是否有索引进程正在运行。如果你监控这类事情,你可能会注意到你的 CPU 会突然升高。这是暂时的。一旦索引过程完成,PyCharm 将再次变得响应。
节能模式
PyCharm 菜单中较为晦涩的条目之一,如 图 4.9 所示,是 节能模式:
图 4.9:节能模式菜单选项可以在文件菜单下找到
我记得第一次点击它的时候。我住的城市街道的街灯瞬间变得更亮了。几分钟前像锯子一样旋转的电表现在懒洋洋地转得比废弃的陀螺还慢。有一次,电力公司的一个家伙甚至打电话来感谢我为拯救地球做出的贡献。
好吧,我编造了所有那些内容,除了关于电力公司那人的部分。那绝对是真实发生的。早期示例中的 b.if
:
图 4.10:一条消息提醒你节能模式已开启,并且你将不会从 PyCharm 收到通常的水平支持
个人来说,我认为这完全是野蛮的!我们该如何工作呢?接下来,您会告诉我我没有访问互联网、Kite、GitHub Copilot、ChatGPT 或 Stack Overflow!您将剥夺我合理价格的packtpub.com订阅权,在那里我可以以一个低廉的价格阅读所有我可以读的电子书。然后您会拿走我的 Herman Miller Aeron 椅子,并限制我每天只能喝一杯 14 美元的中等大杯半脂三倍泡沫双份浓缩咖啡,上面撒上彩虹茉莉花味的糖霜!为什么您还要实施着装规定呢?打倒!打倒!打倒!
抱歉。我有点过于激动了。不用说,这可能是 IDE 中最不受欢迎的功能。也许如果您在国外航班上玩幽灵行动:断点耗尽了笔记本电脑的电池,着陆后突然接到老板的电话,需要立即修复某些东西,这可能会有些用。突然,您需要从剩余的 5%电池电量中榨取每一秒。当这种情况发生时,我真的很讨厌。
自定义代码补全
在 PyCharm 中进行自定义是一个持续的主题。展示您无法自定义的内容可能更容易,但我实际上还没有找到任何东西。代码补全也不例外。无论您更喜欢非常轻量级的体验,几乎不需要帮助,还是希望每行代码都有指导,PyCharm 都可以满足您的需求。
要打开代码补全的设置,请回到我们在第二章,安装和配置中探索的设置对话框。您可以通过点击文件菜单项并选择设置轻松访问它。这会弹出设置对话框。如果您还记得第二章,安装和配置,这个对话框是巨大的!我们正在寻找编辑器 | 常规 | 代码补全,如图图 4.11所示。11*:
图 4.11:代码补全设置允许您自定义 PyCharm 代码补全引擎的行为
您应该花点时间浏览一下这个屏幕上您可以做的事情。PyCharm 给了您很大的控制权!我承诺这不是一个对每个选项的繁琐记录。相反,我将您的注意力引向最流行和最有潜力的设置。
区分大小写
位于窗口顶部,此选项指定建议列表中的项目是否应该与您输入的内容的大小写匹配。例如,如果我想为 Python 中的KeyboardInterrupt
异常输入一个异常表达式,并且想要将正确的类名K
包含在建议列表中。在匹配大小写复选框旁边,您还可以选择仅匹配第一个字母的大小写,或者是否应该匹配所有字母。
我个人总是禁用此复选框,这样我只需输入,例如,小写的k
,就能利用代码补全功能。当我学习一门新语言或 API 时,此设置可以帮助我进行我称之为属性购物的练习。我不知道有哪些属性和方法可用,因此一个按字母顺序排列的列表可能会有所帮助。例如,每种编程语言都有某种形式的字符串处理类或库。可以非常肯定地说,该库将具有修剪函数,即从字符串的前面、后面或两边去除额外空格。这些toUpper
和toLower
方法都是防御性编程的重要组成部分。如果用户在密码输入时意外地在密码的开始或结尾处包含空格,这将导致用户体验不佳。如果您使用涉及用户输入的任何条件逻辑,那么将所有内容都转换为大写或小写将更容易处理。鉴于这些都是我们工作的基础,我们知道它们将在列表中,但每种语言都称它们为不同的名称。我从事的项目需要在不同两种或三种语言之间切换,很容易输入错误的功能名称。以将字符串转换为上档的方法为例。在 JavaScript 中,这样的函数看起来是这样的:
let foo = "some user option";
if(foo.toUpperCase() === "SOME USER OPTION"){
console.log("It matched!");
}
在 PyCharm 中相同的代码,我可能几分钟内就会创建,看起来是这样的:
foo = "some user option"
if foo.upper() == "SOME USER OPTION":
print("It matched")
为了有效匹配,我需要单词“upper”无论大小写都能匹配。
就像我们在代码补全中看到的一切,以及编程中的所有事物一样,这种做法也有其权衡。具体来说,如果匹配大小写被禁用,有时建议列表可能会包含许多更多的不相关选项,这使得找到正确的 API 变得更加困难。然而,同时,您将看到所有可能性的完整列表,这可以帮助您熟悉环境,有时还可以发现 API 中您未曾想象到的功能。
按字母顺序排序建议
正如其名所示,此选项允许您按字母顺序对建议列表中的项目进行排序。对于需要开发者仔细滚动查找所需内容的较长的建议列表,此功能非常有用。
在多次事件中,我们已经看到了 PyCharm 的动态特性,这一特性在此功能中再次得到了体现。具体来说,当在编辑器中与建议列表交互时,您可以通过点击建议窗口右下角图标来随时更改列表中项目的顺序,如图 图 4.12 所示。12*:
图 4.12:您可以在列表内部更改建议的排序方式
点击 …(省略号)元素,您可以更改建议的排序方式:按相关性或按名称字母顺序排序。
机器学习辅助补全
这个新选项既神奇又令人恐惧。默认情况下启用,PyCharm 将根据您的代码训练一个机器学习模型。这使得 PyCharm 能够基于您的代码以及成千上万个其他开发者的代码提出建议。传统的代码补全通常只会为您建议下一个关键字、属性、方法名或您即将输入的参数。如果 PyCharm 提供了由机器学习辅助的完整函数或代码块供您选择,请不要感到惊讶。您会在连接数据库、处理 pandas 数据框或验证用户输入等常见任务中看到这一点。
设置仅允许您开启 PyCharm 支持的各种语言。Python、JavaScript 和 TypeScript 默认启用。只有 SQL,一种用于处理关系数据库的特殊用途语言,默认未启用。我怀疑这是因为您在全局和项目级别都有关于您首选 SQL 语言的额外设置,这些设置有助于建议。考虑到 PyCharm 支持的数据库数量以及它们对未由标准化 SQL 定义的元素的不同实现,期望有一个紧密的建议列表会显得有些尴尬。我们将在 第十一章,使用 PyCharm 理解数据库管理 中稍后查看 SQL 和关系数据库。我保证您不会失望。
在 [...] 毫秒内显示文档弹出窗口
当您启用此功能时,您将看到文档以及建议的代码。您将能够理解您正在输入的代码的功能,而不仅仅是盲目接受建议。这对于新开发者来说是个大优点,无论是完全对编码新手还是对 Python 新手来说都是如此。此功能的优点是,您可以在将光标向下移动到项目时动态地查看所有建议项的文档。
当与具有相似 API 的类和方法一起工作时,这一点尤其有益。我们将在本章的最后部分讨论此功能,以及其他与文档相关的功能。
参数信息
滚动到 JavaScript 部分以下,如图 4.13所示,你会找到一个建议参数信息的选项:
图 4.13:滚动到 JavaScript 以下以找到显示参数信息的选项
第一个选项很简单。它控制了在出现建议之前必须经过的时间量。一般来说,建议是很好的,除非你在教学、进行代码审查或进行某种演示,在这种情况下,它们可能会让你的屏幕变得杂乱。有时提高时间限制是个好主意,这样建议或文档只有在停留几秒钟后才会显示。
第二个选项允许你切换显示完整方法签名。我喜欢这个功能。代码提示会显示整个方法签名,这样你可以一次性看到所有参数。
方法签名独特地定义了在声明它的作用域内的函数或方法。它包括函数的名称。以及函数参数的类型名称,最好还有关于返回类型的提示。这些并不特指 Python。实际上,在 Python 中它们有点模糊。现在让我们将其与使用更严格编程结构的静态语言,如 C#,进行比较。在 Python 中,你可以使用类型提示,这有助于开发者记住传递的参数的预期类型。让我们看看一个没有提示的方法签名的例子:
a = 5
b = 6
def add_two_ints(first, second):
return a + b
这是可以的。这段代码将按预期工作,开发者的意图是清晰的。让我们看看带有提示的相同函数:
a = 5
b = 6
def add_two_ints(first: int, second: int) -> int:
return a + b
这好多了!现在我们确切地知道了作为输入参数期望的类型,以及将要返回的类型。我之前说过,Python 中的方法签名是“模糊”的。我说这个是因为两个代码示例都会工作。Python 在编译过程中完全忽略了提示。提示只是由工具使用,使你的 Python 代码更容易阅读和理解。尽可能地在你的代码中添加提示将增强你和你队友在开启显示完整方法签名选项时看到的方法签名的方式。
代码分析和自动化修复
代码补全是最多代码编辑器和 IDE 的标准功能。正如我们所见,并非所有补全引擎都是相同的。分析引擎也是如此。从概念上讲,代码分析引擎是代码补全的扩展,如果不是在实现上。代码补全试图预测你正在编写的代码,并帮助你更快地完成。代码分析检查你已编写的代码,并试图确定代码在运行时是否有效。就像代码补全一样,这里存在不同的复杂程度,不同的过程检查不同的事情。
最简单的分析形式被称为代码检查。几乎每种编程语言都有代码检查工具,Python 也不例外。虽然有很多可供选择,但 PyCharm 默认使用流行的pylint
库。
代码检查器通过模式匹配的过程对代码进行隐式分析。有两种代码检查操作:逻辑检查和样式检查。逻辑检查查找代码错误、可能产生意外结果或副作用以及危险的代码模式。样式检查查找不符合常见约定的代码。在 Python 中,这个问题不太严重,因为该语言已经有一套严格的代码格式化规则,称为Python 增强提案#8。没有人会这样称呼它。了解的人只是简单地称之为PEP-8。
结合使用pylint
以及所有类似工具,你可以将其视为类似于常规文本中的拼写和语法检查器。代码检查器会查找拼写错误的关键字、格式不正确的代码和明显的语法错误。代码检查器还可以强制执行样式指南,尽管实际上 Python 已经设计得尽可能使代码易于阅读,并强制执行规则。
当指出代码中的问题时,这固然很重要,但如果工具还能提出甚至实施解决问题的方案,那就更有用了。对于人类来说也是如此。指出缺陷很容易,任何人都能做到。关于如何改正你的缺陷的好建议比批评更有用。因此,除了代码检查之外,PyCharm 还提供了一个系统,它会主动帮助你修复由代码检查器暴露的问题。
问题检测
当你编写代码时,PyCharm 会实时执行问题检测。我们之前提到的索引过程在这里也发挥作用,但我们会稍后再谈。首先,让我们关注编辑器中显示你问题所在位置的可见界面。有四个地方可以查看,如图图 4.14所示:
图 4.14:用户界面中的四个地方告诉你存在问题
编辑器的右侧空白处(1)会显示当前打开文件中所有问题所在的行。这个空白处是文件的一个压缩、微型表示。一个文件有数百行甚至数千行是很常见的。你可以点击空白处中看到彩色标记的区域,编辑器会滚动到该位置。
PyCharm 将问题分为三个基本类别:错误(红色)、警告(黄色)和弱警告(灰色)。这将在空白区域的顶部报告整个文件,并显示每种问题类型的计数(2)。除了颜色外,这个区域还提供了不同的形状。错误图标是一个带有感叹号的圆形红色圆圈。警告是一个带有感叹号的三角形。弱警告也使用带有感叹号的三角形来表示,但它们看起来相当暗淡。如果没有检测到任何问题,你将得到一个绿色的勾号。图 4.15显示了两个文件。一个没有问题(1),而另一个在不同类别中存在多个问题(2):
图 4.15:底部的文件没有任何问题,而顶部的文件则不太幸运
任何标红的都是可能阻止你的程序正确运行或根本无法运行的错误。警告意味着你的代码可能能够运行,但在一些明显的情况下它可能不会按预期执行。弱警告通常是一个小缺陷,比如一个不符合英语词典单词的变量名。如果你的区域设置不是英语,PyCharm 将标记来自你本地语言的单词。
第二个寻找问题的地点是在编辑器窗口内部。回顾一下图 4.15的第 13 行。你会看到单词math
下面有一个波浪形的红色下划线(相信我)。这些线的颜色与问题的严重程度相对应。右侧的空白区域显示了问题所在的位置,而下划线则直接显示了问题所在的行。如果你将鼠标悬停在任何下划线上,你会得到关于问题的描述。我将在本章后面更深入地讨论这个问题,当我们谈到意图时,这些意图是关于如何修复代码中问题的建议。
你会发现问题的第三个指示是在项目资源管理器中。回顾一下图 4.15,区域 3。有一个名为bad_actor
的 Python 包。在图 4.15中,该文件没有打开,但我在创建它时打开了它,输入了一些不完整的代码,然后关闭了文件。你可以在图 4.16中看到该文件已打开:
图 4.16:真傻!我开始在文件中输入,但从未完成
如果你正在工作的文件中存在错误,你会在文件列表中找到它。警告会通过目录系统冒泡。项目文件夹顶部有一个红色波浪线,包文件夹中有一个,问题所在的文件下方还有一个下划线。PyCharm 不会检查已关闭的文件。它只检查你打开的文件,但一旦它找到问题,它会记住位置并继续警告你,直到你解决问题。
糟糕的演员
我需要一个典型的反面教材来展示之前的视觉双关语。凯文·科斯特纳是一个糟糕的美国演员。他当然不是最糟糕的,但他在前 10 名之内。如果你不相信我,看看电影《与狼共舞》。如果你仍然不相信我,看看《保镖》。如果你仍然认为他很好,看看《水世界》,并意识到他自掏腰包投资这部电影,因为他坚信它将是一个大热门。
第四位是问题窗口。在图 4.17 中,你可以看到截图区域 4 中的图标上有一个红色圆点。点击图标,问题窗口将打开并显示错误列表,如图4.17 所示:
图 4.17:打开问题窗口,查看所有问题列表。只显示你的代码问题。生活问题不会显示
语法错误
语法错误通常是 IDE 暴露的最浅显的错误。我们已经看到了几个。图 4.17 显示了一个不完整的函数定义,因此它被标记为红色语法错误。在图 4.14 中,第 13 行在单词math
下方有一个红色下划线。PyCharm 将其识别为对 Python math
库的引用,而我忘记导入它。这引发了一个未解决的引用错误。这类错误总是被标记为红色严重错误,因为它们将阻止程序运行。
重复的代码
如果你习惯在项目内部或甚至在不同项目之间复制粘贴代码,你可能会收到 PyCharm 的警告。重复的代码是项目出现问题的信号。
最佳实践是遵循名为不要重复自己(DRY)的概念。我再重复一遍。你希望你的代码始终保持 DRY。永远不要通过复制粘贴来重复代码。确保它是 DRY。好吧,如果你答应注意 PyCharm 关于代码不是 DRY 的警告,我就不再说了。
当你在单个项目中找到这个问题时,你通常可以通过将重复的代码提升到函数中并从代码中重复的部分调用该函数来解决它。
如果你因为在不同项目之间复制粘贴而被标记,你应该将重复的代码转换成一个可以在项目之间共享的 Python 包。
PEP-8 问题
PyCharm 的代码检查器会提醒你代码中的样式问题,这些问题违反了 PEP-8。对于新接触 Python 的开发者来说,最大的问题是处理空白规则。函数之间的缩进和空行都是 PEP-8 规则的一部分,旨在使你的代码非常易于阅读。大多数 PEP-8 问题都被标记为警告。
死代码
这是我个人的一个小烦恼。有人编写了一些代码,最终被不同的函数所取代。旧的、未使用的函数和新函数都坐在代码文件中。它们可能具有相似的名字。它们甚至可能在不同的文件中。当我还是个孩子的时候,我在墙上贴了一张标题为“墨菲的技术法则”的海报。海报宣扬了一种悲观但在我(以及你可能已经做了很长时间的你的)经验中,完全准确的世界观。以下是关于技术的墨菲法则的一些样本:
-
你无法通过看轨道来判断火车行驶的方向
-
逻辑是一种系统地得出错误结论的方法,而且信心十足
-
每当一个系统变得完全定义后,总有一些傻瓜会发现一些东西,这些东西要么废除该系统,要么将其扩展到无法识别的程度
-
技术被那些管理他们不理解的事物的人所主导:如果建造者像程序员编写程序那样建造建筑,那么第一个出现的啄木鸟就会摧毁文明
-
计算机的注意力跨度只与其电源线一样长
-
专家是那些对越来越少的事物了解越来越多,直到他对什么都不了解的人
这与我至少有关,因为我找到并尝试修改死代码(认为它非常活跃,是所有问题的明显来源)的可能性几乎达到 100%。在 99%和 100%之间微小的差异似乎受到我当前咖啡因摄入量和是否跳过早餐的影响。这些影响似乎成反比。
我感谢一个警告我正在查看死代码的系统。我通常的抱怨是,你应该删除死代码。你不需要它,如果你需要,那就是版本控制系统的作用。
方法签名不匹配
当一个函数需要的参数多于或少于你提供的数量时,就会发生方法签名不匹配。当这种情况发生时,PyCharm 会警告你。
好代码的道路是用 PyCharm 的意图铺就的
现在我们已经花时间了解我们的缺点,让我们看看一些帮助我们修复它们的工具。PyCharm 有一个名为“意图”的机制,旨在自动化修复和改进你的代码。看看图 4.18:
图 4.18:将鼠标悬停在任何下划线代码上,查看为什么它被下划线标记,以及可能的修复方法
在显示的代码的情况下,问题是调用了sqrt()
方法,该方法找到平方根。该方法是在math
类中的静态方法。问题是未能导入该类。当我悬停在问题描述下方时,问题描述就会出现。在问题描述下方是最可能的修复方案。按下Alt + Shift + Enter将自动修复问题,通过在文件顶部添加import math
。
如果你想要尝试这个,你可以在我们克隆的示例代码的chapter-04
文件夹中的inspection
项目中尝试,该代码在第二章,安装 和配置。
注意这可能不是唯一的解决方案。在图 4.18中,我们还可以看到更多操作…提示我们点击链接或按Alt + Enter来查看更多可能性。
真正敏锐的读者可能会注意到灯泡。这是同一功能的另一个向量。查看图 4.19以查看灯泡的实际操作:
图 4.19:灯泡是获取意图的另一种方式
点击灯泡会显示可能意图的列表。这次,我们看到一个预览。意图是在文件的第 1 行添加import math
。
有时候灯泡可能会有些棘手,因为如果你离开它们最初出现的那条线,它们有消失的倾向。如果你想使用灯泡,只需点击下划线内的任何位置并等待一个滴答声。它将出现在问题所在行的开头。
在我们正在探讨的情况下,问题是一个合法的错误。程序在问题修复之前无法运行。你可能在前面两个图中没有看到,但灯泡是红色的。你还会看到黄色灯泡表示不那么严重的错误。
现在你如果像我一样,想看到带有绿色勾选和没有下划线的干净文件。让我告诉你,那可能永远不会发生。PyCharm 几乎总是会发现一些需要更改的地方。有时建议的更改并不很有用。你可能会听从一个建议,以某种小的方式更改你的代码。在你这样做之后,灯泡会回来,PyCharm 会提供将代码改回原来的方式。除非它们里面有感叹号,否则黄色灯泡不是你的敌人。
重构
大多数优秀的集成开发环境(IDE)以及专注于开发的文本编辑器,在这方面都有一定程度的重构工具。重构是一项非常重要的实践,但常常被忽视。在我的书中,这本书可在亚马逊(或任何销售最优秀技术书籍的地方)购买,《C# 设计模式的实际应用》,我讨论了一些导致本意良好的编码项目走向失败的无序力。你的代码一开始是纯洁的,整个团队都承诺保持零技术债务。但这种情况从未持续过。时间压力、开发者技能水平、不可避免的变化、可见性和复杂性等因素导致了一个退化的过程。你的代码从精心构建、完美架构的杰作变成了盘子上的一团泥巴!
我知道我在这里指向的是一本 C#的书,但如果你有 Packt 订阅,我强烈建议你阅读这本书的前两章。这两章讨论了工作退化的常见问题和预防措施。你可以做的一件事是保持警惕,永远不要忽视将代码重构作为开发实践常规部分的价值。
什么是重构?
简而言之,重构是在不改变其功能的情况下改进代码。如果你有单元测试(你有,对吧!?),它们应该在重构前后通过,而无需对测试本身进行任何更改。你正在寻找优化代码的可读性和性能的方法。也许你忽略了某些代码优雅之处,比如添加文档字符串,我们将在本章后面讨论。也许你没有给你的方法签名添加类型提示。也许有机会利用设计模式或 SOLID 原则使你的代码更加灵活。
重构背后的理念是,你在代码上再次审视,最好是经过一段时间之后。你有没有曾经看过自己一个月或甚至一年前写的代码,想知道是什么让你编写了那样糟糕的函数体?你究竟为什么要这样做?(在空白处填入你做过的愚蠢事情。)你无法相信那会是你。你比那聪明。考虑定期进行代码同行评审。这可以在写作过程的任何时间进行。此外,有一个人对你的工作投入较少,将能够发现未实现改进的机会。几乎总是,这会涉及到重构。反过来想。你有多少次接过其他开发者编写的代码?可能是离开公司的那个人?你检查了一下,得出结论,编写代码的人显然是最近从当地的精神病院逃出来的。这必须完全重写。这就是你重构别人的代码。
PyCharm 中的重构工具
PyCharm 有一套专门用于重构的菜单选项。其中有一些并不是明确标明为重构工具。
清理你的代码
PyCharm 有一个非常全面的代码清理工具。实际上,这个工具运行了您迄今为止看到的相同检查,但它是以批量方式进行的。您可以让 PyCharm 尝试修复所有问题。当您导入在 PyCharm 之外创建的项目时,这个功能非常有用,比如使用不提供 PyCharm 中看到的帮助的工具。
您可以清理一个打开的文件或您项目中的所有文件。说实话,我不建议您在项目级别上这样做,因为您真的无法预测引擎会对您尚未打开的大量文件做什么。图 4.20显示了代码清理菜单的位置:
图 4.20:代码清理工具可以在代码菜单中找到
一旦您点击菜单选项,您将看到一个对话框询问清理的范围,如图图 4.21所示:
图 4.21:指定代码清理的范围
您可以清理整个项目、未提交的文件、当前文件(对我而言是来自chapter-04
文件夹中inspection
项目的main.py
文件),或者自定义范围。我的建议是不要试图一口吃成胖子。不要在大型项目上做整个项目的清理。通常让 PyCharm 在小批次的文件上施展魔法更明智。未提交的文件选项是在您将更改提交到版本控制系统之前可以采取的一个很好的步骤。
重命名
世界上最受尊敬的软件开发者之一唐纳德·克努斯在他的书《计算机程序设计艺术》中写道,编程中有两件事很难:给事物命名(例如,变量、函数、类、文件等)以及使缓存失效。这有多么真实!编写有意义的变量定义,使其自文档化同时使意图明确,是一种艺术形式。通常需要多次尝试才能做到正确。PyCharm 有一个工具可以轻松地允许更改名称。您可以对任何已命名的对象右键单击并选择重命名。输入要重命名的对象的新的名称。PyCharm 将在引用命名对象的所有地方进行更改。它甚至会在注释和文档字符串中找到并更改引用。
如果更改的范围很小,例如,您正在重命名一个局部函数范围内的某个事物,重命名操作会立即发生。如果您尝试进行范围更广的重命名,例如涉及多个文件的重命名,PyCharm 会通过显示所有受影响的文件来为您预览更改。您可以在更改实际应用之前检查所有更改,以确保更改是适当的。在您满意后,您可以应用这些更改。
文件没有重命名菜单选项!
如果你来自像 Visual Studio Code 这样的其他工具,这可能会让你感到困惑,在 Visual Studio Code 中,重命名文件只是从资源管理器视图中选择文件并点击两次或按F2这么简单的事情。你会在文件中寻找重命名选项,但你不会找到。那是因为它位于重构菜单中。PyCharm 认为重命名文件是一种重构。与其他工具不同,PyCharm 会确保重命名文件不会影响你的代码。
内联变量
PyCharm 让你能够自动内联你的变量。实际上,这是 PyCharm 提供的更常见建议之一。考虑以下代码:
a = 5
b = 6
def add_two(num1: int, num2: int) -> int:
sum = num1 + num2
return sum
add_two(a, b)
sum
变量实际上并不需要。如果你内联变量,代码将变成以下这样:
a = 5
b = 6
def add_two(num1: int, num2: int) -> int:
return num1 + num2
add_two(a, b)
我们已经移除了声明sum
变量并将其赋值为num1 + num2
的行。
提取方法
之前,我提到了一个称为 DRY(不要重复自己)的概念。是的,我意识到再次提到它是在打破规则。我这样做是有目的的。记住,IDE 会指出错误,然后提供建议和工具来解决问题。我想展示一个非常实用的功能。PyCharm 为你提供了一个工具,可以轻松地将代码提取到单独的函数中。你会在几种情况下想要这样做。首先,如果你发现自己正在项目中复制和粘贴代码。你可能需要将那段代码做成一个函数,然后从你粘贴复制的代码的地方调用它。其次,当你找到一个违反单一职责原则的函数时。如果你从未听说过这个,你可以猜到它的意思。一个编写良好的程序应该只包含执行单一任务的函数。包含执行多个职责的函数或方法的代码可能是错误的,应该被拆分成单独的函数。
看一个这样的机会的简单例子。在示例代码中打开chapter-04/not_dry.py
文件。其中的代码确实很糟糕!敏感的观众在打开文件之前可能需要坐下来。看吧!每个人在学习到这是坏主意之前至少做过一次的事情:
computer_science_grades = {
"Guido van Rossum": 100,
"Ewa Jodlwska": 99,
"Fabrizio Romano": 88,
"Henrich Kruger": 87,
"Rick van Hattem": 83,
"Steven Lott": 72,
"Dusty Phillips": 72,
"Quan Nguyen": 92
}
好的,到目前为止,一切正常。我们已经得到了一个包含参加计算机科学课程的人及其成绩的字典。顺便说一句,那些名字并不是随机的。在你完成这一章后,看看你是否能猜出这些杰出人物是谁。我为这些杰出人物对数字本身表示歉意。它们原本应该是大致随机的键,除了范·罗素先生,显然他会得到满分。我相信他们在现实生活中都做得很好。接下来,我们还有另一组课程成绩:
advanced_theoretical_and_applied_recess_grades = {
"Bruce Van Horn": 100,
"Prajakta Naik": 92,
"Kinnari Chohan": 88,
"Pooja Yadiv": 86
}
虽然这是不同的主题和不同的人群,但理念是相同的。现在假设我们需要计算每个班级的平均分。我可以为计算机科学课程编写一个计算平均分的函数:
def computer_science_average(grades: dict) -> float:
raw_total = 0
for grade in grades.values():
raw_total += grade
average = (raw_total / len(grades))
return average
我们的方法签名提供了很多有用的提示。我们有一个描述性的函数名。该函数接受一个参数,我们的提示告诉我们我们期望一个字典。该函数预期返回一个浮点数。
函数体创建了一个名为raw_total
的变量并将其设置为0
。接下来,我们遍历dict
的值,并在每次迭代中,将value
添加到raw_total
中。一旦我们有了总数,我们就将其除以dict
中的键的数量(len
), voila!我们就有了班级平均分。在文件的底部,我们可以看到这个函数被调用的地方:
boring_class_average = computer_science_average(computer_science_grades)
print(f"Boring average is {boring_class_average}")
太棒了!我们调用了computer_science_average
函数,并附带了高度主观(并且可能不准确,因为那可能曾是你的最爱课程)的变量赋值。那么,这有什么问题吗?没有。问题出在接下来,这提供了一个提取方法的机会。下一个函数计算的是不同的类别:高级理论和应用退行。这是一个我本人开创的领域,在这个领域内,我没有对手。不幸的是,由于我在游乐场花更多的时间来完善我的技艺,而在计算机科学课程上花的时间较少,我几乎复制了我们之前写的函数:
def advanced_recess_average(grades: dict) -> float:
raw_total = 0
for grade in grades.values():
raw_total += grade
average = (raw_total / len(grades))
return average
这是对同一个函数的不同命名!我们需要合并!为此,你需要突出显示方法签名结束的冒号和return
语句之间的所有内容。参见图 4.22。不要包括return
语句,否则 PyCharm 不会在你的提取函数中生成return
语句:
图 4.22:选择代码以提取到新的函数或方法
接下来,右键单击所选代码,然后点击重构|提取方法,如图 4.23所示:
图 4.23:右键单击所选代码,然后点击重构,然后提取方法
这会弹出一个用户界面,允许你定义新的方法,如图 4.24所示:
图 4.24:PyCharm 中的提取方法对话框
设置提取方法的名称。我将其设置为compute_average
。PyCharm 已自动填写其余部分。点击确定,你的代码将发生变化。图 4.25显示了重构的结果:
图 4.25:重构的结果。注意 compute_average 函数是从所选代码自动生成的
让我们看看 PyCharm 从重构中生成的代码。首先,computer_science_average
已更改为以下内容:
def computer_science_average(grades: dict) -> float:
average = compute_average(grades)
return average
这个函数现在正在调用提取的函数。提取的函数看起来像这样:
def compute_average(grades):
raw_total = 0
for grade in grades.values():
raw_total += grade
total_number_grades = len(grades)
average = (raw_total / total_number_grades)
return average
这是我们要提取的代码。PyCharm 为我生成了函数。我必须告诉你,在这个阶段,我通常对生成的代码持谨慎态度。它很少完美。在这里,我更希望对 grades
参数有一个类型提示,以及对返回类型有一个提示。尽管这些都是小问题,但结果节省了我一些打字。
可能还有一个悬而未决的问题。为什么 PyCharm 没有检测并标记重复的代码?简短的回答是:我们的示例代码太短。如果我在重复的函数中添加几行代码,它就会被视为重复。让我们试试。修改两个函数中的代码,使其看起来像这样:
def computer_science_average(grades: dict) -> float:
raw_total = 0
fake_var_1 = 1
fake_var_2 = 2
fake_var_3 = 3
fake_var_4 = 4
fake_var_5 = 5
fake_var_6 = 6
fake_var_7 = 7
print(f"{fake_var_1}{fake_var_2}{fake_var_3}{fake_var_4}")
print(f"{fake_var_5}{fake_var_6}{fake_var_7}")
for grade in grades.values():
raw_total += grade
average = (raw_total / len(grades))
return average
我所做的只是添加了一堆虚假的变量声明。它们并不做任何重要的事情,除了使重复的代码片段更长。默认情况下,PyCharm 只查找长度为 10 行或更长的重复片段。短的重复不会通过筛选。我提这一点是因为方法提取的魔力可以自动处理重复。让我们做同样的练习。首先,查看更改后的 PyCharm。你应该会看到一些指示我们有问题的指标,如 图 4.26 所示:
图 4.26:现在我们的重复代码更长,因此被检测并标记
我们可以看到 PyCharm 已经注意到了我们的重复代码。按照 图 4.27 所示,突出显示代码以进行提取:
图 4.27:标记要提取的代码
右键单击突出显示的代码,然后点击 重构 | 提取方法,如 图 4.28 所示:
图 4.28:通过右键单击,点击重构,然后提取方法
按照图 4.29 所示,命名提取的函数 compute_average
:
图 4.29:命名提取的函数 compute_average
点击 确定。这次情况略有不同。你会发现 PyCharm 仍然像以前一样创建函数提取,但这次你被提示替换重复的代码,如 图 4.30 所示:
图 4.30:PyCharm 询问是否要替换重复的片段为提取的函数的引用
将函数导出到另一个文件
你有多少次在错误的地方编写了一个实用的函数?也许你把它放在一个设计用来做特定事情的模块或类中,但你的实用函数最终在许多地方都被广泛使用。适用于函数的单一职责原则也适用于模块和类。有一个连接数据库的函数是一个很好的例子。假设你刚刚得到一份在比利·布兰卡糖果厂工作的工作。他们需要你编写一些脚本,从几种不同的文本格式中导入他们制作的糖果列表,并将它们存储在数据库中。第一个需求来了,你需要从纯文本文件中读取并写入 SQLite 数据库。
在 chapter-04/move_function/read_input_file_a.py
中打开项目。让我们回顾一下内容:
import sqlite3
CANDY_DB = "candy.db"
这前两行是从标准库中导入 sqlite3
库。如果你之前没有使用过 sqlite3
,那么现在你需要知道的是:它是一个基于文件的数据库。通过这种方式,我们指的是你不需要像 Postgres 或 MariaDB 这样的数据库那样安装服务器。这使得它成为教学和原型工作的好数据库。我们将在 第十一章 中详细讲解数据库,在 PyCharm 中理解数据库管理。让我们继续定义将打开文件、读取内容并将其插入数据库的函数:
def read_input_file_type_a(file_path: str) -> None:
with open(file_path, "r") as data:
for line in data:
cleaned = line.strip("\n")
write_to_database(cleaned)
print("Processing Complete!")
我们已经打开了文件。对于文件中的每一行,我们将其作为文本读取,并删除换行符。这对于数据库插入正常工作是必要的。一旦字符串被清理,我们就调用一个写入数据库的函数:
def write_to_database(datum: str) -> None:
connection = sqlite3.connect(CANDY_DB)
cursor = connection.cursor()
sql = f"INSERT INTO candy(name) VALUES ('{datum}')"
print(sql)
cursor.execute(sql)
cursor.close()
connection.close()
我已经将数据库文件包含在代码仓库中,因此不需要编写代码来创建数据库。这个函数只是打开数据库,然后创建一个游标。游标用于使用 SQL 对数据库执行命令。即使你不知道 SQL,我也相信你能弄清楚发生了什么。数据库中有一个名为 candy
的表。该表只有一个字段:name
。我们在这里保持非常简单。我忘记创建主键了,因为现在数据库并不重要。我们应该更多地关注函数本身,而不是它的工作方式。
在生成一个 SQL 语句来从文本文件的当前行中的 candy
名称插入 candy
表的 candy
名称后,我执行了 SQL 语句,这将向 candy
表中插入一行。
在编程中,有一个一般规则:你创建的,你应该销毁;你打开的,你应该始终关闭。因此,我关闭了我的游标和数据库连接,以避免将来出现任何资源锁定。最后,我使用常见的双下划线命名约定来运行文件进行测试:
if __name__ == "__main__":
read_input_file_type_a("../input_file_a.txt")
在 PyCharm 中,我可以通过将我的运行配置设置为 Current File 并点击 Run 按钮来执行此操作。它成功了!文件被读取,我们没有出现错误。
下划线
一个 __init__
?你可以这么说,“下划线下划线 init 下划线下划线”。但这很麻烦。如果你只说init
,你不够具体,因为可能还有另一个没有下划线的名为 init
的函数或方法。所以,你说“下划线 init”或者甚至只说“下划线 init”,大家就知道你在说什么了。
第二天我们来到工作地点,得知有一个新的需求。我们需要另一个脚本,用于从 JavaScript 对象表示法(JSON)文件中读取数据。这个 JSON 文件只包含一个数组,如下所示:
{
"data": [
"truffles",
"turtles",
"dark chocolate bark"
]
}
它仍然只是一个糖果列表,但我们需要以不同的方式处理它。打开 chapter-04/move_function/input/read_input_file_b.py
。你会发现它的代码与其他代码类似:
import json
from read_input_file_a import write_to_database
我们知道我们需要处理 JSON,所以我导入了 json
包。我也知道我需要写入之前相同的数据库。我知道代码重用是好事,所以我从另一个脚本中导入了函数。然后我开始创建一个读取 JSON 文件的代码版本:
def read_input_file_type_b(file_path: str) -> None:
with open(file_path, "r") as json_data:
data = json.load(json_data)
candies = data["data"]
for candy in candies:
write_to_database(candy)
print("Processing Complete!")
if __name__ == "__main__":
read_input_file_type_b("../input_file_b.json")
json.load
方法将原始文本转换为常规 Python 3 dict
。正如您从前面的文件列表中可以看到,dict
中将包含一个东西:一个以 data
为键的糖果数组。所以我抓取那个并将其放入 candies
变量中,然后我遍历这个数组,为数组中的每个 candy
调用 write_to_database
函数。哇!甚至还没到午饭时间!也许我可以四处走走?我听说工厂里有个小女孩在测试一种能让人变成树莓的口香糖。
别急!这段代码可以改进。我们的输入脚本实际上是为了读取文本数据设计的。让其中一个脚本包含数据库函数没有意义,因为它根本不属于读取文本输入文件的脚本。它真的应该在自己的包中。让我们将其提取到自己的文件中。
打开 chapter-04/move_function/input/read_input_file_a.py
。右键点击我们要移动的函数名,如图 图 4**.31 所示:
图 4.31:右键点击要移动的函数,然后点击重构,然后移动
在上下文菜单中,点击 database_helper.py
文件名。点击 确定。仔细观察,因为接下来会发生很多事情。
我得到了一个新的文件名为 database_helper.py
:
import sqlite3
from input.read_input_file_a import CANDY_DB
def write_to_database(datum: str) -> None:
connection = sqlite3.connect(CANDY_DB)
cursor = connection.cursor()
sql = f"INSERT INTO candy(name) VALUES ('{datum}')"
print(sql)
cursor.execute(sql)
cursor.close()
connection.close()
这是移动到其自身文件中的提取函数。PyCharm 发现了相关的导入语句,并将其也移动到这里。我有一个名为 CANDY_DB
的文件名常量。它没有移动那个,那将是我更愿意看到的。不幸的是,PyCharm 当前版本并不是先知。我只好自己移动它。除此之外,这个文件看起来完美无缺。
如果你检查 input_file_a.py
的内容,你会看到它已经改变了。文件中的第一行现在如下所示:
from input.database_helper import write_to_database
同样,如果我打开input_file_b.py
,我也能在那里找到输入。PyCharm 将函数提取到自己的文件中,然后更改了每个文件中指向新位置的每个引用。
现在,我意识到我可能应该将其放入自己的模块中,我也意识到你可能会有完全不同的做法。在你准备在 Twitter 上批评我之前,请记住这本书是关于 PyCharm 的,而不是软件架构。我故意让它保持简单。
文档
没有程序员会怀疑文档在软件工程和开发中的重要性。然而,为程序创建文档的过程可能相当繁琐。此外,如果编写文档的人没有遵循标准实践,最终结果可能甚至不会有效。
考虑到这一点,PyCharm 致力于简化文档过程,使其尽可能直接和流畅。关于文档,我们将考虑此过程中的两个组成部分:查看和创建文档。我们将了解到 PyCharm 为这两个过程提供了极大的支持。
与 docstrings 一起工作
Python 中的文档被称为 docstrings,定义为放置在 Python 模块、函数、类或方法中的任何语句之前的一个字符串字面量。你可以通过查看各种内置 Python 函数的源代码来查看 Python docstrings 的示例。还建议任何你编写的自定义 API 也应具有适当的 docstrings,以提高可读性和可维护性。
创建 docstrings 时最值得注意的微妙之处在于使用三重双引号包围 docstring 的实践(我们将在下一小节中看到示例)。有关 docstring 约定的更多详细信息,请参阅这篇 PEP 文章:www.python.org/dev/peps/pep-0257/。
创建文档
在本小节中,我们将借助 PyCharm 了解为函数编写 docstring 的过程。让我们开始吧。
打开chapter-04/documentation
中的示例源代码项目。打开prime_test.py
文件,它看起来像这样:
import sys
from math import sqrt
def prime_check(n: int) -> bool:
# TODO: docstring goes here
if n < 2:
return False
limit = int(sqrt(n)) + 1
for i in range(2, limit):
if n % i == 0:
return False # return False if a divisor is found
return True # return True if no divisor is found
文件继续进行,但您需要关注的是prime_check
函数方法签名下面的那一行。那里有一个TODO
。如果您来自讲西班牙语的国家,请意识到这并不意味着所有。它指的是代码中的“待办”项。在这种情况下,原始开发者,也就是这本书第一版的作者 Quan Nguyen,表示他没有为这个函数编写文档字符串。他在这里表示他知道这一点,并打算稍后回来修复它。让我们用一点 PyCharm 的魔法来帮助他。在您过于兴奋之前,我遗憾地报告说,PyCharm 中没有工具可以读取您的代码并生成文档字符串。鉴于开发者们讨厌编写文档,我敢打赌,某个地方正在进行一项快速而激烈的努力,让 AI 来完成这项工作。但我们将坚持 PyCharm 提供的原装功能。
删除# TODO
行,并替换为三个双引号("""
),然后按Enter键。您会发现一个生成的文档字符串模板出现:
"""
:param n:
:return:
"""
此模板需要一些填充才能成为一个合适的文档字符串。注意第一组三重引号下面的空白。在这里,您需要写关于函数做了什么的内容。可能像这样:
"""
Check whether an integer is a prime number of not.
Generally, the function goes through all odd numbers
less than the square root of the input integer, and
checks to see if the input is divisible by that number.
:param n:
:return:
"""
在下面是函数期望的参数部分。在这里,函数接受一个名为n
的参数。我们应该对此参数写一些内容,包括其类型:
"""
Check whether an integer is a prime number of not.
Generally, the function goes through all odd numbers
less than the square root of the input integer, and
checks to see if the input is divisible by that number.
:param n: the integer to prime check
:return:
"""
最后部分是关于返回值的文档:
"""
Check whether an integer is a prime number of not.
Generally, the function goes through all odd numbers
less than the square root of the input integer, and
checks to see if the input is divisible by that number.
:param n: the integer to prime check
:return: boolean
"""
考虑我们在按下Return/Enter键后生成的文档字符串模板。:param
和:return:
是模板的一部分,并且每次我们扩展文档字符串时都会包含在内。PyCharm 允许我们更改文档字符串模板的格式,使其高度可自定义并适应需求。
自定义文档字符串模板
与往常一样,文档字符串模板具有高度的自定义性。您可以通过进入设置窗口来找到自定义设置,该窗口在第三章中进行了详细说明,自定义解释器和虚拟环境。只需搜索文档字符串,您就会找到应该引起您注意的区域。第一个如图图 4**.32所示:
图 4.32:您可以将用于渲染文档字符串的通用格式更改为几种行业标准格式之一
另一组设置是颜色方案设置的一部分,它允许您自定义在 PyCharm 编辑器中渲染文档字符串所使用的颜色。
查看文档
想象一下这种情况:您正在使用一个包中的特定方法,但您并不完全确定该方法接受哪些参数以及其返回类型。因此,您需要上网查找该特定方法的文档。
作为 PyCharm 用户,您可以通过两个简单的操作实现相同的功能:将上一节中的prime_check.py
脚本移动到使用math.sqrt()
函数的prime_check()
函数所在的行;它应该在大约第 19 行。
快速文档
假设我们想查看此函数的文档。您只需将鼠标悬停在函数调用上并等待片刻。或者,您可以选择视图 | 快速文档或其对应的快捷键。您将看到一个弹出窗口,显示类似于 图 4**.33 的文档:
图 4.33:快速文档显示所选函数的文档
更重要的是,您还可以使用相同的操作查看您自己的函数、方法、类等文档。当您打开项目时,PyCharm 的索引过程会找到并生成这些信息。
如果您将光标移至以下行中主作用域的prime_check()
调用处(应该在大约第 38 行):
if prime_check(num):
在等待片刻后,您将能够看到之前输入的相同文档字符串,如图 图 4**.34 所示:
图 4.34:快速文档显示第 38 行所选函数的文档
注意文档中的文档字符串格式与窗口中显示的文档格式相匹配。
快速定义
快速定义与快速文档操作方式相同。这在文档提供的信息不足时非常有用,您希望查看特定函数在源代码中的定义方式。为此,将光标置于特定的 API 调用处,然后转到视图 | 快速定义以触发操作。
例如,图 4**.35 显示了在示例中对prime_check()
的调用上触发的快速定义:
图 4.35:快速定义显示了函数的实际代码定义,如果存在,则自然包括文档字符串
总体而言,PyCharm 在 IDE 中动态查看文档和定义方面提供了强大的选项。当程序员无需从开发环境切换到查找文档时,可以节省大量的时间和精力。
摘要
在本章中,我们探讨了 PyCharm 在编程各个方面(包括代码分析、代码补全、重构和文档化)的功能。在这些所有过程中,PyCharm 的智能代码分析器提供了智能和便捷的选项,以便在实时和动态方式中编辑和修复代码中的问题。
除了智能代码分析器可以支持的众多选项之外,PyCharm 还允许用户根据个人喜好自定义分析器的行为。这可以通过通用设置的各个部分实现。总的来说,这些支持功能旨在以定制和有益于您的方式提高您作为开发者的生产力。
在下一章中,我们将专注于编程的一个特定方面:版本控制。我们将学习 Git 版本控制过程的具体细节以及 PyCharm 如何支持和简化这一过程。
问题
-
在 Python 程序中,PyCharm 的代码分析器如何确定问题的严重程度级别?
-
PyCharm 可以通过其智能代码分析器检测并帮助修复哪些常见问题?
-
PyCharm 的代码补全支持与其他工具有何不同?
-
PyCharm 提供了哪些常见的代码补全选项?
-
为什么 PyCharm 的代码补全支持可能无法正常工作?
-
PyCharm 提供了哪些常见的重构选项?
进一步阅读
-
Jolt Awards:为开发者推荐的 最佳书籍 (informationweek.com)
-
Pylint 主页:
www.pylint.org/
-
技术领域的墨菲定律:
www.netlingo.com/word/murphys-laws-on-technology.php
-
请务必查看本书的配套网站:
www.pycharm-book.com
。
第五章:在 PyCharm 中使用 Git 进行版本控制
版本控制是软件开发领域的一项基本最佳实践。然而,对于新开发者来说,这个过程的技术细节可能会让人感到畏惧。犯错很容易。作为南方卫理公会大学(Go Ponies!)的训练营讲师,我亲眼目睹了许多错误,比如不小心将整个家目录添加到 Git 仓库中,在其他仓库内部创建仓库,以及通过错误的推送和拉取顺序抹去辛勤工作的成果。我更喜欢让学生在命令行上掌握 Git。实际上,这是我最先教授的技能之一,在我看来,这也是最难的技能之一。
在开发者对整个 Git 过程有信心之后,不得不不断跳出 IDE 执行四到五个命令,然后再跳回 IDE 继续工作,这确实有些烦恼。虽然你可以使用 PyCharm 中可用的内置终端窗口,但 IDE 提供了一个更好的选择:内置的版本控制 GUI。
PyCharm 支持许多主要的 版本控制系统(VCSs),包括以下内容:
-
Git
-
Mercurial
-
Subversion
-
Perforce
-
微软 团队 基金会服务器
尽管有许多其他版本控制系统可用,但它们无疑是最受欢迎的,并且在这份列表中,Git 已经成为行业中的事实标准。
本章将首先介绍一些关于版本控制和版本控制系统(VCSs)的基本信息,以防你对这个概念不熟悉。在介绍之后,我们将完全专注于 Git 的工具,因为正如我刚刚暗示的,它在市场份额方面具有领先地位。
到本章结束时,你将理解以下内容:
-
使用版本控制系统的优点
-
使用 Git 执行添加、提交、推送、合并和分支操作
-
创建和维护
.gitignore
文件 -
在 IDE 中使用 Git 工具,这可能从多个角度进行参考
技术要求
要成功学习本章内容,你需要以下条件:
-
Python 3.10 或更高版本的正常安装。
-
PyCharm 的正常安装。
-
为你的电脑安装 Git 客户端软件。Mac 和 Linux 系统通常作为标准安装。Windows 用户可以访问
gitforwindows.org
下载免费软件。 -
一个免费的 GitHub 账户。请注册于
github.com
。请注意,尽管 GitHub 有付费功能,但我们不会使用它们
版本控制和 Git 基础知识
软件行业标志性人物乔尔·斯波尔斯基,他创建了 Stack Overflow,在 2000 年写了一篇著名的博客。他的许多有影响力的帖子中,有一篇标题为《乔尔测试:12 步提升代码质量》。这篇帖子旨在为软件开发者提供一个简单的评估方法,以评估任何软件开发组织的成熟度水平。他提出了 12 个问题,每个问题 1 分。一个优秀的软件开发团队应该得到 11 分或更高。列表中的第一项就是本章关注的内容:你们使用源代码控制吗?如果你对其他内容感兴趣,可以在本章的进一步阅读部分找到博客文章的链接,以及斯波尔斯基先生的书籍《乔尔论软件》的引用。
乔尔称之为源代码控制。我称之为版本控制。这两个术语可以互换使用。我将使用版本控制来保持与 PyCharm 的 UI 一致,PyCharm 将我们讨论的这一组功能称为版本控制系统或VCS。严格来说,版本控制是一个随着时间的推移跟踪组成程序的文件变更的过程。VCS 的目标包括以下内容:
-
能够恢复项目中的任何文件之前保存的版本。
-
能够自动合并团队中多个开发者的工作,只要他们的工作不冲突。冲突发生在两个开发者更改了同一文件中的同一位置。
-
提供一种简单的方法来审查冲突并通过协作解决它们。
-
能够跟踪随着时间的推移,哪些开发者对代码进行了每次变更。
-
提供一个分支系统,允许修复错误、增强功能和实验,而不会牺牲生产代码的稳定性。
因此,版本控制系统(VCS)是一种旨在启用该过程的软件系统。VCSs 根据修订的存储方式分为两种类型。集中式 VCS的工作方式有点像你当地的公共图书馆。你的项目被检查到一个仓库中。开发者可以检出代码以便进行创建新修订的工作。这样做将检索项目的最新版本。一些系统,如Perforce,传统上要求你明确检出你想要工作的文件,就像从图书馆借阅一本书一样。在你检出这些文件期间,不允许其他人修改它们,直到你将它们检入并带上你的更改。其他系统,如Subversion,没有这个要求。任何人都可以在他们本地的副本上工作。当工作完成时,开发者会将他们的工作提交到中央仓库。在提交过程中,VCS 会检查冲突。如果没有冲突,传入的工作将被合并,并在 VCS 服务器上存储一个新的版本,然后通过更新过程供其他开发者使用。如果检测到冲突,提交将被拒绝,开发者必须与其他团队成员一起工作以解决冲突,然后才能合并并最终提交工作。在所有集中式系统中,团队中的每个开发者在其本地计算机上只有代码的最新版本。
相比之下,分布式版本控制系统(DVCS)如Git和Mercurial将项目上所执行的每个修订都保存在每个开发者的计算机上。这里的优势是没有任何单点故障可能导致项目代码的完全丢失。在集中式系统中,如果服务器被破坏,项目的修订历史可能会丢失。在分布式系统中,没有中央系统来存储项目的修订历史。每个开发者都有项目完整的历史记录在他们的计算机上,所以任何一台计算机的丢失并不会造成太大的问题,除了更换设备的常规痛苦之外。
话虽如此,分布式系统确实使用一个中央枢纽或远程,以便在每位开发者的计算机上的工作副本之间实现轻松同步。枢纽服务器允许额外的功能,例如管理谁可以查看或更改代码,以及协作功能,如代码审查、错误跟踪、文档和讨论。分布式版本控制系统(DVCS)也提供了一个轻松控制基于 Web 的应用程序新版本发布的方法。许多持续部署系统,如Travis CI、CircleCI和Beanstalk,只是利用 DVCS 的功能来控制发布管理过程。
最著名的分布式版本控制系统(DVCS)是 Git,由林纳斯·托瓦兹(Linus Torvalds)创建,他同样创建了Linux操作系统。最著名的 DVCS 中心是 GitHub,它由微软拥有。许多人将Git和GitHub互换使用。这是一个错误。GitHub 是互联网上的一个地方。用于访问GitHub的软件称为Git。许多使用 Git 作为其 DVCS 主机的项目将他们的仓库中心托管在 GitHub 之外的服务上,例如GitLab、Microsoft Azure DevOps、Atlassian Bitbucket和Beanstalk(beanstalkapp.com
,不要与Amazon Web Services上的 Beanstalk 服务混淆,它与版本控制无关)。
当开发者对其工作副本进行更改时,他们可以将更改提交到本地副本。他们可以在稍后通过从中央中心拉取更改、解决任何冲突,然后将合并的工作推送到中央中心。再次强调,在 DVCS 中,所有操作都在开发者的计算机上完成,而不是在具有集中式系统的服务器上。
在您的计算机上设置 Git
无论您是在 Windows 计算机上安装了 Git for Windows,还是在使用 Mac 或 Linux 计算机上的预安装 Git 客户端,您都需要执行一些额外的设置,而不仅仅是安装软件。接下来的设置任务包括以下内容:
-
设置默认用户名和电子邮件地址。
-
创建一个安全外壳(SSH)密钥,以便您能够安全地与远程中心(如 GitHub)通信。
-
将 SSH 密钥添加到您的 GitHub 账户。
让我们逐一介绍这个过程。
要开始,您需要启动一个终端。如果您使用 Mac 或 Linux,您应该能够在系统上找到一个名为Terminal的应用程序。我将展示的命令将使用Bash shell。Mac 的终端程序默认使用一个名为Z shell(zsh)的 shell,它与 Bash 直接兼容。大多数 Linux 安装默认使用 Bash,所以您已经准备好了。如果您使用 Windows,您需要从gitforwindows.org
安装 Git 软件。安装的程序之一是gitbash。启动gitbash将允许我们无论使用哪种操作系统都能使用相同的命令。
设置默认用户名和电子邮件地址
启动您的终端后,您需要执行以下命令来设置默认用户名:
git config --global user.name "FIRST_NAME LAST_NAME"
自然地,您将填写您的真实姓名。接下来,填写您的电子邮件地址。如果您在一家公司工作,我强烈建议您创建一个单独的 GitHub 账户,以保持您的工作与雇主分开,避免任何不愉快的知识产权纠纷。考虑到这一点,使用以下命令为您的办公工作指定适当的电子邮件地址:
git config --global user.email "MY_NAME@example.com"
除了使用这两个全局变量之外,还有更多广泛的配置选项,但这是设置 Git 最常见的方式,没有这些设置,你将无法将代码推送到 GitHub。如果你想要更多关于使用 Git 的深入信息,我建议查看本章“进一步阅读”部分中的附加阅读材料。
生成 SSH 密钥
远程中心的一个目标是为了确保你的代码免受恶意篡改。实现这一目标的一种方式是确保你电脑和远程之间的所有通信都是加密的。你可以使用 HTTPS 加密或 SSH。虽然两者都是有效的加密工具,但 SSH 被认为远优于其他工具,是真正专业人士的标志。为了使用 SSH,你需要生成一个唯一的密钥对:一个公钥和一个匹配的私钥。在这里我不会深入解释 SSH 的工作原理,但如果你对它感兴趣,我会在本章“进一步阅读”部分提供一些阅读建议。
生成密钥相当简单。在打开 Bash 会话后,只需输入以下命令:
ssh-keygen
密钥生成程序会询问你一些问题,例如在哪里存储你的密钥文件以及如何命名它们。默认情况下,它们被命名为 id_rsa
(私钥)和 id_rsa.pub
(公钥),这两个文件都存储在你主目录下的 .ssh
子目录中。
最后一个问题是要输入密钥的密码。你可以为密钥设置一个密码以增加安全性,或者直接按 Enter 键以使用空密码。这通常是不推荐的,因为创建密码时的安全性更高。拥有密码的缺点是你需要不断输入密码。使用空密码可以避免这些中断,但同时也增加了密钥被破解的风险。我建议输入密码,因为使用 PyCharm 的工具的一个优点是它会将其存储在其自己的加密数据库中。PyCharm 将会为你回答所有挑战,从而消除了使用受密码保护的加密密钥的不便。
输入 passkey 后,你会得到一些额外的反馈。你可以通过输入以下命令来检查你的密钥:
ls ~/.ssh -a
这列出了(ls
)你主目录(~/
)下 .ssh
子目录中的文件,包括任何隐藏文件(-a
表示“所有文件”)。至少你应该能看到 id_rsa
和 id_rsa.pub
文件。你很快就需要 id_rsa.pub
文件的内容,因为你将要把其内容粘贴到 GitHub 的屏幕上。
我们来到了书中提供父亲般安全讲座的部分。永远永远永远永远(乘以无限大加上一)不要将您的私钥粘贴到任何地方。永远永远(令人作呕)不要允许任何人复制您的私钥。不要将其放在 U 盘上。不要将其复制到网络驱动器或云文件共享以进行安全保存。为了神圣的一切,永远不要将其提交到仓库!这适用于任何系统的任何密码。如果您的仓库最终变得公开,至少对您的团队来说是公开的。
公钥用于分享。私钥需要保密并隐藏。如果您的私钥受到损害,只有您的密码短语可以保护您的作品。如果您留空了密码短语,那么您就没有进一步的保护了!
要显示您的公钥内容,请输入以下命令:
cat ~/.ssh/id_rsa.pub
您的公钥内容将显示在终端窗口中。选择整个密钥,右键点击,然后点击复制。
将您的 SSH 密钥添加到 GitHub 账户。
我会很快地介绍这一点,因为我怀疑你们中的许多人以前已经做过。如果您需要关于如何将 SSH 密钥添加到 GitHub 账户的更好教程,请参阅本章末尾的进一步阅读部分,以获取针对 Git 的具体步骤列表。
登录 GitHub 并点击您的头像。我的意思是 GitHub 右上角的头像,假设他们自印刷以来没有重新设计他们的网站。找到与您的个人资料一起工作的选项,然后找到设置。在您的个人资料设置中有一个管理 SSH 密钥的选项。点击该链接,然后找到添加 SSH 密钥的选项。粘贴您刚刚复制的公钥,给它一个容易记住的名字,然后点击添加按钮。
恭喜!您已经完全设置好了计算机与 GitHub 远程之间的安全通信。您可以在您拥有的不同计算机之间重复使用您的 SSH 密钥。只需在计算机之间复制.ssh
文件夹。只是要小心,不要因为将其保存在可能丢失的可移动驱动器或可能被破坏的云文件共享中而使密钥受到损害。
手动设置仓库。
在第三章“自定义解释器和虚拟环境”中,我们手动创建了一个虚拟环境,以便熟悉这个过程。我们在这里也将做同样的事情。让我们使用命令行来创建一个仓库并执行 Git 中所有基本功能。之后,我们将看到 PyCharm 的工具如何允许我们从 IDE 中通过方便的 GUI 执行相同的操作。
不要在任何文件或文件夹名称中使用空格。
我会经常提到这一点,因为我知道很多读者会跳过这类书籍。如果您是软件开发的新手,您可能不知道使用带空格的文件名可能会引起问题。请避免使用带空格的文件和文件夹名称,包括由您的操作系统创建的任何文件夹。相反,使用分隔符代替空格,例如破折号或下划线,或者使用驼峰式命名,这种命名方式省略空格,并用大写字母表示单词边界。
在您的 Bash 终端会话打开后,我们将要做的第一件事是为我们的项目创建一个文件夹。我们将使用以下命令在您的家目录中完成此操作:
mkdir first-repository
接下来,让我们将目录更改为新创建的文件夹:
cd first-repository
这就是我们的代码将要存放的地方!在继续之前,我们需要讨论一个敏感的话题。
GitHub 中的主分支与 main 分支
在 GitHub 成立后的前 12 年里,每当在 GitHub 中创建一个新的仓库时,该工具都会创建一个名为 master 的默认起始分支。在 2020 年,一个名为 The Software Freedom Conservancy 的组织建议 GitHub 将默认分支的名称从 master 更改为 main,因为 master 这个词与奴隶制有关。虽然 Git 不会强迫您使用任何特定的名称,但 GitHub 上的默认名称已被更改为 main。由于 GitHub 不控制 Git,这个名称更改仅适用于 GitHub 创建的仓库。我们一直在使用 Git 命令行创建仓库,这是 PyCharm 的 Git 工具所利用的。无论您使用 Git 命令还是 PyCharm 的集成,有很大可能性您将得到一个以 master 分支初始化的仓库。当您在 GitHub 上创建远程仓库时,该仓库将使用名为 main 的默认分支创建。
您应该注意匹配默认分支的名称,以便在您执行第一次推送时,您的分支能够正确同步。您可以在 GitHub 上更改名称,或者在您的计算机上设置 Git 命令以使用 main 作为默认名称。
如果您想在 GitHub 上更改它,您可以进入您的个人资料设置并点击仓库。您将在顶部找到该设置,如图 图 5**.1 所示。
图 5.1:您可以将 GitHub 仓库的默认名称设置为与您计算机上的默认名称匹配
您还可以使用以下命令在您的计算机上设置全局设置:
git config --global init.defaultBranch main
如果你打算在 GitHub 上做很多工作,同步默认分支名称是一个好主意。如果你不这样做,当你第一次尝试将你在计算机上创建的仓库推送到 GitHub 上创建的远程仓库时,你会遇到错误。本地的默认分支是master,而远程的默认分支是main。远程不会有名为master的分支,所以你会收到一条消息,说明没有名为master的上游分支。你可能需要做一些工作来解决这个问题。如果你遇到这种情况,我会留一个链接到一篇文章,告诉你如何解决这个问题。我鼓励你通过使用前面的 Git 命令将你的本地全局设置设置为main来避免这个问题,这样它就会与 GitHub 匹配。
手动初始化仓库
让我们创建我们的仓库。在你创建了一个代码项目之后创建一个仓库是完全可能的,甚至可以说是正常的。然而,我们将从仓库开始,因为这将会是一个非常简单的演示。要创建一个新的 Git 仓库,或者简称为 repo,请输入以下命令:
git init
这很简单!看起来好像没有发生什么。你会收到一个消息,说明仓库已创建。让我们看看文件夹的变化:
ls -a
ls
命令列出所有文件。-a
选项显示隐藏的文件和文件夹。当你初始化 Git 仓库时,Git 软件创建了一个名为.git
的文件夹。由于文件夹名以点(.
)开头,所以在类似 Linux 的系统中的文件夹是隐藏的。如果你在 Windows 上使用 PowerShell 执行这些步骤,你将能够看到文件夹,但在其他操作系统中它是隐藏的。
.git
文件夹是存储所有你的修订和项目设置的地方。一般来说,你不需要修改这个文件夹的内容。
我们在我们的本地计算机上有一个仓库。让我们添加一个新文件。输入以下命令:
echo "hello world" > test.txt
这创建了一个名为test.txt
的新文件。在该文件中有一行hello world
。这是一个多么好的开始,对吧?你可以通过输入以下命令来检查文件的内容:
cat test.txt
谁需要集成开发环境(IDE),对吧?等等,不,别当真,我刚才那么说了。接下来的几个步骤的顺序很重要。过程如下:
-
将文件添加到仓库中。你添加的文件是你自上次提交以来创建或修改的文件。我们还没有这样的文件。将你的文件添加到仓库也称为暂存文件。我们正在让仓库知道新文件和已更改的文件。你不应该总是将每个文件添加到你的项目中。有些文件你应该从仓库中排除。我们稍后会讨论这一点。
-
提交您的文件。这将在您的本地计算机上创建代码的新版本。提交操作要求您包含一个注释,说明您所做的更改的性质。这是至关重要的!您应该努力编写您更改的简洁摘要,并且您应该像假设您最重要的客户有一天会阅读所有这些注释一样编写这些注释。这不是发泄您挫折的地方!请相信我。我见过项目在意外审计中失败,其中不友好或不适当的评论出现,工作关系被破坏。假设您所写的一切都将被全世界阅读和评判。
-
从远程中心拉取更改。您总是在推送之前拉取。这允许您找到您的工作中可能存在的任何冲突,以及可能由项目中的另一位开发者推送的任何工作。这很重要!如果您在推送之前不拉取,可能会覆盖另一位开发者的修订版本!这当然不是灾难性的,因为所有修订版本都保存在仓库中。然而,这会让您作为其他开发者在明天的站立会议中感到非常尴尬,因为其他开发者可能不会因为您为团队增加了额外的工作而高兴!这很简单。只需记住在推送之前先拉取。即使您是唯一一个在项目上工作的人,也要养成这个习惯!
-
假设没有冲突,将您的工作推送到远程中心。
您的修订版本现在可供团队中的其他人使用,并安全地存储在远程服务器上。让我们来回顾一下这个过程的命令。
首先,将您的新文件和更改的文件添加到 Git 中。记住,这也被称为暂存您的文件:
git add test.txt
如果您的项目中有很多文件,可以使用以下方法一次性添加它们:
git add .
在将最新的更改暂存后,您现在可以将它们提交到本地仓库。这将在您的计算机上创建项目的新版本。您需要包含一个总结您更改的注释:
git commit -m "initialized repository and added test.txt which might be the best code in the world."
-m
开关是我们的注释,用双引号括起来。只需将其记住为提交信息,-m
开关很容易记住。当然,到本章结束时,您不需要记住所有这些命令行内容,因为您将有一个便捷的图形用户界面。
与远程服务器协同工作
您可以在本地计算机上完全处理一个项目。自然地,您会失去由远程中心提供的所有协作优势,包括更改历史记录的远程备份优势。之前,我介绍了一个四步过程:
-
添加或暂存您的更改文件。
-
将您的更改提交以创建一个新的修订版本。
-
从远程服务器拉取更改。
-
将合并后的工作推送到远程服务器。
我们已经完成了列表的前两步,在我们的本地仓库副本上完成了。我们必须在此处中断流程,因为我们目前没有远程服务器。我们将在 GitHub 上创建一个,但当然,您可以使用任何兼容的服务。
在 GitHub 上添加远程服务器
登录 GitHub 并找到添加仓库按钮。它通常是一个非常明显的绿色按钮。以我的运气,他们会在出版时重新设计网站。不过,他们通常会在用户界面(UI)上非常明显地放置创建仓库按钮。
让我们创建一个与本地创建的仓库相匹配的仓库。我会将其设置为公开,这意味着每个人都可以看到它。如果你比较害羞,你可以将其设置为私有。对于这个练习来说,这不会有什么影响。
一旦你创建了仓库,GitHub 将显示如何将 GitHub 版本的仓库添加为远程仓库的说明。只需将 GitHub 生成的代码复制并粘贴到你的终端中,确保你的终端当前位于仓库文件夹中,作为当前工作目录。
你可以通过输入以下内容来验证远程仓库:
git remote -a
GitHub 生成的命令将仓库的 GitHub 副本名称设置为origin。你应该有一个名为origin的远程仓库,而本地仓库的版本则通过当前分支来引用,通常如前所述是main。
第一次推送
现在你有了远程仓库,你可以将你的本地仓库推送到远程。输入以下内容:
git push origin main -u
这会将你的主分支副本推送到远程,并为主分支设置上游跟踪。这仅仅是一个花哨的说法,意思是远程和本地仓库都知道主分支,任何推送到这里的更改都将导致远程主分支被更新。如果远程没有名为main
的分支,将会创建一个,并且所有内容都将同步得很好。
创建、提交和推送更改
现在我们已经有一个完全工作的仓库,包括 GitHub 上的远程仓库,让我们对我们的代码进行一次完整的更改。这次我们将不中断地介绍四个步骤的过程。这是你将在项目期间每天多次遵循的过程。
下一步非常重要。养成这个习惯!
git pull
这会拉取远程上的任何更改,并允许你在它们进入世界之前解决任何冲突!当然,在这种情况下,由于只有我们在这个项目上工作,所以自然不会有任何冲突。
接下来,让我们更改文本文件的内容。输入以下内容:
echo "this is the best code in the world" > test.txt
你可以使用以下方法来检查结果:
cat test.txt
你应该能看到更改后的文件。现在让我们进行一次完整的添加、提交、拉取和推送循环。逐行输入以下内容,每输入一行后按Enter键:
git add test.txt
git commit -m "changed the text in test.txt"
git pull
git push origin main
切换到你在其中创建了仓库的浏览器,并刷新页面。你应该能看到你的代码。你可以在浏览器中点击test.txt
文件来验证新内容是否已推送。你也应该看到有两个版本。
你的仓库已正确设置并运行!让我们将我们的注意力从命令行转移到在 IDE 中工作。
在 IDE 中使用 Git
我们已经介绍了 PyCharm 中的一些 Git 工作流程。在第二章“安装和配置”中,我们使用 PyCharm 克隆了本书的示例代码仓库。由于我们已经介绍了克隆,我们不会再次进行操作。相反,让我们考虑我们刚才手动做的一切:
-
我们使用
git init
初始化一个新的本地仓库。 -
我们对代码进行了更改。
-
我们使用
git add
将更改添加为待提交的文件,以准备提交。 -
我们已将更改提交到本地仓库。
-
我们从远程拉取,以确保我们在这个分支上有最新的代码,并且我们的工作副本和远程之间的代码不存在冲突。远程可能由于其他开发者推送了他们的更改而最近发生了变化。
-
最后,我们将更改推送到远程。
此列表代表了在项目中捕获和管理修订的最基本工作流程。我们可以在 PyCharm 的 GUI 中完成所有这些以及更多。在 PyCharm 中做一些工作,在命令行中做一些工作也是相当常见的。使用一个或另一个不会排除任何事情。例如,我经常在命令行中分支和合并,因为这对我来说既快又简单。如果有冲突,我发现使用 PyCharm 手动解决它比手动方法更优越。这个任务的 GUI 非常出色。我们将在适当的时候讨论所有这些。现在,打开你在 PyCharm 中使用的文件夹,让我们探索 IDE 能为我们手动采取的这些第一步做些什么。
PyCharm 中的版本控制
PyCharm 在 UI 中有几个地方允许您访问您的 VCS,对我们来说就是 Git。它们不是重复的,而是每个区域都为特定任务提供了一个视觉工具。第一个是主菜单,如图 5.2所示。
图 5.2:Git 菜单允许您访问您通常在命令行中使用的所有命令
第二个提供了快速且简单的方式来提交文件。这可以在侧边栏菜单中找到,如图 5.3所示:
图 5.3:Git 提交工具可以在侧边栏中找到
第三个允许可视化分支和提交历史。这可以在屏幕底部的工具窗口中找到。屏幕的这一部分可以折叠,但工具窗口选择器始终在侧边栏的底部可用,如图 5.4所示。
图 5.4:点击 Git 工具窗口将打开一个窗口,显示提交历史,以及所有分支
第四个位于主菜单旁边的顶部工具栏上。这个区域显示你当前正在工作的分支,并允许轻松管理分支。你可以在 图 5.* 中看到这一点。
图 5.5:当前分支显示在主菜单附近。点击此处可以访问一些命令。
第五个是 VCS 操作弹出窗口,如图 图 5.* 所示。这已经存在一段时间了,尽管它还在那里,但我认为它已经过时了。你可以使用 Ctrl + ** 来访问弹出窗口。请注意,这不是引号。``
`` 键,称为压痕标记,位于美国键盘布局中 1(一个)键的左侧。
图 5.6:使用 Ctrl/Cmd + ` 激活 VCS 操作弹出窗口。
最后,你可以在编辑器窗口或编辑器窗口中的标签上右键单击以访问主菜单中找到的相同 Git 菜单,如图 图 5.* 所示。
图 5.7:你可以在编辑器窗口或标签上右键单击以查看 Git 菜单。
如你所见,修订控制工具已经以有组织和智能的方式巧妙地编织到 IDE 中。由于我们打开了一个已经包含 Git 仓库的文件夹,PyCharm 简单地识别这一事实并呈现界面。当你打开一个文件夹或创建一个没有 GitHub 仓库的项目时,大部分工具仍然可见,但它不会是 Git 特定的。由于 PyCharm 支持许多 VCS,尚未使用 Git 或任何其他 VCS 初始化的项目将呈现通用的工具,直到初始化完成。
使用 PyCharm 中的 VCS 工具从头创建新项目。
让我们创建一个新的项目。这个项目的代码在书的仓库中,但你真的不想只打开那个副本,因为它已经与一个仓库相关联,你将看不到我即将展示的所有内容。为此练习,你应该从头开始创建一个新的项目,就像我即将做的那样。
点击应用程序左上角的三明治图标以激活主菜单,然后点击 project_git_demo
。我将保留其他所有默认设置。你可以在 图 5.* 中看到我的项目创建对话框。
图 5.8:我的演示项目设置。
现在我们有一个可以工作的项目。
初始化本地 Git 仓库。
在我的项目创建后,我需要初始化一个 Git 仓库。手动操作时,我们使用 git init
命令来完成。在 PyCharm 中,我们可以使用主菜单的 VCS 选项。目前,它显示为 VCS,因为我们还没有使用任何 VCS 初始化仓库,所以 UI 中的术语是通用的。如 图 5.9 所示,我们有 Mercurial、Git、GitHub、Perforce 和 Subversion 的版本控制选项。还有一个新的选项,用于在 JetBrains 的新协作产品 Space 中共享项目,Space 包含 Git 托管服务。随着 Space 产品的不断发展,您可以在所有 IDE 中期待看到越来越多的集成。我将点击 图 5.9 中所示的 创建 Git 仓库…。
图 5.9:从 PyCharm VCS 菜单创建 Git 仓库
如 图 5.10 所示,您接下来会被提示选择您想要创建仓库的文件夹。默认情况下是当前文件夹,这是正确的。
图 5.10:选择您想要创建 Git 仓库的文件夹
点击 确定 按钮将在所选文件夹中创建仓库。您可以通过检查默认分支(master)是否出现在顶部工具栏的下拉菜单中,其中包含一些 Git 操作来验证是否成功。现在我们已经初始化了一个 Git 仓库,菜单不再使用 VCS 这个术语,而是列出专门针对 Git 的选项。
在 GitHub 上添加远程仓库
PyCharm 中没有创建远程仓库的工具。您需要登录到 GitHub,并像之前一样创建一个仓库。图 5.11 展示了我在我个人账户中设置远程仓库的过程。请注意,这与书中仓库是不同的,书中仓库的目的是存放所有代码,而不是作为创建仓库的演示。
图 5.11:我的远程设置
注意,这里只设置了最基本的内容。没有 .gitignore
文件,没有 README 文件等等。我将在 PyCharm 中添加所有这些。当我点击 创建仓库 按钮时,GitHub 会生成我需要用于添加远程仓库的 URL。我在 图 5.12 中指出了这一点。
图 5.12:您需要远程仓库的 URL 才能将其添加到本地仓库
接下来,切换回 PyCharm 并在主菜单中找到 git 菜单。记住,一分钟前,菜单显示为 VCS,因为我们还没有定义仓库,但现在它显示为 git。
在 git 菜单中,您会找到一个名为 管理远程 的选项。点击它,您将看到一个模态对话框,如 图 5.13 所示,它允许您添加您刚刚在 GitHub 上创建的远程。
图 5.13:Git 远程对话框允许您添加您在 GitHub 上创建的远程
点击 + 按钮添加远程。您会在上面再看到一个对话框,如 图 5.14 所示。
图 5.14:添加您从 GitHub 页面复制的 URL
点击 main.py
文件是红色的。您只需相信我。它是红色的,因为它是一个未跟踪的文件。我们需要将我们的文件添加到我们的仓库中,这样我们就可以跟踪它们。
添加项目文件
设置项目和远程通常只在项目期间进行一次。如果您是加入一个现有的团队,它可能是在您加入之前很久就完成了。然而,接下来的这一系列操作是您将每天都会使用的。
当您正在处理对项目的更改时,您会发现 提交 窗口非常有用。如果您忘记了如何定位 提交 窗口,请参考 图 5.3。
添加 .gitignore 文件
如果您不熟悉,.gitignore
文件定义了您不想包含在您的仓库中的文件和文件夹。作为一个一般规则,任何可以从您的代码生成的文件都不需要包含在您的仓库中。当与 Python 项目一起工作时,您不想在您的仓库中的东西可能包括以下列表:
-
venv
文件夹,因为这个文件夹是通过运行pip
生成的 -
Python 缓存文件夹(您会看到它们作为
__pycache__
文件夹) -
字节编译的 DLL 文件
-
C 扩展
-
从发行和打包中构建文件
-
PyInstaller 清单
-
应用程序日志文件
-
测试覆盖率报告
这绝对不是一份详尽的列表。列表将真正取决于您正在构建的应用程序类型以及项目运行期间确切生成的内容。关于 .gitignore
文件的内容有很多建议。GitHub 在一个 gist 中有一个这样的列表。您可以在 githubgitm/github/gigitnore/blgitmain/Python.gitignore
获取此文件。它也是本书 Git 仓库中的 .gitignore
文件。如果您愿意,可以使用这个 .gitignore
文件,但为了保持简短和简单,我只是在我们将要创建的 .gitignore
文件中包含了一些条目。
切换回文件视图,如 图 5.15 所示。您可以通过点击文件夹图标(project_git_demo
项目标题(.gitignore
。这里不允许有创意。它必须称为 .gitignore
,全部小写。
图 5.15:切换回文件视图,创建一个名为 .gitignore 的新文件
当你创建新文件时,PyCharm 会提示你将文件添加到 Git,如图 图 5**.16 所示。如果你想将新文件添加为默认,你可以点击 不再询问 复选框,你创建的所有文件都将自动添加到 GitHub。
图 5.16:每次你在 PyCharm 中创建文件时,都会提示你将其添加到存储库
无论你对复选框做什么,.gitignore
文件在项目资源管理器中显示为绿色,因为它已被添加,而且还没有任何变化。在 .gitignore
文件中,添加以下行:
venv
__pycache__
这将排除整个 venv
文件夹和 Python 生成的任何缓存文件夹。PyCharm 在我们保存文件时保存文件,所以让我们将这个文件和 main.py
文件添加到存储库中。切换到 提交 窗口,你会看到类似 图 5**.17 的内容。
图 5.17:添加我们的 .gitignore 文件后的提交窗口
哇!这一切都是从哪里来的?我们只是添加了一个文件!正如你所见,.gitignore
文件是在我们创建文件时看到的对话框的响应下添加的。项目中唯一的其他代码文件是我们创建项目时 PyCharm 生成的 main.py
文件。那么其他文件呢?为什么在未版本化的文件列表中列出了第二个 .gitignore
文件?
这些都来自 PyCharm 创建的 .idea
文件夹。关于这个文件夹是否应该归入源控制,有一些争议。JetBrains 在他们的文档中有一个页面,讨论了他们对这个问题的看法。intellij-support.jetbrains.com/hc/en-us/articles/206544839
。总结本文,JetBrains 建议存储与项目相关的 .idea
文件夹内容,但不包括包含用户特定设置的任何文件,以下是一些例子:
-
workspace.xml
-
usage.statistics.xml
-
shelf
目录中的任何内容
本文旨在涵盖所有 .gitignore
文件。我们还没有提交任何内容,所以将这些行添加到你的 .gitignore
文件中:
.idea/workspace.xml
.idea/usage.statistics.xml
.idea/shelf
让我们检查所有复选框,以便添加所有这些文件,如图 图 5**.18 所示。
图 5.18:所有文件都已添加,我们准备提交
在框中添加提交信息。你可以点击提交按钮将更改提交到本地仓库,或者点击提交并推送…将更改直接发送到 GitHub。一般来说,除非你完全确定自上次推送以来没有人推送过更改,否则不应使用提交并推送…按钮。由于我们从未推送过,所以这可能是安全的;然而,我将点击提交按钮来演示之前安排好的整个过程。你添加更改,提交,拉取,然后推送。到目前为止,我们已经添加了更改,现在正在提交。
如果一切顺利,屏幕右下角会出现一个提示,说明你的文件已成功提交。如果你在手动练习期间没有设置全局用户名和电子邮件地址,系统将通过对话框提示你设置它们。
拉取和推送
现在第一个提交已经完成,我们可以进行过程的最后半部分。我们总是在推送之前拉取,以确保我们在当前分支上有最新的修订版。这给了我们机会解决自上次拉取以来可能出现的任何冲突。在 Git 中,我们使用的命令是pull
。Git 工具中有一个pull
选项,但在我们前往那里之前,我要指出另一个选项:更新项目…。这可以在工具栏的当前分支下拉菜单中找到,如图 5.19所示。
图 5.19:"更新项目… "命令实际上不是一个 Git 命令。它指的是一个更新策略
当你点击更新项目…时,系统会要求你设置更新本地 Git 仓库的首选项。接下来的对话框中展示了两种可能性,如图 5.20所示。
图 5.20:你可以从两种更新策略中选择
我们之前执行的git pull
命令体现了第一种选项。拉取操作将从远程获取任何更改,并自动将这些更改合并到我们的本地副本中。rebase
操作是一种不同的策略。它改变仓库的结构,以显示更清晰的更改时间线。我不会深入讨论使用哪种策略的争论,但如果你想要深入了解,我将在本章的进一步阅读部分包含一个链接。
个人而言,我更喜欢第一种选项,因为它更简单、更安全。我只需点击确定按钮。如果我不想在使用前被提示,可以点击不再显示复选框。PyCharm 将永远使用我现在做出的选择。
如果您在提交方面遇到问题,我并不是指一般意义上的,而是指在执行 git commit
时,您完全可以跳过这个对话框。只需使用 Git 菜单上的 “拉取...” 菜单项,如图 图 5**.21 所示。
图 5.21:您可以从任何 Git 菜单中执行传统的 Git pull 操作
除了推送之外,没有其他事情可做。也许之前在 图 5**.19 中展示的快速方法已经显示了,在那里我们遇到了 “更新项目...” 命令。
分支和合并
我们已经讨论了任何版本控制系统最基本的功能,这些功能通过跟踪随时间推移的修订版来保护项目的代码。另一组非常重要的功能包括将项目工作分割成几个副本。这种做法被称为 “分支”。分支有许多好处;我无法在这里全部涵盖。如果您想了解更多关于 Git 的版本控制细节,请参阅本章 “进一步阅读” 部分中列出的 Packt 书籍 《程序员 Git》。然而,我将提供我认为最重要的好处。
考虑一个典型的网络初创公司,它经历了一个从创建仓库到迭代开发至首次发布的周期。该发布版本位于云中的某个生产服务器上,并且客户已经开始使用该应用程序。初创公司不仅希望通过添加额外功能来增强其产品,而且还会遇到需要修复的 bug 和问题。让我们假设开发和质量保证团队的工作非常出色,以至于发布版本中几乎没有任何 bug。公司对自己感到满意,想要开始添加新功能。所有工作都在主分支上进行,因为这是唯一存在的分支。为了添加新功能,我们需要在代码深处进行一些更改。一旦开始更改,可能需要一周时间才能完成,在这段时间里,产品的开发版本非常不稳定。
突然,现实降临。除非软件是由独角兽或 Chuck Norris 编写的,否则几乎不可能有一个没有错误的软件发布。让我们假设这不仅仅是一个错误。这是一个严重的错误。比如有人忘记在 SQL 语句中添加 WHERE customer_id=@customerId
,您的应用程序错误地向所有客户显示所有客户数据。这是一个职业生涯的终结!我见过这种情况,它并不好看。您需要立即修复它!但是,如果没有撤销可能使应用程序不稳定的所有更改,您就无法修复。这是一个选择。另一个选择是,您可以回滚到您发布的代码,然后实施修复并保住您的职业生涯。然后,您将花费大量时间尝试挑选提交以挽救您的功能工作。您还可以选择使用 Dvorak 布局和完全空白的键盘在记事本中编写所有代码。您可以做到,但这不会是您时间的高效利用。
这就是分支发挥作用的地方。启动阶段可以在主分支上完成所有工作。他们不应该这样做,但让我们将故事限制在我们目前所拥有的内容。一旦软件发布,他们可以创建一个新的分支,也许会称之为 development-branch。名称并不重要。分支操作创建了已发布内容的副本。正是在这个分支上,我们勇敢的开发团队开始进行可能使开发版本不稳定的变化。
咔嚓!坏事情发生了。您可以从 development-branch 切换回 main。现在您有了发布时的代码,您可以修复问题。修复到位后,您可以将修复合并到您的开发分支,并在 development-branch 上继续工作不稳定的应用程序。保持工作分离,让您可以独立于紧急错误修复工作在新功能上工作。
我在图中过分简化了大多数团队使用的典型分支策略。我更愿意将这一主题的覆盖留给专门针对版本控制的著作。我将在本章的 进一步阅读 部分留下关于分支策略的建议。让我们继续讨论与分支一起工作的机制。这对您日常的软件开发实践至关重要。
创建分支
创建一个新的分支非常简单。点击顶部菜单栏上的分支菜单,然后点击 新分支。您将看到 图 5.22 中所示的对话框。
图 5.22:创建新分支对话框允许您创建一个新的 Git 分支
为您的新分支输入一个名称。分支在本地创建,并且您将自动切换到新分支。
在分支之间切换
这也很简单。回到分支菜单。本地分支列在分支下拉菜单中,如图 图 5.23 所示。
图 5.23:本地分支列在分支菜单中
切换到不同的分支只需从列表中选择所需的分支,然后点击 检出。如您所见,您还可以在分支上执行其他操作。
合并
当您准备好将分支合并回主分支或其他任何分支时,只需切换到该分支,然后在分支菜单中使用 合并 命令,如图 图 5**.24 所示。
图 5.24:合并命令可以在分支菜单中找到
如 图 5**.24 所示的 合并 对话框,将允许您选择要合并到当前选中分支的分支。
如果您需要比标准合并操作更复杂的功能,有一个 修改选项 下拉菜单,允许您执行在手动合并过程中通常使用的几个命令行开关的等效操作。
查看分支图
工具窗口中的 Git 工具提供了您仓库中各种分支的图形视图。您可以在 图 5**.25 中看到它。当您需要查看仓库中最近发生了什么更改时,这可能很有用。这是您可能早上第一件事就会做的事情,以便您可以审查过去 24 小时内团队所做的更改。
图 5.25:通过点击 PyCharm 窗口左下角的工具栏可以激活 Git 工具
要激活它,请点击工具窗口中的 Git 图标(1)。您可以使用工具栏上的各种工具搜索修订历史记录并对其进行过滤(2)。选择一个提交(3)允许您查看该提交的详细信息(4)以及提交信息。
差异和冲突解决
总有一天,您在推送更改之前执行拉取操作,会发现存在冲突。即使对于经验丰富的开发者来说,这也可能令人畏惧和压力重重,因为您冒着破坏他人最近贡献的风险。尽管如此,这种情况总会发生,您需要一种处理问题的方法。这让我们想到了 PyCharm 中我最喜欢的功能之一。在 第一章,“PyCharm 简介——最受欢迎的 Python IDE”,我向您讲述了我在微软调试器方面的经验。在我职业生涯的早期就体验过它,对我来说,其他任何东西都无法替代。我对 PyCharm 的合并工具也有同样的敬意。在其他任何地方解决冲突合并都感觉痛苦。
项目中只有一个文件。到现在为止,您会认出它是 PyCharm 为新项目生成的 main.py
文件。我们将通过以下步骤生成冲突:
-
在 PyCharm 中创建一个新的项目。
-
将
main.py
文件的内容更改为单行代码以确保我们生成冲突。 -
在项目文件夹中初始化 Git 仓库。
-
添加并提交所有项目文件。
-
创建一个名为 development 的新分支。
-
修改显示 Hello World 消息的代码行。
-
将更改提交到分支。
-
切换回主分支。
-
对这一行代码进行不同的更改。
-
提交更改。
-
现在尝试将 development 分支合并到主分支。
这 11 个步骤的结果将是一个冲突。让我们来解决它!
首先,在 PyCharm 中创建一个名为 conflict_resolution
的新项目,如图 5.26 所示。确保你在驱动器上创建项目时,位置不在书籍仓库的文件夹结构内。记住,书籍的代码已经在仓库中了。你无法在另一个仓库内创建新的仓库。
图 5.26:我的 conflict_resolution 项目设置。它们只是默认值
PyCharm 生成一个包含 main.py
文件的项目。删除 main.py
中的所有行,并用以下代码替换它们:
foo = "foo"
为了确保清晰,你的 PyCharm 窗口应该看起来就像图 5.27 中的我的窗口一样。
图 5.27:main.py 文件已被缩减为一行代码
在 文件 菜单中点击 VCS 菜单,然后点击 创建 Git 仓库。如果你不记得在哪里找到这个菜单项,请参考图 5.9。注意创建的默认分支。对于这个练习,我们不需要远程仓库,所以它是否是 main、master 或其他都无关紧要;你只需要记住它的名字。我将假设它被称为 main。
使用提交窗口将项目文件添加到仓库中。图 5.3 将提醒你如何操作。输入提交信息,继续提交文件。
接下来,我们需要创建一个分支。点击分支下拉菜单,然后点击 development
。
打开 main.py
文件。将代码替换为这一行:
foo = "bar"
通过限制自己只做一行操作,我们可以确保我们的操作将导致冲突。将此更改提交到 development
分支。
使用分支下拉菜单切换回主分支。将 main.py
的内容更改为以下代码:
foo = "baz baz baz"
将此更改提交到主分支。你刚刚模拟了两个开发者在同一区域更改同一文件的情况。Git 将无法解决这些差异。当主分支处于活动状态时,点击分支下拉菜单,点击 development 分支,然后点击 将 development 合并到 main。你将看到一个冲突消息,如图 5.28 所示。
图 5.28:在我们合并之前,有一个冲突需要解决
在解决冲突时,你通常有三个选项。你可以选择忽略接收到的更改,并通过点击接受你的按钮将你的本地工作作为正确代码。同样,你可以选择接收代码更改并丢弃你的本地副本,通过点击接受他们的。然而,通常你需要将你的本地修订的一部分与接收到的更改合并。为此,你需要激活合并工具,如图 5**.29所示,通过点击合并…按钮。
图 5.29:激活合并工具以从接收到的代码中选择部分与你的代码合并
当合并工具处于激活状态时,你可以看到三个面板。左侧的面板(1)表示来自主分支的冲突代码。右侧的代码(2)是来自开发分支的修订。中间的面板(3)表示两者的合并。你可以选择你代码的一部分和接收代码的一部分,以制作出最佳组合。通过实验>>按钮(4),可以将代码复制到中央合并结果中。标有X(4)的按钮将忽略冲突的行。你可以使用上下箭头(5)跳转到文件中的下一个未解决冲突。
让我们假设解决我的冲突的最佳方案是将左侧面板和右侧面板的行放在一起。在现实生活中,这不会起作用,因为我们最终会在相邻的位置得到两个变量赋值,但这确实展示了工具的工作方式。要使用左侧面板的行,我可以点击图 5**.30中箭头指示的按钮。
图 5.30:此按钮将代码从左侧面板移动到工作合并解决方案中
接下来,我想将右侧面板的行放在我刚刚移动的行下方。通常,你希望使用来自两边的代码,而不仅仅是单方面的对或错。点击图 5**.31中箭头指示的按钮,将代码从左侧面板复制到中间面板当前行的下方。
图 5.31:点击双箭头将此行复制到我们从右侧选择的行下方
一旦完成此操作,你就解决了冲突!你会得到一条漂亮的绿色消息告诉你这一点。
您的目标是使用左侧和右侧的窗格创建中间窗格中代码的最佳版本,这代表着冲突的解决。完成之后,您可以点击应用按钮。如果您在文件中还有未解决的冲突,PyCharm 会告诉您。如果您都解决了,您尝试的合并就完成了。如果您正在与远程仓库一起工作,一旦提交成功,您应该推送结果冲突解决方案。
查看差异
您不需要冲突就可以使用我们刚才看到的差异窗口。差异窗口是一个显示两个或多个代码版本的并排窗口。您可以在分支之间或修订之间进行差异比较。比如说,我通过添加这一行快速更改了我们的合并代码:
text = "I'm adding this line"
我还删除了第 2 行,内容如下:
foo = "bar"
我的代码现在非常不同。如果我想将我的新代码与分支中的最新版本进行比较,我可以右键单击main.py
标签并点击Git | 显示差异。我将看到两个文件的并排差异,如图图 5.32所示。
图 5.32:编辑器中的内容与最后提交版本之间的差异视图
这个差异现在以并排格式显示。您可以通过更改标记为(1)的下拉设置将其更改为内联视图,这在 GitHub 上查看提交时通常显示。您还可以使用您刚才在合并中看到的相同行移动工具编辑文件。(2)。
摘要
在本章中,我们涵盖了两个主要主题——应用开发和编程中版本控制的概念及其重要性,以及如何在 PyCharm 中使用 Git 和 GitHub 来实践它。具体来说,我们学习了如何以两种不同的方式使用 Git 和 GitHub 进行版本控制:手动和通过 PyCharm。
带着这些知识,PyCharm 用户可以以灵活的方式将版本控制应用于自己的项目,跳过终端/命令行中的手动和繁琐过程。我们看到,通过提供这些功能,PyCharm 允许我们在任何软件工程项目中专注于实际的开发过程。
除了版本控制之外,在应用开发中还有其他实践——PyCharm 提供了直观、直接的命令来简化这些过程。没有这些命令,应用开发可能会相当复杂和令人畏惧。这些过程包括测试、调试和性能分析,所有这些内容将在下一章中讨论。
问题
-
术语版本控制在编程的上下文中具体指什么?
-
使用版本控制有哪些好处?
-
使用 Git 和 GitHub 对您自己的项目进行版本控制的基本步骤是什么?
-
什么是分支,为什么它很重要?
-
列出 PyCharm 中提供访问 Git 和其他 VCS 命令的各种窗口和工具位置。
进一步阅读
-
一定要查看本书的配套网站:
www.pycharm-book.com
. -
如何 Secure Shell (SSH) 工作:
en.wikipedia.org/wiki/Secure_Shell
-
将新的 SSH 密钥添加到您的 GitHub 账户:
docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account
-
为什么 GitHub 将其 master 分支重命名为 main:
www.theserverside.com/feature/Why-GitHub-renamed-its-master-branch-to-main
-
Git 合并策略选项和 示例:
www.atlassian.com/git/tutorials/using-branches/merge-strategy
-
Git 分支指南:
learn.microsoft.com/en-us/azure/devops/repos/git/git-branching-guidance
-
Git 初学者必备:
www.packtpub.com/product/mastering-git/9781783553754
-
Liberty, J. (2021). 程序员必备 Git:掌握 Git 以有效实施版本控制,适用于您的编程项目. Packt Publishing Limited.
-
Narebski, J. (2016). 精通 Git. Packt Publishing Ltd.
-
《乔尔测试:提升代码质量的 12 步》:
www.joelonsoftware.com/2000/08/09/the-joel-test-12-steps-to-better-code/
-
一定要查看本书的配套网站:
www.pycharm-book.com
.
第六章:无缝测试、调试和性能分析
在 第五章,“PyCharm 中使用 Git 进行版本控制”,我谈到了 乔尔测试。这个测试只是一个最佳实践的列表。列表的顶端是使用版本控制,这是上一章的主题。如果你查看了这个列表,你可能不会对测试也出现在列表上感到惊讶。形式化的软件测试实践,如 测试驱动开发(TDD)和 行为驱动开发(BDD),是软件质量控制的基础。使用这些方法有助于你创建在生产环境中不太可能失败的应用程序。如果做得正确,它还有附带的好处,例如防止范围蔓延,并允许在可能忽视了最佳实践和承担了大量技术债务的项目上进行有效的重构。
目前实践中存在多个测试级别,包括以下内容:
-
单元测试,旨在在函数或类级别测试基本低级功能
-
集成测试,旨在测试更大系统中组件之间的协作方式
-
用户界面测试,旨在测试系统交互元素的工作方式
-
端到端测试,在类似生产环境中对整个系统进行测试
就像所有成熟的编程语言一样,Python 拥有一套丰富的测试库。由于 Python 是“内置电池”,因此标准库中包含了一些优秀的测试工具。自然地,第三方解决方案已经发展起来,并通过 PyPi.org 提供。
我在 Java 的 JUnit 库上磨炼了我的技能,后来又转向其 .NET 版本 NUnit。我发现这使得软件开发变得非常愉快。每天开始时,有一套无法通过的测试,然后整天编写代码让每个测试通过,这很有趣。如果你自律,你会编写所需的最少代码,随着你的测试工具颜色从红色变为绿色,你将逐渐看到进步。你不应该走捷径,也不应该被诱惑去编写看似酷炫但可能以后不需要的功能。当我多年前跳转到 Python 时,我很高兴看到关于测试库和框架的许多选项。我也同样高兴地看到 PyCharm 在 IDE 中支持大多数流行的工具。
在本章中,我们将探讨在遵循 TDD(测试驱动开发)原则的同时,在 Python 代码中创建单元测试。在 TDD 中,你通常创建一组旨在证明你的软件满足一系列要求的测试。这些测试在你创建程序中的任何功能之前编写,并且最初是失败的。你的任务是使用尽可能简单的代码使测试通过。
在这个过程中,你可能需要使用调试器逐步通过有问题的代码,这些代码要么无法解释地失败,或许更糟的是,无法解释地工作。一旦你的代码运行正常并通过测试,你通常想要考虑执行速度。大不列颠的国家卫生服务(NHS)开发了一个算法,将器官捐赠与系统中的患者匹配。这个复杂的算法必须快速,因为摘取的器官在移植前有一个有限的时间窗口是可行的。许多其他类型的应用程序也存在类似的时间限制。作为开发者,我们需要工具来帮助我们定位效率瓶颈。
本章将涵盖以下主题:
-
使用 PyCharm 在 Python 中进行单元测试
-
使用 PyCharm 强大的可视化调试器
-
使用 PyCharm 的性能分析工具来查找代码中的性能瓶颈
技术要求
以下为本章的先决条件:
-
安装好 Python 3.10 或更高版本
-
安装好 PyCharm
测试,测试,1-2-3
单元测试是一种旨在证明你的代码按设计工作的实践。一组好的测试将匹配功能规范。一组优秀的测试将做到这一点,但也会考虑到任何明显的失败路径。为了入门,让我们从一些简单的事情开始:你的银行账户。好吧,它不一定是你的。考虑一个典型的交易,你使用 ATM 卡在商店购买东西。
你前往你最喜欢的实体书店挑选你下一本关于软件开发领域的优秀读物。假设你找到了我第一本书的副本,《C# 设计模式的实际应用实现》,由 Packt 出版。鉴于其作为即时经典的地位,你无法抗拒以任何价格购买一本。你将卡片在书店的收银系统上刷一下,然后发生两件事:
-
相当于 39.95 美元——顺便说一句,这是一个绝对的便宜货——从你的银行账户中扣除。
-
相同的金额被转入书店的银行账户。
这是一个事务性操作。从形式上讲,一个事务是一个多步骤操作,每个步骤都必须无错误地完成。它应该是一个全有或全无的操作集。如果第一步完成但第二步失败,那么 39.95 美元就会从你的银行账户中消失,你无法带着书回家。如果第二步工作但第一步失败,你将得到一本免费的书,但当地的书店会破产。我们需要两个步骤都完成,或者最坏的情况是,完全失败,这样就不会有任何金钱交易。
这种重要性的水平是学习单元测试的好场景。
使用 PyCharm 在 Python 中进行单元测试
在 PyCharm 中使用纯 Python 项目模板创建一个新的项目。我们可以称它为 bank_account
。你可以在本章的源代码库中找到完成的示例,但如果你想要练习创建和测试必要的代码,只需按照以下步骤进行。
PyCharm 创建了一个名为 main.py
的文件。我们稍后会使用它,但让我们将我们的银行交易代码放入一个单独的模块中。编写良好代码的一个原则是编写 可测试的代码,而编写可测试代码的最佳方式是遵循 单一职责原则 (SRP),其中你创建只有单一职责的代码单元。SRP 是一个更大集合的规则的一部分,用于创建一个名为 SOLID 的健壮编码架构,SOLID 是以下原则的缩写:
-
单一职责 原则 (SRP)
-
开闭 原则 (OCP)
-
里氏替换 原则 (LSP)
-
接口隔离 原则 (ISP)
-
依赖倒置 原则 (DIP)
SOLID 通常在开发使用静态语言(这些语言严格面向对象)的 完全面向对象 (FOO) 架构时考虑。Java、C++ 和 C# 是此类语言的经典例子。Python 允许许多不同的开发范式,并且它的 面向对象编程 (OOP) 实现并不像许多其他语言那样完整,或者可能不是那么传统。如果你作为 Python 开发者从未听说过 SOLID,那可能就是原因。有书籍和博客,人们试图将 Python 代码塞进去,但在我看来,这通常感觉是勉强的。
SRP 是你应该绝对遵循的原则。它适用于任何语言和任何范式。简单来说,你创建的元素,无论是函数、Python 包还是对象,都应该只做一件事,并且做好这件事。通过分解代码的责任,你可以创建可重用的元素,这些元素可以轻松地进行单元测试,因此也容易维护。每一件事都应该只做一件事。当然,总会有一些东西将它们全部联系在一起——可能是一个程序中的 main
函数,它的唯一目的就是调用其他所有内容,并为你的程序提供流程。
OCP 表示一旦你将一个类发布到生产环境中,你就永远不应该更改它。你应该以这样的方式编写代码,使得你的类可以扩展,但不可修改。这个原则旨在保护你已经测试并发布的功能。如果你打开类并更改它,那么你将引入引入错误的风险,你必须重新测试整个程序。如果你将更改限制在扩展上,那么你只需要担心测试扩展。
LSP 在 Python 中不容易实现。它指出,任何子类都应该能够在不影响程序正确性的情况下替换其超类。换句话说,如果一个程序正在使用基类,那么在用派生类替换基类时,程序应该能够正确运行。当你遵守 LSP 时,你正在推广类内多态的概念。这允许通过它们的共同超类型统一地处理不同的对象,从而实现更灵活和模块化的设计。在像 Python 这样的动态语言中实现 LSP 很困难,因为这些语言允许动态类型和方法的后期绑定。因此,LSP 在静态强类型语言中的重要性甚至更高。挑战在于缺乏 C#、C++或 Java 中获得的严格编译类型检查。你犯的任何设计错误都不会在运行时显现出来。作为一个 Python 开发者,你必须非常小心地进行设计,并且比在其他语言中更加强烈地进行测试。
ISP 指出,类或模块应该具有针对其特定需求的接口。指定类结构和行为的接口不应包含对该类不必要的任何内容。这不太适用于 Python,因为 Python 缺乏像 Java 和 C#这样的语言中找到的传统接口。接口一词可以理解为普通超类,在这种情况下,超类不应该包含在子类中从未使用过的属性和方法。
DIP 是面向对象编程中的一个基本原则,它处理类和模块之间的依赖关系。它指出,高级模块不应该依赖于低级模块,但两者都应该依赖于抽象。此外,它强调抽象不应该依赖于细节;相反,细节应该依赖于抽象。
这里是 DIP 的关键思想:
-
高级模块不应该依赖于低级模块:高级模块代表应用程序的高级逻辑或功能,而低级模块处理实现细节和低级操作。根据 DIP,高级模块不应该直接依赖于低级模块。相反,两者都应该依赖于抽象。
-
抽象不应该依赖于细节:抽象,如接口或抽象类,定义了合同,指定了协作对象预期的行为和功能。DIP 指出,这些抽象不应该依赖于底层模块的具体实现细节。它提倡面向接口编程而不是具体实现。
为了遵循迪普原则(DIP),引入抽象,例如接口或抽象类,并针对这些抽象进行编程,而不是针对具体的实现进行编程,这是至关重要的。这促进了松散耦合,并使得代码库具有更大的灵活性和可维护性。确保你不会将此与依赖注入(DI)混淆。它们是相关的,但不是同一件事。
DI 是一种设计模式或技术,它促进了迪普原则的实施。DI 是一种从外部源提供类所需依赖项的方式,而不是让类内部创建或管理其依赖项。
在 DI 中,创建和提供依赖项的责任被委托给一个外部实体,通常称为“注入器”或“容器”。容器负责创建类的实例并注入其依赖项。这允许更好的解耦和灵活性,并且由于在单元测试期间可以轻松地替换或模拟依赖项,因此更容易进行测试。
DI 可以被视为实现迪普原则中概述的原则的实施策略。它通过提供一种反转依赖项控制机制,将对象的创建与其使用分离,从而有助于遵循迪普原则。
总结来说,DIP 是设计模块化、松散耦合系统的指南,而 DI 是一种用于实现 DIP 的技术或模式,它通过外部化管理依赖项的责任来实现这一点。
如果你对此类架构感兴趣,你应该查看我之前提到的那本书,因为 SOLID 原则在其中得到了广泛的覆盖,尽管语言是 C#。然而,SRP 与任何语言或范式都很好地结合,包括 Python。
当你坚持使用只做一件事情并且做得很好的函数和类时,测试它们就变得非常容易,因为功能是隔离的。试图做太多事情的函数或类由于依赖项之间的相互作用而更难测试。让我们构建一些东西来使这一点更清楚。
选择测试库
对于 Python 3,有几种流行的单元测试库可用。以下是一些最广泛使用的:
-
unittest
:这是 Python 的内置单元测试框架,通常被称为unittest
。它提供了一套用于编写和运行测试的类和方法。unittest
遵循 xUnit 风格的单元测试,并提供了测试发现、测试固定作用域和断言方法等功能。 -
pytest
:pytest
是一个流行的、功能丰富的测试框架,它提供了一种比unittest
更简洁、更易于表达的方式来编写测试。它支持测试发现、固定作用域、参数化测试和强大的断言方法。pytest
以其简单性和灵活性而闻名。 -
nose
:nose
是另一个流行的测试框架,它扩展了unittest
的功能。它提供了额外的功能,例如自动测试发现、测试生成器、插件以及高级测试选择和过滤选项。虽然nose
被广泛使用,但近年来其受欢迎程度有所下降,转而支持pytest
。 -
doctest
:doctest
是一个独特的测试框架,允许您在文档字符串或文档注释中以交互示例的形式编写测试。它提取并执行这些示例作为测试,验证实际输出是否与预期输出匹配。doctest
非常适合测试代码文档和示例。
这些只是 Python 中流行的单元测试库的一些例子。每个库都有自己的特性、风格和优势,因此值得探索,以找到最适合您项目需求和您个人偏好的库。与 PyCharm 一起工作的好处是,它支持所有这些测试库,并且运行测试和查看结果的 UI 总是一致的。
由于这是一本关于 PyCharm 的书,而不是关于测试框架的阐述,我将使用 unittest
库,它是 Python 标准库的一部分。这将使我们的示例代码不依赖于外部依赖项。
添加银行账户类
在项目窗口中右键单击项目标题,并选择 bank_account.py
。
接下来,添加以下代码:
class BankAccount:
def __init__(self, name: str, account_number: str, \
balance: float):
self.name = name
self.account_number = account_number
self.balance = balance
到目前为止,我们已经创建了一个名为 BankAccount
的类,创建了一个构造函数,并初始化了三个成员变量,分别称为 name
、account number
和 balance
。接下来,我们将添加一个用于处理取款的方法,但前提是取款金额必须小于余额:
def withdraw(self, amount: float) -> None:
new_balance = self.balance - amount
if new_balance > 0:
self.balance = new_balance
else:
raise ValueError("Account overdrawn!")
如果取出的金额超过 balance
,我们将抛出一个 ValueError
并显示一条消息,指出 Account overdrawn!
。接下来,我们需要一个方法来向账户中添加资金。它需要是一个正数;否则,我们将进行取款而不是存款:
def deposit(self, amount: float):
if amount > 0:
self.balance += amount
else:
raise ValueError("Deposit amount must be greater \
than 0.")
到目前为止,一切顺利,对吧?由于我们的方法中包含一些业务逻辑,我们应该为它们创建单元测试。
测试银行账户类
在项目窗口中右键单击项目标题,并选择 新建 | Python 文件,但这次,将其设置为 Python 单元测试,如图 图 6**.1 所示:
图 6.1:有几种模板用于创建新的 Python 文件
在处理测试文件方面存在几种约定。有些人认为创建一个仅包含测试的文件夹是个好主意。其他人则认为测试文件应该紧挨着它所测试的文件。我喜欢这种约定,因为它让我能轻松地看到项目中哪些文件缺少测试。按照类似的约定,我将文件命名为back_account_test.py
。约定规定,我的文件名要么以单词test开头,要么以它结尾。我把它放在了末尾,因为如果不这样做,测试文件就不会在文件资源管理器中紧挨着它所测试的文件。
PyCharm 创建了文件,但它不是空的。文件中的代码看起来是这样的:
import unittest
class MyTestCase(unittest.TestCase):
def test_something(self):
self.assertEqual(True, False) # add assertion here
if __name__ == '__main__':
unittest.main()
集成开发环境(IDE)为我们提供了一个包含继承自 Python 内置单元测试框架的测试类的模板。这个框架简单而缺乏想象力地被称为unittest
。模板包含文件顶部的必需导入、一个测试类、一个测试方法和一个允许脚本独立运行的dunder-main
块。为了使测试工作,你需要修改这个文件。首先,向包含你想要测试的类的文件中添加一个导入。添加的行如下所示(加粗):
import unittest
from bank_account import BankAccount
接下来,将类的名称更改为BankAccountTestCase
。然后,完全移除test_something(self)
方法,并用这个方法替换它:
def test_init(self):
test_account = BankAccount("Bruce Van Horn", \
"123355-23434", 4000)
self.assertEqual(test_account.name, "Bruce Van Horn")
self.assertEqual(test_account.account_number, \
"123355-23434")
self.assertEqual(test_account.balance, 4000)
老实说,这是一个有点愚蠢的测试,因为构造函数的逻辑非常简单。即使是我也会想跳过它。但情况并不总是这样。如果你在构造函数中做了复杂的事情,你应该对其进行单元测试。在这里,这个例子作为一个简单的例子,让我们开始。我们在这里所做的只是创建了一个BankAccount
类的新实例,并传递了一个名称、一个账户号码和一个初始余额。然后,我们使用了unittest
类的assertEqual
方法来检查每个成员变量,以确保它们被正确设置。除非你犯了错误,否则几乎不可能出错,这正是重点所在。
小心那些讨厌的自定义 self):
在完成了一章关于自动补全奇迹的讨论后,我必须承认,有时它可能会让人感到烦恼。这就是那种时候。当你输入测试方法的开始括号时,PyCharm 就会自动填充单词self
,以及结束括号和行尾的冒号。因为我打字很快,所以我经常在意识到发生了什么之前,就打出了类似test_init(selfsel):
的东西。我已经训练我的右手,在按下开始括号的同时,立即找到键盘上的End键。这会将你带到自动完成的行尾。按下Enter键,你就能到达你想要的位置。
我们将在第一个测试下方添加两个更多的测试。第一个测试将测试withdraw
方法。在第一个测试下方,但在带有dunder-main
测试的行上方输入新的方法:
def test_withdraw(self):
self.fail()
在test_withdraw
方法下方输入存款测试:
def test_deposit(self):
self.fail()
如果你还没有猜到,这两个测试将会失败。这没关系。我喜欢看到它们失败,这样我知道整个测试设置是正常工作的。作为长期软件开发者的一个好处和副作用是,你不会假设任何东西都会正常工作,无论是谁写的,或者这个事物花了多少钱。称之为生存本能。如果你跳过这个步骤,那么愿好运永远在你这边。
运行测试
让我们运行我们的测试。像许多事情一样,运行测试有很多种方法。你无疑已经注意到了在你的测试代码中出现了绿色箭头,如图 图 6.2 所示。
图 6.2:创建测试时,IDE 中将出现绿色运行箭头
这些绿色箭头在你点击时会触发一个菜单。现在,我们将点击第一个项目,即 BankAccountTestCase
,测试运行器将出现在 IDE 窗口底部的工具窗口中。你可以看到我的 图 6.3:
图 6.3:测试运行器显示了运行的测试,包括通过和未通过的测试,以及控制台输出
测试运行器本身在其窗口中集成了完整的一套工具。我在 图 6.3 中对它们进行了编号:
-
此面板显示了通过和未通过的测试。它们以与调用层次结构相匹配的层次结构显示。
-
此面板显示了测试运行的控制台输出。
-
在输出面板上方是通过测试的数量摘要,以及测试套件运行的时间。
-
在同一工具栏的左侧是一组五个按钮,之后跟着一个垂直省略号。✓ 和 ⃠ 按钮将分别过滤掉所有通过和未通过测试。过滤掉通过测试让你能专注于未通过的部分。过滤掉未通过测试可以减少当你有 100 个测试中只有 5 个通过时那种普遍的沮丧感和彻底的绝望感。当这种情况发生时,我通常会吃个三明治,感觉会好一些。这样想:只要你有未通过的测试,你的工作可能还是安全的,因为训练一个替代者可能需要更长的时间,而不是等待一切开始正常工作。把它看作是半满的玻璃杯。省略号之前的下三个按钮允许你排序测试结果,从另一个文件导入测试,并回顾你的测试运行历史。假设你有一个测试是通过的,然后失败了,你想回去看看它最后一次通过的时间。如果你需要,历史记录就在那里。省略号还包含一些其他选项,包括针对测试运行器本身的杂项设置。
-
这个工具栏允许你重新运行所有测试,只重新运行失败的测试,以及停止长时间运行的测试。再次强调,我们有一个垂直省略号,但这个省略号有一个有趣的选项,可以切换自动测试。开启这个选项后,对于那些无法忍受光标回到重新运行按钮的时间的人来说,测试将不断运行。
当你第一次运行测试时,PyCharm 会自动为你创建运行配置。你可以在顶部工具栏的运行配置下拉菜单中看到它们。
修复失败的测试
我们有两个测试总是会失败,无论我们做什么。让我们先从bank_account_test.py
文件中的test_withdraw(self)
方法开始修改。改为如下:
def test_withdraw(self):
test_account = BankAccount("Bruce Van Horn", "123355-23434", 4000)
test_account.withdraw(2000)
self.assertEqual(test_account.balance, 2000)
第一行实例化了BankAccount
类,并使用了一些可测试的值。接下来,我们调用withdraw
方法,取出$2,000。我希望它是为了做一些有趣的事情!通常情况下,是我的女儿们借我的钱包去购物或者可能为工程项目的原材料购买。我可以期待,对吧?我现在期待我的余额从$4,000 降到$2,000。所以,我使用unittest
类的assertEqual
方法,它是我的BankAccountTestCase
类的超类。我传入test_account.balance
,这将与预期结果进行比较。
我完全期待这个测试会通过!点击图 6**.3中显示的重新运行失败的测试按钮。它通过了!现在,让我们编写test_deposit
方法:
def test_deposit(self):
test_account = BankAccount("Bruce Van Horn", "123355-23434", 4000)
test_account.deposit(5000)
self.assertEqual(test_account.balance, 9000)
这里的解释与上一条相同,只是这次,我们将$5,000 存入我的账户。这种情况在现实生活中很少发生,所以请给我一点时间,让我庆祝一下。
重新运行失败的测试。现在它们都应该通过了!但我们还没有完成,对吧?
到目前为止,这些测试都遵循了无故障路径。这意味着到目前为止,我只测试了按照我设计的运行方法。现实世界中的用户永远不会这样做。我们需要测试故障路径。
测试故障路径
我们在设计系统时故意设置了一个明显的故障路径:透支。如果我们试图取出比当前余额更多的钱,会发生什么?或者,用我女儿们可能会说的话,我们如何生成一个信号,告诉我们是时候从商场回家并隐藏收据了?
我们在代码中考虑了这一点:
def withdraw(self, amount: float) -> None:
new_balance = self.balance - amount
if new_balance > 0:
self.balance = new_balance
else:
raise ValueError("Account overdrawn!")
如你所见,我们检查新的余额是否会变成负数。如果是,我们抛出一个ValueError
。这个测试将会有一点不同。我们不是使用assertEquals
来测试无故障的结果,而是想验证当这个条件存在时,我们不仅抛出一个错误,而且抛出正确的错误类型。这很重要,因为我们期望抛出ValueError
,但如果产生了其他错误,如果我们只测试通用的Exception
,测试将会给出一个错误的阳性结果。在BankAccountTestCase
类中添加以下测试:
def test_overdraft(self):
test_account = BankAccount("Bruce Van Horn", "123355-23434", 4000)
self.assertRaises(ValueError, test_account.withdraw, 5000)]
如前所述,我们使用一些可测试的值实例化BankAccount
类。对于测试,我们想要断言如果传入的金额超过余额,withdraw
方法会引发ValueError
。在这里,我们使用self.assertRaises
,它接受三个参数。第一个参数是我们期望的错误类型。
第二个参数是受测试的方法。注意,我们以 lambda 风格传递函数的引用。我们不需要执行函数,因为我们需要assertRaises
函数来做这件事。最后,我们需要传递任何参数的值——在这个例子中,一些比实例化时用的四千更大的数字。在这种情况下,我传入5000
。当我运行这个测试时,它应该通过,因为函数会因我期望的ValueError
异常而失败。
剩下只有一个测试:我们需要确保当我们向deposit
方法传入负数时,会得到ValueError
。我将这个留给你练习。完整的代码在章节代码库中。
自动生成测试
到目前为止,我们已经花了一些时间编写BankAccout
类,但回想一下我们最初为单元测试编写的用例:一个金融交易。这次,我们将编写一些需要测试的代码,但我们将生成一个更精确的测试,而不是通用的测试模板。
让我们从将要测试的代码开始。在你的项目中创建一个名为transaction.py
的新文件。这个文件的 内容应该如下所示:
from bank_account import BankAccount
我们需要BankAccount
类,因为整个想法是编写代码,在物品销售时从一个账户转移到另一个账户。说到物品,让我们创建一个类来表示我们将要购买的东西:
class Item:
def __init__(self, name: str, price: float):
self.name = name
self.price = price
这里没有什么太疯狂的东西——只是两个实例变量name
和price
。现在到了困难的部分:我们需要一个类来表示交易。记住,交易是一个原子操作。所有步骤都应该完成。如果在过程中出现任何错误,之前发生的一切都需要回滚:
class Transaction:
def __init__(self, buyer: BankAccount, seller: \
BankAccount, item: Item):
self.buyer = buyer
self.seller = seller
self.item = item
我们用构造函数开始这个类,它初始化两个银行账户和一个项目。然后是交易本身的逻辑:
def do_transaction(self):
original_buyer_balance = self.buyer.balance
original_seller_balance = self.seller.balance
我们需要存储原始余额。如果出现任何问题,我们需要这些信息来将一切恢复原状。接下来是金钱交换的部分。我将用try
来封装:
try:
self.buyer.withdraw(self.item.price)
self.seller.deposit(self.item.price)
except ValueError:
self.buyer.balance = original_buyer_balance
self.seller.balance = original_seller_balance
raise ValueError("Transaction failed and was \
rolled back")
我们尝试从买家的账户中取款,然后将相同金额存入卖家的账户。如果抛出ValueError
异常,我们将通过恢复余额到原始值将所有钱款退回。一旦钱款被恢复,我们仍然应该抛出一个错误,以便主应用程序知道错误发生了。这个函数需要向用户界面报告结果,让用户知道交易发生了什么。最后一行为我们处理了这一点。在实际应用中,你可能想创建自己的自定义错误,以便提供更多信息,但这个错误对于演示目的来说已经足够好了。
生成交易测试
之前,我们使用class Transaction
创建了一个新的测试。然后,点击图 6.4中所示的“生成…”菜单选项:
图 6.4:当你右键单击类定义时,可以找到“生成…”菜单项
接下来,点击图 6.5中所示“测试…”:
图 6.5:点击“测试…”按钮以生成测试
在这一点上,将弹出一个对话框,你可以控制将要生成的测试,如图 6.6所示:
图 6.6:PyCharm 将根据这些设置生成一个单元测试文件
PyCharm 将创建一个名为test_transaction.py
的文件。在该文件中,将不会有一个通用的测试类名,而将有一个名为TestTransaction
的类定义。最后,在文件中,假设你勾选了复选框,将生成一个名为test_do_transaction
的测试方法占位符。
生成的文件包含以下代码:
from unittest import TestCase
class TestTransaction(TestCase):
def test_do_transaction(self):
self.fail()
在第一章中,我告诉过你们,IDE 的一个好处是可以减少样板代码。第一次,PyCharm 为我们生成了一些通用的样板代码。至少我们不必手动输入,但修改它所生成的内容也需要相当多的努力。这次,我们甚至有更少的工作要做。如果我的类中有许多方法,将为每个方法生成一个正确命名的占位符。
我现在必须编写使test_do_transaction
方法通过的代码。看吧!
from unittest import TestCase
from bank_account import BankAccount
from transaction import Transaction, Item
我们从必要的导入开始。我知道我需要两个测试,而不仅仅是 PyCharm 生成的那个。PyCharm 生成了一个测试方法,我正在用它来测试无故障路径。我将传入一些按预期工作的事物。既然我知道我有两个测试,我可以重用卖家账户来保持测试的DRY。如果你不熟悉这个缩写,它代表不要重复自己。通过将此代码提升到文件顶部,我只需要输入一次。这段代码将初始化一个卖家银行账户,余额为$4,000。它还设置了我们将要购买的物品,这些在测试之间不会改变:
initial_seller_balance = 4000
seller_account = BankAccount("PacktPub", "839423-38402",
initial_seller_balance)
item = Item("Python book", 39.95)
接下来,我们将继续进行测试类本身,这个类是为我们生成的。我们已经有这部分内容了:
class TestTransaction(TestCase):
def test_do_transaction(self):
我正在用我希望能使测试通过的自定义代码替换生成的self.fail()
:
buyer_account = BankAccount("Bruce Van Horn", "123355-23434", 99)
item = Item("Python book", 39.95)
test_transaction = Transaction(buyer_account, \
seller_account, item)
和往常一样,我在测试中实例化了将要使用的类。到目前为止,我已经创建了两个账户和一个带有价格的物品。接下来,我将运行测试方法:
test_transaction.do_transaction()
然后,我会检查我的结果:
self.assertEqual(buyer_account.balance, 99 - 39.95)
self.assertEqual(seller_account.balance,\
initial_seller_balance + 39.95)
你可能会想用花哨的测试代码。小心这个。花哨的测试代码和它打算测试的花哨代码一样容易出错。如果你正在测试复杂的数学,请不要在测试中重复计算,然后将其与被测试的代码进行比较。你应该输入已知输入并检查已知输出。仅此而已!
这个测试代表了无故障路径。我完全期待它会通过,因为这个练习仅仅是在理想条件下工作的所有事情。让我们看看我是否正确。点击任何一个绿色的运行按钮。我的结果在图 6**.7中显示:
图 6.7:到目前为止,一切顺利!我的测试通过了!
我们需要至少一个故障路径的测试。在这种情况下,我们将测试当我账户中的余额不足以购买一本书时会发生什么。
这是我针对该情况的测试:
def test_transaction_overdraw_fault(self):
initial_buyer_balance = 5
buyer_account = BankAccount("Bruce Van Horn", \
"123355-23434", initial_buyer_balance)
test_transaction = Transaction(buyer_account, \
seller_account, item)
当我今天离开去上班时,我的账户中至少有$9,000。但我的女儿菲比从我的夹克口袋里“借”了我的卡。她说她要创建一个机器人自行车工厂。我没有在意。她在开玩笑,对吧?所以,我下班后去书店,打算买最新的杰作:
test_transaction.do_transaction()
交易发生了。你知道当 Pac-Man 被幽灵吃掉时发出的声音吗?我现在就在模仿这个声音。这次销售将失败;让我们看看交易是否能够正确回滚:
self.assertEqual(buyer_account.balance, initial_buyer_balance)
self.assertEqual(seller_account.balance, initial_seller_balance)
这段最后的代码验证了买家和卖家的余额都回到了原始值。运行测试——它们都应该通过!见图 6**.8查看我胜利的测试运行。我迫不及待地想回家放松!
图 6.8:我最好给我的妻子打电话,让她为我热饭
看起来我有点过于自信了。输出窗口显示了一组所有出错的堆栈跟踪。它太长了,我不得不向下滚动很多才能到达这个截图的好部分。在跟踪(此处未显示)中,我可以看到一些我以为会抛出的错误确实发生了,这很好。我们在这里可以看到的两个不是。首先,我打算验证异常传来的消息是否与我定义中指定的值匹配。再次,我这样做是为了确保我抛出的错误是我们看到的错误,而不是由于错误导致的其他错误。看起来我没有理解错误的结构,实际上,没有名为 message 的属性。我发誓!等等——那可能来自其他语言。好吧,我可以查一下。
另一个更令人不安的错误是我的事务没有回滚!当你查看跟踪信息时,你会看到其中包含超链接,允许你直接导航到跟踪中提到的错误代码。移动和查找问题非常容易。我可以在堆栈跟踪列表中找到第一个问题的行,如图图 6.9所示:
图 6.9:堆栈跟踪中充满了超链接,可以直接跳转到代码中的问题部分
点击这个链接会带我去图 6.10中显示的问题代码:
图 6.10:哎呀!IDE 甚至告诉我第 34 行是错误的,但我没有听
我有几个选择,不是吗?我可以通过悬停在e
变量上使用 PyCharm 的文档功能。我们在第四章中讨论了自动文档功能。图 6.11显示了如果你一直在跳来跳去,这会是什么样子:
图 6.11:自动文档功能将给我一个链接到官方文档
这里没有简单的答案,对吧?当然,我可以点击底部的链接并访问 Python 网站阅读文档。不过,如果我那么做,我就会失去你,读者,对我的信任。当你们都在看着的时候阅读手册?不可能!我确信我会找到答案,但代价是我的自尊。
我还有一个想法!我之前谈过 PyCharm 的控制台。我想试试。看看图 6.12:
图 6.12:如果 PyCharm 控制台按钮(2)不在你的工具栏上,点击省略号(1)来打开它
指向2的箭头将打开 PyCharm 控制台。如果你从未这样做过,该图标不会在工具栏上。你需要点击1处的省略号,然后点击Python 控制台区域。这将将其添加到你的工具栏中。我的控制台会话如图 6**.13所示:
图 6.13:重新访问控制台允许我们快速实验以解决我们的错误
在控制台中,我首先在第一行按了Enter键。我为你做了这件事。如果我没有这样做,控制台会把所有东西都挤在一起,看起来不会那么漂亮。接下来,我输入了以下内容:
check = ValueError("This is a test")
我怀疑如果我把检查转换为字符串,我会得到我想要的消息。称之为直觉。或者称之为“我背着你用ChatGPT查看了。”我选择直觉。
如果我输入str(check)
,Python 交互式解释器将评估表达式并打印结果。这个想法是可行的。我可以纠正我的代码。test_transaction.py
中的第 34 行现在将是以下内容:
self.assertEqual(str(e), "Transaction failed and was rolled back")
现在,如果我再次运行测试,它将失败,如图 6**.14所示:
图 6.14:软件开发中的进度可以通过错误列表减少的速度来衡量
我们预料到了这一点。问题列表变短了,所以这是一个胜利!让我们清除最后一个问题。交易失败后,事务未能正确重置卖家的账户值。我们可以盯着它看一会儿,或者我们可以通过启动 PyCharm 的调试器并逐步通过整个测试来采取更积极主动的方法。
使用 PyCharm 的调试器
在第一章中,我赞扬了 PyCharm 的调试器是使用 IDE 而不是像标准 Python 调试器(称为pdb)这样的命令行调试器的最大原因。请别误会——你应该学会使用 pdb,因为有时 IDE 不可用。然而,我怀疑一旦你使用了 PyCharm 的,你将更喜欢它。让我们看看我是否正确。
我们在Transaction
类中有一个问题,并不完全准确。在测试时,总有两种可能性:
-
代码失败是因为待测试代码中的缺陷。
-
代码失败是因为测试代码的问题。
由于我们目前不知道哪种可能性是正确的,调试器将允许我们逐行执行我们的代码并检查其内部工作原理。为此,我们需要设置一个断点。断点标记了您代码中的一个位置,您希望在此处停止执行并检查变量的内容、堆栈等。您可以通过在编辑器中的空白处点击行号来创建一个断点,如图图 6.15所示。我将把断点添加到测试的开始处,这样我们就可以逐步执行它。测试从第 25 行开始,所以我将点击那个行号;观察发现行号已被一个红色圆点所替代:
图 6.15:点击行号以创建断点,该断点将用红色圆点替换数字
接下来,我们需要运行调试器。在编辑器窗口中方法定义test_transaction_overdraw_fault(self)
旁边点击绿色箭头。这次,点击图 6.16中显示的“调试‘Python tests for tes…’选项,以运行失败的测试:
图 6.16:点击绿色箭头提供菜单,可用于对正在运行的测试进行修改,包括运行调试器
当调试器运行时,程序将开始,然后在我们的测试的第 25 行停止。IDE 发生了显著变化。让我们看看图 6.17:
图 6.17:PyCharm 中的暂停调试器
您可能会立即注意到一些事情。首先,IDE 顶部的运行按钮现在是绿色的,红色的停止按钮是亮的(1)。这些都是视觉线索,表明有东西正在运行,当然我们确实在运行。
IDE 的下半部分现在被调试工具(2)占据。还有一个标签栏(3),允许您同时运行多个调试会话。这在开发 RESTful 微服务架构时很有用,我们将在接下来的几章中讨论这一点,特别是在第九章,使用 FastAPI 创建 RESTful API。
在右侧有一个线程列表(4),允许您在各个线程之间切换并检查它们。然而,大多数时候,您会到达正确的位置,可能只会偶尔使用它。位于位置5的区域显示了当前作用域内的所有内容。目前,那只是self
,您可以看到它是TestTransaction
类的一个实例。
6显示了两个标签,允许您在我们现在看到的视图之间切换,这允许您在5区域检查程序的状态。如果您切换此标签,将显示print
语句,以便您可以在程序运行时查看输出。
标记为7的工具栏包含一组非常实用的工具,而表达式窗口(8)允许您使用当前作用域内的任何内容添加一个监视器或评估一个表达式。
调试窗口最有用的部分是检查区域(5),标签切换,您可以使用它来在变量检查器和线程检查器以及控制台输出(6)之间切换,以及调试工具栏(7)。让我们更仔细地看看调试工具栏:
图 6.18:PyCharm 中的调试工具栏
我已经为每个按钮编号。让我们回顾一下:
-
此按钮重新启动调试运行。您可以在 IDE 窗口顶部靠近运行按钮的位置找到重复的重新启动按钮。
-
此按钮停止调试运行。您可以在 IDE 窗口顶部靠近重新启动按钮的位置找到重复的停止按钮。
-
这是Continue按钮。调试器将在遇到任何断点时停止,并等待您使用(5 – 8)中的一个步骤按钮或您点击此按钮以继续运行。
-
Pause按钮将暂停运行。如果您正在运行一个循环或需要一段时间才能完成的算法,并且想要暂停运行,这可能会很有用。
-
Step Over按钮将执行调试器暂停的当前行。如果该行是调用您程序中函数的函数调用,该函数将正常执行并返回,之后您将转到代码的下一行,调试器将保持暂停。在这里,您正在跳过下一行的执行。
-
相反,这是Step Into按钮。如果您的调试器暂停在包含函数调用的行上,点击此按钮将允许您进入该函数并像在函数开始处放置断点一样逐步执行。Step Over跳过此执行,而此按钮进入它。
-
Step Into My Code是一个变革性的功能!这个按钮就像Step Into按钮(6)一样,只不过这个按钮不会进入您未创建的代码。我的意思是,Step Into按钮会高兴地让您进入第三方库代码的深处,或者进入构成 Python 本身的代码。这很少有用。Step Into My Code按钮只会进入您项目的一部分代码。
-
这是Step Out按钮。如果您发现自己进入了一些显然不是问题的代码,或者您可能已经进入了您未创建的库代码,退出代码将带您回到进入的点。
注意 Visual Studio 用户
PyCharm 调试器中的按钮与 Visual Studio 中的按钮工作方式不同!这对我来说需要一些适应。在 Visual Studio 中,你可以点击顶部工具栏上的绿色按钮来开始调试会话。当你遇到断点时,你可以点击相同的按钮来继续。在 PyCharm 中,继续按钮位于调试工具栏中,图 6**.18 中的区域 3。如果你点击用于启动调试器的相同按钮,你会启动第二个调试会话。除非你在运行配置中勾选了允许同时运行多个的复选框,否则 PyCharm 通常会对此表示不满。
使用调试器查找和修复我们的测试问题
我们的单元测试揭示了代码中的问题。当我们的交易由于透支错误失败时,我们期望买方和卖方的余额恢复到原始值。在这个时候,卖家在交易失败后获得了 $39.95 的信用。让我们使用调试器逐步执行并看看我们是否能找出这是为什么。
根据 图 6**.19,我们已经启动了我们的单元测试的调试器,并且已经停止在 test_transaction.py
中的第 25 行。在这个阶段,测试方法中的任何内容都没有运行。当你正在查看 PyCharm 调试器中的高亮行时,你需要记住,高亮行尚未执行。要执行该行,请点击 buyer_account
已经实例化,并且我们的高亮将移动并停止在 图 6**.19 中显示的第 26 行:
图 6.19:点击了“单步执行”按钮后,调试器已停止在第 26 行
要查看对象的内容,你需要展开箭头,我在 图 6**.19 中用圆圈标出。你可以看到 buyer_account
的余额为 $5. 然而,我们这里感兴趣的是卖家账户,因为问题就出在这里。
点击第 30 行以在那里添加断点,然后点击 do_transaction()
方法来监视其执行。点击 进入我的代码 按钮。如果你不记得是哪个按钮,请参考 图 6**.18 并查看 7。
这将带我们到 transaction.py
中的第 17 行。单步执行第 17 和 18 行,到达第 19 行并检查我们的状态。你会看到问题,如 图 6**.20 所示:
图 6.20:调试器显示卖家余额的起始值错误
调试器显示原始卖家余额为 $4,039.95,而我们预期它应该是 $4,000. 你可以在两个地方看到这个值。变量窗口显示给我们 (1),但你也可以在编辑器窗口中悬停在任何作用域变量上 (2) 来查看其值。
现在,为什么我们的起始余额会是错误的呢?这是一个作用域问题!由于我在test_transaction.py
的第 5 行将seller_account
变量提升到全局,第一个测试成功地将余额更改为$4,039.95,就像它应该做的那样。由于它是全局的,这个数字就保留了下来。为了修复这个问题,我们需要在test_transaction_overdraw_fault(self)
方法的开始处重置卖家账户的余额。我们开始调试工作是在第 25 行。让我们就在那里做我们的更改。点击调试工具栏上的停止按钮,然后在第 25 行添加以下代码:
seller_account.balance = 4000
不带调试重新运行测试。大胆一点!假设它成功了!如果您没有跟上,请礼貌地移到座位边缘,开始紧张地咬指甲。我们的英雄在图 6**.21中会取得胜利吗?提示管弦乐:duhn duhn duuuuuhn!
图 6.21:胜利!
它成功了!现在,是时候回家加热晚餐了,因为我们已经恢复了人们对国际银行业的信心。
检查测试覆盖率
单元测试在覆盖程序中的每个类、方法、函数或模块时最为有效。随着软件代码的增长,很容易忘记编写测试,或者可能推迟到有更多时间再进行。PyCharm 有一个工具可以告诉您测试覆盖率是多少,并帮助您找到测试更多工作的未开发机会,这可能比您自己能想到的还要多。
要检查测试覆盖率,您只需稍微改变一下运行测试的方式。我们一直是在测试文件内部单独运行测试。我们需要一起运行所有测试,以便我们可以有一个全面的报告,显示我们在哪些地方缺少覆盖率。为此,我们将创建一个新的运行配置。点击工具栏上的运行配置下拉菜单,然后点击unittest
模板。请确保您使用的是我的设置,如图 6**.22所示:
图 6.22:创建一个一次性运行所有测试的运行配置
对于脚本路径,请输入存放测试的文件夹。设置模式为*_test.py
。这将使测试运行器找到所有以_test.py
结尾的文件,这与默认设置不同。默认设置将查找以“test.”开头的文件。我不太喜欢这种做法,因为它会将所有测试都堆放在项目文件窗口中,而不是将测试放在被测试文件旁边。
通过设置模式和将测试运行器设置为文件夹而不是单个文件,运行器将找到所有符合模式的文件并将它们作为测试运行。说到运行,您可以通过点击运行和调试按钮旁边的省略号来完成。参见图 6**.23以定位运行‘所有测试’****带有覆盖率菜单项:
图 6.23:使用“运行所有测试”并启用覆盖率可以让你运行测试并找出你的应用程序中有多少部分没有被单元测试覆盖
第一次这样做时,你可能会看到一个错误消息——不是来自你的代码,而是来自 PyCharm。见图 6.24 以了解我的意思:
图 6.24:第一次运行测试覆盖率时,如果你还没有安装覆盖率软件或启用捆绑版本,你会收到警告
使用覆盖率运行需要一些软件,即coverage.py
,我们还没有安装。这里你有两个选择:你可以将coverage.py
添加到你的项目中,或者你可以使用与 PyCharm 捆绑的版本。我更喜欢使用捆绑版本。你可以点击错误消息中的单词“启用”,它显示为一个蓝色超链接,PyCharm 会为你打开这个设置。如果你想自己管理这个设置,请参阅图 6.25 以查看设置在哪里:
图 6.25:使用捆绑的 coverage.py 文件的设置允许你使用 coverage.py 而无需将其添加到你的项目中
启用coverage.py
后,重新运行覆盖率测试。让我们看看我们做得怎么样:
图 6.26:我希望我在大学时的成绩能这么好!
天哪!交易测试中我们实现了 100%的覆盖率,但在bank_account_test.py
文件中却惨不忍睹——如果你认为 94%的覆盖率算失败的话。作为一个完美主义者,我想看看我是如何错过这些点的。我可以通过双击显示bank_account_test.py
中 94%的行来查看,我会看到一个彩色编码的侧边栏。在这里,我再次为这本书是黑白印刷表示歉意。图 6.26 中的区域1被涂成了红色。这些是测试未覆盖的行。说实话,我不记得我输入过这些。我不需要它们,因为我的测试运行器会为我执行测试。我可以简单地删除这些行,并用Ctrl + Alt + L/Cmd + Opt + L重新格式化我的文件。重新运行带有覆盖率的测试。图 6.27 中的结果显示我们接近了:
图 6.27:我们已经实现了 100%的覆盖率!继续保持,你可能会得到加薪
bank_account_test.py
文件现在有 100%的覆盖率,但在顶部,我仍然可以看到bank_account
文件夹只有 98%的覆盖率。这绝对不行!目前,我将我的项目资源管理器窗口关闭,以最大化编辑器和覆盖率窗口的空间。如果我重新打开它,在运行了覆盖率之后,我会得到更多信息。图 6**.28显示了我们应该查看的地方。bank_account.py
文件只有 92%的覆盖率。双击打开它,我会看到我遗漏的行被红色标记,如图6**.28所示:
图 6.28:页边空白处的灰色红色区域表示第 17 行和第 18 行没有被任何单元测试覆盖
看起来我们还需要编写另一个单元测试。我忘记编写错误条件的测试了。这是合法的!如你所回忆,我把存款测试留给你作为一个挑战。我在这本书的代码中编写了它,但忘记编写错误测试。覆盖率救了这一天!
打开bank_account_test.py
并添加以下测试,该测试将覆盖尝试存入负数的场景:
def test_deposit_negative_number_fail(self):
test_account = BankAccount("Bruce Van Horn", "123355-23434", 4000)
self.assertRaises(ValueError, test_account.deposit, -2000)
运行测试并验证其通过,然后重新运行带有覆盖率的所有测试配置。我的结果如图6**.29所示:
图 6.29:我们已经对所有文件实现了 100%的覆盖率
这次,我们应该得到满分!现在我们感觉很好,我将指出屏幕右侧的覆盖率窗口。它显示了一个结果列表,我们之前已经看到了。注意右侧工具栏中的盾牌图标。你可以通过点击这个盾牌来显示或隐藏覆盖率窗口。
测试覆盖率输出
除了图形显示之外,PyCharm 还会为覆盖率运行输出一个报告。你将在输出窗口中看到提到的输出,与通常的测试输出并列。我的输出如下:
Wrote XML report to /home/brucevanhorn/.cache/JetBrains/PyCharm2023.1/coverage/bank_account&All_Test.xml
XML 文件是由我们之前启用的coverage.py
生成的。正如你可能猜到的,coverage.py
是一个流行的 Python 工具,用于在测试运行期间测量代码覆盖率。它是一个开源工具,可以帮助你识别你的 Python 代码哪些部分被测试所执行,哪些部分没有被执行。该工具通过收集测试运行期间代码执行的信息,然后生成一个报告,显示代码覆盖率百分比。XML 输出被 PyCharm 用于渲染我们一直在使用的彩色 UI 显示。XML 输出也可以由你的coverage.py
使用,如果测试覆盖率低于设定的阈值,则会导致构建失败。
性能分析
创建一个优秀程序的第一步是确保程序完全运行正常。第二步是执行自动化测试以证明程序按预期工作。最后一步应该是调整代码,使程序尽可能快和高效地运行。性能不佳的程序最坏的情况可能是无法使用,最好的情况也可能因为低采用率而无法推广。在英国,NHS 有一个算法,旨在将器官移植接受者与最近采集的器官相匹配。该算法复杂但时间敏感。采集的器官必须迅速移植;否则,其组织将死亡并变得无用。简而言之,该算法必须非常准确;否则,移植的器官可能会被拒绝,导致患者死亡。它还必须快速,因为器官将失去活力,这也可能导致患者死亡。突然间,我很高兴我的工作是处理硬件系统容量规划和预测。没有人因为我数据库查询太慢而死亡。至少,据我所知。
除了能够使用覆盖率运行测试外,您还可以使用性能分析运行测试。虽然覆盖率报告以图形方式告诉您哪些代码区域尚未经过测试,但 PyCharm 的性能分析器会向您报告哪些代码部分消耗了整体运行时间的很大一部分。这使您能够发现瓶颈,从而可以将重构努力集中在使代码及其执行更高效上。
与存在多个广泛使用的测试库一样,Python 开发者也有各种各样的性能分析工具,包括 Yappi、cProfile 和 VMProf。PyCharm 支持它们所有,但它们的工作方式并不相同。cProfile 是 Python 内置的,也是默认的性能分析器。Yappi 是 cProfile 的改进,因为它允许您对多线程应用程序进行性能分析,并支持 CPU 时间分析。VMProf 支持统计抽样。当您使用此工具进行性能分析时,它不会简单地计时您的程序的单次运行;相反,它会运行并抽样多次运行,为您提供更真实的性能分析。如果可用,PyCharm 将使用 VMProf。如果不可用,它将寻找 Yappi。如果找不到 Yappi,它将使用 Python 内置的 cProfile 解决方案。对于本书,我将坚持使用默认的 cProfile 工具。
在 PyCharm 中进行性能分析
我们将要进行性能分析的代码可以在本书的仓库中找到,位于chapter-06
文件夹中。profiling.py
文件包含以下代码:
def custom_sum(n=1000000):
result = 0
for i in range(n):
result += i
return result
def built_in_sum(n=1000000):
result = sum(range(n))
return result
if __name__ == '__main__':
print(custom_sum())
# print(built_in_sum())
这段代码将比较两种计算整数和的方法,范围从 1 到以n表示的上限,其默认值为 1,000,000。custom_sum
函数遍历所有元素,将每个元素加到累计和中。built_in_sum
函数利用 Python 的内置sum()
方法。
在主作用域中,我们将使用注释在两个函数调用之间切换,以测试两种方法。我们将首先查看我们的自定义求和函数,因此built_in_sum
的调用目前已被注释掉。
通常的说法是内置函数通常比任何你可能会编写的代码都要快。在这个例子中,我们将能够通过我们的分析过程来核实这个说法,并通过运行时统计进一步对其进行限定。让我们开始吧。
就像测试和覆盖率一样,我们可以通过使用编辑器内的绿色箭头或屏幕顶部的运行按钮省略号来启动配置文件运行。图 6.30 显示了这两种选项:
图 6.30:你可以使用右上角的省略号菜单或通过点击第 14 行 dunder-main 入口点旁边的绿色箭头来运行配置文件
当配置文件运行完成后,我们将获得一个性能报告,如图 6.31 所示。31:
图 6.31:自定义 sum 函数的性能配置文件
在我的电脑上,这是一个 VMWare 虚拟机,配置非常简单(2 个核心,4GB 的 RAM,7200RPM 的旋转硬盘),custom_sum
函数完成了 41 毫秒。时间和百分比在我的显示上有点拥挤,但我们可以看到 100%的时间都花在了custom_sum
函数上。如果这是一个更复杂的程序,在运行期间调用了许多函数,我们会看到每个函数的完整列表以及每个函数花费的时间。注意自有时长列与时间列之间的区别。
在 PyCharm 的性能分析器中,时间列显示了执行特定函数或方法所花费的总时间,包括执行其中任何子函数或方法所花费的时间。
另一方面,自有时长列显示了仅执行函数或方法本身代码所花费的时间,不包括执行子函数或方法所花费的时间。这意味着自有时长列可以让你更好地理解特定函数或方法内代码的性能,而不受任何外部因素(如其他函数或方法性能)的影响。
为了说明差异,考虑一个函数A(),它调用两个其他函数B()和C()。如果你查看A()的时间列,它将包括执行B()和C()所花费的时间,以及执行A()本身代码所花费的时间。然而,如果你查看A()的自有时长列,它将只显示执行A()内代码所花费的时间,而不显示执行B()和C()所花费的时间。
通常,时间列可以让你对特定函数或方法的整体性能影响有一个大致的了解,而自身时间列可以帮助你专注于该函数或方法内部的代码性能。
与内置的 sum() 函数的性能比较
让我们看看我的 72 毫秒运行时间与内置的 Python sum()
函数相比如何。通过在 main.py
文件的底部注释掉 custom_sum
函数并注释掉 built_in_sum
函数来进行修改,如下所示:
if __name__ == '__main__':
# print(custom_sum())
print(built_in_sum())
使用此配置运行配置文件。你可以在 图 6.32 中看到我的结果:
图 6.32:内置的 sum 函数似乎在 11 毫秒内运行得更快
哇,这根本不是比赛!在我的电脑上,利用内置的 sum()
函数快了七倍!在现实生活中,我建议运行每个配置文件几次并取平均值,因为运行时间可能会有所不同。在我的情况下,built_in_sum
函数的后续运行时间从 11 毫秒到 26 毫秒不等,这是一个相当大的差异。
查看调用图
除了统计表之外,你还可以将配置文件以调用图的形式查看。此图表示了程序运行的树状视图,如图 图 6.33 所示:
图 6.33:调用图显示了程序运行的树状视图
调用图中的节点被涂成绿色和红色。红色越深,表示该节点所指示的函数花费的时间越多。在 图 6.33 中,几乎所有的时间都花在了 custom_sum
函数上,它的红色最深(相信我)。当在主函数中打印求和结果时,内置的 print
方法占用了极小但非零的时间。
使用性能配置文件进行导航
你可以使用统计表或调用图中的相应节点来导航到函数。只需右键单击,如图 图 6.34 所示:
图 6.34:你可以通过右键单击函数并选择“导航到源”来导航到你的代码
你也可以在调用图中做同样的事情。在调用图上右键单击一个节点时,你会得到相同的导航选项,带你到源代码。这可以帮助你直接导航到任何你可能想要检查的代码。
cProfile 性能快照
当你使用 cProfile 进行配置文件运行时,PyCharm 将在您的家目录中保存 .pstat
文件:
Snapshot saved to /home/brucevanhorn/.cache/JetBrains/PyCharm2023.1/snapshots/profiling4.pstat
当我进行严肃的性能分析工作时,我通常会把这些文件复制到一个更方便的文件夹中,并更改它们的名称以表明它们是在什么条件下运行的。例如,在我们的例子中,我可能会将第一个 .pstat
文件命名为 custom_sum_performance_1.pstat
;第二个可能被命名为 built_in_sum_performance_1.pstat
。
我这样做是为了为每个文件都有一个基准性能配置文件。在现实生活中,我怀疑您很少会有我们在这里展示的这样简单的替代方案。您更有可能有几个使用不同算法设计方法的函数版本。在这些情况下,保留您的 .pstat
文件以便与未来的运行进行比较可能非常有用,即使只是为了在下次员工评审时炫耀。
您可以使用工具菜单打开您较旧的 .pstat
文件,如图 图 6 所示。35*:
图 6.35:您可以通过工具菜单打开您的旧快照
打开此 .pstat
文件将显示统计表和调用图。如果您已经重构了函数的名称,那么您不应该期望导航仍然有效;然而,您可以看到旧的结果,并将它们与较新的一次运行进行比较。
总体而言,PyCharm 打开和比较旧 .pstat
文件的能力可以是一个有用的工具,用于跟踪代码随时间的变化性能,并识别可以改进性能的领域。
摘要
测试、调试和性能分析是我们可以用来分析应用程序以寻找改进正确性和性能的高级任务,但对于初学者开发者来说可能会相当复杂。PyCharm 为这些过程提供了直观和直观的界面,使它们更容易访问和简化。
单元测试是确保大型系统各个组件按预期工作的过程。PyCharm 提供了方便的命令来生成测试骨架/样板代码,这通常需要开发者手动编写,会花费很多时间。在测试程序时,考虑预期的错误以及针对预期功能的明显测试是很重要的。
在调试会话中,开发者试图缩小并确定在测试期间检测到的错误和错误的起因。结合图形界面和跟踪程序中变量值的各种选项,PyCharm 允许我们以相当大的自由度动态地调试我们的程序。各种单步执行函数也为我们提供了灵活地遍历我们试图调试的程序的方式。
最后,性能分析的目标是分析程序的性能并找到改进的方法。这可能包括寻找计算值的更快方式或识别程序中的瓶颈。PyCharm 能够生成关于每个执行函数的运行时间的综合统计数据,以及调用图,这使得开发者能够轻松地导航已分析程序的不同组件。
本章也标志着本书第二部分的结束,我们在此部分专注于提高我们的开发效率。从现在起,我们将考虑 PyCharm 在更多专业领域的使用,特别是 Web 开发和数据科学项目。
在下一章中,我们将介绍在 PyCharm 环境中三种通用网络开发语言的基础——JavaScript、HTML 和 CSS。
问题
回答以下问题以测试你对本章知识的掌握:
-
在软件开发背景下,测试是什么?有哪些不同的测试方法?
-
PyCharm 如何支持测试过程?
-
在软件开发背景下,调试是什么?
-
PyCharm 如何支持调试过程?
-
在软件开发背景下,性能分析是什么?
-
PyCharm 如何支持性能分析过程?
-
PyCharm 编辑器中的运行箭头有什么意义?
进一步阅读
要了解更多关于本章所涉及主题的信息,请查看以下资源:
-
《敏捷软件开发:原则、模式和实战》,马丁,R. C. (2003)。普伦蒂斯·霍尔出版社。
-
《整洁架构:软件结构和设计的工匠指南》,马丁,R. C. (2017)。普伦蒂斯·霍尔出版社。
-
《C#设计模式的实际应用》,范·霍恩,B 和 西蒙斯,V. (2022)。Packt 出版社。
-
请务必查看本书的配套网站
www.pycharm-book.com
.
第三部分:在 PyCharm 中进行 Web 开发
本部分书籍专注于 Python 编程中的 Web 开发过程以及 PyCharm 为 Web 项目提供的支持。读者将能够使用 PyCharm 及其功能来高效地开发他们的 Web 应用程序。
本部分包含以下章节:
-
第七章, 使用 JavaScript、HTML 和 CSS 进行 Web 开发
-
第八章, 使用 Flask 构建动态 Web 应用程序
-
第九章, 使用 FastAPI 创建 RESTful API
-
第十章, 更多全栈框架:Django 和 Pyramid
-
第十一章,在 PyCharm 中理解数据库管理
第七章:使用 JavaScript、HTML 和 CSS 进行网络开发
本章标志着关于使用 PyCharm 进行网络编程的五个章节系列的开端,所有这些章节都将涵盖通用网络应用程序的开发。我一直期待着撰写这一部分,因为这正是我的专长。自从有网络以来,我就一直在开发网络应用程序。PyCharm 的专业版为网络开发者提供了一份特别的礼物:JetBrains 的 WebStorm 的完整副本,这是通过预安装的插件提供的。这意味着我们获得的不仅仅是 HTML、CSS 和 JavaScript 的语法高亮!我们还可以完全访问 Node.js 工具和现代网络 UI 框架,如 React。我可能会觉得这有点过度,但事实并非如此。我每天工作的产品是由 Python 3、NodeJS 和 React 编写的 微服务 的混合体。我利用了三种不同的数据库:Microsoft SQL Server、MongoDB 和 Redis。我永远不需要离开 PyCharm!正如我刚才提到的,我内置了强大的 JavaScript、NodeJS 和 React 工具。所有数据库都在 PyCharm 中得到支持,我们将在 第十一章 中介绍,使用 PyCharm 理解数据库管理。我可以在 PyCharm 中创建调试运行配置,运行多个通过 REST 调用相互通信的服务,并执行跨服务调试。我还可以在 React 前端设置一个断点,在另一个使用 Flask 或 FastAPI 的独立项目中设置另一个断点;当我处理应用程序的工作流程时,无论我在哪个项目中,或者项目使用的是哪种语言,我的断点都会停止。我不会在其他任何 IDE 中尝试这样做。我提到过我对开始撰写这一系列章节有多兴奋吗?我有点过于激动了。
本章将讨论的主题包括在 PyCharm 中集成常见的网络编程语言(JavaScript、HTML 和 CSS),以及如何以简单直观的方式使用它们进行开发。到本章结束时,你将获得如何使用这三种语言的全面知识,以便你可以开始使用 PyCharm 进行网络开发项目。
本章将涵盖以下主题:
-
在网络开发过程中介绍 JavaScript、HTML 和 CSS
-
在 PyCharm 中处理 JavaScript、HTML 和 CSS 代码的选项
-
如何实现网络项目的实时编辑和调试
-
如何在 PyCharm 中使用 HTML 模板选项
技术要求
本章的先决条件如下:
-
安装 Python 3.10 或更高版本的工作安装。
-
一个可工作的 PyCharm Professional 安装。如果您使用的是社区版,本章中我们将讨论的大部分内容将无法使用,因为您只能获得有限的 HTML 支持。您仍然可以处理 CSS 和 JavaScript 文件,但与专业版相比,体验将非常有限。
-
Chrome 网络浏览器。如果您想调试浏览器中运行的 JavaScript 代码,您将需要它。
-
一个可工作的 NodeJS 和node 包管理器(npm)安装。这是可选的。如果您想使用 React 或 Angular、Vue、Express 等现代 JavaScript 框架,则需要这些。React 是一个高级主题,本书将不会对其进行深入探讨,而仅限于设置和操作 React 项目。
-
我将向您展示如何使用 PyCharm 的部署功能。为此,我假设您了解如何使用 WinSCP 或 FileZilla 等工具将 Web 项目部署到远程主机,或者如何使用安全复制(SCP)或文件传输协议(FTP)等命令行工具传输文件。
-
您可以在
github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-07
找到本章的示例代码。我们在第二章中介绍了如何克隆仓库,安装和配置。
HTML、JavaScript 和 CSS 简介
我将这些称为网络开发的三大支柱。它们构成了你在成为全栈网络开发者过程中可以学习到的最基本技能。当提到全栈时,指的是你擅长开发应用程序的前端部分,包括后端和数据库。接下来的六章将致力于使用 PyCharm 进行全栈网络开发。
严格来说,我们在这里将要讨论的三个语言中只有一个是编程语言。超文本标记语言(HTML)用于创建网页或应用程序 UI 的结构和布局。层叠样式表(CSS)用于控制 UI 的外观,同时遵守关注点分离原则:我们将按钮、文本和交互元素的布局与视觉外观的定义分开。HTML 和 CSS 本身没有能力创建任何级别的交互性,除了几个 CSS 技巧,例如当用户将鼠标悬停在按钮上时改变按钮的颜色。
基于网络的浏览器前端的真实交互性来自于 JavaScript,或者如它应被正确称呼的那样,ECMAScript。大约一百万个互联网年前,一家名为网景的公司正在为新兴的浏览器用户社区争夺人心。这是一场大卫与歌利亚的较量,歌利亚是微软。网景的浏览器需要支付一小笔许可费,而微软的 Internet Explorer 是免费的,内置在 Windows 操作系统中,并配备了一套工具,允许 IT 经理集中定制浏览器行为,同时向大型企业用户群体推广。网景的价值主张根本不存在。他们需要一个颠覆性的变革。
大约在这个时候,太阳微系统公司正在大力推广其新的旗舰编程语言,名为Java。和网景公司一样,太阳公司也受到微软的竞争困扰,因此两家公司联手。网景开始创建后来成为浏览器内编程语言的 ECMAScript,而太阳公司则获得了 Java 名称的许可,前提是重新命名的 JavaScript 将永远不会在任何地方运行,除了浏览器内部。太阳公司的最终目标是确保每个人都在使用 Java 来创建他们的网络应用程序后端,而网景公司通过提供互动体验赢得了前端渲染的市场份额。JavaScript 与 Java 在营销之外毫无关系。它们是两种非常不同的语言,绝不应该混淆。
我假设你对浩瀚的 HTML 开发世界有所了解。我还想提醒你,这是一本关于 PyCharm 的书,而不是关于 Web 开发的书。鉴于这是我谋生的手段,我的经验很丰富,我会很快从“这是一个 HTML 标签”(这是绝对的基础)过渡到“这里是 React”(也就是说,如果你一直只做 Python 工作,那么这里会有龙)。我唯一的遗憾是 Packt 不允许我用一种恐怖电影风格的字体写“这里会有龙”。如果你想了解更多关于前端或全栈开发的一般知识,我会在本章的“进一步阅读”部分留下书籍和其他资源的参考。
使用 HTML 编写代码
HTML,就像任何其他代码一样,是在一个文本文件中创建的。在 HTML 的情况下,文本是通过一组 HTML 标签高度结构化的。你可以把标签看作是编程语言中的关键字,只不过这些标签是用尖括号区分的。记住,HTML 用于内容结构和布局。它是蒂姆·伯纳斯-李(不是阿尔·戈尔)创建的,作为一种在互联网的前身国防高级研究计划局网络(DARPANET)上展示科学期刊文章的方式。这是美国国防部的一个附属机构。在科学界,这个问题在于发表同行评审期刊文章所需的时间。在科学需要快速发展的案例中,我们需要一种格式,可以在不经过同行评审和印刷出版流程的情况下,电子发布研究,这个过程可能需要数月。HTML 被设计成电子模拟纸质出版物。由于这种情况,HTML 元素的结构参照文档对象模型(DOM)也就不足为奇了。文档被组织成段落、标题、副标题、部分、图表、图像等。考虑以下示例:
<html>
<head>
<title>Advances in the Application of Time Travel</title>
<head>
<body>
<h1>Introduction</h1>
<p> Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Vestibulum tincidunt tempus lectus vitae
euismod.</p>
</body>
</html>
这是一个结构类似于典型研究论文的 HTML 文档。它有一个标题,在浏览器的标签页中显示。<title>
位于文档的<head>
标签中,该标签可能还包含元数据和页面使用的 CSS 文件和 JavaScript 的引用。
<body>
标签包含文档的内容。头部和主体被包含在一个 HTML 标签内。记住,标签是用尖括号括起来的;例如,<body>
。每个标签都有一个开标签和一个闭标签,内容位于两者之间。例如,标题以<h1>
开始,插入内容引言,然后以</h1>
结束。闭标签会在匹配元素前添加一个正斜杠。
这种结构使得网页浏览器能够轻松解析文档,并将内容以电子页面的形式显示出来。然而,在现代 HTML 中,只有结构被定义。页面上的元素布局,以及字体、颜色、大小等视觉定义,都是由外部链接的 CSS 文档控制的。
如果我们将此代码添加到我们之前的文档中,在<head>
标签内,我们将在浏览器中得到一个看起来非常不同的页面:
<head>
<title>Advances in the Application of Time Travel</title>
<link rel="stylesheet" href="mystyle.css">
<head>
注意,添加的链接标签略有不同:它没有闭标签。HTML 中存在一些这样的例外。此外,链接标签还有一些属性附加。你可以把属性看作是函数的参数。它们定义了标签使用的额外输入。在这种情况下,HTML 中可能有几种不同类型的链接标签。我们通过rel
属性定义这个标签为样式表。href
属性告诉 HTML 页面在哪里找到 CSS 文件。在这里,页面将在与 HTML 文件相同的文件夹中寻找名为mystyle.css
的文件。
在 PyCharm 中创建 HTML
HTML 前端开发体验仅在 PyCharm 专业版中可用。社区版允许你在 Python 项目中创建 HTML 文件,但这仅此而已。在本章中,我将严格考虑专业版。
我们将创建一个新的项目,但这次,我们不会创建一个 Python 项目。实际上,我们根本不会在 PyCharm 中创建新项目。PyCharm 以前有创建空项目的选项。在某个时候,它被移除了,但这没关系。PyCharm 项目只是一个包含 .idea
文件夹的文件夹。
创建一个空项目
创建一个空项目,这将绕过设置虚拟环境的流程,我们不需要这个流程。只需在电脑上的某个位置创建一个文件夹。我在桌面上创建了一个名为 html-project
的文件夹。我喜欢用中划线命名不使用 Python 的项目,而不是蛇形命名,这样我可以很容易地发现它们之间的区别。当然,你可以随意命名。
确保 PyCharm 没有打开的项目。如果有,请使用文件 | 关闭来关闭当前项目。
接下来,只需将你的空文件夹拖到 PyCharm 中。你会得到一个通常的信任和打开项目对话框,如图 图 7**.1 所示:
图 7.1:将空文件夹拖到 PyCharm 项目窗口以创建一个没有 Python 环境的空项目
你可能已经注意到 PyCharm 专业版的新项目窗口中有 HTML 项目类型,并想知道我为什么忽略它们。在我向您展示基于谷歌确立的理想创建整个站点的 HTML 模板项目之前,我更愿意先从基本示例开始。让我们先步行,再飞行。如果你已经是全栈老兵,请不要跳过这一节。鉴于这不是一本关于 HTML 的书,我不打算在基础知识上停留太久。我将在这个简单示例中介绍一些功能。
以这种方式创建项目时,它实际上是一个没有解释器的 Python 项目。你会收到一条消息,说明默认的 Python 安装正在用作环境,这是可以的。我们不需要它:
图 7.2:右键单击项目文件夹以创建新的 HTML 文件
你将被提示给文件命名。命名为 index.html
。这是在 Web 服务器上显示的默认文件,因此是你最想首先创建的文件。PyCharm 将根据模板生成基本的 HTML 页面结构。更重要的是,PyCharm 将在创建过程中提示你填写模板的基本元素。看看 图 7**.3:
图 7.3:PyCharm 为您提供了一个模板供您填写
此模板中只有一个模板变量:Title
。正如您所看到的,第 5 行被突出显示,而单词Title
的突出显示更加明显。PyCharm 期望您输入一个标题,然后按Tab键。这样做会设置标题,然后直接带您到 body 标签的内部。
在添加此代码后,一些事情变得明显:
-
所有您期望在 Python 中使用的自动完成功能也应用于 HTML 标签。有效的属性也在代码补全中提供。当您输入样式表的链接标签时,您会注意到这一点。
-
PyCharm 会自动为您创建闭合标签。当您输入
<h1>Introduction
时,一旦完成<h1>
标签,PyCharm 会为您插入</h1>
闭合标签。未能关闭您的标签可能导致布局问题或内容缺失。 -
Python 关键字和库的相同文档功能也适用于 HTML。将鼠标悬停在任何元素上,您将看到该元素的文档。
-
我们在 Python 代码中看到的相同的代码检查功能也应用于 HTML 代码。图 7.4显示了在编辑器中讨论这些功能时我们在相同区域看到的相同类型的警告:
图 7.4:我们可以看到编辑器对 Python 和 HTML 的处理有很多相似之处
屏幕顶部的错误摘要(1)表明存在一些排版错误。实际上并没有,只是字典假设是英语,我的占位符文本是拉丁语。不过,有一个警告。如果您点击1旁边的黄色三角形,问题窗口将打开(3)。我还可以看到2处的黄色警告标记。所有这些都指向我们尚未创建第 6 行引用的 CSS 文件。
除了我们在编辑器中通常看到的东西外,我们还可以在4处看到一些新内容:有一些图标对应于 PyCharm 在您的计算机上知道的浏览器。
预览网页
点击任何浏览器图标都可以让您在该浏览器中预览您的代码。在我的情况下,我有三个浏览器:Chrome、Firefox 和 PyCharm 的内置预览窗口。当您的鼠标光标接近编辑器窗口的右上角时,这些图标会淡入淡出。
为了纪念本章开头 Netscape 的提及,让我们看看图 7.5中的 Firefox,因为它是 Netscape 的后继者:
图 7.5:浏览器启动并显示我们的页面
在图 7.5中有三件事值得关注:
-
PyCharm 并没有只是用文件:/// URL 在浏览器中打开页面。如果你想那样做,你可以点击浏览器图标时按住Alt或Option键。在我们的情况下,PyCharm 启动了其内部网络服务器。这很方便,因为使用文件:/// URL 在浏览器中预览你的工作非常有限制。许多功能根本无法工作。
-
<title>
标签中的内容在浏览器标签页上显示为标题。 -
1991 年有人打电话来,他们想要要回他们的网站。HTML 只是创建结构。没有 CSS,这个页面看起来相当丑陋。我们应该修复这个问题。
在我们这样做之前,我想指出 PyCharm 给我们提供的另一个巧妙的功能:自动重新加载。
保存时重新加载浏览器视图
如果你研究图图 7.5中的浏览器中的 URL,你会注意到RELOAD_ON_SAVE
属性。你可能能猜到这会做什么,但无论如何,我们还是来了解一下。在图图 7.6中,我已经将窗口平铺,使得 PyCharm 位于左侧,我的浏览器位于右侧。这是网络开发者常用的配置,尽管它通常在多个显示器上运行:
图 7.6:每次你保存对文件的更改时,PyCharm 都会自动在浏览器中重新加载页面
我添加了一个新的<p>
标签,内容如下:
<p>This is a paragraph I added later</p>
当我按下Ctrl + S(在 Mac 上为Cmd + S)时,浏览器会更新为新内容。这极大地加快了开发速度!这几乎算不上革命性的;大多数编辑器以某种方式支持这一点,但 PyCharm 也支持。
Live Edit 插件
在支持 HTML 开发的旧版 JetBrains IDE 中,你过去需要使用一个名为 Live Edit 的插件来获取这个自动重新加载功能。现在你不再需要它了,因为这个功能已经集成到 IDE 中。
使用 PyCharm HTML 预览
如果你更愿意将 100%的工作保留在 PyCharm 中,而不是使用外部浏览器,那么有一个新的 HTML 预览功能,如图图 7.7所示,它会启动 Chromium 浏览器的内部版本:
图 7.7:PyCharm 预览窗口允许你在 Chromium 浏览器窗口中查看你的工作,该窗口作为 PyCharm UI 标签实现
如果你不太熟悉,Chromium 是 Chrome 的开源版本。这一点很重要,因为多年来,我看到了很多包含预览产品捆绑的 Web 开发产品,这些产品与你的工作在真实浏览器中的显示几乎没有关系。
配置可用的浏览器
出现在预览浏览器中的浏览器图标是可以配置的。你可以将你的计算机上的任何浏览器添加或删除到配置中。你可以通过转到工具|Web 浏览器下的设置,然后是预览来找到它,如图图 7.8所示:
图 7.8:你可以添加和删除浏览器以进行预览,并配置重新加载行为
我认为你会发现添加新浏览器就像点击+图标并定位浏览器的可执行文件一样简单。如果你想移除令人不快的 Internet Explorer 的存在,只需点击它,然后点击–图标。
注意下拉菜单,允许你设置默认启动浏览器为系统默认,即列表中的第一个,或者一个自定义路径。你还可以配置是否显示 HTML 文件和/或 XML 文件的启动图标。
可以在屏幕底部配置重新加载行为。你可以设置在保存时、更改时重新加载,或者关闭它。在我看来,更改时设置触发得太频繁了。我将其设置为保存时。
使用结构窗口导航结构代码
结构窗口允许你查看代码的结构。大多数时候,结构窗口只显示函数和全局变量的列表。如果你打开一个类文件,你会看到类的属性和方法。图 7**.09展示了当你使用结构窗口与 HTML 文档一起使用时会发生什么:
图 7.9:结构窗口允许你在代码窗口中查看和导航 DOM
当你在 HTML 页面上打开结构视图时,你可以查看并轻松导航整个 HTML 文档的 DOM 结构。
添加 CSS
右键点击项目文件夹,添加一个名为 mystyle.css
的样式表文件,如图 图 7**.10 所示。系统会提示你选择样式表类型。我们坚持使用 CSS 文件。其余的选项都很花哨,但最终它们都会通过Babel库和WebPack构建脚本转换成常规 CSS。如果你是前端工作的初学者,我建议在学习Less或Sass等特殊主题之前,先学习纯 CSS:
图 7.10:使用文件 | 新建 | 样式表创建一个新的 CSS 文件
确保文件名为mystyle.css
,以便与我们在 HTML 文件中的<link>
标签中的内容匹配。它还需要与index.html
文件在同一个文件夹中。添加以下代码:
body {
background-color: lightblue;
margin: 20px;
font-family: Arial, Helvetica, sans-serif;
font-size: 18px;
}
h1 {
font-size: 32px;
color: navy;
text-decoration: underline;
}
保存文件并返回浏览器。不要期望它已经更新;PyCharm 的重新加载功能正在监视index.html
文件,而不是新创建的mystyle.css
文件。在浏览器中点击重新加载按钮。我不会提供彩色 HTML 页面的黑白截图。变化应该立即显而易见。现在浏览器已经加载了 CSS,你可以在 PyCharm 中编辑 CSS。每次保存,页面都会更新,就像你在index.html
页面中编辑 HTML 时一样。
当你输入 CSS 代码时,无疑注意到了出色的代码补全。颜色代码都按名称以及十六进制值显示,如图图 7.11所示:
图 7.11:当你输入 CSS 颜色名称时,你会看到名称、十六进制值以及颜色的预览
在 CSS 颜色名称旁边也有颜色的预览,这样你可以看到你正在设置的颜色。
使用颜色选择器
你可以使用名称或十六进制值来设置颜色,或者你可以使用色块编辑现有的颜色,如图图 7.12所示。在这里,我点击了mystyle.css
文件第 10 行的侧边栏中的颜色色块:
图 7.12:你可以通过点击侧边栏中的色块并选择新的颜色来编辑颜色
从这里,我可以使用 RGBA 或十六进制值选择颜色,或者只需将光标在颜色窗口周围拖动。这里还有一个色调滑块以及 alpha 通道的滑块。这控制了不透明度,UI 将其显示为百分比而不是传统的 0-255 整数值。
你还会注意到有一个吸管工具。你可以使用吸管选择屏幕上可见的任何颜色。这允许你,例如,将用于你的文本的颜色与屏幕上可见的图像中的颜色相匹配。
添加 JavaScript
是时候通过在我们的页面上添加一些交互性来完善我们的 HTML、CSS 和 JavaScript 体验了。右键单击项目文件夹,并添加一个名为index.js
的 JavaScript 文件。
我们将向我们的 HTML 文件添加一个按钮,然后使用 JavaScript 使其在点击时做出反应。我们首先编写 JavaScript 代码;然后,我们将返回并添加按钮。
添加一些 JavaScript 代码
将此代码输入到你的index.js
文件中:
const btn = document.getElementById("btnClickMe");
const textDisplay = document.getElementById("textDisplay");
let clickCount = 0;
我们将在稍后添加一些在 JavaScript 中引用的 HTML 文件中的元素。首先,我们将添加一个 ID 为btnClickMe
的按钮。然后,我们将在 ID 为textDisplay
的段落内添加一个span
标签。最后,我们将创建一个名为clickCount
的变量。你可能已经猜到了。当你加载页面时,clickCount
的值将是0
。每次你点击按钮时,我们都会增加clickCount
变量,然后更新span
标签内的 HTML 以反映新的值。
要使这生效,我们需要为按钮添加一个onclick
处理程序。添加以下代码:
btn.onclick = function() {
clickCount++;
textDisplay.textContent = String(clickCount);
}
按钮的onclick
处理程序被分配给匿名函数,该函数增加clickCount
并更新显示。我甚至更进一步,将clickCount
转换为字符串。
在我们的 JavaScript 就绪后,让我们更新我们的 HTML。
将元素添加到 HTML 文件中
现在,让我们回到index.html
文件并添加两个新元素。首先,我们将添加一个文本区域:
<p>You have clicked the button
<span id="textDisplay">0</span>
times. </p>
接下来,让我们添加一个按钮:
<button id="btnClickMe">Click me</button>
按钮有一个 ID 属性btnClickMe
,我在 JavaScript 中将其用于浏览器的onclick
事件。每次点击clickCount
。然后,该函数会更改出现在具有id
为textArea
的 span 标签中的文本。
我们几乎完成了!我们只需要在 HTML 文件的底部添加一个脚本标签。将此行添加到关闭 body 标签之上:
<script type="text/javascript" src="img/index.js"></script>
这行代码将在所有 HTML 都加载到浏览器窗口后加载你的 JavaScript 文件。手动刷新你的浏览器。你可以点击点击我按钮,看到计数器增加。你可以在图 7.13中看到我的:
图 7.13:我们的网页现在可以交互了!
在你工作的过程中,你无疑已经利用了我们一直在讨论的常规代码补全功能。这真是太好了,对吧?但是还有更好的!我将向你展示如何在 IDE 中进行调试。通常,你只能依赖浏览器中的调试工具,这意味着需要在浏览器中的代码显示和你的 IDE 之间来回切换。至少有一次或两次,我忘记了我在哪个工具中,发现自己试图直接在浏览器的调试工具中编辑。这可以工作,但是它不会将文件保存回实际的代码文件。诚然,这只有在我真的累的时候才会发生,但是有两个上中学的女儿,一份全职工作负责管理一个开发团队,还要写书,我几乎总是真的很累。我之前确实提到过调试器是我最喜欢的功能,所以无需多言,我给你带来客户端 JavaScript 调试!
调试客户端 JavaScript
你可以通过设置 Google Chrome 为默认浏览器来调试浏览器中运行的 JavaScript。Chrome 内置了一个非常好的远程调试服务器。PyCharm 可以连接到它,并在 PyCharm UI 中直接显示调试信息,就像我们在第六章,无缝测试、调试、和性能分析中调试 Python 代码时一样。
打开index.js
文件,并在第 7 行设置断点,如图图 7.14所示:
图 7.14:通过在客户端代码中设置断点,你可以像调试 Python 代码一样轻松地调试客户端 JavaScript
要在 PyCharm 中直接使用 JavaScript 调试器,你需要创建一个运行配置。我们在第六章,无缝测试、调试和性能分析中详细介绍了创建运行配置。要创建一个能够与加载到 HTML 中的 JavaScript 文件一起工作的运行配置,请使用 JavaScript 调试模板创建一个运行配置,如图图 7.15所示:
图 7.15:使用 JavaScript 调试模板设置运行配置,允许您在 PyCharm 中直接调试 JavaScript 代码
一旦您选择了模板,您将看到一个设置屏幕,类似于图 7.16*中的:
图 7.16:填写设置以运行您的 HTML 页面,该页面加载 JavaScript 以进行调试
关键设置字段在图 7.16*中编号。
在位置1,您可以填写运行配置的名称。您可以选择任何您喜欢的名称,因为名称不会影响运行配置的工作。对于 URL,您需要点击2处的文件夹图标,然后浏览到您的index.html
文件。当您这样做时,PyCharm 将填写 localhost URL,以及 PyCharm 内置 Web 服务器使用的端口号。3处的选择确定要启动哪个 Web 浏览器。PyCharm 仅支持基于 Chromium 的浏览器进行调试。例如,Chrome、Chromium、Edge 或 Brave 都可以正常工作,但 Safari 或 Firefox 则不行。
在4处的设置很重要,因为它在OnPageLoad
事件触发之前加载您的 JavaScript 断点。如果您正在使用基于$(function()
调用的 JavaScript 进行加载,您应该勾选此框。我们的脚本非常简单,所以对我来说,不勾选这个框也行。请注意,勾选此框将减慢调试器启动的加载时间。
要开始您的调试会话,请点击常规调试按钮。如果您不记得在哪里,请参阅第六章。Chrome 将带您的网页启动。在点击我们之前制作的点击我按钮后,PyCharm 中的调试器应该会拦截该事件。与我们在第六章中的调试器覆盖范围一样,您可以使用所有相同的功能来检查和逐步执行您的 JavaScript 代码。
使用 Emmet 模板化工作
Emmet 是 PyCharm 中一个强大且广泛使用的插件,它提供了一种高效且简化的方式来编写 HTML、JavaScript XML(JSX)和 CSS 代码。它提供了一系列模板功能,这些功能可以显著提高开发者的生产力和加快编码过程。通过利用 Emmet 缩写和代码片段,开发者可以使用简写语法编写代码片段,并通过几个按键将其扩展为完整的 HTML 或 CSS 结构。唯一的缺点是您需要学习和记忆 Emmet 缩写。我没有空间将其变成一个 Emmet 教程。有一个作弊表可以在docs.emmet.io/cheat-sheet/
找到。
要在 PyCharm 中使用 Emmet,您只需在编辑器窗口中输入一些 Emmet 缩写,然后按Tab键。例如,通过输入ul>li.item$*5
,然后按Tab键,Emmet 可以生成一个包含五个列表项的无序列表,其中$
符号会自动为每个项目递增。此功能在处理重复的 HTML 结构时特别有用,并消除了手动输入重复代码块的需要。
在 PyCharm 中,Emmet 的另一个强大功能是您可以高效地导航和编辑 HTML 和 CSS 代码。使用转到编辑点功能,开发者可以在扩展缩写中的预定义编辑点之间导航,从而实现快速修改和调整。此外,Emmet 的自动标签关闭功能确保 HTML 标签自动关闭,减少了语法错误的可能性,并在编码过程中节省了时间。转到编辑点功能实际上允许您在代码模板中放置占位符。如果您尝试了之前的无序列表示例,您可能会注意到代码已生成,但您的光标位于第一个列表项标签内,如图图 7**.17所示:
图 7.17:Emmet 通过模板中定义的编辑点生成了无序列表
Emmet 还提供了智能 CSS 缩写,简化了编写和展开 CSS 属性的过程。通过使用“bg”代表“background”或“p”代表“padding”等快捷方式,开发者可以快速生成 CSS 代码片段,而无需记住完整的属性名称。此功能加快了 CSS 开发过程,并提高了代码的可读性。
PyCharm 专业版中的 HTML 项目类型
之前,我提到我想从一些基本的空项目开始,这些项目包含一些简单的 HTML、CSS 和 JavaScript 代码,这些代码是我们从头开始创建的。PyCharm 专业版提供了新的纯 HTML 项目类型。这里的“纯”意味着它们不使用任何现代 JavaScript 框架,如 React 或 Angular。这些现代框架代表了前端开发的一个范式转变。纯 HTML 项目将继续使用非常传统的 DOM 操作,就像我们在之前的 JavaScript 示例中所做的那样。要更改点击计数器 span 的内容,我们使用了以下代码:
Document.getElementById
由于像这样的客户端 JavaScript 在浏览器中运行,代码可以访问文档对象,这实际上就是浏览器窗口。JavaScript 可以操作当前加载的文档,甚至可以更新浏览器窗口本身的内容。
现代 JavaScript 框架不再操作 DOM。相反,它们依赖于状态变化机制,并与阴影 DOM 的概念相结合。在 React 的情况下,你的程序,主要由 JavaScript、CSS 和 JSX 生成的 HTML 标记组成,维护一个由组件驱动的状态机。状态的变化会在 React 框架内触发事件,进而触发 UI 中受影响区域的重新渲染。
这对于初学者来说是一个相当复杂的范式,而且由于这不是一本关于现代 JavaScript 的书,我将把更深入解释的想法推迟到本章末尾的 进一步阅读 部分的其他书籍中。
我们可以查看两种基于 DOM 的项目类型。让我们来看看它们。
HTML 5 模板
HTML 5 模板(H5BP)项目可以在 html5boilerplate.com/
找到。这个项目自 2011 年以来一直存在,它代表了一个网站生成工具,体现了 HTML 开发的最佳实践。如果你能从一个 100% 符合规范的网站作为起点,然后对其进行修改以实现几乎任何功能,你就有 H5BP。
这里是其几个关键特性:
-
一个经过深思熟虑且结构化的
index.html
文件,包含所有可能的元数据标签和优化。只需添加内容即可。 -
Normalize.css
和Main.css
负责处理 CSS 重置并提供一些基础样式,用于辅助工具、响应式设计的媒体查询,甚至提供打印友好的选项。 -
内置 Google Analytics。
-
Modernizr 库已包含在内,这样你可以检测运行你代码的浏览器,并相应地做出反应。
-
服务器设置文件,以便你可以提供性能和安全。
-
移动设备占位符图标、favicon 和渐进式 Web 应用。
正如我说的,这是任何网站终极的 HTML 5 起始套件,它不会让你陷入很多限制。旧版本使用 jQuery,但像许多其他框架一样,较新的构建在你不希望使用它的情况下会将其作为依赖项移除。
让我们在 PyCharm 中创建一个 H5BP 项目。点击 h5bp
,我正将其放入电脑上默认的 PyCharm Projects
文件夹中。
在幕后,PyCharm 将通过 npm 使用 H5BP 生成一个大型项目。图 7.18 展示了我生成的项目:
图 7.18:我的 HTML 5 模板项目,index.html 已打开
哇!看看所有你不必输入、查找或记住的代码!加载了一个更广泛的项目后,让我们看看一些我尚未指出的有用特性。
使用外部工具预览和编辑图形
你可以通过双击文件在 PyCharm 中预览图形。我将打开 图 7.19 中看到的 tile.png
文件:
图 7.19:只需打开即可在 PyCharm 中查看图形
我可能通过暗示您可以在 PyCharm 中编辑图像而让您有些困惑。您不能。但您可以配置一个外部编辑器,当您打开图形时,它将被启动。
打开设置窗口,找到工具 | 外部工具。点击顶部工具栏中的+图标。在图 7.20中,我正在设置Gnu 图像处理程序(GIMP),这是一个Adobe Photoshop的开源替代品:
图 7.20:您可以将 GIMP 配置为外部编辑器以在项目中打开图像
请不要误解我:我更喜欢 Photoshop。如果您有它,请使用它。它不在 Linux 上运行(对我来说,Wine 不算),所以 GIMP 是下一个最好的选择。PyCharm 无法编辑的任何类型的文件都可以从外部编辑器中受益。我经常添加的另一个好工具是Inkscape或Adobe Illustrator,用于处理可缩放矢量图形(SVG)文件。这些很有用,因为它们是分辨率无关的。同样,如果您提供 PDF 文件,添加Adobe Acrobat或其他用于操作 PDF 的程序作为外部工具也是一个不错的选择。
要在外部编辑器中打开文件,只需右键单击文件,点击外部工具菜单选项,并选择适当的工具。
上传您的网站到服务器
许多大型网站和软件项目使用持续部署系统将代码部署到服务器。如果您的项目还没有达到那个阶段,或者您可能只需要一些简单的东西,PyCharm 有一个发布机制,旨在帮助您轻松发布网站或应用程序。就这个而言,它可以用来发布任何类型的软件,但我最常见的是用它来部署 Web 项目到 Web 服务器。
您可以在工具菜单中找到部署配置和工具,如图 7.21 所示:
图 7.21:通过访问工具 | 部署可以找到部署设置和工具
设置部署的过程包括配置一个或多个远程服务器。让我们看看这是如何操作的。
配置远程服务器
点击图 7.21中显示的配置…菜单选项。您将看到一个显示请添加一个 Web 服务器以进行配置的普通灰色窗口。点击窗口左上角的+图标,并选择您打算使用的连接类型。您可以在图 7.22中看到选择项:
图 7.22:选择您想使用的服务器连接类型
您可以从几个连接协议中进行选择:
-
安全文件传输协议(SFTP)。这应该是您的首选。在列表上的选项中,它是最安全的,因为它依赖于安全壳协议(SSH)。
-
文件传输协议(FTP)。在现实世界中,你永远不应该使用这个选项。FTP 本身会将所有认证数据以未加密的文本流发送出去。只有当你想让你的网站被黑客攻击时才使用它。
-
安全文件传输协议(FTPS)是带有安全套接字层(SSL)加密的 FTP。你可以使用与 HTTPS 一起用于保护网站的加密证书来使用 FTP,使其成为 FTPS。
-
Web 分布式创作和版本控制(WebDAV)是在微软和 Macromedia(后被 Adobe 收购)等公司向网页设计师销售网页创作工具的时代创建的——也就是说,这些人编写 HTML,但并不非常技术化。其中大多数在 1998 年的大开发者叛乱中消失了。WebDAV 允许创作工具无缝地连接到服务器,几乎无需麻烦。然而,像 FTP 一样,它也有其安全问题的份额。我不建议使用它。
-
本地或挂载文件夹用于当你直接访问用于托管你的网站或应用程序的文件夹时。
-
构建
,然后将必要的文件复制到那个文件夹中。我发现本地或挂载文件夹选项更有用。
我将指导你如何使用 SFTP 连接到服务器。当你点击Web 服务器,如图 7.23所示:
图 7.23:给你的服务器起一个描述性的名称
接下来,你需要凭证。你通常可以从你友好的本地系统管理员或托管服务提供商那里获得这些凭证。这些凭证将输入到一个 SSH 配置中,它与你的部署服务器配置是不同的。UI 在这里有点奇怪。要设置 SSH 配置,点击图 7.24中显示的省略号按钮:
图 7.24:创建部署需要你点击此处显示的省略号来创建 SSH 配置
这会弹出一个另一个空白的灰色窗口。点击这个窗口右上角的+号,你将看到一个像我一样的屏幕,如图 7.25所示。
填写你的连接详情——即你的主机、端口、用户名和密码。除了密码外,你还可以使用 SSH 密钥或连接到 OpenSSH 配置和认证代理。我会保持简单,坚持使用密码。点击测试连接按钮会告诉我我的凭证是否有效。
接下来,我们需要一个根路径。这应该是你网站服务器的文档文件夹的根目录,假设你打算部署一个网站。那里有一个自动检测按钮,它会找到 SSH 用户的家目录。如果你从那个位置提供服务,这是可以的。通常,人们会从远程服务器上的/var/www/html
这样的位置提供服务。如果这是你的服务器使用的文件夹,请确保你的系统管理员给你提供访问该位置的凭证。
在下面,你会看到一个关于网站服务器 URL 的条目。服务器上的根路径应该映射到网站服务器 URL。你可以在图 7.25中看到我的设置:
图 7.25:我的部署设置(到目前为止)
你的下一个步骤是映射标签页。图 7.26显示了带有指定映射的映射标签页:
图 7.26:映射标签页指定了计算机上的文件夹与服务器上的文件夹之间的映射
对于一个网站项目,通常只需要在计算机上包含你的代码的文件夹和网站服务器的文档根目录之间建立一个映射。接下来,切换到排除路径标签页,如图图 7.27所示:
图 7.27:排除不需要复制到服务器的路径
排除任何你不想复制的文件。你可以指定不应复制到服务器的本地文件,以及不应复制到你的计算机的远程文件。我添加了doc
文件夹的内容,该文件夹包含开发者的文档,以及.idea
文件夹的内容,该文件夹由 PyCharm 使用。点击确定,你的部署将被配置。
上传到服务器
现在,你已经准备好上传你的项目了。回到工具 | 部署菜单,这次点击上传到网站服务器,如图图 7.28所示:
图 7.28:上传控制位于部署菜单中
如果你给你的远程服务器取的名字不是 Web Server,那么这个名字就会出现在菜单中。你将需要确认上传操作。PyCharm 将显示一条消息,表明上传成功,如图图 7.29所示:
图 7.29:index.html 文件已成功传输
上传操作只复制了打开的index.html
文件。我们可以通过点击工具 | 部署 | 浏览远程主机来检查这一点。这会显示远程主机的视图,允许你查看主机上的文件。你可以在图 7.30中看到我的文件:
图 7.30:您可以在服务器上查看映射的项目根文件夹
您可能希望上传整个网站而不是单个打开的文件。在远程主机窗口内右键单击并点击 上传到此,如图 图 7.31 所示:
图 7.31:右键单击然后选择“上传到此”以上传整个网站
如菜单所示,您可以对服务器上的文件进行完全的图形化控制,包括所有文件操作,如重命名、复制或删除。您可以将本地文件与远程文件进行比较,以查看差异,当然,文件操作是双向的,因此您可以将直接在服务器上完成的工作同步回您的计算机。
总而言之,这个工具比 FileZilla 这样的独立文件传输程序更强大。它直接集成到 IDE 中,甚至还有一个自动同步设置,在您保存文件时自动将本地副本上传到远程。PyCharm 提供了您在网站或应用程序的 HTML 前端工作所需的一切,包括直接从 PyCharm 发布的能力。
创建 Bootstrap 项目
Bootstrap 是另一种在 PyCharm Professional 中可以使用的以 DOM 为导向的 HTML 5 项目类型。如果您正在构建一个应用程序而不是网站,这个类型将更有用。Bootstrap 是由 Twitter 开发的一个样式化 HTML 组件库,它帮助用户快速开发应用程序。本质上,您可以通过简单地生成此项目,然后复制粘贴 Bootstrap 片段(如按钮、卡片布局、滑块、开关和移动优先网格系统)来制作一个看起来很棒的应用程序。
您可以通过点击 文件 | 新建项目,然后在右侧模板中选择 Bootstrap,如图 图 7.32 所示来创建一个 Bootstrap 项目:
图 7.32:创建 Bootstrap 项目
这个模板所做的只是创建一个 js
文件夹和一个 css
文件夹。然后 PyCharm 下载 Bootstrap 并将库文件放置在相应的文件夹中。就是这样。它甚至不会为您生成一个 index.html
文件。这只是快速设置带有 Bootstrap 的项目的一种方法。
使用现代 JavaScript 和 NodeJS
PyCharm 在 Web Storm 产品中提供了所有其功能和模板,Web Storm 是为与现代 JavaScript 项目一起工作而设计的。由于我假设您主要对 Python 项目感兴趣,因此我不会花太多时间深入探讨服务器端 JavaScript 开发的细节,因为我们在接下来的几章中将会介绍 Python 选项,例如 Flask、FastAPI、Pyramid 和 Django。
然而,您应该知道,如果您有 JavaScript 项目和 Python 项目,没有必要购买两个独立的产品。
创建 NodeJS 项目
要创建一个新的 Node JS 项目,只需点击NodeJS
项目模板。这相当于使用npm init -y
命令生成项目。你得到的是一个通用的package.json
文件。它相当基础,但它确实可以节省你启动终端并运行init
命令的麻烦。
除了基本的 Node 项目外,你还可以生成Next.js项目或Express项目。Express 是 JavaScript 对 Flask 的回应,我们将在下一章中介绍。它用于开发项目的后端。另一方面,Next.JS 是前端和后端开发的融合,提供了一个易于使用的托管服务。有关更多信息,请参阅nextjs.org
。
创建 React 项目
React 是目前最受欢迎的前端框架之一。它在前端开发中代表了一种范式转变,因为它不使用 DOM 来操纵 Web UI 的外观。而不是使用 DOM 根据需要显示和隐藏单页应用(SPA)中的组件,你而是操作一个状态对象,这会导致 React 更新页面。
React UI 是以一组组件的形式开发的,这些组件协同工作,并使用生命周期方法或钩子来处理传统或自定义事件。我在这里展示了更多关于 React 的工作,因为它是我日常工作中使用的,而且在下一章中,我将展示如何使用 FastAPI 与 React 前端结合使用。
创建 React 项目就像我们之前看到的那些一样简单。只需点击create-react-app
,这通常是从命令行执行的:
图 7.33:从 PyCharm GUI 运行 Create React App
PyCharm 还提供了一个简单的方法来在默认的 JavaScript 和许多开发者的首选 TypeScript 之间进行选择。TypeScript 是微软在 Douglas Crockford 所著的《JavaScript,好部分》一书之后开创的一种 JavaScript 变体。TypeScript 旨在修复许多不良部分,例如缺乏强大的类型系统。
其他前端框架
PyCharm 支持其他几个现代前端框架。
Angular与 React 类似,因为它使用由状态机驱动的组件。最大的区别,除了因主要版本发布不向后兼容而臭名昭著之外,是组件之间的双向通信。在 React 中,属性从上到下钻取。在 Angular 中,组件之间的通信是双向的。这可能会使你的应用程序更难调试。
React Native是 React 的一个分支,旨在为移动和桌面应用程序创建原生用户体验。微软最新的.NET 框架版本包括一个 React Native 变体,用于创建桌面应用程序的 Windows 用户界面。
Vite是一个旨在解决与冗余相关的性能问题的现代框架。在其他任何框架中,一个项目可能需要数十个甚至数百个 JavaScript 模块导入,这会减慢开发和应用程序本身的性能。Vite 使用高级打包工具来简化前端开发过程。
Vue是另一个提供声明式组件化编程模型的 Frontend 框架。虽然 React 严重依赖于 JSX,但 Vue 使用标准的 HTML、CSS 和 JavaScript,并且可以配置以包含路由、服务器端渲染等功能。
摘要
本章介绍了将 PyCharm 作为网页和应用程序的前端开发工具的使用。我们发现 PyCharm 在这个领域具有非常丰富的功能,因为 JetBrains Web Storm 产品直接集成到 PyCharm Professional 中作为预安装的插件。
这提供了所有功能强大的、专门用于开发 HTML、JavaScript 和 CSS 的工具体验。所有在 Python 中可用的调试能力在 JavaScript 代码中同样适用,无论是客户端还是服务器端。虽然我们没有花时间讨论服务器端 JavaScript,但与 NodeJS 项目一起工作的能力是存在的。
PyCharm Professional 为我们提供了大量的项目模板,包括传统的基于 DOM 的开发策略,如 HTML 5 Boilerplate 和 Bootstrap,以及基于现代状态机系统的 Angular 和 React。
我们学习了如何利用 PyCharm 的部署工具将网站和应用程序上传到远程服务器。我们还学习了如何配置外部工具,如图像编辑器,以便它们可以直接从 PyCharm 启动。
在介绍了前端工具之后,在下一章中,我们将探讨如何使用 Flask 框架开发全栈 Web 应用程序。
请务必查看本书的配套网站www.pycharm-book.com
。
问题
回答以下问题以测试你对本章知识的掌握:
-
HTML 代码的目的是什么?HTML 文件是如何结构的?
-
CSS 代码的目的是什么?CSS 文件是如何结构的?
-
JavaScript 代码的目的是什么?一般来说,是什么让它成为最受欢迎的 Web 编程语言之一?
-
在 PyCharm 中,如何将 CSS 样式表或 JavaScript 脚本包含在 HTML 文件中?
-
Emmet 是什么?PyCharm 是如何支持它的?
-
在 PyCharm 中调试 JavaScript 有哪些可用的选项?
-
从 PyCharm 部署 Web 应用程序最安全的方法是什么?
第八章:使用 Flask 构建动态网络应用
我很幸运,自从网络开发的诞生以来,我就一直在这个领域。在万维网出现之前,至少是公众使用的万维网,我就是一名软件工程师。我记得第一次有人让我构建一个网络应用。我不得不问这是什么。那个人告诉我,我记得当时在想,“嗯,这很愚蠢!为什么人们不用 CompuServe 或 美国在线(AOL)来做这件事呢?”我认为互联网最多只会是一个时尚。它很复杂,充满了术语,与当时的在线服务相比,用户界面很糟糕,而且一切看起来都很粗糙。我想我错了。
一旦我明白了这一点,我就学习了 HTML 和 JavaScript。CSS 还没有出现。不久我就遇到了 HTML 功能的瓶颈。正如你所知,HTML 不是一个编程语言。它是一种内容标记语言,用于控制静态内容的呈现。JavaScript 的最早版本并不太有用。你可以验证表单。就这些了。在 HTML 3 出现之前,使用 JavaScript 生成动态内容并不是一个功能。
就像我说的一样,我遇到了瓶颈。我需要从浏览器中获取用户交互数据,并使用它来与数据库交互、生成文件等等。使用 HTML 和 JavaScript,这根本不可能。我需要一个后端语言。最初,那就是 C 语言。即使是那也有限制。你必须用 C 语言编写模块,这些模块可以通过一个名为 公共网关接口(CGI)的接口与 Apache 网络服务器交互。
在最初的几年里,就是这样了。编写动态网络应用很困难,远远达不到今天的能力。新的语言和范式出现,使得网络开发的实践更加容易。我第一次良好的体验是与一个名为 .cfml
扩展的产品有关,它以不同于普通 HTML 文件的方式处理这些文件。我能够非常容易地访问 Oracle 数据库,并且,结合许多漫长的夜晚和我的青春期的创造力,我创建了一些图形管道软件,这为我的雇主赢得了软件专利。
CFML 是一个增长趋势的一部分。许多其他新兴公司和堆栈也采用了同样的技术,包括以下这些:
-
Microsoft 创建了 活动服务器页面(ASP Classic)
-
Sun Microsystems 推出了 Java 服务器页面(JSP)
-
超文本预处理器(PHP)
-
国家超级计算应用中心(NCSA)创建了 服务器端包含(SSIs),虽然它的功能不如列表中的其他产品丰富,但仍然作为一种生成动态内容的方式存在。
在本章中,我们将迈出巨大的步伐,探讨一个更现代的用于创建动态内容的框架,这个框架是在服务器端而不是在浏览器客户端内部生成的。具体来说,我们将探讨一个名为Flask的框架,这是一个流行的、无偏见的 Python 网络应用程序创建解决方案。到本章结束时,你将理解以下内容:
-
网络开发的基础,例如客户端-服务器架构,以及网络使用的无状态请求-响应模型。
-
Flask 是什么,以及它与其他 Python 网络开发框架相比如何
-
如何在 PyCharm 中创建 Flask 应用程序。
-
如何在 PyCharm 中使用Jinja2模板,这些模板用于提供动态内容,与常规 HTML 标记、CSS 样式和 JavaScript 交互性混合。
-
如何创建一个返回 JSON 格式数据的 RESTful API 端点,而不是内容。
-
如何使用 PyCharm 的 HTTP 请求功能测试你的 API
请记住,本章的目的不是提供一个 Flask 教程。它是一个关于如何使用 PyCharm 与 Flask 一起工作的教程。如果你正在寻找一个完整的 Flask 教程,请访问我的网站www.maddevskilz.com
。那里有几个扩展的 Flask 教程,深入探讨了构建整个项目。
技术要求
为了继续本章的学习,以及本书的其余部分,你需要以下内容:
-
安装并运行良好的 Python 解释器。我将使用来自
python.org
的最新版本。 -
安装了
pip
和virtualenv
。当你安装 Python 在 Windows 上时,这些会自动安装,macOS 系统上通常也包含它们。如果你使用 Linux,你需要单独安装包管理器,如pip
,以及虚拟环境工具,如virtualenv
。我们的示例将使用pip
和virtualenv
。 -
安装并运行良好的 PyCharm。安装已在第二章**,安装和配置中介绍,以防你在本书的中间部分开始阅读。
-
本书提供的示例源代码来自 GitHub。我们已经在第二章**,安装和配置中介绍了如何克隆代码。你可以在这个章节找到相关的代码:
github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-08
。
网络基础 - 客户端-服务器架构
当我在 1991 年开始我的 IT 职业生涯时,我在一家名为电子数据系统(EDS)的公司工作。那是一个不同的时代。当时,任何严肃的计算机操作都是由被称为大型机的单一系统完成的。想象一下,大型机计算机就像在个人电脑和智能手机时代之前存在的极其强大和大规模的计算机。它就像一台超级计算机,能够处理大量数据并执行复杂的计算。
大型机通常被安置在特别设计的房间或数据中心,因为它们需要大量的空间和专业的电源和冷却系统才能正常工作。典型的大型机通常大约有微型货车的尺寸,而其独立的电源分配单元(PDU)的尺寸大约是典型洗衣机的两倍。直接访问存储设备(DASD)位于另一个同样大小的长方形金属箱中。连接这些各种组件的是直径大约与用过的纸巾卷相似的粗电缆。IBM Z14 系列型号的重量在 2,500 至 4,000 公斤(5,500 至 8,800 磅)之间。
在过去,大型机通常被大型组织,如银行、政府机构、大学和大型企业所使用。它们负责处理和管理大量数据,运行关键业务应用程序,并支持整个企业的运营。在我的情况下,我与负责运行通用汽车(GM)等公司的汽车装配线操作的 IBM 大型机系统一起工作。
大型机以其可靠性、安全性和高性能能力而闻名。它们能够同时处理多个任务并提供快速响应时间,即使在处理大量工作时也是如此。人们通过终端或其他连接设备访问大型机以执行任务或检索信息。
随着技术的进步和个人电脑的出现,大型机的角色已经发生了演变。虽然它们在特定行业中仍然发挥着至关重要的作用,但许多曾经仅限于大型机的计算任务现在由分布式系统、相对便宜的机架式 Intel 和 IBM Power 系统、云计算以及笔记本电脑和智能手机等小型设备来处理。
主机计算机,我想现在仍然是,拥有和运营成本非常高。不仅硬件昂贵,而且通常需要一支由专家计算机操作员和维护人员组成的团队来保持系统运行。这种成本对于除了最大型的公司和大学之外的所有人来说都是难以承受的。较小的公司和甚至更小的国家不得不在其他人的主机上购买时间,以获得大规模计算的机会。实际上,这就是 EDS 提供的服务。我们在全球几个大型数据中心拥有大片空间,并向几乎所有的《财富》500 强公司出售时间和提供服务。最终,成本以及摩尔定律的影响导致了主机的衰落。
摩尔定律是由英特尔公司联合创始人戈登·摩尔在 1965 年提出的一个观察和预测。它指出,微芯片上的晶体管数量大约每两年翻一番,导致计算能力和性能显著提高,同时降低了电子设备成本。
摩尔最初指出,这种晶体管密度的指数增长自集成电路发明以来一直在发生,并且他预测这种增长在未来可预见的时期内将继续。多年来,摩尔定律表现得非常准确,半导体制造技术的进步使得晶体管越来越小,集成电路越来越复杂。
每两年晶体管密度的翻倍对计算领域产生了深远的影响。它使得更强大、更高效的计算机的发展成为可能,提高了处理速度、内存容量和存储能力。随着可以在芯片上装入更多晶体管,电子设备的整体性能得到提升,而其物理尺寸却减小了。
随着计算机变得越来越小,以及精简指令集计算(RISC)和最终英特尔 x86 架构的出现,一种新的计算模式出现了:客户-服务器。
主机计算机是集中的,并通过“哑”终端进行访问。这些终端没有计算能力,没有存储,只有足够的内存来维护一个通信缓冲区,以便将您在键盘上输入的内容发送到主机进行处理。
客户-服务器架构将一些计算、存储和内存转移到本地客户端,这通常是一个 PC。客户端通过局域网(LAN)连接到服务器,服务器通常比 PC 更强大,能够运行企业级计算负载。通常,您会有客户端软件,它由在 PC 操作系统上运行的桌面用户界面组成。客户端软件与服务器上运行的集中式软件进行交互。
除了客户端和服务器硬件外,大约在同一时间出现了一个最终组件:一个称为传输控制协议/互联网协议(TCP/IP)的标准网络协议。在我上大学之前,还没有 TCP/IP,为了与俄克拉荷马大学的巨型机交互,我必须随身携带一叠软盘,里面装有奇异的、不匹配的通信协议集合。一些系统使用名为高效、远程和多个计算机交互内核(KERMIT)的协议。我还有用于XMODEM、YMODEM和ZMODEM的磁盘。根据我想访问哪种类型的计算机,我必须使用不同的协议。TCP/IP 通过一套标准协议改变了这一切,这些协议被从主机和 PC 到现代智能手机和互联网连接的烤面包机等所有设备所支持。
如果这听起来像是互联网,你就对了——但有一些注意事项:
-
客户端/服务器非常慢,而且通常甚至不是全双工的,这意味着数据只能一次单向流动。
-
大多数客户端程序没有真正的 GUI,也不支持鼠标交互。它们被称为“绿色屏幕”界面,因为它们使用文本菜单在通常是绿色的单色屏幕上渲染。后来,厚客户端应用程序具有真正的 GUI,通常是用 Java、Visual Basic、C++或 Delphi 编写的。它们被称为厚客户端,因为程序的大小足够大,以至于通过典型的连接下载 GUI 可能需要数小时。这与在现代互联网上运行的典型网络应用程序形成鲜明对比。
-
客户端软件始终围绕一组特定的用例进行设计。相比之下,今天的网络浏览器,作为客户端,可以运行任何类型的软件,从通用的文字处理应用程序到特定的业务线应用程序。
网络是客户端/服务器架构的自然演变。轻量级的客户端软件,即网页浏览器,连接到一个集中式服务器,在那里使用普遍接受的网络协议处理大部分实际工作。
探索 HTTP 中的请求-响应机制——客户端和服务器如何通信
20 世纪 80 年代的一项伟大成就,除了含有 CFC 的喷雾剂外,就是开发了一套通用的网络协议,称为 TCP/IP。如果你对此还不太熟悉,正确的发音方式是作为字母:tea sea pea eye pea。斜杠是沉默的,就像忍者一样。
虽然它花了很长时间才被普遍采用,但最终它确实被采用了,TCP/IP 协议构成了现代网络的基础。尽管有许多有用的协议服务于无数的功能,但我想要将你的注意力集中在超文本传输协议(HTTP)上。你可以继续包括其安全的对应版本,HTTPS,其中S代表安全。它们有效地以相同的方式工作,只是 HTTPS 是加密的。
请求-响应机制涉及一系列事件,描述了在网页浏览器或客户端与 Web 服务器之间发生的对话。你可以在图 8.1中观察这个对话的展开。
图 8.1:HTTP 中的请求-响应机制将浏览器对服务器的请求传递过去,服务器计算响应并将其发送回浏览器
HTTP 使用的请求-响应机制是客户端(如网页浏览器或移动应用程序)与万维网上的服务器之间的基本通信模式。它的工作方式如下:
-
GET
、POST
、PUT
或DELETE
,这表示要在服务器资源上执行的操作,以及额外的头信息,在某些情况下,还包括携带数据的请求体。 -
服务器接收请求:在收到请求后,服务器处理请求中提供的信息。这可能涉及访问数据库、执行计算或根据请求的性质执行其他服务器端操作。最简单的请求是请求一个 HTML 文档或其他简单返回的文件。
-
服务器生成响应并发送给用户:在处理请求后,服务器生成一个 HTTP 响应。响应包含一个适当的状态码,指示请求的结果。例如,成功的请求在响应头中带有状态码 200。如果你请求的服务器上的资源不存在,你会得到一个 404 代码,表示资源未找到。响应头还包括提供更多信息的一些附加字段,以及包含请求的数据或任何相关信息的响应体。
-
浏览器接收响应数据:客户端从服务器接收 HTTP 响应并处理其中包含的信息。这可能涉及渲染 HTML 内容、处理数据或根据响应执行其他操作。
-
请求-响应周期完成:收到响应后,请求-响应周期即完成。客户端可以选择向服务器发送额外的请求以执行进一步交互,或者过程可能结束。
这种请求-响应机制是客户端和服务器之间通过 HTTP 交换信息的基础。它允许客户端请求资源或对服务器执行操作,服务器则以相应的结果或必要的信息进行响应。这个周期使得 Web 应用程序和服务具有动态和交互的特性。
有一个需要注意的事情是 HTTP 是无状态的。这意味着每个请求-响应周期都是独立的。在 HTTP 中没有原生的方法在请求之间共享或保留数据。
什么是 Flask?
Flask 是一个无观点的框架,用于处理 HTTP 中发现的请求响应机制。它只做一件事,而且只做一件事:它帮助你将请求接收到一个简单的 Python 对象结构中,然后使用 Python 代码构建响应。
让我们回到“无观点”这个词。我的意思是,Flask 设计上只处理请求响应周期。我意识到我已经说过这一点了,但值得重复。当你将 Flask 与其虚拟对立面 Django 进行比较时,差异是明显的。
Django 对如何创建你的 Web 应用有着非常强烈的观点。Django 决定了文件结构、应用程序模式和要使用的数据库。它拥有自己的对象关系映射器、自己的请求响应机制以及自己的一套编码规范。简而言之,Django 将决定你的技术栈以及你项目的多数架构细节。
Flask 提供了一些建议,但这些并不是固定不变的,如果你不想使用,也可以不用。几年前,我重新编写了我公司旗舰软件产品 Visual Storage Intelligence(见www.visualstorageintelligence.com
)作为一个 Flask 应用,严格是因为它没有观点。我认为自己在根据我的知识、经验和对我公司业务需求的理解来选择最佳技术栈方面是一个专家。
例如,我几乎从不使用 ORM。我对 SQL 和关系型数据库系统有深入的了解。我能够编写和调整查询、存储过程和视图,以在各种商业和开源数据库中构建快速、响应灵敏的 Web 应用。ORM 的设计目的是从你的手中拿走所有这些,并在数据库之上提供一个抽象层,这样开发者只需处理对象即可。
ORM 实际上是一个黑盒。大多数开发者不知道它是如何工作的,也不知道如何提高 ORM 生成的查询性能。对我来说,它只是额外的开销。我个人更愿意自己构建和调整这些内部结构。顺便说一句,如果这些数据库相关的术语让你感到困惑,请保持关注。我将在第十一章理解数据库管理中详细讨论数据库。
重点是,Flask 不关心我如何与数据库交互。它不关心我如何构建我的应用程序,也不关心我的技术栈看起来如何。它只做两件事,其中之一是可选的。
使用 Werkzeug 处理请求响应和路由
Flask 为我们做的第一件事是使处理 HTTP 中的请求响应机制变得非常简单。严格来说,一个传入的请求在本质上来说是二进制的。作为开发者,我们更愿意处理类似于文本的对象抽象的二进制结构。直接处理任何二进制内容都是非常过时的。幸运的是,有一个名为 Werkzeug 的 Python 库可以处理这个问题。
图书馆的名字“Werkzeug”源自德语,其意为“工具”或“仪器”。Flask 框架及其底层实用库 Werkzeug 最初由德国软件开发者 Armin Ronacher 开发。
选择“Werkzeug”这个名字是为了反映该库作为构建 Web 应用程序的多功能和强大的工具集的本质。正如工匠依赖一套工具来创造和塑造他们的作品一样,开发者可以利用 Werkzeug 来处理 Web 开发的各个方面,如路由、请求处理和 HTTP 实用工具。Flask 建立在 Werkzeug 之上;它添加了额外的抽象和功能,以提供一个轻量级且用户友好的 Web 框架。
Flask 使用 Werkzeug 的功能来处理 HTTP 请求和响应的低级细节,使开发者能够快速高效地构建 Web 应用程序。结果是能够创建像创建任何其他 Python 应用程序一样简单的 Web 应用程序:您创建函数来处理传入的 HTTP 请求,它返回一个格式正确的 HTTP 响应。如果您能在 Python 中编写一个函数,您就可以创建一个 Web 应用程序。
Flask 中的实现如下所示:
@app.route('/hello')
def hello_world():
return 'Hello, World!'
如您所见,这是一个简单的 Python 函数,它不接受任何参数并返回一个字符串。这个函数唯一奇怪的地方是函数定义上方的装饰。我们稍后会学到更多关于这个装饰的内容。现在,请理解这个装饰与您应用程序 URL 上的一个路由相匹配。您习惯于输入一个带有常规语法的 URL,例如www.maddevskilz.com/
,这将带您访问该地址的网站根文档。在前面的代码块中,我们的函数将响应任何对www.maddevskilz.com/hello
的 Web 请求,并返回字符串'Hello, World!'
。自然地,您将学会处理比这更复杂的请求,但我的观点是:如果您能在 Python 中创建一个函数,您就可以编写一个 Web 应用程序!
使用 Flask 的这个特性是不可避免的。如果您不想使用 Werkzeug 的这个抽象,那么您就不想使用 Flask。Flask 再次是中立的,这意味着这真的是它“关心”的唯一事情。我们将在第十二章**,使用 Django 构建 Web 应用程序中看到对比。Django 非常具有意见。它希望您使用其预定义的堆栈。Flask 不在乎。然而,它确实提出了一个强烈的建议,如果您愿意,可以安全地忽略。
使用 Jinja2 进行模板化
Flask 确实提供了一个关于默认模板库 Jinja2 的建议。当您安装 Flask 时,您会得到 Jinja2 作为依赖项。模板化系统旨在允许您将内容注入到一个标记文档中。以下是一个 Jinja2 脚本的示例,它在 HTML 页面上生成一个无序列表中的某些项:
from jinja2 import Template
# Define the template
template_str = '''
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
'''
模板只是一个包含一些特殊字符的字符串,这些字符指示 Python-like 代码的执行位置。所有模板语言都是这样工作的,从经典的 ASP 到 JSP 再到 PHP。Flask 进程从 Web 服务器获取请求。它解析请求,在这里我们使用模板来渲染响应。模板本身是一个标记片段。创建了一个名为template_str
的变量,并将其设置为空字符串。记住,这不是 Python 代码,它是 Python-like 代码,因为它存在于标记的上下文中。单引号被单引号转义,这意味着py ``
在代码中被解释为单引号。
如您所见,我们有一个for
循环遍历一个名为items
的列表,这个列表将被作为数据传递到模板中。模板中的代码用{%
和%}
来界定。每个<li>
都有一个用双大括号绑定的表达式。在这种情况下,表达式只是循环中定义的迭代变量 item。
在我的例子中,模板字符串是在代码中定义的,但在现实生活中,模板通常在文件中,因为这样更容易维护。接下来,我们创建template
对象,并将template
字符串传递给模板构造函数:
# Create the Jinja2 template object
template = Template(template_str)
现在,我们回到了普通的 Python 代码。我们将创建一个名为items
的列表并填充它:
# Generate the list of items
items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6', 'Item 7', 'Item 8', 'Item 9', 'Item 10']
现在,我们渲染template
,它生成一个新的字符串,并将其赋值给一个名为output
的变量:
# Render the template with the items
output = template.render(items=items)
在这里,我们正在打印输出,但在 Flask 应用程序的上下文中,输出将作为响应返回:
# Print the rendered output
print(output)
Jijna2 拥有一套强大的语言特性,这些特性在大多数优秀的模板系统中都很常见。但请记住,Flask 自称为无偏见。你不必使用 Jinja2。还有其他模板库可供选择,或者你可以完全放弃使用模板系统。在我公司的产品Visual Storage Intelligence中,我使用了 Jinja2 来构建产品的第 4 版本。当时,我对 Python 不太熟悉,并且习惯了 Microsoft 的 C# Web 应用的 MVC 模板。我将我的学习曲线限制在满足截止日期的范围内。Jinja2 并不难学。它主要是 HTML,附带一些额外的语法。在产品的第 5 版本中,我回过头来用 React 前端替换了应用程序的 UI 层。这并不需要对 Flask 应用程序进行任何更改,除了删除我不再使用的模板。
提醒一下——这不是一本关于 Flask 的书
我花了一些时间回顾了 Flask 的高级基础知识,以防你是作为一个 Web 开发新手遇到这个问题。虽然看起来我好像要教你 Flask,但我只想介绍与 PyCharm 相关的 Flask 开发功能。虽然我们将要构建一个项目,但我不会深入探讨代码是如何和为什么是这样的。
因此,如果你确实是一个新手,并且你对我在这里提供的 Flask 的有意隐晦覆盖感到好奇,并且想要一个合适的教程,我将在本章末尾的 进一步阅读 部分包含更多深入的资源。
关于文件和文件夹命名的注意事项
在我们进一步创建下一个项目之前,我想根据我总是向我的学生指出的一个原则给你一些建议。%20
应该被认为是有效的。Web 服务器自动处理这种编码,但手动编码空格可能会导致 URL 中的可读性问题,并且在尝试在你的本地计算机上运行你的项目时可能会引起问题。
在一个 Web 项目中,你的大多数项目路径最终都会成为 URL。即使你的项目根本不是为 Web 设计的,这也可能发生。更糟糕的是,不同的操作系统在它们的文件系统中以不同的方式处理字符。Windows 的文件名不区分大小写。如果你将文件或文件夹命名为 MyProject
,然后尝试创建一个名为 myproject
的文件夹,你会因为文件夹已经存在而遇到冲突,尽管名称中的大小写字母不同。在 Linux 和 macOS 上,文件和文件夹名称是区分大小写的。在同一个文件夹中同时拥有 MyProject
和 myproject
是完全可以的。
在所有这些中,我的建议是选择一个你喜欢的标准并使用它。对于 Python 项目来说,最常用的约定是将你的文件命名为 MyProject
。相反,你会称其为 my_project
。
话虽如此,这本书中的大多数项目都是以 PyCharm 的默认命名方式命名的,这些通常以驼峰式或帕斯卡式表示。在这些通常由 Java 和 C# 开发者采用的案例标准中,省略了空格,并且使用大写字母强调单词之间的边界。myProject
和 MyProject
分别是驼峰式和帕斯卡式的例子。我怀疑项目名称默认使用驼峰式可能是因为 PyCharm 的根源是一个 Java IDE。还有其他策略可以消除代码文件中的空格。在 JavaScript 开发中常见的 Kebab 式使用破折号而不是下划线。MyProject
变为 my-project
。
我强烈建议在编码实践中避免在任何文件夹或文件名中使用空格。这包括任何上游文件夹,例如你的操作系统的家目录。
在 PyCharm Professional 中创建 Flask 应用程序
Flask 的工具仅存在于 PyCharm 专业版的特性中。当然,你可以在 PyCharm 的免费版本中创建 Flask 应用,但在创建文件、设置运行配置、特殊调试等方面,你将完全独立。
要在 PyCharm 中创建 Flask 应用,只需选择 文件 | 新建项目,然后选择 Flask 模板,如图 8* 所示。2*。
图 8.2:在 PyCharm Professional 中创建新的 Flask 项目,从新建项目对话框中选择模板
我在图 8.2中标记了最重要的部分:
-
在 PyCharm Professional 的新建项目对话框中,你可以找到一个Flask项目的模板。
-
这一部分与我们迄今为止所做过的任何项目都没有不同。填写项目的位置。
-
创建你的虚拟环境。一旦填写完毕,PyCharm 将创建并激活虚拟环境,并安装 Flask 及其依赖项。
-
这一部分是 Flask 项目的独特之处。我提到 Flask 鼓励使用 Jinja2 模板引擎。你可以选择不使用它,PyCharm 会为你处理。还有一个设置用于你想要使用的 Jinja2 模板文件夹。我们将保持默认设置,如果你打算使用 Jinja2 模板,我建议你保持设置不变,因为这是大多数开发者预期它们所在的位置。
一旦填写完毕,点击对话框底部的创建按钮,PyCharm 将为你设置项目,包括创建一些起始代码,如图 8.3所示。
图 8.3:PyCharm 自动生成你的 Flask 项目
在之前的Hello World
习语位置,除了这个是响应你的 Web 应用程序的根路由。在顶部,我们导入 Flask,然后我们将 Flask 实例化为app
变量。现在你知道了这一点,第 6 行上的装饰对路由更有意义;app.route
只是来自 Flask 的app
实例。
所有这些代码都包含在app.py
文件中,位于图 8.3的(2)位置。名称不是必需的,如果你愿意的话可以更改它。你还可以看到,在(3)位置,PyCharm 已经为 Jinja2 模板和静态文件(如你的图片、CSS 和 JavaScript 文件)生成了两个文件夹。
创建动态 Web 应用程序
PyCharm 生成的Hello World
程序是新建应用程序的好起点。它为你提供了一些认知提示,以防你很久没有从头创建 Flask 应用程序。自然地,我们希望用稍微更有用的东西来替换Hello World
。
让我们创建一个用于编目新 Python 库的非常简单的应用程序!这类项目通常使用数据库,但不必如此。我们的应用程序将包含 Python 库列表及其描述,以及我们认为它们有多有用的 1-5 评级。为了完成数据库部分,我们将简单地使用内存中的列表数组。采用这种方法可以避免深入研究 PyCharm 的数据库功能,这些功能将在第十一章中介绍,即《使用 PyCharm 理解数据库管理》。我们还将利用每一个机会来实际应用我们之前介绍的一些功能,例如使用上一章中的 HTML 功能。
设置静态部分
在构建 Web 应用程序时,最简单的起点是让静态部分工作。我指的是应用程序的非动态部分,例如具有基本结构的index.html
页面,以及我们可能需要的任何 CSS、图像和 JavaScript。
首先,在Templates
文件夹中右键单击并创建一个名为index.html
的新文件。这是一个 Jinja2 模板,因此它应该放在Templates
文件夹而不是static
文件夹中。
图 8.4:右键单击模板文件夹并创建一个新的 HTML 文件
注意,我们创建了一个普通的 HTML 文件。Jinja2 文件没有特殊的文件类型。该文件是使用我们在第七章中介绍的 Emmet 模板系统创建的,即《使用 JavaScript、HTML 和 CSS 进行 Web 开发》。希望您还记得,HTML 文件中的title
属性允许您填写 HTML 模板中的空白部分。您可以在图 8**.5中看到我的示例。
图 8.5:HTML 创建模板将光标置于标题标签中,以便您填写页面该部分的内容
接下来,让我们像这样修改<head>
标签的内容:
<head>
<meta charset="UTF-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3
.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
crossorigin="anonymous">
<title>Python Libraries R Us</title>
</head>
这将引入来自内容分发网络(CDN)的 bootstrap CSS 和 JavaScript 库。如果您不想手动输入所有这些内容,您可以从书籍的代码库中复制代码,或者您可以去getbootstrap.com
,并在他们网站的入门部分找到最新版本,通常包括可复制的 CDN 链接。
我们使用 CDN,因此不需要在我们的项目中保留这些常用文件。此外,一旦您的惊人应用程序投入生产,CDN 通常比您的 Web 服务器更快地提供这些类型的文件。
接下来,将<body>
标签的内容更改为以下内容:
<body>
<h1>Python Libraries R Us</h1>
<h2>All the libraries that are fit to use!</h2>
</body>
接下来,我们将使用我们在第七章**,使用 JavaScript、HTML 和 CSS 进行 Web 开发中学习到的 Emmet 功能来生成 HTML 表格标题。在新的一行中,在<h2>
标签下方输入以下 Emmet 代码:
table>thead>tr>th*4
这个缩写将生成一个表格,后面跟着一个thead
标签,包含一个表格行(tr
)和四个表头字段(th*4
)。按Tab键展开缩写。PyCharm 的 Emmet 插件将生成您的代码,并直接带您到th
标签的内容,如图图 8**.6所示。
图 8.6:Emmet 将代码扩展为表头所需的所有代码
一旦 Emmet 扩展了您的代码,您将看到th
标签内的提示,允许您编辑内容,如图图 8**.7所示。
图 8.7:Emmet 将图 8.6 中的第 15 行缩写扩展为我们看到的内容
传统上,您会使用Tab键在字段之间移动,但在 PyCharm 中,Tab键被绑定到扩展 Emmet 的动作,正如我们刚才看到的。如果您来自不同的 Emmet 启用编辑器,您可能无法像预期的那样使用Tab键进行导航。相反,您需要找出您系统上的快捷键,因为这将取决于您在安装 PyCharm 时配置的键盘快捷键布局。我选择了 Windows 布局,因此对我而言,在字段之间移动是使用Alt + Shift + ]完成的。
在第一个th
标签内,输入图书馆名称
。然后使用Alt + Shift + ]键移动到下一个字段,并将其更改为描述
。第三个字段将被称为评分
,第四个字段标题为URL
。
如果Alt + Shift + ]对您不起作用,让我们找出什么会起作用。进入 PyCharm 的设置,并找到如图图 8**.8所示的键映射设置。
我在搜索框中输入了Emmet
,可以看到我的导航 > 下一个/上一个 Emmet 编辑点设置是Alt + Shift + ]和**Alt + Shift + **,分别。如果您的不同,您将在这里看到它们,并且正如我们所学的,您可以将其更改为您想要的任何内容,只要更改不与另一项冲突。
![图 8.8:您的 Emmet 导航设置在设置中找到并设置此处图 8.8:您的 Emmet 导航设置在设置中找到并设置此处在这个阶段,您的 HTML table
代码应该看起来像这样:py<table> <thead> <tr> <th>Library Name</th> <th>Description</th> <th>Rating</th> <th>URL</th> </tr> </thead> </table>
让我们在table
代码中添加另一项内容。在</thead>
闭合之后但在</table>
闭合之前输入以下内容:py<tbody> <tr> <td colspan="4">No libraries to display</td> </tr></tbody>
如果您的代码变得混乱,请使用 PyCharm 的代码重排功能来整理。这通常是Ctrl + Alt + L,或者在 Mac 上为Cmd + Opt + L。我们已经布局了一个基本的网页。在我们使其动态之前,让我们为应用程序创建一个运行配置,这样我们就可以预览我们的作品。## 运行 Flask 应用当我们创建项目时,PyCharm 为我们创建了一个 Flask 运行配置。让我们看看它,以便了解 PyCharm 将如何运行该应用。点击运行配置下拉菜单,然后点击编辑配置…,如图 8.9所示。
图 8.9:编辑运行配置以查看其构成
设置看起来像图 8.10中所示。
图 8.10:PyCharm 生成的 Flask 运行配置
现在大多数设置都很熟悉了,所以只有少数被标记。
运行 Flask 应用有几种方法。最简单的方法是直接运行app.py
文件,这是运行应用的dunder-main
行,这完全没问题。回想一下app.py
底部的代码:
if __name__ == '__main__':
app.run()
您也可以使用模块名来运行它,但现在让我们先使用 PyCharm 生成的配置。
在FLASK_DEBUG
环境变量位置,它有自己的复选框。当你勾选这个框时,它将专用的 Flask 开发服务器置于调试模式,这带来了一些好处。其中最大的好处是,每当您更改代码时,应用服务器都会重新启动。这让您不必每次更改时都记得停止和重新启动服务器。一般来说,您希望勾选这个框。勾选框上方是常用的FLASK_ENV
环境变量,它也被传递给运行的应用。它默认为development
。您可以使用这个环境变量在您的应用中打开和关闭某些行为,包括设置日志详细级别。
在PYTHONPATH
文件夹位置的两个PYTHONPATH
复选框,这可以防止您收到 Python 找不到您的应用的错误。您希望勾选这些。
注意,PyCharm 提供了设置传递给运行应用中的常见环境变量的能力,这些复选框正是如此。这很好,因为在操作系统级别设置它们是额外的工作,我们经常忘记,而且根据您的操作系统,更改环境变量可能不会像预期的那样有效。当您在操作系统级别使用环境变量时,通常需要重新启动或注销并重新登录,以确保新值生效。PyCharm 直接将变量注入到运行的开发服务器中,这节省了大量的时间和挫折。
点击确定关闭运行配置对话框。
在启动之前,我们需要对app.py
进行代码更改。我们需要修改我们的Hello World
代码以加载并显示模板。修改后的代码如下:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/', methods=["GET"])
def root():
return render_template("index.html")
if __name__ == '__main__':
app.run()
首先,我添加了对 Flask 中的render_template
方法的导入。我们将使用它来渲染我们的 Jinja2 HTML 模板。
接下来,我在路由装饰器中添加了一些代码。之前我们有 @app.route('/')
,它定义了我们的网站 root
路由,该路由将由我们的 root
函数处理。我添加了一个第二个参数,methods=["GET"]
。Flask 允许你将路由锁定到之前章节中描述的 HTTP 请求方法之一或多个。将 Flask 应用程序端点锁定到特定的 HTTP 方法,如 GET
或 POST
,是 Web 开发中的基本实践,有以下几个重要原因:
-
安全性:不同的 HTTP 方法有不同的用途和安全影响。例如,GET 请求通常用于检索数据,它们不应在服务器上产生任何副作用。相比之下,POST 请求用于向服务器提交数据,它们可以产生副作用,如创建、更新或删除资源。通过将端点限制到特定的 HTTP 方法,你可以防止意外的或恶意的行为。这被称为“基于方法的访问控制”。
-
可预测性:将端点锁定到特定的 HTTP 方法可以使你的 API 或 Web 应用程序更加可预测和自文档化。其他开发者(甚至你未来的自己)将更清楚地了解如何与你的应用程序交互。例如,如果一个端点是为 GET 请求设计的,那么它显然是用来检索数据的。
-
一致性:API 设计的一致性可以提高用户体验并减少困惑。当用户或客户端知道特定端点期望特定的 HTTP 方法时,他们不太可能发出错误的请求。
-
防止意外:端点的意外误用可能导致意外的后果。通过限制允许的 HTTP 方法,你可以减少 UI 开发者犯错误的机会,例如尝试使用 GET 请求删除数据。
-
框架支持:Flask 与许多其他 Web 框架一样,提供了基于 HTTP 方法的内置路由请求支持。这使得实现基于方法的访问控制变得更容易,因为你可以为每个 HTTP 方法定义单独的路由和处理程序。
如果你仔细看,我们传递的参数是一个数组。你可以传递一个或多个方法,允许一个路由以不同的方式处理一个或多个方法。在这里,我们将 root
路由函数锁定到 HTTP GET 方法,这是当你访问网站时浏览器发出的请求。
接下来,我们将 Flask 的 render_template
方法添加到了我们的导入中。我将函数的名称从 hello_world
改为 root
,然后将返回值从 'Hello World'
字符串改为 render_template
函数的结果,该函数接受模板的文件名。Flask 会知道在 Templates
文件夹中查找 index.html
。
现在我们准备尝试一下。确保你的 Flask 应用程序在运行配置下拉菜单中被选中,然后点击绿色的 运行 按钮。运行 选项卡将出现在 PyCharm 屏幕的底部。我的看起来如图 8.11 所示。
图 8.11:我们的 Flask 应用的运行窗口
有几个值得注意的元素。第一个是那个大红色的警告信息。我知道,对你来说它是灰色的,但当你第一次尝试时你会看到它。我们正在使用 Flask 内置的开发网络服务器运行我们的应用。请不要在生产环境中使用它。相反,你需要使用一个生产质量的网络应用服务器,例如 Green Unicorn。这超出了使用 PyCharm 进行开发的范围,但使用内置服务器部署你的应用是一个如此巨大的错误,我觉得有必要指出并解释为什么这个警告要以大红色字母的形式出现。
其次,它告诉你应用正在http://127.0.0.1:5000
上运行。地址被列为超链接,你可以点击它来打开浏览器。我的浏览器在图 8.12中打开。
图 8.12:它工作了,但 1991 年刚打电话来说他们想要他们的网页设计
真的是太难看了,不是吗?我们费了这么大的劲添加了 Bootstrap,至少应该用上它。
让我们让它看起来好一点
从设计角度来看,页面并没有太多动作,虽然我们不会构建一些可能让你在苹果公司得到设计工作的惊人用户体验,但我们可以通过添加一些 Bootstrap 类至少让这个页面看起来更整洁。我们将在 Jinja2 模板index.html
内的body
标签内完成这项工作。
首先,让我们设置 Bootstrap 的布局网格。这允许我们创建能够优雅地适应任何尺寸屏幕的应用,从小型手机浏览器到最大的 8K 显示器。
将此代码添加到body
标签内。完成后,它应该紧挨着我们之前创建的table
代码:
<div class="container-fluid h-100">
<div class="row">
<div class="col-12">
<h1>Python Libraries
<span class="flipped-letter">R</span> Us</h1>
<h2>All the libraries that are fit to use!</h2>
<hr/>
</div>
</div>
<div class="row">
<div class="col-8">
<h5>Here are the libraries:</h5>
在这里,我们添加了一个标记为container-fluid
类的div
。这将为我们提供一些急需的布局和填充,以便我们的内容不会紧贴浏览器窗口的边缘。我还将其设置为占据浏览器窗口中可用的全部高度,预计我们的表格中会有许多有用的库。我是用 Bootstrap 的h-100
类做到这一点的。
之后,我添加了一个div
作为行,然后又添加了一个作为列。我已经将列的类设置为col-12
,在 Bootstrap 中这意味着它应该占据浏览器窗口的全宽,并带有适当的边距和填充,这些由我们之前添加的祖先div
标签中的container-fluid
类定义。
接下来的三个标签只是内容添加——一些使用H1
和H2
标签进行标注,解释用户看到的内容,然后是一个水平线。我添加了一个 CSS 类来翻转字母 R,使其让人联想到美国一家流行但现已破产的玩具店。我童年的另一个图标已经灰飞烟灭。请注意,我们还没有创建这个 CSS 类。我们将在下一分钟内完成。
让我们在表格上添加 Bootstrap 的.table
类。通过添加class="table"
来更改你的table
代码:
<h5>Here are the libraries:</h5>
<table class="table">
<thead>
<tr>
<th>Library Name</th>
<th>Description</th>
<th>Rating</th>
<th>URL</th>
</tr>
这样就可以调整表格及其周围的间距,使所有内容不会挤在一起。
为了结束这一切,我们需要在表格关闭后添加所有必要的关闭标签。这只需要三个嵌套的div
关闭标签:
</div>
</div>
</div>
第一个关闭列,第二个关闭行,第三个关闭容器。最后一步是添加一些 CSS。
添加一些 CSS
右键点击static
文件夹,创建一个名为index.css
的新 CSS 文件。将以下代码添加到文件中:
body {
margin: 92px;
height: 100%;
}
.flipped-letter {
display: inline-block;
transform: scaleX(-1);
}
.gold-star {
color: gold;
}
在这个 CSS 文件中,我们添加了一些额外的页边距,并将高度设置为 100%,这样我们就不会得到一个又短又小的页面,这个页面完全依赖于我们表格中的内容量。
我在index.css
中添加了一个flipped-letter
类来翻转我们想象中的网站标题中的字母 R。我们将为每个图书馆显示一个评分。我想,与其只显示一个无聊的数字,不如放一些星星进去。不仅仅是任何星星——是金色星星!所以,有一个类,我称之为.gold-star
。如果你对 CSS 不熟悉,前面的点很重要。它标记了后面的gold-star
作为一个自定义类。在 CSS 中,类与面向对象开发无关,所以如果你学过一些 Java 或其他基于类的语言,那么“类”这个词与那些语言中的概念无关。你很快就会看到,当类在 HTML 中使用时,点将不存在。这不是一个错误。
现在我们已经在static
文件夹中有一个 CSS 文件了,我们需要在 HTML 文件中引用它。由于它位于具有特殊意义的静态文件夹中,我们的代码与之前我们看到的纯 HTML 中的直接 CSS 引用略有不同。在你的head
标签内添加以下代码行:
<link href="{{ url_for('static', filename='index.css') }}"
rel="stylesheet"
type="text/css">
注意 Jinja2 表达式解析了静态文件夹的位置。
我刚才提到我想用星星来显示我的评分。我并不想真正使用图形。相反,我更愿意使用一个字体,特别是Font Awesome。Font Awesome 基本上是一个巨大的网络字体,它不包含字母字符,而是包含数百个有用的图形图标,这些图标对于制作现代 UI 和网站设计非常有用。有关 Font Awesome 的更多信息,请参阅www.fontawesome.com
。
与其将 Font Awesome 包含在我们的项目中,当然这是一个选项,我将链接到一个托管在 内容分发网络(CDN)上的 Font Awesome 版本。CDN 是托管内容的一种理想方式,因为它们被设计用来以极快的速度提供静态内容。它们不仅通过正常的服务器优化来实现这一点,而且还通过在全球范围内战略性地放置服务器来实现。当你的页面从 CDN 加载静态内容时,对该内容的请求会被路由到最近的服务器。印度的用户将连接到印度的 CDN 服务器,而堪萨斯州(美国中部)的用户将从更近的服务器获取内容。
Font Awesome 在其网站上列出了其 CDN 链接。我将使用我从 Font Awesome 网站复制的链接,并将其添加到我们页面的 head
标签中。由于这是从 CDN 而不是静态文件夹中获取的,所以我不需要任何 Jinja2 的魔法来解析它:
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
页面看起来应该好多了!我会在我们添加了动态内容后再发布截图。我不想破坏这个大揭秘!别忘了,你可以在从 GitHub 克隆的章节存储库文件夹中检查最终的代码。
使页面动态化
我们已经创建了一个 Flask 应用并使一个模板工作。让我们使用 Flask 和 Jinja2 为页面添加动态性。我们将使用一个与我们在表中放置的字段相对应的字段列表的 dicts
来模拟数据库。
切换到 app.py
文件,找到这条线,它应该接近第 3 行:
app = Flask(__name__)
这一行是由 PyCharm 在我们创建项目时生成的,它创建了一个 Flask 实例,并将其分配给一个名为 app
的变量。我们将在这一行下面开始添加新的代码。让我们先创建一个全局变量来保存我们的数据:
library_data = list()
library_data.append({"python_library": "Flask",
"description": "An unopinionated web framework",
"rating": 5,
"url": "https://pypi.org/project/Flask"})
library_data.append({"python_library": "Jinja2",
"description": "Templating library",
"rating": 3,
"url": "https://pypi.org/project/Jinja2"})
通常情况下,全局变量,尤其是在大型程序中,应该避免使用。在这种情况下,我们使用全局变量来模拟数据库连接,这通常是规则的例外。
在这里,我们正在向我们的模拟数据库添加一些记录。如果你是 Jinja2 的粉丝,请不要恨我。我在内容中给它评了 3 分,只是为了在两个样本记录之间有一些视觉差异。
只需再做一个修改。将第二个参数添加到 render_template
调用中:
return render_template("index.html", library_data=library_data)
render_template
方法是一个 可变参数函数。你可以传递尽可能多的参数给它。Jinja2 将能够在模板中渲染传递的数据。在这里,我们只是添加了一个数据变量。
我们已经完成了 app.py
!最终的代码如下所示:
from flask import Flask, render_template
app = Flask(__name__)
library_data = list()
library_data.append({"python_library": "Flask",
"description": "An unopinionated web framework",
"rating": 5,
"url": "https://pypi.org/project/Flask"})
library_data.append({"python_library": "Jinja2",
"description": "Templating library",
"rating": 3,
"url": "https://pypi.org/project/Jinja2"})
@app.route('/', methods=['GET'])
def root(): # put application's code here
return render_template("index.html", library_data=library_data)
if __name__ == '__main__':
app.run()
现在我们将一些数据传递到模板中,我们需要回到并修改 index.html
模板以渲染数据。你需要将 tbody
标签的内容更改为以下内容:
<tbody>
{% if library_data|length > 0 %}
模板中的{%
和{{
标记表示逻辑和内容发生的地方。在这里,我们正在检查数组的长度。如果它大于零,我们将使用数组的内容渲染表格行。向下还有else
,它将渲染我们现在有的内容,即表示没有数据的单行:
{% for data in library_data %}
<tr>
<td>{{ data.python_library }} </td>
<td>{{ data.description }}</td>
<td>
{% for _ in range(data.rating) %}
<i class="fas fa-star gold-star"></i>
{% endfor %}
</td>
<td><a href="{{ data.url }}"
target="_blank">View on pypi.org</a></td>
</tr>
{% endfor %}
在前面的代码中,我们正在遍历library_data
列表并生成一个表格行(tr
)。然后为表格行提供列。{{ }}
占位符表示当前列表迭代中dict
的内容应该放置的位置。第一列显示data.python_library
的内容。第二列显示描述。
第三处是我们添加一些炫酷的东西!我们添加一个代码块,使用 range 循环来生成该列中的星星。如果评分是 3,循环运行 3 次,我们得到 3 颗星星!
对于 URL,我使用了data.url
的值作为超链接的href
属性。
这就剩下我之前提到的else
语句:
{% else %}
<tr>
<td colspan="4">No libraries to display</td>
</tr>
{% endif %}
</tbody>
总结一下,如果我们传递一个包含零个元素的数组,Jinja2 将渲染我们之前的行,表示没有数据。如果数组中有数据,Jinja2 将遍历数组并为数据中的每一行生成一个表格行。
运行项目并将浏览器指向http://localhost:5000
。你会看到一个渲染了两个记录的表格,如图图 8**.13所示。
图 8.13:大揭秘!我们的页面现在是动态的
让我们暂停一下,评估 PyCharm 是如何帮助我们。
用于与 Flask 和 Jinja2 一起工作的编辑器增强功能
在这个练习中,你已经得到了很多帮助。如果你不相信我,试着用 Vim 或 Notepad 重复这个练习!到现在为止,你已经开始习惯 PyCharm 如何减轻你的认知负担,以及物理上处理大量的输入。
你可能甚至没有注意到以下内容:
-
HTML 编辑功能,如语法高亮、自动关闭标签和标记中嵌套的打开和关闭标签的颜色编码指示器。
-
我们使用了 Emmet 来生成一些我们的标记。
-
PyCharm 理解 Jinja2 语法,并且当你开始到处放置大括号时,检查不会让你感到恐慌。
-
事实上,它会自动关闭
{{ 表达式 }}
和{% code
blocks %}
的大括号。 -
如果你在这个过程中犯了任何错误,你可能已经注意到 PyCharm 的检查和建议清楚地理解如何使用 Flask。这不是简单的内省自动完成。PyCharm 将为你提供针对 Flask 开发的特定帮助。
让我们看看一些不那么明显的功能:
-
切换到
app.py
,将光标放在模板文件名(index.html
)内,然后按Ctrl / Cmd + B。这将在模板中激活library_data
变量,然后按Ctrl / Cmd + B,它将带你到渲染模板的调用,该调用将变量传递给 Jinja2。 -
在
app.py
中调试器工作并不令人惊讶。它同样也适用于代码块内的 Jinja2 模板。在 Jinja2 模板的index.html
中的for
循环处设置一个断点,使用debug
启动你的应用,调试器将在循环处停止。你可以使用与调试其他任何内容相同的单步执行功能。你可以检查模板变量,就像检查正常 Python 脚本中的变量一样。考虑到 Jinja2 实际上是一种元语言,与 Python 完全不同,并且专注于它能做什么,这相当令人瞩目。 -
如果你犯了任何错误,Werkzeug 有一个很棒的错误页面,其中包含指向你的堆栈跟踪的链接,其中包括你的模板跟踪。PyCharm 捕获这些信息并在运行窗口中显示,包括超链接。这些超链接将带你到 PyCharm 中的代码,而不仅仅是像 Werkzeug 在浏览器窗口中那样显示它。
-
你会发现对流行的 Flask 插件的支持与对 Jinja2 的支持相同。Flask 被设计成可扩展的。有许多插件可以帮助你在许多 Web 开发领域更轻松地工作,从 REST API 开发到数据库 ORM,再到会话处理和身份验证。你会在任何你遇到的 Flask 开发场景中找到 PyCharm 的引导。
摘要
我已经多次指出,这一章是关于 Flask 的一个糟糕的教程。我们只是刚刚触及了 Flask 能做什么的表面,但我们涵盖了 PyCharm 如何帮助你进行 Flask 开发的全部方法,并且它是少数几个在开发这个简单项目时提供我们所见到的帮助和工具水平的 IDE 之一。
首先,Flask 提供了一个简单的方法来生成 Flask 项目的项目结构和起始代码。就像任何其他项目一样,PyCharm 会设置你的虚拟环境,并为你提供一些起始代码。当我们使用 PyCharm Professional 中的 Flask 模板时,PyCharm 也会为你安装项目依赖,并为你的项目设置一个专门的运行配置。
一旦你开始编辑你的项目,你会发现我们在前面章节中介绍的所有功能都汇集在一起。HTML、CSS 和 JavaScript 相关功能不仅与普通 HTML 项目兼容,还与 Flask 本地的 Jinja2 模板语言兼容。我们不仅获得了针对 Flask 的特定检查、代码提示和文档,还获得了针对扩展 Flask 生态系统的文档。
我们甚至发现我们可以像调试实际的 Python 代码一样调试 Jinja2 模板!结合一些非常棒的导航增强功能,帮助你在这两种逻辑之间切换——前端逻辑和后端逻辑——作为 Flask 开发者,你将手握一种无与伦比的力量和功能组合。
Flask 并非市场上唯一的游戏。在过去的几年中,一些新的开发模型变得流行:特别是单页应用(SPAs)与后端纯 RESTful API 的结合。下一章将重点介绍使用名为 FastAPI 的框架构建 RESTful API 的快速、现代方法。
正如你将看到的,FastAPI 与 Flask 有一些相似之处,但也有一些重要的区别。Flask 使用工作模型来服务内容或数据,而 FastAPI 则更像 NodeJS,它使用异步编程模型。FastAPI 更倾向于专注于仅创建 RESTful API,而 Flask 中则缺少这些模板工具。
进一步阅读
一定要查看该书的配套网站 www.pycharm-book.com
。该网站允许我列出可用的最新资源。以下资源也将非常有用:
-
Gaspar, D.,& Stouffer, J. (2018). 《精通 Flask 网络开发:构建企业级、可扩展的 Python 网络应用》。Packt Publishing Ltd.
-
Van Horn II, B. (2019). 《使用 Flask 构建 RESTful API》。LinkedIn Learning:
www.linkedin.com/learning/building-restful-apis-with-flask/restful-apis-with-python-3-and-flask-4
。 -
Van Horn II, B. (2021). 《Python 开发者用 MongoDB》。MadDevSkilz.com。
www.maddevskilz.com/courses/mongodb-for-python-developers
。注意:尽管标题如此,这是一篇关于创建使用 MongoDB 的 Flask 应用的简短教程。它与我 LinkedIn 上的课程非常相似,而且更新更早。
第九章:使用 FastAPI 创建 RESTful API
在上一章中,我们学习了一个名为 Flask 的框架。Flask 在一个传统的网络开发框架领域中代表了 Python,这些框架旨在服务器上生成内容并将其发送回浏览器。这就是我们几十年来开发网络应用的方式。2010 年代带来了一个范式转变,但这并不是一夜之间发生的。
2004 年,术语 AJAX(异步 JavaScript 和 XML 的缩写),由 Jesse James Garrett 在一篇题为 Ajax: A New Approach to Web Applications 的文章中提出。这篇文章帮助推广了异步网络应用的概念和技术。到 2005 年,主流浏览器都支持了一个新的 XMLHttpRequest(XHR)网络 API 调用。这个特性允许开发者请求纯数据,而不是与标记结合生成的 HTML 页面。
2010 年代单页应用(SPAs)的兴起与 JavaScript 框架(如 AngularJS(现在为 Angular)、React 和 Vue.js)的进步密切相关。这些框架为开发者提供了构建动态、交互式网络应用的工具和能力,这些应用与传统应用不同,它们同时请求 HTML 和数据。SPAs 在一个请求中加载所有标记、CSS 和 JavaScript。之后,应用使用 XHR 请求数据,在接收到数据和用户交互的响应后,开发者使用 JavaScript 显示和隐藏用户体验中的不同元素,而不是每次数据或用户交互发生变化时都重新渲染整个页面。
由 Google 于 2010 年发布的 AngularJS,在推广 SPAs 概念方面发挥了重要作用。它引入了一种声明式的方法来构建网络应用,允许开发者创建丰富的、响应式的用户界面,而无需进行完整的页面刷新。AngularJS 为构建 SPAs 提供了一个坚实的基础,其成功也激发了其他 JavaScript 框架的开发,这些框架进一步精炼和改善了 SPAs 的开发体验。
由 Facebook 开发并于 2013 年发布的 React,也促进了 SPAs 的普及。React 引入了一种基于组件的架构,这使得管理应用程序的状态和 UI 组件变得更加容易。React 的虚拟 DOM(文档对象模型)diff 算法和高效的渲染机制使其非常适合构建快速可扩展的 SPAs。
由 Evan You 创建并于 2014 年发布的 Vue.js,因其轻量级和易于接近的特性而成为构建单页应用(SPAs)的流行框架。它提供了一个温和的学习曲线,并提供了灵活直观的方式来构建用户界面。
总体而言,这些 JavaScript 框架的组合,加上浏览器技术和 API 的进步,导致了从 2010 年代初开始单页应用(SPAs)的兴起。如今,SPAs 通过在单个网页上动态更新内容,消除了完全重新加载页面的需要,并为 Web 应用提供了更类似应用程序的感觉。
我们当然可以使用 Flask 创建一个 SPA 后端,但这可能不是最佳选择。特别是如果你预期你的应用程序的用户基数很大。当服务器请求量高时,Flask 被认为有点慢。幸运的是,游戏中有其他参与者,在本章中,我们将重点关注 FastAPI。
FastAPI 最有趣的地方在于它处理 Web 请求的方式。大多数产品,包括 Flask,都使用工作模型。一群工作进程负责在操作系统管理的不同进程中服务多个传入请求。FastAPI 使用异步编程模型。在异步编程领域,函数会被调用,但并不保证立即返回结果。让我们来探索以下具有说明性的 Python 代码:
async def add_two(a: int, b: int) -> int:
return a + b
在同步编程环境中调用add_two
时,执行线程会暂停,直到函数完成并返回结果。然而,当使用异步编程时,机制会有所不同。异步调用不会停止调用线程。相反,在执行完计算a + b
后,调用线程会继续执行。重要的是要认识到,这可能不会立即发生,但在计算过程中,其他操作不会被阻塞。当然,执行整数的加法运算很可能会迅速完成。
考虑一个替代场景,其中对网络资源(如获取网页)发出请求。响应速度不再仅仅取决于处理器的执行速度。它包括计算机性能、网络延迟以及可能引入从几秒到甚至几分钟的延迟的各种其他因素。这对于居住在高速互联网接入良好、连接紧密的城市地区的那些人来说尤其如此。由于异步软件不会阻塞,因此它可以串行地服务许多请求,因为它们中没有一个正在等待前一个函数调用或,在这种情况下,网页请求完成。你可以把它想象成只有一个服务员的一家餐厅。你与其他用餐者一样串行地提交订单,但厨师只有在食物做好后才会给你送来。有了这样的预期,厨师可以自由地一次烹饪他们炉灶允许的尽可能多的菜肴,并且有一个隐含的承诺,即一旦你的食物做好,服务员就会把它给你。订购一杯果汁的人可能会立即得到他们的订单,而你的烤阿拉斯加可能需要更长的时间来制作。
相比之下,一个同步餐厅可能会雇佣,比如说,八个厨师。可以同时进来八个订单,但每个厨师都会完全专注于完成那个订单,直到它完成。结果证明,至少对于计算机来说,在典型的 Web 请求工作负载中采用异步模型时,吞吐量通常更快。
在 Python 3.4 中,我们得到了一个新的模块,名为asyncio
,它将异步编程特性带到了我们最喜欢的语言中。三年后,我们发现了第一个名为Starlette的库项目的 GitHub 提交。Starlette 是一个异步 Web 框架,用于使用 Python 构建高性能应用程序。它以高效的方式提供了处理 HTTP 请求和响应的核心功能。Starlette 以其简洁、速度和对现代 Python 特性的支持而闻名,使其成为构建 Web 应用的理想基础。尽管如此,像为 Flask 提供动力的 Werkzeug 一样,Starlette 最初也只打算作为一个基础。本章涵盖了FastAPI,这是一个建立在 Starlette 之上的框架,代表了专门用于开发RESTful 应用程序编程接口(REST APIs)的完整 Web 框架。与 Flask 使用工作模型不同,FastAPI 使用异步模型;与 Flask 旨在创建传统的往返、模板驱动的内容生成不同,FastAPI 通常使用单页应用(SPAs)来处理以JavaScript 对象表示法(JSON)形式提供的数据。
我意识到我们刚刚介绍了很多术语,这些术语可能对那些不是通常的 Web 开发者的 Python 开发者来说是新的。在我们介绍使用 PyCharm 构建简单的 FastAPI 项目时,我们将根据上下文解释我们的术语。
到本章结束时,你将能够做到以下几件事情:
-
解释传统模板驱动的内容生成系统(如 Flask)与严格服务数据的系统之间的区别
-
描述 HTTP(和 HTTPS)的无状态特性,以及如何使用表示状态转移(REST)来弥补 HTTP 中的状态缺失
-
使用 PyCharm Professional 内置的模板创建一个 FastAPI 项目
-
使用 PyCharm 的 HTTP REST 客户端对 FastAPI 项目进行测试
-
在 PyCharm 中创建一个独立的但连接(附加)的 React 前端应用程序,这样你就可以在不混淆前端 JavaScript 代码和后端 Python 代码的情况下开发全栈应用程序
-
在 PyCharm 的调试器中管理多个运行配置并调试整个请求-响应流程
请记住,本章的目的不是全面介绍 FastAPI 或 React。本书的核心目的是在创建应用程序的背景下教授 PyCharm。我们对 FastAPI 的介绍可能比较隐晦,而我们对 PyCharm 作为创建 FastAPI 应用程序的工具的介绍将非常全面。
技术要求
为了继续阅读本章,以及本书的其余部分,你需要以下内容:
-
安装并正常工作的 Python 解释器。我将使用来自
python.org
的最新版本。 -
安装了
pip
和virtualenv
。当你将 Python 安装在 Windows 上时,你会自动获得这些,macOS 在每台系统上都包含它们。如果你使用 Linux,你需要单独安装包管理器,如pip
,以及虚拟环境工具,如virtualenv
。我们的示例将使用pip
和virtualenv
。 -
安装并正常工作的 PyCharm 副本。安装已在第二章,安装和配置中介绍,以防你在书的中途开始阅读。
-
本书提供的示例源代码来自 GitHub。我们在第二章,安装和配置中介绍了代码克隆。你可以在
github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-09
找到本章的相关代码。
在一个无状态的恶劣世界中没有 REST
我听说有人说过,计算机是地球上最愚蠢的生物。它们只会按照指示行事,并以极其字面的方式接受你的指令。这就是编程计算机如此困难的原因。你必须选择完全正确的语法,并精确简洁地组织你的想法,因为任何含糊不清都会导致错误。
唯一能让我们的生活变得艰难的方式,就是将我们的工作和职业建立在一种像果蝇一样注意力分散的系统上。我当然是在谈论网络服务器。术语网络服务器可以指两件不同的事情:它可以指硬件或软件。硬件是指运行该软件的任何计算机系统。我看到过人们用火柴盒大小的硬件构建网络服务器,也看到过网络服务器硬件占据了宽敞的机架式系统,这些系统位于特别冷却的数据中心中。实际上,至少对我们来说,硬件是无聊的部分。
网络服务器软件对我们这些编写代码的人来说更有趣。网络服务器软件,例如 Apache、Nginx(发音为“engine-ex”)、LightHTTPD 以及其他一些,都是为了实现 HTTP 协议的通用规范而设计的简单而健壮的实现。这些规范是由 万维网联盟(W3C)在国际上达成一致的。该协议本身非常直接。我们已经在前面的章节中提到了一些关于它的内容,例如以下内容:
-
客户端-服务器模型:HTTP 遵循客户端-服务器模型,其中客户端(通常是网络浏览器)向服务器发送请求,服务器以请求的数据进行响应。
-
GET
、POST
、PUT
或DELETE
),一个 统一资源标识符(URI)用于标识正在访问的资源,以及提供额外信息的头部信息。服务器会以状态码的形式响应请求的成功或失败,并在响应体中包含请求的数据。如果你错过了,可以在 第八章 的 使用 Flask 构建动态网络应用程序 中找到对请求-响应范式的完整讨论(包括图片)。 -
基于文本的协议:HTTP 是一种基于文本的协议,这意味着请求和响应消息都是可读的。消息遵循一种称为 HTTP 消息格式的特定格式,该格式由头部和可选的消息体组成。
-
安全版本:HTTP 可以通过使用 HTTPS(HTTP Secure)来增加加密和安全功能。HTTPS 在客户端和服务器之间传输的数据的机密性和完整性方面添加了一层 传输层安全性(TLS)或 安全套接字层(SSL)加密。
在我们当前的讨论中,我想指出一个我们之前忽略的要点:HTTP 是无状态的。这意味着来自客户端的每个请求都被独立处理,没有任何关于之前请求的知识。服务器不会保留关于客户端之前互动的任何信息。在美国,有一个著名的为拉斯维加斯市做的广告活动,其口号是“拉斯维加斯发生的事情留在拉斯维加斯。”同样,请求-响应机制中发生的事情也留在那个请求-响应的生命周期中。一旦响应被网络浏览器接收并确认,除了某个地方的日志条目表明请求发生过之外,就没有其他东西了。
在协作中,我们这里所拥有的只是一个执行它没有记忆的请求的愚蠢机器。这可能会让我们的工作变得有些令人沮丧。我们希望用户持续地与我们的应用程序互动,但服务器不会不经过某种形式的哄骗就帮助我们做到这一点。
近年来,已经发明了许多机制来帮助我们在应用程序中处理状态。如果你不确定我所说的状态是什么,我喜欢将其描述为你最喜欢的电子游戏中的保存游戏。
想象你正在玩一个冒险游戏。你已经玩了很长时间,你找到了位于第三层的黑暗城堡的入口。你已经回答了守门人的谜题,并获得了斩妖剑。你已经积累了 32,768 枚金币,你的角色处于满血状态。然后你的妈妈叫你去倒垃圾。众所周知,这是“妈妈代码”表示“停止玩电子游戏,做些有 productive 的事情。”她永远不会理解,对吧?自然地,你希望从你离开的地方继续,所以你保存了游戏。你可以关掉电脑,明天再打开,然后你可以加载你的游戏,它将表现得好像你从未停止过玩。这是因为你的保存游戏代表了程序的一个保存状态。这是你在保存游戏时游戏所使用的所有对象、变量和数据的快照。这是程序的状态。这是网络应用程序本质上缺乏的,至少在协议层面上。
状态可以通过几种技术进行管理。服务器解决方案,如 Flask,允许你实现粘性会话,其中 HTTP 请求生成一个令牌。请求详情可以通过令牌在会话之间存储。这不是由你的 web 服务器处理的,而是由你的应用服务器处理的,它利用 web 服务器进行通信部分:请求-响应机制。会话通常是不受欢迎的,因为在大多数情况下,它们无法扩展。如果你有大量的网络流量,通常的做法是在几个服务器之间平衡流量负载。如果你的会话在第一次请求时存储在服务器 A 上,而后续请求转到服务器 B,它将不会包含你的会话。自然地,我们也为此想出了解决方案,但让我们不要陷入细节。
你可以使用 cookies,它们在客户端存储数据。不幸的是,cookies 由于多年来被过度使用而声名狼藉。主要的浏览器暴露了你正在保存的 cookies,而你那些永远警惕的用户可以选择不接受你存储 cookies 的请求。
最好的办法是将应用程序的状态存储在应用程序本身中。这里的想法被称为表示状态转移,简称 REST。在 RESTful 场景中,我们在客户端内存中维护程序的状态。我们使用请求-响应机制传输任何状态的部分,甚至(但通常不是)整个状态。程序基本上发送服务器需要以完成请求的状态部分。服务器执行它应该执行的操作,然后在响应中发送修改后的状态。如果你熟悉软件设计模式,REST 让我想起了命令模式:请求封装了服务器完成请求所需的所有内容。
现在舞台已经搭建好了,让我们记住,单页应用(SPAs)现在负责维护它们的状态,所有的标记 HTML、CSS 和 JavaScript 都在浏览器中加载,并且所有后续请求只包含状态数据,这些数据被发送到服务器,在那里被修改并返回。
数据传输的格式可以是任何形式的文本。最常见的是 JSON。在古老的过去,我们使用 XML,但我们停止使用它,因为 XML 在浏览器中的处理速度极其缓慢。JSON 更快,因为浏览器已经内建地理解 JavaScript,因此不需要解析文本。以防这是你第一次参加这样的活动,让我们比较一下这两种格式。首先,这里有一些 XML:
<person>
<firstName>Bruce</firstName>
<lastName>Van Horn</lastName>
<dateOfBirth trueDate="Heck No">12/19/1987</dateOfBirth>
</person>
XML 是类似于 HTML 的基于标签的标记语言,但你可以使用 XML 架构定义自己的标签。这是最初在浏览器中使用 XMLHttpRequest (XHR)API 调用的格式。这里的 X 实际上代表 XML。XHR 仍然在使用,但几乎没有人(不包括微软 Azure API 团队)还在使用 XML。相反,出于已经提到的性能原因,我给你同样的东西,但以 JSON 格式:
{
"person": {
"firstName": "Bruce",
"lastName": "Van Horn",
"dateOfBirth": {
"date": "12/19/1987",
"trueDate": false
}
}
}
数据的表示方式与数据结构相同。作为一名 Python 开发者,您无疑已经认出这是一个dict
。我们不再有带有内容和属性的标签,而是有存储在花括号内的键值对。这是 JSON 的一个规则,即键和文本值必须用双引号括起来。在这里要小心。JavaScript 和 Python 都认为单引号和双引号可以互换,但 JSON 不行。只有双引号是可接受的!幸运的是,Python 标准库中有一个json
库,可以将您的结构转换为 JSON,然后再转换回来,无需任何麻烦:
import json
# Convert dictionary to JSON
data_dict = {"name": "John", "age": 30, "city": "New York"}
json_data = json.dumps(data_dict)
# Print the JSON data
print("JSON data:", json_data)
这几行首先导入json
库,然后使用json.dumps
方法将其转换为 JSON 对象。只需记住,我们是在转换为字符串,因此使用dumps
(s代表string)。现在让我们转换另一个方向:
# Convert JSON to dictionary
parsed_dict = json.loads(json_data)
# Access the dictionary
print("Name:", parsed_dict["name"])
print("Age:", parsed_dict["age"])
print("City:", parsed_dict["city"])
我们使用json.loads
将 JSON 转换回dict
。只需记住它为“我们正在加载一个 JSON 字符串”,因此使用loads
(s代表string)。
您现在已经理解了我们将要使用 FastAPI 的基本机制。请求将像 Flask 中那样到来,但通常,不是简单的GET
请求,这些请求会被拦截并使用 Jinja2 模板引擎处理,而是请求将包含 JSON 有效负载,我们将对其进行处理。处理的结果将以 JSON 形式返回。请求-响应机制将使用异步函数处理,因此代码将看起来与 Flask 略有不同。
让我们动手实践,以便您能理解我的意思!
在 PyCharm Professional 中创建 FastAPI 项目
到现在为止,我们在 PyCharm Professional 中已经创建了多个项目,这并没有太大不同。我要提醒您,这一系列功能仅在 PyCharm 的专业版中可用。如果您需要使用社区版,您当然可以,但您将不得不自己设置项目,因为您将无法访问我们即将使用的工具。
在 PyCharm 中通过点击文件 | 新建项目来创建一个新的项目。然后,在模板列表中找到FastAPI。您可以在图 9.1中看到我的示例:
图 9.1:PyCharm 项目菜单包含一个 FastAPI 项目模板
与 Flask 一样,FastAPI 项目模板为我们生成了一些启动代码和运行配置,如图 9.2 所示:
图 9.2:模板生成我们的 FastAPI 启动代码和运行配置
在图 9.2中有许多内容可以讨论,所以我为它编号以供参考。
PyCharm 为我们创建了一个虚拟环境,以及两个文件,如 main.py
和 test_main.http
所见。我们稍后会回到这些文件。PyCharm 为我们生成了一个运行配置。你可以通过它现在是当前选中的运行配置,在 (2) 处,如果 PyCharm 没有设置运行配置,那个菜单通常会显示 当前文件 来判断。
在我们的 FastAPI 项目中,位于 Hello World
端点。这些入门代码模板的强大之处在于它们提供的心理提示。如果你使用过任何用于处理网页路由和端点的系统,无论语言或框架如何,你都可以阅读这段代码并了解正在发生什么。
第 1 行和第 3 行展示了导入的 FastAPI 实例的典型构造函数。第 6 行展示了 FastAPI 如何装饰端点。@app
来自第 3 行的实例化,我们在这里调用 HTTP GET
方法,因此使用该方法发送的请求将被接收和处理。如果客户端使用其他 HTTP 动词,如 PUT
或 POST
发送请求,他们将收到错误,因为目前没有代码来处理该 HTTP 方法。
在第 7 行装饰器下方定位到 async
函数,我们可以看到我们返回了一个看起来像 dict
的东西,它也看起来像 JSON。
定位到 GET
端点,定义为 /hello/{name}
。花括号中偏移的 name
变量指的是 URL 中可以变化的部分,称为路径参数或路径变量。你可以在第 12 行的 async
函数定义中看到它被重复。第 11 行分支(name
)的内容应该与第 12 行函数的参数名称匹配。
定位到 f
字符串表达式,将名称填充到数据中。除了正常的 Python f
字符串之外,没有特殊的模板机制。
运行 FastAPI 项目
自然地,我们可以通过点击 图 9**.2 中靠近 (2) 位置的绿色箭头运行按钮来运行 FastAPI 项目。如果你跳过了关于设置和使用运行和调试配置的章节,你可能想看看 第六章,无缝测试、调试和性能分析,以了解 PyCharm 中此功能的工作方式。你可以在 图 9**.3 中看到我的程序正在运行:
图 9.3:我点击了绿色的运行按钮,这在 PyCharm 的运行窗口中为我的项目创建了一个标签
这看起来与 Flask 中的运行类似,但没有关于开发服务器的警告。这是因为 FastAPI 在名为uvicorn
的应用中运行,它是 Green Unicorn (gunicorn
)的一个变体。Uvicorn 是生产就绪的,所以没有警告。您可以使用与您将应用部署给客户时相同的同一应用程序服务器软件进行开发。uvicorn
和gunicorn
之间的区别在于,uvicorn
处理异步编程模型,而gunicorn
使用前面在本章中描述的传统工作者。
在这一点上,我们面临一些困惑。当我们用 Flask 做这件事时,应用为我们生成了一些可以查看的 HTML。而这个应用没有。如果您愿意,可以在浏览器中查看端点,就像我在图 9.4中做的那样:
图 9.4:访问根路由并不令人兴奋,但它有效
我还可以访问包含路径参数作为 URL 一部分的其他 URL,如图图 9.5所示:
图 9.5:提供路径参数会改变响应中的数据
这次,我在 URL 的末尾添加了路径参数,并且正如我们在前面的代码中看到的,我们正在返回生成的 JSON 数据。
使用浏览器存在问题。浏览器只允许您提交 HTTP GET
请求。在 REST API 项目中通常使用相当多的 HTTP 方法。事实上,最常用的四种方法,有时被称为动词,映射到数据库应用程序中使用的典型 CRUD 操作。GET
和POST
。
除了您请求的标准之外,您还可以通过最佳实践使用适当的 HTTP 状态码来标准化响应。这些由 W3C 提供的 HTTP 规范中进行了记录。阅读规范是治疗失眠的灵丹妙药,所以我将向您推荐优秀的Mozilla 开发者网络(MDN)页面中的状态码。这是您在浏览器中需要书签的方便地址:developer.mozilla.org/en-US/docs/Web/HTTP/Status
。
HTTP 动词 | 方法 | CRUD 操作 | 成功响应的 HTTP 状态码 | 说明 |
---|---|---|---|---|
GET |
READ |
200 (OK) |
用于从服务器或数据库检索或获取信息。例如,获取应用程序中的用户列表。 | |
POST |
CREATE |
201 (Created) |
用于在数据库中创建新记录。在服务器上创建任何内容时都使用此方法。例如,新用户注册。 | |
PUT |
UPDATE |
204 (No Content) 或200 (OK) |
当您向已存在的东西发送更新时使用此方法,例如数据库记录。例如,更新密码。 | |
DELETE |
DELETE |
204 (无内容) 或 200 (OK) |
当你想删除某些内容时使用此操作。 |
图 9.6:HTTP 方法与 CRUD 操作一一对应,并且有标准响应代码来表示成功的响应
由于你的浏览器仅限于 HTTP GET
请求,并且我们有 100%的可能会在我们的项目中使用至少其他一种方法,因此我们需要比浏览器更好的工具来测试我们的 API。我们有几种选择:
-
有一些浏览器插件允许你发送不同类型的请求。你可以在你喜欢的浏览器市场里找到它们。
-
命令行工具,如 cURL,允许你使用任何 HTTP 方法来构建 HTTP 请求。
-
专门的 API 测试工具,如 Insomnia (
www.insomnia.rest
) 或 Postman (getpostman.com
),为你提供了一个用于处理 API 请求的图形化工具。这些工具可能非常庞大,因为它们旨在做更多的事情,而不仅仅是允许你发送各种请求类型。话虽如此,我之所以使用这两个工具,是因为它们的普及率很高。信不信由你,我的团队中有些开发者不使用 PyCharm。 -
PyCharm 内置的 HTTP
Requests
功能。
显然,我们将专注于第四个选项。
使用 PyCharm 的 HTTP 请求进行工作
当我们生成项目时,PyCharm 创建了两个文件。它创建了main.py
,这是我们之前已经检查过的。它还创建了一个名为test_main.http
的文件。这个文件是 PyCharm 特有的。让我们检查图 9.7中显示的文件:
图 9.7:PyCharm 作为 FastAPI 项目一部分生成的 HTTP 测试文件
你可以直接在main.py
旁边找到该文件test-main.http
,这让我们确切地知道正在测试什么。main.py
文件将包含端点,而test_main.http
将包含所有端点的测试。
这个http
测试文件不是代码,正如我们在第六章中看到的,无缝测试、调试和性能分析。这是一个 HTTP 请求的规范。定位main.py
,这是一个很好的起点。这些测试非常简单,就像单元测试一样,可以使用绿色箭头单独运行。如果你想运行文件中的所有测试,可以使用工具栏(2),其中有一个带有两个绿色箭头的按钮。还有一个环境选择器,我们很快就会回到它。(5)位置显示了一个链接,提供了可以粘贴到测试文件中的一组示例。我们也会回到那里。首先,让我们练习我们的测试并看看它做了什么。我会点击图 9.8中顶部箭头所示的双箭头运行按钮:
图 9.8:点击双箭头以运行所有测试,你会发现它们都失败了!哦,不!
这并不好。两个测试都彻底失败了。我说“彻底失败”是因为 PyCharm 没有留下任何歧义。我看到一个红色的横幅告诉我测试失败了。我看到红色的文字,通过下方的箭头,告诉我两个测试失败了。我看到测试列表旁边的红色 X。我看到一条消息说“停止。测试失败 2 个中的 2 个”。如果还不够,还有一个日志区域也会告诉你你的代码没有达到预期。简而言之,这个屏幕上的红色标记比编辑器第一次审阅后这一章的手稿上的还要多!相信我,那真的很多!究竟出了什么问题?为什么 PyCharm 会在一个“Hello World”示例中生成那样的失败测试代码?
代码没有问题!在第六章,“无缝测试、调试和性能分析”中,我们学习了单元测试。单元测试由使用断言测试你正在测试的代码的代码组成。HTTP 文件不包含代码,这不是单元测试,这是集成测试。这些测试需要运行中的服务器才能工作。让我们再试一次。查看图 9.9并跟随操作,如果你愿意的话:
图 9.9:在运行 HTTP 测试之前,你必须运行开发服务器
点击 API 项目的运行按钮(1)。然后点击测试的运行按钮(2)。服务器运行时,所有测试都将通过(3)。如果它们没有通过,请联系 JetBrains 并要求退款。这可能不会奏效,但这没关系,因为你的测试应该会通过。
检查返回的详细信息
知道我们通过了测试是不够的,因为这实际上只意味着两个请求都是针对本地开发服务器进行的,并且都返回了状态码200
。你真正想要的,大多数情况下,是能够看到响应中返回的 JSON 数据。让我们去找找看。找到测试的输出,向下滚动直到你看到类似图 9.10中所示的 JSON 文件提及:
图 9.10:在日志中滚动,找到指向 JSON 文件的链接。点击它们在编辑器的选项卡区域打开数据
日志窗口中的这一部分告诉我们测试中请求和响应的所有详细信息。我们可以看到以下内容:
-
正在使用的 HTTP 版本(1.1)
-
响应的状态码(
200 OK
) -
请求的日期
-
生成结果的服务器(
uvicorn
) -
响应的内容类型(
application/json
) -
测试结果保存的位置
Yahtzee! PyCharm 将响应结果保存在一个带日期戳的 JSON 文件中,其中包含状态码,并在日志中以超链接的形式展示。如果您点击链接,JSON 文件将像任何其他文本文件一样在其自己的标签页中打开。在 图 9**.10 中,我不得不将日志扩展到足够大,以便拍摄到良好的截图,但这掩盖了打开标签页的内容,因此请查看 图 9**.11 以查看点击日志中的超链接后其标签页中的返回数据:
图 9.11:点击超链接后,返回数据出现在其自己的标签页中
如果您注意到,它说文件已保存,但它没有出现在项目文件中。如果您想知道它在哪里保存,您可以按照 图 9**.12 中的指示右键单击标签页:
图 9.12:右键单击标签页,使用“打开方式”菜单打开测试结果 JSON 文件的存储位置
菜单的内容将根据您的操作系统而有所不同。您正在寻找的是顶部选项,对我来说是 文件,这是 Gnome 42 的默认文件管理器。Windows 将有对资源管理器的引用,而 macOS 应该有对 Finder 的引用。点击该选项将在您特定的操作系统文件管理器中显示位置。您可以在 图 9**.13 中看到我的:
图 9.13:我的文件管理器显示了 HTTP 响应文件的存储位置,该位置在项目的 .idea 文件夹中
如您所见,测试结果存储在 PyCharm 项目文件夹中:当您在 PyCharm 中创建新项目或打开文件夹时,IDE 创建的隐藏的 .idea
文件夹。请记住,任何以点开头的文件夹在 Mac 或 Linux 上都是隐藏的,但在 Windows 上您可以看到它们,因为比尔喜欢与众不同。如果您经常测试,这些文件可能会开始以数量堆积。我个人更喜欢将它们排除在我的存储库之外。
我们刚刚生成了一个新的运行配置
从测试窗口运行所有测试的一个整洁副作用是 PyCharm 会创建一个新的运行配置,您可以在 图 9**.14 中所示的运行配置下拉菜单中找到:
图 9.14:在第一次运行 HTTP 测试后,PyCharm 自动创建了一个新的运行配置
如果我们编辑配置,您可以看到更多详细信息,如图 图 9**.15 所示:
图 9.15:HTTP 请求测试运行的选项可以在运行配置编辑器中找到
如你所见,你可以选择运行单个请求或文件中的所有请求。如果你专注于一个端点的代码并需要反复运行它,运行单个请求就很有用。如果你在测试中有许多端点,你的测试可能需要一段时间才能完成。
使用运行配置中的“启动前操作”
我们已经看到,我们每次运行 HTTP 测试都会生成一个新的包含响应的 JSON 文件。这可能会很快让文件夹里充满数百个文件。由于 PyCharm 为我们提供了运行配置,我们有设置一些自动化来删除旧文件的机会。在本节中,我们将探讨 PyCharm 运行配置设置中可用的自动化功能。
要做到这一点,你需要一个脚本来删除你想要的 JSON 文件。也许你想要删除一周之前的所有文件,或者你可能只想保留最后 25 次运行。这部分取决于你。
为了自动化这个过程,你需要为你的操作系统创建一个 shell 脚本。如果你不是 shell 脚本高手,那没关系,因为现代人工智能可以来帮助你。图 9.16显示了我是如何请求 ChatGPT 为 Bash shell 创建一个只保留我们测试运行生成的最后 25 个 JSON 文件的 shell 脚本的:
图 9.16:我询问 ChatGPT 关于删除旧文件的一些不同方法,并为我的需求生成了一个脚本
我意识到你无法在图中看到 Bash 脚本的全部内容,所以我将确保将其包含在本章的项目源代码中。
警告 - 永远不要运行你不理解的生成脚本!
你绝不应该在没有完全理解其工作原理的情况下盲目运行由 ChatGPT 或其他任何东西(或任何人)生成的脚本!ChatGPT 可能不会给你和我相同的结果,所以在运行它给出的任何脚本时要格外小心。如果脚本中包含类似-Force
这样的开关,就像图中的 PowerShell 脚本那样,更要特别小心。如果你不知道脚本做什么,不要在你的电脑上运行它!
要使用生成的代码,你只需像添加其他任何文件一样将新文件添加到你的项目中。我将在“项目”窗口中右键单击我的项目,创建一个名为delete-old-http-test-results.sh
的新文件。当然,如果你使用的是 Windows,你将想要使用 PowerShell,它通常有一个.ps1
的扩展名,所以它将是delete-old-http-test-results.ps1
。一旦你完全理解运行该脚本的影响,就复制 AI 生成的脚本并保存文件。
确保脚本具有运行权限
确保你创建的脚本文件有权限在你的计算机上运行。大多数版本的 Windows 严格限制运行任何 PowerShell 脚本,而你执行此操作的能力甚至可能受到你雇主的安全策略的限制。
在 Mac 和 Linux 上,你可能需要在 PyCharm 执行脚本之前在终端中运行此命令:
chmod +x delete-old-http-test-results.sh
如果你无法手动运行脚本,那么它可能也无法在 PyCharm 中运行。
接下来,我们需要创建一个执行脚本的运行配置。点击运行配置下拉菜单并点击 编辑配置。如果你不记得如何操作,请回顾 第三章,自定义解释器和虚拟环境。使用 Shell 脚本模板添加一个新的运行配置。你可以在 图 9**.17 中看到我的配置:
图 9.17:为刚刚创建的 shell 脚本创建一个运行配置
你可以通过项目 .idea
文件夹中的 httpRequests
文件夹中的文件夹按钮来导航到 shell 脚本。点击 工作目录 文本框中的文件夹图标来浏览此文件夹。如果你不在 Windows 上,这将是一个隐藏文件夹,所以请确保在选择对话框中像我一样打开查看隐藏文件夹,如图 图 9**.18 所示:
图 9.18:为了找到 .idea 文件夹中的 httpRequests 文件夹,你需要通过点击顶部工具栏上的眼睛图标(ouch)来打开隐藏文件夹
现在,你已经有一个执行你的 delete
脚本的运行配置了。你应该测试它。运行你的测试 26 次,并验证你 httpRequests
文件夹中只有最后 25 个结果。我将在接下来的 25 张全页彩色屏幕截图中展示我的结果。开玩笑的。有时候吓唬我的编辑也很有趣。
如果一切顺利,并且你感到满意,那么你还可以做一件事来让它更加出色。你可以将你的删除脚本运行配置与你的测试运行配置链式连接。编辑测试的运行配置并添加一个“在构建之前”条件。点击 + 按钮添加一个新条件。点击 运行另一个运行配置。点击 删除旧 HTTP 结果运行配置。你应该会看到类似于 图 9**.19 的内容,它显示删除脚本将在每次新的测试运行之前运行:
图 9.19:删除运行配置现在将在每次测试启动之前运行
为了本书的目的,我将我的保留数量更改为最后五个 JSON 文件并尝试了一下。每次运行后,我可以在我的文件浏览器中查看,如图 图 9**.20 所示,看看它是否在运行:
图 9.20:运行后,由于删除脚本在运行前将文件数量减少到五个,并且测试为两个端点生成结果,因此有七个 JSON 文件
你文件夹中最多只有 25 个结果,或者你脚本中指定的那么多!不过,别忘了,删除脚本首先运行,PyCharm 将为每个测试端点生成一个新 JSON 文件。如果我将我的删除脚本设置为保留五个 JSON 文件,并且运行生成的测试脚本,那么运行后我将有七个文件,因为有两个端点正在被测试。
使用这种技术,你可以为你的代码启用许多自动化场景。Python 通常没有像许多其他语言那样的构建脚本,所以知道你可以在 IDE 本身中拥有这种自动化水平是件好事。除了运行另一个运行配置之外,还有几个选项。我建议你探索所有可能性!
与 HTTP 请求环境一起工作
大多数 Web 项目最初是在开发者的电脑上开始的。当你测试时,你会在你的笔记本电脑或 PC 上本地运行你的应用,而你所有的测试请求通常都会发送到 localhost,这是分配给每个带有网络卡的电脑的回环地址。项目从这里开始,但假设应用达到任何程度的成功,它将不会停留在那里。
最佳实践规定,打算发布的应用应该使用某种持续集成(CI)环境,以便运行自动化测试。实际上,JetBrains 有一个名为 Team City 的 CI 产品。我使用 Team City 多年,可以证明它是一个易于设置且免费的优秀 CI 系统,适用于小型团队。如今,甚至还有一个云版本,所以如果你不想设置自己的服务器,你也不必这么做。然而,Team City 拥有我们一直在使用的插件,用于在 FastAPI 项目中为我们的 HTTP 端点创建测试。这允许你在有人向你的源代码库提交代码时自动测试你的项目。
一旦你的代码通过了 CI 系统的审查,通常会将代码部署到测试服务器。这应该是一个尽可能接近生产环境的服务器。有些人称之为预发布服务器,有些人称之为用户验收测试(UAT)。无论你叫它什么,它代表了一个环境。生产环境也是一个环境。环境简单来说就是一个配置环境,你可以在其中运行你的代码。为了清楚起见,到目前为止,我们已经提到了四种这样的环境:
-
你的本地电脑(localhost)
-
一个 CI 环境,这些天可能是一个 Docker 容器
-
一个 UAT/预发布环境,你可以在将应用发布到生产环境之前对其进行测试
-
一个生产环境,你的真实客户在这里使用你的应用
这些环境中的每一个可能都有不同的属性。例如,您在笔记本电脑上运行的 FastAPI 应用程序可以通过 IP 地址 127.0.0.1
访问。默认情况下,应用程序在端口 8000
上运行,并且默认情况下,使用的协议是 HTTP 而不是 HTTPS,因为很少有开发者在他们的笔记本电脑上花费时间设置 SSL/TLS 证书。
然而,当您在预发布环境中测试您的应用程序时,所有这些参数都不同。您可能有一个 SSL 证书,所以您将使用 HTTPS 作为协议。您肯定有一个不同的 IP 地址。您甚至可能有一个域名服务器解析一个漂亮的测试域名供您的应用程序使用。您可能不会使用端口 8000
,因为这不太像生产环境。相反,您将使用端口 443
或 80
,在这种情况下,您可能根本不需要填写端口。
生产环境也将有不同的属性。
在我们的 API 测试中,我们能够配置一组变量用于我们的测试脚本,这些变量被分配给一个环境名称。请确保您有 test_main.http
文件,或任何 .http
文件。点击 图 9**.21 中显示的环境下拉菜单:
图 9.21:您可以为运行测试选择一个具有各自配置变量的环境
我会坦白。我提前工作并设置了一个,但我们假装我没有。现在,您唯一的选择是 http-client.env.json
。您可以在 图 9**.22 中看到我的:
图 9.22:PyCharm 创建了 http-client.env.json,这将允许我为测试设置不同的环境
如果您选择了 http-client-private.env.json
。这两个文件的目的和区别在我写这篇文档时并未在文档中说明,所以我们将发挥想象力。我个人将我想与我的开发团队共享的环境放在公共文件中。如果我想创建某种私有环境,比如虚拟机、Windows Subsystem for Linux、Docker 中的自定义实验,或者可能在实验室中的 Kubernetes 集群,我可以用私有文件,我可能会将其放在我的 .gitignore
文件中。由于文件内容相同,我将专注于公共文件。
我将添加一个定义用于我的本地计算机。将 http-client.env.json
的内容更改为以下内容:
{
"dev": {
"protocol": "http://",
"base_url": "localhost",
"port": 8000,
"api_version": "v1"
}
现在我们已经设置了一个带有一些环境变量的开发环境。让我们在 test_main.http
文件中使用它们。您的第一个端点定义看起来像这样:
要使用环境变量,请将您的 URL 中的部分替换为 mustache 格式的文本。Mustache 格式意味着将您想要解析的变量放在双大括号中,例如 {{ this }}:
GET {{protocol}}{{base_url}}:{{port}}
结合起来,URL 将解析为原始地址,即 localhost:8000
,或者如果您更喜欢,127.0.0.1:8000
。现在您可以用同样的方式创建其他环境。您可以在不更改测试的情况下切换环境并运行测试,环境变量会为您解析 URL。
如果您查看本章示例代码中的我的代码,您会发现我以这种方式对整个文件进行了修改。
让我们变得更加 CRUD(创建、读取、更新、删除),然后再进行测试!
目前,我们应用中唯一的东西是两个预生成的端点。让我们再添加一些,使这个应用更有趣。我们将创建一个假名单应用,就像我们在第八章中做的那样,使用 Flask 构建动态 Web 应用。
打开 main.py
并为每个 CRUD 操作添加一个端点,以及构建 RESTful API 时使用的四个主要方法。我们将进行一些重大更改,所以我会从文件顶部开始简单介绍它们。
在第一行,我们将导入的地方改为这个:
from fastapi import FastAPI, status, HTTPException
app = FastAPI()
应用实例化没有变化。不过,下面添加这个令人惊叹的人名单:
names = ["Bruce", "Karina", "Kitty", "Phoebe"]
这个列表将作为假数据库使用,节省我们设置服务器和等等的时间,这会严重膨胀书籍,并且不会对我们的即将成为冠军级的 PyCharm 技能做出贡献。我们将保留前两个生成的端点不变,所以请在 say_hello
函数的返回下面开始编辑。我们的第一个 CRUD 端点将提供一个类似从数据库查询得到的名单:
@app.get("/api/v1/names")
async def get_names():
return [{"id": idx, "name": name} for idx, name in enumerate(names)]
在您的项目中启动预期返回 JSON 数据的端点,而不是标记,使用前缀 API
后跟版本标识符,这是一种最佳实践。当我说您想这样做时,请相信我。随着您的 API 代码成熟,您可能希望提供更新的端点,这些端点可能不一定与旧客户端向后兼容,而这种技术允许您在为较新客户端引入改进功能的同时,保持 API 与旧客户端向后兼容且不会中断。随着代码进一步成熟,您可以在您选择的时间逐步删除 v1
端点:
@app.post("/api/v1/names", status_code=status.HTTP_201_CREATED)
async def create_name(name: dict):
new_name = name["name"]
names.append(new_name)
return {"message": "Name added successfully"}
在先前的 POST
端点中,对于成功的调用返回 HTTP 状态码 201
,表示 API 创建了新数据,这是一种最佳实践。FastAPI 的工作方式与 Flask 非常不同。它没有离散的请求和响应对象,一切都是隐式的。如果您期望将 JSON 作为有效载荷发布到您的 API,您只需指定一个 dict
类型的参数即可。在这种情况下,我期望以以下格式发布数据:
{"name": "Igor"}
一个真正的应用程序会有更丰富的结构。我们保持简单。当这个 JSON 作为POST
的负载进来时,我们提取名字并将其添加到我们的列表中。为了简化,我在这端点没有进行任何验证。你应该始终验证你的输入数据,以保护自己免受有毒数据和注入攻击。这是另一本书的主题。话虽如此,我不会完全偷懒。我会在PUT
端点做一点,这样你可以看到一些这样的样子。记住,PUT
调用是一个UPDATE
操作。我们正在获取一个id
值和一个新的name
值,并将相应地更改现有值。在你的create_name
函数返回下面添加这些行:
@app.put("/api/v1/names")
async def update_name(update_data: dict):
id = update_data["id"]
现在回到代码。让我们确保提交的 ID 属性是一个数字,具体来说是一个整数:
if not isinstance(id, int):
raise HTTPException(status_code=400, detail="ID must be an integer")
这里发生了什么?如果你提交了一个无法解析为数字的 ID 属性,我会给你返回一个 HTTP 错误状态码400
,这意味着你的请求格式不正确。为了表示友好,我会添加一条消息告诉你你做错了什么。例如,假设你这样使用PUT
:
{ "id": "2", "name": "Igor" }
一切都应该正常工作。但是,如果你使用以下PUT
:
{ "id": "this isn't a number", "name": "Igor" }
你的请求将会失败。提升 HTTP 扩展会导致响应立即发送,并且这段代码的其余部分不会执行。所以,让我们继续。如果你提交了良好的数据,我们可以继续并获取新的名字:
name = update_data["name"]
接下来,我们检测你提交的数字 ID 是否超出了范围。我们的列表最初包含四个名字。如果你尝试向id
600 发送更新,除非你通过POST
端点至少添加了同样多的名字到列表中,否则这是不应该工作的。在PUT
中,如果你提供了一个无效的id
值,通常的做法是返回一个404
错误,表示你无法在数据库(或在我们的情况下是列表)中找到该 ID:
if id >= len(names):
raise HTTPException(status_code=404, detail="Name not found")
names[id] = name
return {"message": "Name updated successfully"}
已经完成了三个,只剩下一个。对于DELETE
端点,我只需要id
。为这样简单的事情发送 JSON 负载是愚蠢的,所以我将只从端点 URL 中获取 ID。我仍然需要检查它是否在names
列表的长度范围内:
@app.delete("/api/v1/names/{id}")
async def delete_name(id: int):
if id >= len(names):
raise HTTPException(status_code=404, detail="Name not found")
如果是这样,我就会直接从列表中移除它,并返回一条友好的消息:
deleted_name = names.pop(id)
return {"message": f"Deleted name: {deleted_name}"}
运行应用程序并确保它启动。进行必要的调整。记住,最终的代码在本书的示例代码中,位于我们从仓库中检出第二章的示例代码中。
变得更加挑剔
如果我心情不好,可能会很烦躁,对吧?现在我们有更多样化的端点,让我们学习如何使用 HTTP 客户端测试它们。事实上,这个工具非常丰富,就像 Insomnia 或 Postman 这样的专用工具一样丰富,只是没有那么多 UI 界面阻碍。
打开你的test_main.http
文件,让我们添加一些测试。不仅仅是简单的测试,让我们做一些真正的测试,感受一下 PyCharm 工作流程如何帮助你构建 RESTful API。
PyCharm 的这些测试功能与任何框架都兼容
我应该提到,虽然我们正在使用 FastAPI,但这些功能并不是独特的或与该框架相关联。如果你在 Flask 或其他框架中开发 API,这些工具也会以相同的方式工作。
到目前为止,你只有两个测试,这些测试是在你创建项目时生成的:
# Test your FastAPI endpoints
GET {{protocol}}{{base_url}}:{{port}}
Accept: application/json
###
GET {{protocol}}{{base_url}}:{{port}}/hello/User
Accept: application/json
我没有明确要求你使用我们之前创建的环境变量修改第二个测试端点,但我还是做了,因为这看起来像是一个明显的改进。随着你向测试文件添加代码,有几个值得注意的事项:
-
请求可以存储在具有
.http
或.rest
扩展名的文件中。到目前为止,我们使用的是与项目一起生成的.http
文件。在野外,你可能看到任一扩展名,它们之间除了在 IDE 中显示的图标外,没有根本的区别。这完全是装饰性的。 -
代码高亮和语法补全都是激活的。这些文件的语言是专业的,但你可以期待在这里获得与其他任何地方相同级别的工具。
-
此文件中的代码也将显示内联文档。
-
请求必须通过三个井号/磅符号(
###
)进行分隔。如果你没有添加这些符号,IDE 会通过一片红色的波浪线来提醒你。 -
你可以将命令行 cURL 命令粘贴到这个文件中,它将自动将其转换为 PyCharm 使用的语法。
-
支持模板。我们已经看到了环境变量替换在起作用。请保持关注,它即将变得非常精彩!
测试编辑器有一点点用户界面,但说实话,一旦你学会了语法,你可能就不会使用它。让我给你展示一下。参见图 9.23:
图 9.23:测试编辑器具有最小化的用户界面
让我们看看数字:
(1)处的+图标允许你添加另一个测试。说实话,直接在编辑器中这样做更容易,但如果你喜欢点击东西,这里就是你的选择。
在.idea
项目文件夹内的httpRequests
文件夹中有一个时钟图标。由于 PyCharm 在文件更改时会自动重新加载文件,所以保持此文件打开状态可以让你看到所有发生的事情,而无需查看单个响应文件。之前,我们添加了自动化功能来删除许多这些响应文件,因为通常会有很多。这不会影响日志文件,因为请求信息是从日志窗口而不是响应文件本身附加的。实际上,日志也显示了响应数据。
高级技巧 - 在拆分视图中打开日志,这样你就可以始终看到它
如果你右键单击包含日志文件的选项卡并选择拆分选项之一,例如拆分右侧,则选项卡将在单独的选项卡组中打开。如果你有一个宽大的 4K 显示器,你有很多空间同时打开日志、代码和测试文件,以便你可以一起查看它们。
(3)处的文档图标是为了让命令行爱好者感到高兴。如果您选择一个请求并点击此按钮,您就可以生成一个 cURL 命令,然后可以将其粘贴到终端窗口中。(4)处的导入图标为您提供了一个 UI,您可以将 cURL 命令粘贴到其中,然后它将被转换为 PyCharm 使用的请求格式。这并不是特别有用,因为您可以直接将 cURL 命令粘贴到编辑器中,转换会自动发生。
我们已经看到了(5)处的运行所有测试按钮,以及环境选择下拉菜单。除了历史日志之外,此工具栏上最有用的项目可能是(6)处的示例链接。这并不花哨,但很有用。点击此链接将打开一个包含大量示例的文件,您可以复制、粘贴并修改这些示例。本质上,这与您点击位置(1)处的 UI 按钮时发生的事情相同。对我来说,打开示例更快、更简单,因为您可以在一个地方看到所有内容。要使用它,只需点击如图图 9.24所示的示例链接:
图 9.24:测试示例按类别划分
我喜欢使用旁边的POST
请求示例,如图图 9.25所示:
图 9.25:您可以将示例打开到旁边以便轻松粘贴
要像这样拆分文件,只需右键单击选项卡并点击拆分右侧,或根据您的偏好选择左、上或下。
创建测试
让我们创建这些测试!请确保您正在test_main.http
中工作。在您的最后一个测试下方添加以下行:
###
GET {{protocol}}{{base_url}}:{{port}}/api/{{api_version}}/names
三个井号(#)是测试之间的分隔符。其余的都是来自我们之前创建的环境变量的变量。这将转换为 http://localhost:8000/api/v1/names。请确保您的应用程序正在运行,并运行测试。您应该会看到像图 9.26中那样的结果:
图 9.26:当您运行测试时,您可以在日志视图中看到响应数据。我的结果左侧通常会与您的不同
这很棒,对吧?您可以看到您得到了一个良好的状态码(200
),并且您可以看到您的数据。如果您能将此转换为有效的测试怎么办?您可以!回到test_main.http
文件。美国(也许是世界)历史上的每一个伟大时刻都 precedes by the phrase I will utter next: Hold my beer and watch this。
在您的测试下方,添加以下代码:
> {%
client.test("Request executed and 200 returned", function() {
client.assert(response.status === 200, `Response status is ${response.status} but should be 200`);
});
第一个字符至关重要。你必须输入一个>
后面跟着{%
。如果不这样做,PyCharm 会变得非常不高兴!只需暂时凝视一下它的庄严。全部吸收进去。这里有一个测试。用 JavaScript 编写的!很抱歉,我应该提醒你坐下或类似的事情。然而,我确实提到了啤酒。这里有一些魔法。在这个测试窗口中有一个固有的client
对象,这与浏览器窗口中始终存在的window
对象非常相似。我们在那个客户端对象上调用.test
方法,并传递两个参数。第一个是一个描述测试的字符串。这可以是任何你想要的东西。内容不会以任何方式影响测试。第二个参数是一个匿名函数,它实际上执行了测试。如果你不熟悉 JavaScript 中 lambda 风格函数的使用,你只能照着例子复制这些测试。我想你也可以去学习 JavaScript,但这可能需要一段时间。幸运的是,client.assert
部分看起来就像我们在第六章,“无缝测试、调试和性能分析”中编写的测试,所以让我们继续。断言接受一个将评估为真或假的表达式。JavaScript 使用三个等号来测试不进行强制转换的相等性。如果你还没有听说过这个,现在,只需知道你应该始终使用三个等号,因为使用两个等号意味着其他事情,你不想那样。Python 中常见的双等号可能会在 JavaScript 测试代码中产生错误的失败,因为 JavaScript 会尝试强制任何遇到的数据类型匹配。三个等号避免了这种情况。
在条件语句之后,我们有一个 JavaScript 模板字符串。这相当于 Python 的f
字符串。你使用引号标记。引号标记是美式键盘上按下的波浪号(~),它看起来像重单引号:`. 将字符串包围在引号标记内指定该字符串为模板字符串。你可以使用`${whever}`在花括号中替换表达式或变量值。所以,在 Python 中,给定`foo = bar variable`,一个`f`字符串将是这样的:
f"I'd rather be at
the {foo}."
在 JavaScript 中,给定const foo = "bar"
,它将是这样的:
`我宁愿在
${foo}`
这里,我使用了模板字符串来提供更多一点的信息,因为第二个字符串实际上暴露了测试失败时看到的消息。
这个特定的测试确保请求的 HTTP 响应代码是200
,这表示它是成功的。如果你不包含这样的测试,那么即使你的端点代码熔化了服务器,从宇宙中心的深处召唤出伟大的克苏鲁,你的结果也总是绿色的。尽管预期的代码可能会根据你所做的事情而改变,但这种测试应该每次都包含在内。
你应该始终添加的更多测试
检查你的状态码是最基本的要求。我断言(看看我做了什么?)你应该检查尽可能多的事情。在这里,我将添加一个测试来验证返回的数据具有 application/json
的 MIME 类型,这对于客户端如何消费这些数据非常重要。在下面的早期测试下方添加此内容:
client.test("Response should be json", function() {
const type = response.contentType.mimeType;
client.assert(type === "application/json", `Expected application/json
but got ${type}`);
});
%}
记住,这是 JavaScript!所以在这里,我添加了一个名为 type
的常量,并从测试窗口中可用的另一个神奇对象中获取值。response.contentType.mimeType
会给你这个值。我们测试以确保它是 "application/json"
。我个人认为关注细节很重要,如果内容类型没有正确设置,我认为专业人士创建的 API 非常不专业。碰巧的是,FastAPI 为我们做了这件事,但并非每个框架都会这样做。
现在您已经掌握了基础知识,我邀请您探索本章示例代码中的完成后的 test_main.http
文件。那里有针对我们放入 PUT
端点的所有条件的测试,以确保如果您的用户发送非数字 ID,则返回 400
状态。还有一个测试来确保如果您的 ID 超出了 names
列表的可接受范围,您会得到 404
错误。
通过附加项目来编辑和调试全栈应用程序
PyCharm 有能力将多个项目组合在一起,让您能够在同一个 IDE 实例中处理全栈应用程序。当您在 PyCharm IDE 中运行多个附加项目时,您可以无缝地一起调试它们!仅此一项功能可能就足以让您停止使用 vim 或 VS Code,并且永远不再回头!虽然您可以在其他 IDE 或甚至 VS Code 中这样做,但 PyCharm 使其变得如此简单,您可能不会想再使用其他工具。
在单独的项目中创建一个 React 应用程序
为了为我们的 FastAPI 后端创建前端,我们将利用 React。像往常一样,我无法在这里向您传授很多关于 React 的知识。我会在本章末尾的进一步阅读部分确保有一些参考资料。
在 PyCharm Professional 中创建一个 React 应用程序,只需使用您可能已经习惯的常规文件 | 项目操作即可。这里有一个项目模板,它利用了一个名为 create-react-app(CRA)的开源产品。这是一个被 React 开发者广泛使用的工具,因为实际上,从头开始设置一个完整的 React 应用程序既繁琐又耗时。
为了使这个 create-react-app
脚本正常工作,您需要安装 Node.js。如果您没有安装,PyCharm 将尝试为您安装,正如您在图 9.27 中所看到的。27*:
图 9.27:在创建一个 JavaScript 项目,例如 React 项目时,您需要安装 Node.js,否则 PyCharm 会尝试为您安装
根据我的经验,这并不总是奏效。Visual Studio 中也有同样的功能,我通常建议我的学生手动安装 Node.js,而不是让 IDE 来做。一般来说,IDE 通常不会指向 Node.js 的最新版本。我可以在 图 9.27 中看到 PyCharm 意图安装 16.16.0,这不是最新的。此外,查看正确的版本,我进一步建议您使用 Node.js 的最新 长期支持 (LTS) 版本,而不是版本号最高的版本。LTS 产品保证稳定,而绝对最新的版本则不一定。
我还推荐使用 Node.js,以及 Python,使用包管理器,如 Chocolately (chocolatey.org/
) 或 Homebrew (brew.sh/
)。包管理器在 Linux 上是标准配置,每个发行版都有自己的包管理器,所以如果您使用 Linux,我会假设您知道您需要哪个。我提倡使用包管理器安装 Node.js 的原因是,包管理器使得更新,甚至完全删除您的软件变得非常容易。如果您想尝试 Mac 上的 Homebrew,我网站上有一个简短的、免费的课程,教您如何为 Node.js 开发设置苹果电脑。您可以在 www.maddevskilz.com/courses/setting-up-a-nodejs-development-workstation-in-macos
找到它。在我完成这本书的写作之后,我会看看制作更多这样的课程,所以如果您想查看 Windows 版本,请访问该网站。
手动安装了 Node.js 之后,让我们回到创建我们的 React 项目。我给我的项目命名为 namelist
。React 对名称很挑剔,所以我选择了一个我知道会工作的名字。我会把更花哨的名字留给关于 React 的书籍。点击 创建 按钮。CRA 需要一些时间,但一旦完成,你应该会有一个准备好的项目文件夹。我们现在可以全栈式工作了!接下来,我将向您展示我在 PyCharm 中最喜欢的技巧,即同时处理这两个项目,就像它们是一个项目一样,同时保持 UI 代码与后端完全分离。
将我们之前创建的 FastAPI 项目附加到项目中
打开我们在本章开头创建的 fastAPIProject
文件夹。现在,使用包含 React 项目的 namelist
文件夹。通常,当我们这样做时,我们会告诉 PyCharm 在新窗口或当前窗口中打开项目。这次,我想让您告诉它将项目附加到当前项目上,如图 图 9.28 所示:
图 9.28:打开 React 项目时选择附加选项
您将在 IDE 中同时找到两个项目打开。这在图 9.29的项目窗口中很明显:
图 9.29:一个全栈应用的两侧都可以同时打开,允许你像处理一个项目一样运行和调试它们
在全栈开发中使用附加项目会带来非常愉快的体验。考虑以下好处:
-
技术上,它们是分别存储在各自仓库中的独立项目。
-
PyCharm 允许您为所有附加项目创建运行和调试配置。例如,您可以在 React 应用程序中设置断点来检查提交之前的数据。然后在 FastAPI 项目中,您可以在端点函数中设置断点来检查接收到的数据,这使得在两个项目之间查找数据格式错误变得容易。
-
其他不能全栈工作的人仍然可以单独工作在各自的项目上。
在第十一章“使用 PyCharm 理解数据库管理”,我们将了解到您可以在一个 PyCharm 窗口中与前端、后端和数据库一起工作。对于全栈开发,PyCharm 难以匹敌!但在下一章中,让我们先看看 PyCharm 支持的第三个 Web 开发框架:Pyramid。
摘要
在本章中,我们有效地涵盖了您需要了解的所有内容,以便在 PyCharm 中使用 FastAPI 开始创建 RESTful API 项目。
FastAPI 与 Flask 以及其他许多以模板为导向的 Web 开发框架不同,因为它专门设计用来创建仅 RESTful API。RESTful API 是一种后端,它从任何前端标记、布局、交互性或显示逻辑中解耦。相反,API 专注于仅接收携带用户交互数据的请求,并返回处理后的数据,例如从关系型数据库检索或处理的数据。
由于这些类型的应用程序专注于数据,我们了解到 SPA 通常用作前端表示层。包括 React、Angular 和 Vue 在内的许多现代框架支持这种范式。控制应用程序状态是前端应用程序的职责,因为 HTTP 是无状态协议,任何后端都不适合承担这一责任。
使用 PyCharm Professional 内置的模板创建 FastAPI 项目非常简单。它生成了起始代码以及 PyCharm 特有的特殊测试文件。HTTP 文件包含请求的规范,以及基于 JavaScript 的测试框架,这允许我们使用与我们在单元测试早期接触到的相同的断言逻辑来验证响应。
最后,我们在 PyCharm 中创建了一个独立的、但连接的(附加的)React 前端项目,允许你在不混淆前端 JavaScript 代码与后端 Python 代码的情况下开发全栈应用程序。在下一章中,我们将讨论 Django 和 Pyramid。
问题
-
哪个框架是 FastAPI 的基础,它与 Flask 和 Werkzeug 有什么不同?
-
“表征状态转移”是什么意思,它解决了什么问题?
-
在使用 SPA 作为其前端的一个 RESTful API 项目中,应用程序状态存储在哪里?
-
以下是最常用的四种 HTTP 方法是什么?
-
CRUD 方法是什么,HTTP 方法是如何映射到 CRUD 方法的?
-
保持前端和后端项目分开有什么好处,PyCharm 是如何使处理此类全栈项目更简单的?
进一步阅读
-
Garret, J. J. (2005). 网络应用程序的新方法。
www.adaptivepath.com/publications/essays/archives/000385.php
。 -
Pandey, R. (2023) 使用 FARM Stack 构建全栈项目 [视频]. Packtpub.com
www.packtpub.com/product/build-full-stack-projects-with-farm-stack-video/9781803236667
-
Van Horn, B. (2021) 在 Windows 10 中设置 Python 开发工作站. maddevskilz.com
www.maddevskilz.com/courses/setting-up-a-python-development-workstation-in-windows
-
Van Horn, B. (2021) 在 MacOS 中设置 NodeJS 开发工作站. maddevskilz.com
www.maddevskilz.com/courses/setting-up-a-nodejs-development-workstation-in-macos
-
一定要查看本书的配套网站:
www.pycharm-book.com
。
第十章:更多全栈框架 - Django 和 Pyramid
我们到目前为止所讨论的 Web 框架在工作方式上都是典范。Flask 是一个无意见的微框架。这意味着 Flask 只处理端点路由,并且可选地提供模板页面内容。FastAPI 提供了一个专门用于构建 RESTful API 的框架,而不是提供超出数据的服务。它还提供了一个类似于在现代 NodeJS 框架中运行的 JavaScript 框架的异步编程模型。
在 PyCharm Professional 的 新项目 菜单中,你还会找到另外两个框架,我们将在下面介绍。Django 是一个非常流行的框架,从哲学上讲,它是 Flask 的完全对立面。Django 是一个高度有意见的框架,试图为你做出所有平台和框架的选择。
我们将要讨论的最后一个框架是 Pyramid。Pyramid 旨在 Flask 和 Django 之间找到平衡点,提供了比 Flask 更多的捆绑功能,但比 Django 更灵活。
在本章中,你将学习以下内容:
-
如何使用 PyCharm Professional 的新项目模板生成 Django 项目
-
如何识别由 PyCharm 生成的模板项目中的主要文件和文件夹
-
如何在 PyCharm 中使用 Django 特定的工具来执行
manage.py
任务 -
如何在 PyCharm Professional 中创建 Pyramid 项目
我们已经在早期章节中讨论了关于 Web 应用程序工作原理的大量理论,所以让我们直接使用 PyCharm 开发这两个框架。与其他 Web 开发框架一样,这些功能仅在 PyCharm 的专业版中可用。
技术要求
为了继续阅读本章,以及本书的其余部分,你需要以下内容:
-
安装并运行 Python 解释器。我将使用来自
python.org
的最新版本。 -
安装
pip
和virtualenv
。当你安装 Python 到 Windows 上时,你会自动获得这些,macOS 在每个系统上都包含它们。如果你使用 Linux,你需要单独安装包管理器,如pip
和虚拟环境工具,如virtualenv
。我们的示例将使用pip
和virtualenv
。 -
安装并运行 PyCharm。安装已在 第二章**,安装和配置 中介绍,如果你是在书的中途开始阅读。我使用的是 PyCharm Professional 2023.1(构建号 #PY-231.8109.197),并且开启了新界面。
-
本书从 GitHub 上的示例源代码。我们已在 第二章**,安装和配置 中介绍了代码克隆。你可以在
github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-10
找到本章的相关代码。
这一切关于 Django 的喧嚣是什么?
如果你问大多数 Python 开发者他们更喜欢哪个框架,我敢打赌,Flask 和 Django 之间是平分的。我还会打赌,Django 群体是 Django 的狂热粉丝,而 Flask 似乎更像是一个用来完成工作的东西。这就像对螺丝刀感到兴奋。Django 有很多意见,因此也很多“直面”。你几乎注意不到 Flask,因为它只是拼图的一部分。Django 是一个盒子里的所有拼图,加上你用来把拼图变成图片的胶水,加上一个昂贵的框架,用于完成粘合后的拼图,加上一个海滩屋,这样你就有地方挂你的拼图。海滩屋里的枕头甚至可能有薄荷糖,但我不能保证。
另一方面,Django 承诺成为一个处理 Web 应用开发中繁重和重复性工作的 Web 框架。Web 开发者可以腾出时间专注于他们应用程序的具体逻辑。Web 框架通常将常见的设计模式和最佳实践实现到其结构中,因此使用框架开发的 Web 应用将默认符合常见标准,而无需开发者手动将这些标准集成到应用程序中。
Django 的目标是将开发 Web 应用所需的一切都集成到框架中。既然这是真的,你就根本不需要考虑你的技术栈。Django 不仅仅是一个框架;它变成了一种理念。你变成了 Django 方式的粉丝。你甚至可以说“这就是方法,”而 Django 可能会回应你。以下是 Django 擅长的一些事情:
-
速度:与 Python 本身类似,Django 强调将想法转化为实际代码的简便性。凭借简单而广泛的 API,Django 旨在适应各种 Web 应用和功能。
-
安全性:Web 开发是编程中一个以安全性为最高优先级的主题。Django 框架提供了功能,帮助 Web 开发者、初学者和专家 alike,避开他们应用程序中的安全漏洞。
-
可扩展性:当网站获得更多客户时,可扩展性变得越来越重要。Django 中的可扩展性可以通过灵活和直观的方式实现;事实上,一些互联网上最大的网站(如 Bitbucket、Instagram、Pinterest 等)都是用 Django 构建的,出于这个原因。
这些都没有被 Flask 具体解决。事实上,Flask 通常不用于大型 Web 应用,因为它相对较慢。我个人已经将所有产品后端代码从 Flask 迁移到了 FastAPI,因为性能明显更好。
到目前为止,Django 听起来比我们之前讨论的任何其他东西都要好。尽管 Django 是一个流行且强大的 Web 框架,但像任何技术一样,它也有其批评者。关于 Django 的一些常见批评和担忧包括以下内容:
-
学习曲线陡峭:一些开发者发现 Django 的学习曲线相对陡峭,尤其是对于在 Python 或 Web 开发方面经验有限的初学者来说。其广泛的功能集和全面性可能会让新手感到不知所措。
-
魔法和抽象:Django 对“内置电池”和抽象的强调可能是一把双刃剑。虽然它可以节省开发时间,但一些开发者认为它可能会掩盖底层机制,使得理解和调试复杂问题变得更加困难。
-
小型项目的开销:对于小型项目或简单的网站,一些开发者觉得 Django 的丰富功能和结构可能是过度设计,增加了不必要的开销和复杂性。
-
单体性质:批评者认为 Django 是一个单体框架,可能不适合微服务架构或需要轻量级框架的高度专业化的应用。
-
灵活性与有偏见:Django 遵循特定的设计哲学并强制执行某些模式,一些开发者认为这过于有偏见。这可能导致关于“Django 方式”做事与替代方法之间的争论。
-
性能:尽管 Django 的性能相当不错,但一些开发者声称它可能不如某些微框架或专用工具快。性能关键型项目可能需要额外的优化工作。
-
ORM 限制:虽然 Django 的 ORM 功能强大且易于使用,但它可能无法涵盖所有边缘情况,或提供与编写原始 SQL 查询相同级别的控制。在某些情况下,开发者可能更倾向于使用其他 ORM 或查询构建器。
-
版本升级:在 Django 的主要版本之间进行升级有时可能具有挑战性,尤其是对于严重依赖已弃用功能的旧项目。这可能导致维护问题和额外的开发工作。
-
定制复杂性:虽然 Django 提供了灵活性,但一些开发者发现定制某些内置组件(如管理界面)以适应特定设计要求具有挑战性。
-
社区和生态系统:尽管 Django 拥有庞大且活跃的社区,但其生态系统可能没有某些其他 Web 框架那么广泛,可用的第三方包也可能更少。
需要注意的是,尽管存在这些批评,Django 拥有庞大的用户基础,许多开发者都欣赏其生产力、稳定性和全面的功能集。最终,选择 Web 框架取决于项目和发展团队的具体需求和偏好。
Django 框架组件
Django 包含了一系列包容性的组件,使其成为 Web 开发的全面且功能丰富的框架。以下是一些关键组件:
-
URL 路由: Django 使用 URL 分派器将传入的 HTTP 请求路由到适当的视图函数或基于类的视图。这为你的 Web 应用程序提供了干净和逻辑的 URL 模式。
-
视图函数和基于类的视图: Django 中的视图负责处理用户请求并返回 HTTP 响应。你可以使用简单的函数作为视图,或者使用 Django 的基于类的视图来编写更组织化和可重用的代码。
-
模板: Django 的模板系统允许你使用带有占位符的 HTML 模板来定义你的 Web 页面的结构和布局,这些占位符用于动态内容。这种关注点分离(逻辑和展示)使得维护和扩展你的 Web 应用程序变得更加容易。
-
模型-视图-模板 (MVT) 架构: 与 模型-视图-控制器 (MVC) 模式类似,Django 遵循 MVT 模式。模型代表数据结构和数据库模式,视图处理逻辑和加工,模板负责渲染输出。
-
对象-关系映射 (ORM): Django 的 ORM 是其定义性特征之一。它提供了一种高级、Pythonic 的方式来与数据库交互,无需编写原始 SQL 查询。它允许你将模型定义为 Python 类,ORM 负责将这些模型映射到数据库表。
-
表单: Django 包含一个表单处理系统,简化了表单创建、数据验证和处理用户输入的过程。它有助于处理 HTML 表单并将用户提交的数据转换为 Python 数据类型。
-
管理界面: Django 的管理界面是一个自动管理界面,可以用来管理你的应用程序的数据模型。它提供了一个现成的解决方案来管理模型数据,并且可以根据你的特定需求进行定制。
-
中间件: Django 中的中间件组件是允许你在请求到达视图之前或离开视图之后全局处理请求和响应的钩子。它实现了包括身份验证、安全检查和请求/响应修改在内的功能。
-
静态文件: Django 内置了对管理静态文件(如 CSS、JavaScript 和图像)的支持。它简化了在开发和部署期间提供静态内容的过程。
-
身份验证和授权: Django 提供了一个强大的身份验证系统,用于管理用户账户、权限和组。它使得向你的应用程序添加用户注册、登录和密码管理功能变得容易。
-
国际化与本地化: Django 支持国际化与本地化,允许你创建可以翻译成多种语言并适应不同地区的应用程序。
-
测试框架: Django 附带了一个测试框架,它有助于你的应用程序的单元测试和集成测试。你可以编写测试用例以确保你的代码按预期工作并避免回归。
所有这些准备工作之后,我们很幸运拥有 PyCharm 这样强大的工具。让我们开始创建一个 Django 应用程序。
创建 Django 项目
这个过程与其他大多数项目没有不同。点击文件 | 新项目,然后点击如图图 10.1所示的Django选项。
图 10.1:创建一个新的 Django 项目与其他项目非常相似
一定要展开更多设置部分。在这里,你可以设置模板语言。你可以使用Django 模板语言或Jinja2,这是我们之前在第九章中使用的,使用 Flask 构建动态 Web 应用程序。由于我们已经介绍了 Jinja2,让我们继续使用 Django。你可以设置用于模板的文件夹名称,并且绝对应该为应用程序名称字段设置一个值,如图图 10.1底部所示。
不要跳过应用程序名称
如果你跳过了我们稍后将要介绍的manage.py
工具,但那可能不是你在创建新项目时大多数情况下想要的。此外,你不应该将应用程序命名为与 PyCharm 项目相同的名称,因为这可能会在 PyCharm 的索引中引起混淆,更不用说对于在项目上工作的人类了。
Django 项目通常包含多个应用程序,这里的应用程序名称设置用于在项目内创建和命名第一个应用程序。在我们的项目中,我们将构建一个单独的库应用程序。由于术语库在编程书中是模糊的,我将明确应用程序的名称,以便我们理解库是一个服务于书籍而不是编程库的地方。
新创建的项目结果如图 10.2所示。
图 10.2:Django 项目已创建,PyCharm 以 urls.py 和 settings.py 文件作为起点打开
考虑到组件列表非常长,创建的文件如此之少确实有点令人惊讶。PyCharm 已自动打开两个作为起点:urls.py
和settings.py
。让我们看看这些以及新创建的应用程序中的其他文件和文件夹的用途。
Django 项目的结构
你可能首先注意到的是项目,我将其命名为djangoProject
,它有一组与应用程序不同的文件,该应用程序位于TheGreatBookery
文件夹中。djangoProject
文件夹中的所有文件都用于将你的应用程序部署到生产环境,以及运行内置的开发服务器。
在 TheGreatBookery
文件夹内部,我们有一个用于数据库迁移的文件夹。这对于使用 ORM 的应用程序来说是常见的。当你将新版本滚动到生产环境中时,你需要一种方法来更改生产数据库。你不能简单地删除数据库并重新构建,因为这会删除你应用程序的所有数据。需要有一个系统来将模式更改迁移到现有数据库,同时保持现有的生产数据。Django 的迁移系统利用存储在这个文件夹中的迁移脚本。
模板文件夹,从技术上讲位于应用程序文件夹之外,是我们存储 HTML 模板的地方。
你可能可以通过文件名猜测剩余文件的功能:
-
admin.py
用于注册模块、数据模型,也许从名称上看最明显的是,用于控制管理用户界面。 -
apps.py
提供了一个中心位置来定制和配置 Django 项目中应用程序的行为。它允许你指定应用程序的各种设置和元数据,例如人类可读的名称、默认应用程序配置和信号。 -
models.py
将包含你的数据模型,它受到 ORM 的影响。 -
tests.py
将包含你的应用程序单元测试,这些测试使用 Django 自身的测试框架运行。 -
views.py
将包含视图函数。这些是 Python 函数,它们接受一个 HTTP 请求作为参数,并返回一个 HTTP 响应。在这些函数中,你可以处理请求,处理来自模型或其他来源的数据,并生成适当的 HTTP 响应,通常是通过渲染包含动态数据的模板来完成的。
初始配置
虽然这不是一本关于 Django 的书,但如果我不指出 settings.py
文件中的几个问题,我会感到失职。像 Flask 一样,Django 内置了一个开发 Web 服务器,它不是为生产环境设计的。settings.py
文件中包含一些危险的设置,这些设置仅适用于本地开发。打开 settings.py
并找到这些行:
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-39u&w+cgs2t4*jwe3nuz4y4j^s!s65^xb7eqtb_a3bl!a_s%tn'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
你应该尽早做的一件事是确保从你的代码外部提取这些值。你永远不应该在应用程序代码中硬编码一个秘密,也不应该将秘密提交到版本控制系统!你可以使用来自 PyPi.org
的库,通过环境变量、.ini
文件或 .env
文件来外部化这些值。将 DEBUG
设置硬编码可能会导致危险,因为抛出的错误可能会清楚地显示出来,包括堆栈跟踪和其他可能被恶意行为者利用的详细信息。
你可能还想审查另一个设置,即数据库引擎。你将在 settings.py
的底部找到这些行:
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
PyCharm 默认为我们选择了 SQLite3,这实际上并不适合生产应用。SQLite3 是一个基于文件的数据库,在本地开发中使用它有优势,因为它不需要安装服务器。你不应该用 SQLite3 来服务生产应用,在我看来,你应该使用你将在生产中使用的相同数据库进行开发。
话虽如此,我们不会将这个应用推向生产,所以我将保持设置不变。你的数据库将作为名为db.sqlite3
的文件出现在项目目录中。我们将在第十一章中学习 PyCharm 的数据库功能。要知道 PyCharm 有一套丰富的工具,用于查看和使用数据库,包括 SQLite3,这有助于开发过程。
运行 Django 项目
当我们创建项目时,PyCharm 为我们创建了一个运行配置。我们在第六章中详细介绍了运行配置。我们只想运行 PyCharm 生成的那个,所以只需简单地点击图 10.3中显示的绿色运行按钮。
图 10.3:点击“运行”按钮将启动开发服务器
运行项目将在 IDE 窗口底部打开运行面板,如图图 10.4所示。
图 10.4:我们的项目正在运行,并在本地主机端口 8000 上提供服务
我们可以看到我们的项目正在端口8000
上运行,还有一个方便的链接,可以打开默认浏览器并显示正在运行的应用,如图图 10.5所示。
图 10.5:点击链接将打开浏览器并显示一条友好的消息,告诉你一切正常
PyCharm 已经为我们创建了一个最小的工作应用。在实际项目中,这通常是我处理通过持续集成(CI)和持续交付(CD)进行部署的地方。我总是给新晋的 Web 开发者一条建议,那就是尽早将生产机制建立起来。如果你做得正确,你就不必再麻烦它们很长时间。
JetBrains 有一个非常优秀的 CI/CD 服务器,名为 TeamCity。我过去 10 年一直在使用它,直到去年才不情愿地转向 Microsoft Azure DevOps。TeamCity 是一个更好的系统,如果你在寻找 CI/CD 服务器,它绝对值得你花费时间和精力。JetBrains 还为 PyCharm 提供了插件,允许你直接从 IDE 中控制和监控构建。
尽管如此,这一章是关于 Django 的,所以让我们继续进行,转到与数据模型一起工作的内容。
创建 Django 模型
在 Django 应用程序中,模型扮演着核心和关键的角色。它们是应用程序数据结构的骨架,负责定义数据库模式和处理数据操作。模型允许开发者以面向对象的方式与底层数据库进行交互,使得管理和操作数据变得更加容易。
模型充当应用程序和数据库之间的接口。它们抽象掉了 SQL 查询的复杂性,并提供了一个高级 API 来执行 创建、读取、更新、删除(CRUD)操作。通过使用模型方法和查询集,开发者可以与数据库交互,而无需编写原始 SQL 代码。与应用程序数据库驱动相关的一切(包括许多开发者通常忽略的事情,如验证和约束)都可以在 Django 模型中找到。
在一些放弃使用 ORM 的项目中,有时会出现一种常见的反模式,即使用数据库来封装业务逻辑。这种做法的理由通常是更改数据库快速、简单,且不需要新的编译或部署。这是一种愚蠢的经济行为,在需要治理和文档化的变更控制的现代时代,它变得令人反感。我的团队中的一位资深数据库开发者,同时也是 第十一章 的技术审稿人,将这种做法描述为在卡车以每小时 100 英里的速度在高速公路上行驶时更换轮胎。然而,Django 模型,由于它们在技术上属于对象,能够有效地处理业务逻辑,且不受禁忌。这是因为只有数据模型的结构被转换到数据库中,而逻辑仍然保留在对象内部。
关于使用 ORM(即我们在创建和操作模型时所做的工作),您需要记住的是,我们正在与数据库的抽象进行工作。当我们操作模型实例时,我们正在更改数据库中的数据。同样,当我们更改类的结构时,我们也在更改数据库的结构。在您的编程语言中数据类型的工作方式与在数据库中的工作方式之间存在差异。我们将在稍后看到这一点,但现在我想让您思考这样一个事实:Python 是一种动态语言,使用鸭子类型。在 Python 中,我可以以任何适合我的方式更改任何结构或类型。然而,数据库是静态的且强类型化的。鉴于 Python 与类、类型和变量交互的方式与数据库与表交互的方式之间的范式差异,Django 的 ORM 有很多工作要做。
让我们看看如何创建一个简单的模型。定位到 models.py
文件。您可以在 *图 10**.6 中找到它,或者您可以使用 PyCharm 中的导航功能,通过按 Ctrl + Shift + N (models.py
) 来找到它。
图 10.6:打开 models.py 文件以便我们可以添加一个模型
代码块内的生成代码将如下所示:
from django.db import models
# Create your models here.
我们将为 Bookery 应用程序创建两个模型:author
和book
。为此,让我们按照以下方式修改models.py
中的代码:
from django.db import models
import datetime
from django.utils import timezone
在这里,我们添加了两个与时间和日期相关的导入。我们将在一些业务逻辑中使用这些库,这些逻辑将附加到书模型上。不过,我们先不要急于求成。让我们创建一个简单的模型来表示书作者:
# Create your models here.
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
def __str__(self):
return f'{self.last_name}, {self.first_name}'
类希望足够简单明了。Author
类从 Django 的model.Model
类继承。有两个字段用于作者的姓和名,并使用models.CharField
类型来表示数据库中的 varchars。关系数据库需要声明一个强类型,在 varchar(一个可变长度的字符字段)的情况下,我们必须提供一个最大长度。双下划线字符串方法简单地为我们提供了一个漂亮的格式,如果我们请求将模型的内容作为字符串。在这里,我们选择将作者的名字格式化为姓,逗号,空格,然后是名。
每个 ORM,无论平台如何,工作方式都是相同的。它的目的是让开发者只与本地语言对象工作。话虽如此,他们通常需要一个自己的类型系统,以便 ORM 能够在语言(Python)的类型系统和数据库之间进行转换。你将在第十一章中了解到,数据库数据类型与编程语言中的对应类型并不相同。至少,像将字符串称为 varchars 这样的差异是明显的,并且 int 和 float 类型在两种情况下都存在。有时它们甚至在数据库平台之间不同。SQLite3 中有一些数据类型在 MySQL 中不存在,反之亦然。
ORM 必须在任何地方都能工作,因此它为模型有自己的类型系统,这允许开发者有一个单一的类型系统,可以适应任何数据库平台。这是 Gang of Four 的适配器模式的经典实现,我在 Packt 出版的《Real-World Implementation of C# Design Patterns》一书中进行了介绍。
author
类并不复杂。让我们在Book
模型代码中做一些更有趣的代码:
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE,)
pub_date = models.DateTimeField('date published')
到目前为止,我们有一个继承自models.Model
的Book
类,就像之前一样。我们有一个标题字段,最大长度为 200 个字符。有一个author
字段,但不是将其作为一个字符串,我们使用了Author
类来设置一个authors
表,该表由我们刚才创建的Author
类控制。外键关系指定了任何添加到数据库的Book
都必须包含一个与现有作者相关联的数据库(这是一个关系数据库)。从操作的角度来看,这意味着我们必须确保作者存在于数据库中,然后我们才能将作者的书添加到数据库中。
外键约束作为一种内置数据验证形式,但它们还通过强制执行规则来帮助维护数据库中的数据完整性。如果我向数据库中添加一个作者,然后添加该作者写的一批书籍,我们将在两个表之间建立良好的人际关系。如果我从authors
表中删除作者,我们仍然会有一批没有相关作者的书籍记录。这些将是孤儿记录。孤儿记录很糟糕,因为它们占用数据库空间,并破坏了作者和书籍之间关系的一致性。
在这个类中,当我们定义author
字段时,我们使用外键约束并定义了一个实际规则,覆盖了删除作者时会发生什么。代码on_delete=models.CASCADE
告诉 ORM,当删除作者时,他们所写的书籍记录也会被删除。这被称为级联删除,因为删除从作者到书籍记录,这反过来又可能级联到可能具有类似书籍关系的其他表。一个结构良好的数据库可以在数据库结构可能很复杂的情况下保持自身清洁,没有孤儿记录。
最后,我们有一个日期来包含出版日期:
pub_date = models.DateTimeField('date published')
如果外键
字段没有给您带来足够的惊喜,让我们再添加一个东西。由于我们正在处理一个对象,并且由于对象可以包含数据和功能逻辑,让我们在我们的Book
模型中添加一个函数:
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
这为我们模型添加了一个字段,但这个数据不是存储在数据库中,而是在调用方法时计算得出的。我们正在使用静态的出版日期字段,并计算与当前日期的时间差(delta)。我们考虑了时区,并以自书籍出版以来的天数来呈现。这允许我们向用户展示书籍的年龄(以天为单位),这可能会被用来提示作者出版新书或更新版本书籍。
使用manage.py
执行迁移
为您的 Django 项目生成的文件之一是manage.py
。您可以在图 10.7中看到它。
图 10.7:manage.py
文件提供了与您的 Django 项目一起工作的实用工具,这样您就不必输入长命令或记住完整的路径
manage.py
任务的目的允许您从命令行运行 Django 特定的命令,而无需记住 Django 项目的完整路径或手动设置 Python 环境。通过使用manage.py
,您确保命令在正确的 Django 项目上下文中执行。
您可以使用manage.py
执行的一些常见任务包括以下内容:
-
使用
manage.py runserver
。这允许您在开发期间本地测试您的应用程序。之前,我们使用生成的运行配置来运行 Django 项目。我们也可以使用manage.py
来做这件事。 -
使用
manage.py makemigrations
和manage.py migrate
,您可以分别创建和应用数据库迁移。这有助于您管理模型的变化并保持数据库模式最新。 -
使用
manage.py createsuperuser
。 -
使用
manage.py test
来确保您的应用程序按预期运行。 -
使用
manage.py collectstatic
。 -
使用
manage.py makemessages
和manage.py compilemessages
。
这只是一个简短列表。manage.py
中还有许多更多实用工具。manage.py
文件作为 Django 管理命令的入口点,使得在不离开您的开发环境的情况下执行管理任务变得容易。在您点击 PyCharm 中的终端按钮之前,实际上有一个更简单的方式来与manage.py
一起工作。如图图 10**.8所示,点击工具 | 运行 manage.py 任务...。
图 10.8:PyCharm 有一个专门用于处理 manage.py 的工具面板
这为您提供了一个新的面板,如图图 10**.9所示。
图 10.9:运行 manage.py 任务面板是一个专门的命令行界面,让您可以轻松调用针对 Django 的 manage.py 文件特定的命令
manage.py
面板在 IDE 的底部打开,就像许多其他面板一样。乍一看,它看起来像一个终端窗口,但这个面板是针对 Django 和manage.py
文件的。正如您所看到的,面板提供自动完成功能,以帮助完成命令。
为了创建我们的迁移,您应该在面板中输入此命令:
makemigrations TheGreatBookery
结果将类似于图 10**.10。
图 10.10:makemigrations 命令的结果
您将在manage.py
面板中找到一些消息,以及在migrations
文件夹中名为类似0001_initial.py
的新文件。makemigrations
命令生成了迁移所需的代码,但尚未执行迁移。执行迁移是影响数据库变化的方式。为了运行迁移,请在manage.py
任务面板中输入以下内容:
sqlmigrate TheGreatBookery 0001
命令的输出,如图图 10**.11所示,表明数据库结构已更新以匹配模型。
图 10.11:迁移的结果是一系列消息和我们的数据库文件的外观
我们看到了一些 SQL 语句,这些语句被生成以使数据库结构匹配模型的结构。我们还看到了一个关于我们数据库的新文件。如果你记得,我们之前看到这个文件的名字和路径是在settings.py
文件中设置的。由于 SQLite3 是一个文件数据库,这就是实际的数据库本身。这类东西你可能希望在真实项目中排除在版本控制之外。
Django 管理界面
这是 Django 的一个杀手级特性:它为你创建了一个基于 Web 的管理面板。这意味着你不需要为处理用户、处理登录或创建简单数据输入到你的模型结构中的屏幕来构建界面。每个应用程序都需要一个管理用户或超级用户。对于应用程序来说,拥有基于角色的用户访问是很常见的,超级用户是能够做任何事情的用户,包括添加新用户。这类事情可能需要开发者两三天才能完成,但使用 Django,这一切都已经准备好了。
创建超级用户并登录
在用户、登录或我们在这里讨论的其他任何好东西工作之前,你需要应用在创建项目时为你生成的迁移。
让我们回到我们的manage.py
任务面板,并输入以下命令:
migrate
就这样,自然而然地。这将为应用程序添加所有基础表。我们可能应该先做这件事,但我不想打断我们正在工作的流程。好吧,实际上我忘记了,但那样听起来更好。
通过这次迁移运行,我们现在数据库中已经有了所有表和结构,以支持 Django 的登录和用户管理功能。接下来,让我们创建超级用户。在面板中,输入以下内容:
createsuperuser
你会被问到一系列问题,这些问题旨在为你的应用程序创建一个超级用户,如图图 10.12所示。
图 10.12:创建超级用户的管理.py 面板交互
让我们尝试一下管理界面!虽然你可以直接使用运行配置,但让我们看看从manage.py
任务面板运行它的感觉。输入以下内容:
runserver
然后点击图 10.13中面板消息中的链接。
图 10.13:这次,让我们从面板开始运行!
事实上,这正是你第一次运行应用程序时点击绿色运行按钮所得到的结果。点击链接后,你的浏览器会打开到我们之前看到的同一页面。在浏览器中更改 URL 为127.0.0.1:8000/admin
。这将带你到显示在图 10.14中的管理登录页面。
图 10.14:最好的登录系统是你不需要自己制作的!
在任务面板中输入你指定的超级用户用户名和密码。图 10.15显示了应该出现的管理屏幕。
图 10.15:无需编写任何代码,我们就可以向应用程序添加用户和组
点击查看 Django 为我们生成的一切。尽管你没有为这个应用程序编写任何代码,但你仍然可以添加用户和组。这里的缺点是,它是基于 Django 项目对管理面板应该是什么样子以及应该如何工作的想法生成的。虽然可以自定义一些此功能,但请记住,我一直在说 Django 是有偏见的。它内置了一些假设。不那么有偏见的框架可能需要你构建这个或使用插件,但你将完全控制这个功能的实现方式。
将作者和书籍模型添加到管理界面
虽然你可以免费获得用户和组,但你必须做一点工作才能让作者和书籍显示在管理界面中。让我们从Author
模型开始。
在TheGreatBookery
文件夹中打开admin.py
文件,并找到一条注释,内容为# Register your models here
。添加以下代码:
# Register your models here.
from .models import Author
admin.site.register(Author)
如果你的应用停止或崩溃,请继续重启它,然后像以前一样导航到管理页面。你现在应该会在管理页面上看到作者,如图 10.16所示。
图 10.16:只需付出很少的努力,我们现在就可以添加、编辑和删除作者
我们有作者模型;让我们添加“书籍”。这个模型稍微复杂一些。记住,书籍和作者之间存在关系。每个作者可以与多本书相关联,反之亦然,多本书可以与一个作者相关联。
由于如此,我们希望管理界面不仅反映这种关系,而且在 UI 层强制执行。你只需将书籍模型的导入粘贴进去,就可以期望它完美工作。
返回到admin.py
文件,并添加以下代码:
from django.contrib import admin
# Register your models here.
from .models import Author, Book
class BookInLine(admin.TabularInline):
model = Book
extra = 1
fieldsets = [
(None, {'fields': ['title']}),
('Date information', {'fields': ['pub_date']})
]
class AuthorAdmin(admin.ModelAdmin):
inlines = [BookInLine]
admin.site.register(Author, AuthorAdmin)
这段代码为Author
UI 添加了一个功能,允许你内联添加书籍,也就是说,当你添加作者时,你将有机会添加书籍。没有先添加作者,你不能添加书籍。因为我们这样定义了关系和 UI,添加和编辑书籍的能力是管理作者的一部分。你永远不会有机会在添加作者之前添加书籍,或者以某种方式将书籍与作者解关联。
重新启动你的服务器,并刷新你的浏览器。没有任何变化!等等,这是正常的。我们还没有尝试添加作者。点击作者,然后点击添加,你将看到图 10.17所示的界面。
图 10.17:我们的代码将内联书籍添加到管理作者的 UI 中
如您所见,添加书籍的表单现在已内联附加到处理作者的工作表单中。
创建 Django 视图
管理员 UI 非常神奇,但它实际上仅设计用于创建用于在数据库中管理数据的简单屏幕。迟早,您将不得不为应用本身实际创建一些真正的屏幕。
在这里我们需要注意术语,因为如果你熟悉更传统的 Web 开发设计模式,那么“视图”这个词可能不会像你预期的那样。主流的行业模式被称为模型-视图-控制器(MVC)。Django 使用的模式被称为模型-视图-模板(MVT)。在这两种模式中,“视图”的含义并不相同。让我们比较一下这两种模式。
MVC | MVT |
---|---|
模型指的是数据结构,通常来自数据库。 | 模型在 MVC 中的含义与 MVC 相同。 |
视图层通常指的是用户界面中的视觉元素。 | 视图层指的是控制器层,它接受传入的请求,执行逻辑,并返回内容或数据。 |
控制器层接受传入的请求,执行逻辑,并返回内容或数据 | 模板在 MVC 中指的是视图,即应用中的 HTML 或用户界面元素。 |
总结来说,Django 视图实际上是控制器,而 Django 模板将是 MVC 应用中的视图。因此,当我们即将创建视图时,我们谈论的是接收请求、执行某些操作然后返回响应的应用部分。
打开TheGreatBookery
文件夹中的views.py
文件。将代码更改为以下内容:
from django.shortcuts import render
from .models import Book
# Create your views here.
def index(request):
latest_books = Book.objects.order_by('-pub_date')[:5]
context = {'latest_books': latest_books}
Book model, then defined a method designed to accept a request, retrieve all the books in the database, and order them by the pub_date field in descending order indicated by the minus sign next to pub_date. We’re only going to display the first five books we find as indicated by the slice [:5].
Next, we create a variable called context ,which becomes a dictionary that will be used to pass data to the template during rendering. In this case, it creates a key-value pair where the key is `latest_books` and the value is the `QuerySet` of the latest books retrieved in the previous step.
The `return` line calls the `render` method imported at the top of the file. It passes the `request` object, a path to the template (which we have yet to create), and the `context` dictionary variable we just created.
This view function won’t do all the work by itself. Before it can be used, it must be registered in the `urls.py` file so we have a route to employ this function.
Open `urls.py` and change the code to this:
from django.contrib import admin
from django.urls import path
from TheGreatBookery.views import index
urlpatterns = [
path('admin/', admin.site.urls),
path('', index, name='index')
]
We added an import to the `index` function we just created in the `views.py` file. Next, we added by way of the `path` function an empty string, which will map the root route for the app, a reference to the `index` function that will execute in response to requests at the root route, and a friendly name.
We’re almost done. There’s just one more thing. Go back to the `view.py` file and you’ll see some problematic yellow highlighting. See *Figure 10**.18*.

Figure 10.18: We have a view and a URL mapped, but we haven’t made the template yet
Hover your mouse over `index.html` and note that the tooltip has an action available to create the template for us. Either click the blue link text in the tool-tip window or use *Alt* + *Shift* + *Enter* as indicated. *Figure 10**.19* shows the ensuing dialog.

Figure 10.19: The dialog you get when you use the in-line action to create the missing template is automatically filled in based on some obvious assumptions
When you click `Templates` called `TheGreatBookery`. It is possible to have multiple apps in your Django project. Since that is the case, you might want to segregate your templates. Go ahead and let PyCharm create the folder for you. After it does, you get an empty file called `index.html`.
This isn’t a book on web page design, so we aren’t even going to try to make this pretty. Enter this code into your `index.html` page:
{% if latest_books %}
- {{ book.title }} by {{ book.author }}
{% for book in latest_books %}
{% endfor %}
{% else %}
没有可用的书籍。
{% endif %}
This looks a lot like Jinja2 code we saw in *Chapter 8* with Flask. It is very similar. All this code does is check whether or not a dictionary key called `latest_books` exists on the data context that was passed in. If you go back and look at the `render` function we called in the view, it’s passing this in.
If the key is there, we render the contents using the templates for loop syntax. This isn’t exactly an amazing UI. It is just going to render an unordered list with each book as a list item. If the key were not there, then we’ll be rendering a paragraph that says **No** **books available**.
What’s with the weird Python icon in the template gutter?
You might have noticed a big, juicy Python icon in the gutter of the `index.html` file. Check out *Figure 18**.20*.

Figure 10.20: Look at that icon up there just begging to be clicked!
That Python icon is actually called a *Pythicon*.
OK, no it isn’t, I just made that up. Still, you really want to click it, don’t you? Go ahead, click it! It takes you to the Python code in `views.py` where we invoke it. Travel works both ways. In `views.py`, you’ll notice a more subtle icon in the gutter where the template is invoked. See *Figure 18**.21*.

Figure 10.21: Click the HTML icon to navigate to the template
Again, just click the html icon, er, I mean HTML icon, to go to the template. I wouldn’t blame you for clicking back and forth between the two while saying, *“Take that, vi* *and emacs!”*
Run it!
Start or restart your server and check the results as seen in *Figure 18**.22*.

Figure 10.22: This might be the best website ever!
If you direct your browser to [`localhost:8000/`](http://localhost:8000/) you should see either some books or a message stating there are no books. I added some obvious reading choices and so my run looks absolutely glorious!
Clearly, you could leverage the HTML tooling we learned in *Chapter 7* to make this nicer, but we still have more ground to cover, so I’ll leave that up to you.
Building Pyramid applications with PyCharm
We’ve seen three popular web frameworks so far. There is one left that is listed in PyCharm’s **New Project** menu: **Pyramid**. You can find details on Pyramid at [`trypyramid.com/`](https://trypyramid.com/).
The organization behind Pyramid bills it as a framework that allows you to start small, finish big, and stay finished. To my mind, this might be a little bit of a dig at its main competitors. Django is criticized as being overly opinionated, and for being overkill for small projects. This fits with the “start small” idea in Pyramid. You have more leeway in choosing the components for your application, and you can leave out what you don’t need. For example, you can choose from a couple of ORM libraries, or simply choose not to use one. Django really wants you to use theirs.
Flask, in contrast, doesn’t come with an ORM or anything really, besides Jinja2 for templating. Flask is criticized for not being performant in large applications. The “finish big” concept seems to speak to this criticism. I’m not sure what, if anything, the “stay finished” directive is criticizing. I think that’s something we all want.
Having seen the first-rate tooling for Django, switching to Pyramid now is going to seem underwhelming. Django is a good candidate for custom tooling because it is complex, and it makes a lot of technical decisions for you. We can consider the `manage.py` task panel as an example. There are tons of commands in `manage.py`, so having its own panel makes sense. It also helps that Django is wildly popular. Pyramid isn’t. So from a PyCharm perspective, this is a much simpler proposition in terms of tooling.
Creating a Pyramid project
Creating a Pyramid project is no different from any other. Just use **File | New Project**, and pick the **Pyramid** template, as shown in *Figure 10**.23*.

Figure 10.23: PyCharm has a project template for Pyramid
As with Django, you should twirl down the advanced settings, as shown in *Figure 10**.23*. You are able to set a project name, as you were in Django. Be careful with this setting!
Pyramid project name
Make sure the project name is different than the PyCharm project name entered in the **Location** box.
When working with Pyramid, it’s a best practice to have a PyCharm project name that is different from the project name you define within Pyramid. This separation of names is recommended to avoid potential conflicts and confusion between the two namespaced systems: PyCharm, an IDE, and Pyramid, a Python web framework.
Here’s why it’s advisable to keep them different:
* **Namespace clashes**: If your PyCharm project name matches your Pyramid project name exactly, you may encounter namespace clashes or naming conflicts. These conflicts can make it challenging for PyCharm to distinguish between project-specific settings, configurations, and files, and the Pyramid framework-specific files and configurations. This can lead to confusion and potential errors in your development process.
* **PyCharm project settings**: PyCharm uses the project name for its internal settings and configurations, including virtual environments, code analysis, and project-specific settings. If your PyCharm project name is the same as your Pyramid project name, PyCharm might overwrite or interfere with Pyramid-specific configurations and settings.
* **Clarity and maintainability**: Keeping distinct names for your PyCharm project and Pyramid project helps maintain clarity and organization in your development environment. It makes it easier to understand which aspects of your development are controlled by PyCharm and which are specific to your Pyramid web application.
* **Flexibility**: Having different names provides flexibility, especially if you work on multiple projects with Pyramid or use PyCharm for various other Python projects. It prevents potential conflicts when switching between projects.
I’m going to name my imaginary project `giza_application`, a nod to the location of the Great Pyramids of Egypt.
Having set the project name, you can select from two template engines: **Jinja2** and **Chameleon**. Finally, you have settings for the ORM, which here is specified as **Backend**. The two choices are **SQLAlchemy**, which is very popular, and **ZODB**, which is somewhat exotic.
SQL Alchemy is a straightforward ORM used for working with relational databases. You create models as we did with Django’s ORM, and through a migration process, you can apply your database schema changes to a new or existing database.
ZODB refers to the Zope database. Generally speaking, Zope is an open source web application server written in Python. Less generally speaking, it is used as a content management system similar to WordPress. The database component, ZODB, has some interesting features that distinguish it from typical relational databases. It’s a powerful tool that can be used in conjunction with the Pyramid web framework to manage data in a more Pythonic way. Here’s how ZODB integrates into a Pyramid project and some of its features:
* **Python native**: ZODB lets you store Python objects without needing to serialize them into a different format. You can work with complex data structures directly, without having to map them to a relational database schema.
* **ACID transactions**: ZODB supports **Atomicity, Consistency, Isolation, Durability** (**ACID**) transactions. This ensures that your data remains consistent, even if something goes wrong during a transaction.
* `pyramid_zodbconn` package. This provides a smooth way to get a ZODB connection within Pyramid’s request handling.
* **Hierarchical storage**: ZODB allows you to organize your data in a tree-like structure. This can be particularly useful in a Pyramid application using traversal, where the URL structure often mirrors a hierarchical data structure.
* **Scalability**: ZODB can be scaled across multiple machines, allowing for more extensive and complex applications.
* **Versioning**: ZODB supports object versioning, allowing you to keep track of changes to objects over time. This can be useful for implementing features such as undo/redo.
* `repoze.catalog`.
* **No schema required**: Unlike relational databases, ZODB doesn’t require a fixed schema, offering flexibility in data modeling. You can modify the Python classes used for storage without needing to migrate data.
* **Blob support**: ZODB supports the storage of large binary objects (BLOBs), such as images or videos, alongside regular objects.
* **Persistence**: ZODB provides a straightforward persistence model. Any changes to persistent objects within a transaction are automatically saved to the database when the transaction is committed.
* **Compatibility**: ZODB works well with various WSGI servers and can be integrated into a Pyramid application running on different platforms.
ZODB can be a compelling choice for Pyramid developers looking for a database that aligns closely with Python’s object-oriented paradigm. Its integration into a Pyramid project allows for intuitive data management without the need for complex SQL queries or ORM mapping, making it an attractive option for certain types of applications. If the hierarchical, object-oriented nature of ZODB fits the data model of your Pyramid application, it may be an excellent choice. From a PyCharm perspective though, there is no special tooling for this database.
Clicking the **OK** button will generate the project structure and the run configuration.
I really wish there was more to say here, but in truth, besides Djano, any other framework that could have been covered last would have the same problem. We’ve already covered PyCharm’s tools for developing web applications in an unopinionated framework. We have seen the support for Jinja2 templates, working with an ORM, and obviously general Python project work.
Summary
In this chapter, we examined various PyCharm features regarding supporting and automating tasks in the process of web development with Django. While this list of features is in no way exhaustive, I hope it can offer you a solid starting point to continue discovering other powerful features for your web development process.
First, we see that, by specifying the PyCharm project type as Django, an extensive project skeleton will be generated with convenient boilerplate code already filled out. With the implementation of the `manage.py` panel inside the project window as well as its run/debug configuration, PyCharm additionally allows for a higher level of development, with various tasks traditionally achieved via the command line, such as running the server or making migrations. Finally, by acknowledging integrated views and templates in Django, PyCharm makes it as easy as possible for developers to work with them in the editor—be it generating a missing template, code completion even in HTML and Jinja, or even dynamically switching between views and templates.
We concluded with a short coverage of Pyramid. Pyramid is a framework that aims to be more flexible than Django but packs more features than Flask. It’s a happy medium between the two. Unfortunately for Pyramid, we covered it last. PyCharm doesn’t have any special tooling for Pyramid beyond the project template that creates the project structure. To be fair, if Flask had gone last, it would have suffered the same fate because most of PyCharm’s web development tools are useful regardless of which framework you choose. Pyramid has some great features and is worthy of consideration for any project.
In the next chapter, we will tackle the last major component of any web application: the database. Buckle up, because the tooling for databases in PyCharm is extensive!
Questions
1. What are the major characteristics of Django, and how do they set Django apart from another popular Python web framework, Flask?
2. What is the purpose of the PyCharm `manage.py` panel in a Django project, and how does one open and utilize it?
3. What is the purpose of the Django admin interface? How does one create an instance of a model (that is, a new entry in a database table) in this interface? How does the process change if the model references another model?
4. What is the purpose of the run/debug configuration in PyCharm in the context of running a Django server?
5. Does PyCharm’s code completion logic only apply to Python code in Django projects?
6. What is the significance of being able to switch between Django views and corresponding templates in PyCharm?
7. Describe the PyCharm tooling available for the Pyramid framework.
Further reading
Be sure to check out the companion website for the book at [`www.pycharm-book.com`](https://www.pycharm-book.com).
Check the following resources to expand on what was covered in this chapter:
* *Web Development with Django,* *Second Edition*
* *Hands-On RESTful Python Web Services,* *Second Edition*
第十一章:在 PyCharm 中理解数据库管理
马蹄蟹、肺鱼、鳄鱼和关系数据库有什么共同之处?我等你查一下肺鱼。这四种生物都存在了数百万年,但进化很小。好吧,数据库没有存在数百万年,但它们已经存在了数百万个互联网年。众所周知,互联网年是短暂的。JavaScript 开发者经常开玩笑说,在午餐时间之前,世界上任何地方都会发明数十个新的框架,它们会崭露头角,失去青睐,然后在你吃完面条之前就被遗弃。
在 20 世纪 70 年代初,一位名叫 E. F. Codd 的研究员在加利福尼亚州 IBM 的圣何塞研究实验室工作。他开发了一种革命性的概念,称为数据关系模型。在他的开创性论文《大型共享数据银行的关系数据模型》中,发表于 1970 年,Codd 概述了这种新方法组织数据的原则和基础。
Codd 的关系模型提出了一种将数据表示为关系集合的方法,即关系,每个表由行和列组成。他引入了关系代数的概念,这是一种用于在这些表中操作和查询数据的数学框架。该模型强调了使用数学集合论和逻辑来定义关系和执行数据操作。
Codd 的思想挑战了当时盛行的层次结构和网络数据库模型,这些模型更复杂且灵活性较差。关系模型提供了一种更简单、更直观的方式来管理数据,为结构化查询语言(SQL)和其他在关系数据库中使用的工具奠定了基础。
1974 年,IBM 发布了第一个商业可用的关系数据库管理系统(RDBMS),名为System R,基于 Codd 的工作。System R 实现了 Codd 研究中概述的许多概念,并成为后续关系数据库系统的一个有影响力的先驱。1974 年,IBM 基于 Codd 的工作发布了 System R,但由于 IBM 高管担心会损害公司其他数据库系统的销售,该产品并没有积极地进行市场推广或销售。在那个时期,另一家初创数据库公司诞生了:Oracle。Oracle 的最初版本是由拉里·埃利森、鲍勃·米纳和埃德·奥茨开发的。1983 年发布的 Oracle 通常被认为是第一个商业上的成功,尽管 IBM 有明显的先发优势。
关系数据库技术在 20 世纪 80 年代和 90 年代不断发展,在这个过程中,它成为了全球每个角落每个行业中企业 IT 的必备技术。1986 年,SQL 语言实现了标准化。随着时间的推移,标准也在不断发展,但说实话,在 SQL 发明 49 年后,大多数开发工作都是使用最简单、最古老的语句集完成的。
刚才,我说过 Codd 的想法挑战了当时盛行的层次化和网络数据库模型,这些模型更加复杂且灵活性较低。值得注意的是,在 2000 年代初,人们开始从关系型数据库技术转向我们所说的NoSQL 数据库。IT 界常常像摆钟一样,永远在来回摇摆。我们曾经有过相对低功耗的 PC,能够在邮票大小的窗口中以每秒 12 帧的速度播放视频文件。技术得到了改进,我们可以全速在 PC 上观看 4K 高清视频,但随后我们发明了小型手持设备,如 iPod,我们又回到了低端处理和粗糙的视频。最终,这些改进成了 iPhone,它能够以高清和高帧率播放视频。有些 iPhone 比我在大学时拥有的电视还要大。
同样,在数据库领域,有一天世界正在使用层次化和面向网络的数据库。第二天,一切又转向了一个新事物,称为关系型。如今,我们看到又回到了另一个方向。越来越多的项目今天倾向于非关系型数据库,其中许多支持层次化数据。无论你偏爱哪种技术,可以说,你几乎构建的任何项目,尤其是在企业 IT 领域,都将以某种方式与数据库交互。
2015 年,JetBrains 推出了一款新产品,旨在成为数据库开发人员流行的 IDE。DataGrip的创建是为了提供用于处理各种数据库的统一界面和强大的工具集。它提供智能代码补全、高级 SQL 编辑功能、模式导航、数据分析以及与版本控制系统的集成。与 JetBrains 的 Web 开发产品 WebStorm 一样,DataGrip 产品通过其插件系统集成到 PyCharm 的专业版中。考虑到你在 PyCharm 专业版上的 99 美元投资,你得到了一个 Python IDE、一个 JavaScript IDE、一个用于 HTML 和 CSS 前端工作的 IDE,现在还有一个用于数据库工作的完整 IDE,你会发现这是一个非常划算的交易!
在本章中,我们将介绍 PyCharm 与数据库相关的功能。到本章结束时,你将学习以下内容:
-
一些数据库历史和一些基础知识,以确保我们在术语方面处于同一页。如果你已经开发软件一段时间了,这可能是一次复习。如果你是新手,我将努力为你提供数据库技术的最佳可能介绍。
-
如何导航到 PyCharm 中的数据库工具,这些工具隐藏在界面右侧的一个标签页中。我遇到过许多使用 PyCharm 多年的开发者,但他们甚至不知道这些工具的存在。
-
如何连接到不同的数据库,包括如何在 PyCharm 中添加必要的连接驱动程序。JetBrains 使这一过程变得非常简单!
-
如何为单个项目和全局配置 SQL 方言。
-
如何使用 SQL 生成模板来帮助你更快地编写 SQL 查询。
-
如何生成实体关系图(ERD)。
-
如何使用图形设计器轻松构建表格。
-
如何使用控制台创建和运行针对任何数据库的即席查询。
顺便说一句,除了 12 岁时学习 BASIC 编程之外,关系技术是我 30 多年前进入 IT 领域时掌握的第一项技能。这是一个一直有需求,并且可能在未来许多年里仍将有需求的技能集。这是我有着丰富经验的一个主题,我很高兴能在本章与你分享这些经验。那么,让我们开始吧!
技术要求
为了继续本章,以及本书的其余部分,你需要以下内容:
安装并运行 Python 解释器。我将使用来自python.org
的最新版本。
安装并运行 PyCharm 的副本。安装已在第二章,安装和配置中介绍,以防你是在书的中途开始阅读。
在本章中,数据库服务器是一个不错的选择,这样你就可以在实际数据库上练习。有数十种流行的关系型数据库可供选择,因此对我来说涵盖所有这些并不实际。我将使用MySQL和Docker Desktop运行。如果你打算跟随,你需要在你的计算机上安装 Docker Desktop。你可以在www.docker.com/products/docker-desktop/
找到安装说明。
本书提供的示例源代码来自 GitHub。我们已在第二章,安装和配置中介绍了代码克隆。你可以在github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-11
找到本章的相关代码。
简要介绍关系数据库
E. F. Codd 提出的关于关系数据的概念基于几个简单的原则。首先,数据可以用称为表的集合来表示。一个books
。books
表将包含定义这些列内数据的列——可能像这样:
标题 | ISBN | 页数 | 作者 | 价格 |
---|---|---|---|---|
资治通鉴 | 1599869772 | 68 | 孙子 | 4.99 |
五轮书 | 8387743849 | 43 | 宫本武藏 | 4.50 |
此表有五个列,用于结构化关于书籍的数据。我们有两本书,它们作为行存储在表中。
结构化查询语言
E. F. Codd 的关于关系代数的论文是 SQL 的基础。SQL 与您将使用的任何其他编程语言都不同,因为它仅是少数几种使用声明性范式的语言之一。您使用的多数语言,包括 Python,都使用命令式范式。简而言之,这种语言作为一个句法框架,向计算机提供指令,告诉它您希望它做什么。您基本上是一个微观管理者。您必须指定每个输入、每个输出以及程序在处理过程中将采取的每个步骤,以将输入转换为输出。您必须非常细致,因为计算机将严格地执行您的指令。如果您遗漏了任何细节,您就是在设置系统出现故障。
另一方面,声明性编程仅仅涉及指定您希望从隐含输入中获得的输出。您对从输入中推导出输出的操作几乎没有控制权。考虑以下设计用于从您的书店数据库中获取一些行的 SQL 语句:
SELECT title AS Title, isbn AS ISBN, author AS Author, price AS Price FROM books ORDER BY price DESC
这个查询将产生一些输出,包括一个表格,其中包含您原始数据库中的一些列,这些列作为隐含输入。您将得到以下列:
标题
ISBN
作者
价格
但您不会得到页数,因为您在查询中没有要求。输入数据源隐含在查询的FROM books
部分中。最后,我们有一个按价格降序排序结果的要求(DESC
),这将使我们的结果表格从最贵到最便宜排列。
SQL 的两个部分
SQL 本身分为两个独立的语法集合:前面提到的SELECT
语句是一个读操作的例子。SQL 有INSERT
、UPDATE
和DELETE
操作的关键字,这些操作对于记录来说是直接的,所以我想专注于 DDL,因为这将是你 PyCharm 工作中的一大部分。
我们之前在书店应用程序中使用的表格示例中有一个名为books
的表。这个表包含了一组列,我们将使用 DDL(数据定义语言)如下定义这些列:
CREATE TABLE books (
title VARCHAR(255),
isbn VARCHAR(255) UNIQUE,
price DECIMAL(10, 2),
author VARCHAR(255),
page_count INT
);
如果您是 SQL 的新手,您可能已经注意到关键字都是大写的。实际上,我敢打赌您正确地假设那些大写的单词是关键字。虽然关键字大写不是强制性的,但这是一个好的实践——尤其是如果您的项目数据库负责人是一位从美国海军陆战队退休的大个子,他对他的数据库中的句法细节非常讲究的话!在这种情况下,好的实践可能成为生存的关键。
在表的 DDL 中,你读到的内容大部分都很容易理解。我们正在创建一个名为books
的表。我们已经知道了列名。SQL 使用强类型系统,这意味着你必须定义将要放入列中的数据类型,违反这个约束可能会带来后果。我以前说过很多次,开发者的工作就是保护程序不进入无效状态。同样,数据库开发者的工作也是如此,以及VARCHAR
指的是一组可变长度的字符。程序员称之为字符串。类型后面的数字(255
)指的是VARCHAR
的最大长度。数据库中所有的VARCHAR
(字符串)字段长度都被限制在 255 个字符以内。数字 255 非常常见,因为我们这些老一辈的人都是在 8 位计算的世界中长大的。数字 255 是 8 位无符号整数的最大值,因此是字段长度的常见最大值。当我们不确定数据可能有多长时,我们会将其设置为这个值。
自然地,今天,我们通常在 32 位甚至 64 位架构上工作,所以最大值可以更高。将我们限制在旧的最大值是正常的,因为 255 个字符通常足够用于大多数事情。指定合理的最大值有助于保持你的数据库存储和内存需求合理。
看一下price
字段。它被指定为DECIMAL
类型,这显然类似于浮点数。数据库系统对它们的类型有不同的命名,所以你应该始终查阅你的数据库系统文档以获取确切的命名。DECIMAL
确实是一个浮点数,但我们还指定了精度级别为(10,2)
。这意味着我们可以有一个 10 位数字,有 2 位小数。这通常用于指定价格。一些数据库系统有专门针对货币的类型。SQL Server 就是这样一个例子,它们有MONEY
和SMALLMONEY
类型。再次提醒,检查你的数据库系统文档以获取适当类型,因为这些专用类型不是标准 SQL 的一部分。
关系
考虑到我们的书店自然会增长规模和复杂性。假设我们添加了一些不同的字段,并删除了一些我们没有使用的字段。我们新的表结构看起来像这样:
标题 | 作者姓名 | 作者邮箱 | 页数 | 价格 |
---|---|---|---|---|
C 程序设计语言(第 2 版) | 布莱恩·肯尼根和丹尼斯·里奇 | bkernigan@notrealaddress.com | 272 | 53.60 |
C#设计模式实战应用 | 布鲁斯·范·霍恩 | bvanhorn@notrealaddress.com | 442 | 44.99 |
使用 PyCharm 进行实战应用开发 | 阮文 | qnguyen@notrealaddress.com | 785 | 35.45 |
使用 PyCharm 进行实战应用开发(第 2 版) | 布鲁斯·范·霍恩 | bvanhorn@notrealaddress.com | 840 | 44.99 |
除了来自更多记录的复杂性之外,我们在设计方面也引入了一些复杂性。在某个时候,对我们来说,跟踪任何给定书籍作者的更多信息变得很重要。我们添加了姓名和电子邮件地址字段,起初这似乎解决了我们的问题,但最终导致了更多问题。
C 语言书籍有两个作者,但我们只有一个字段。虽然将两位作者都存储起来可能没问题,但我们还有一个电子邮件地址字段,而这个字段只能容纳一个地址。这并不理想,因为我们的目标可能是向作者发送版税声明或销售报告。一个地址就是一个问题。
如果我们存储了两本同一作者的书籍,电子邮件地址将会重复。如果作者更改了电子邮件地址,所有记录都必须更新。这相当于在程序中多个地方硬编码的值。你必须记住在许多地方更改它。如果这个电子邮件地址数据在几个不同的表中重复,问题会更加复杂。
这个问题的解决方案由 SQL 和关系代数背后的思想规定。你需要一个作者数据的单一来源,而不是将其与其他表混合。因此,我们为作者创建了一个新表:
CREATE TABLE authors (
id SERIAL PRIMARY KEY,
first_name VARCHAR(255),
last_name VARCHAR(255),
email_address VARCHAR(255)
);
这个表格有姓名、姓氏以及电子邮件地址字段。还有一个名为id
的字段,它作为主键发送到服务器。主键是一个可以唯一标识该行信息的字段。这里的想法是每个作者只得到一个行来存储他们的信息。我们需要一些数据来唯一标识该行。以我自己的记录为例。我的姓名和姓氏在数据库中作为唯一定义我的记录的选择很糟糕。我知道至少有其他三个人叫布鲁斯·范·霍恩。一个是我的父亲,他在医学领域发表作品。一个是励志演讲者,在那个文学领域出版书籍。我在领英上找到了另一个布鲁斯·范·霍恩,信不信由你,他也是一个软件开发者!所以,名字作为唯一标识符是一个糟糕的选择。
电子邮件地址可能可行,但我们已经发现电子邮件地址可能会更改。我至少有五个电子邮件地址,其中至少有一个被垃圾邮件淹没得如此严重,以至于我甚至不再检查它。电子邮件是不可行的。
最佳实践是使用一些独特但随机的数据,它与记录中的其他数据没有任何关联。有两种方法可以做到这一点。最常见的是使用数据库序列。序列是一个自动递增的整数源。每次将记录插入到表中时,序列生成器都会生成一个顺序号,该顺序号保证是唯一的。
另一种方法是使用6f35e0e7-d99a-4437-b894-f73ff35bd3ad
这样的记录与具有id
值为16
的记录相比。你更愿意在查询中输入哪一个?
现在我们有一个字段可以唯一标识作者的记录,我们可以调整我们的书籍表结构如下:
Title | pages | price | author_id |
---|---|---|---|
Real-World Implementation of C# Design Patterns | 442 | 44.99 | 2 |
Hands-On Application Development with PyCharm | 785 | 35.45 | 1 |
Hands-On Application Development with PyCharm, 2nd Edition | 840 | 44.99 | 2 |
我们的 authors
表看起来是这样的:
author_id | first_name | last_name | |
---|---|---|---|
1 | Quan | Nguyen | qnugyen@notrealaddress.com |
2 | Bruce | Van Horn | bvanhorn@notrealaddress.com |
authors
表中的作者 ID 在 books
表中用作相关列,通过一对一关系。每个作者将有一个记录,该记录与 books
表中的多个记录相关联。
更多关系结构
我们使用一对一的表结构解决了一个大问题。我们不再在多个记录中为作者重复数据。然而,我们并没有解决所有问题。例如,C 语言书籍有多个作者。我们如何以允许每本书支持多个作者的方式存储作者?
我恐怕我们已经到了这样一个地步,我告诉你这既不是一本关于 SQL 的书,也不是一本关于关系理论的书籍。如果我让你对这本书产生了兴趣,我可以推荐一些我学习这些知识时用过的优秀书籍。我们已经提供了足够的数据库关系词汇,以帮助你理解 PyCharm 工具中看到的内容,那才是我的真正目标。
由于我并不是一个彻底的堕落者,无论你在 Stack Overflow 上读到关于我的什么,我都会快速地给你解决方案,而不会用很多页的解释。
你会使用我们所说的映射表来解决这个问题。你的结构需要添加一个名为 books_authors_map
的表:
Id | Book_id | Author_id |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
你的 books
表将如下所示:
Book_id | Title | Pages | Price |
---|---|---|---|
1 | The C Programming Language | 442 | 44.99 |
我们移除了 author_id
字段,并添加了一个 book_id
字段,这个字段本来就应该在那里。authors
表看起来会相同:
Author_id | First_name | Last_name | |
---|---|---|---|
1 | Brian | Kernighan | bkernigan@notrealaddress.com |
2 | Dennis | Ritchie | dritche@noterealaddress.com |
映射表可以将 books
和 authors
进行映射!如果任何作者独立出版书籍而没有合作者,那也是可以的!books
表的记录将在映射中有一个相关记录,该记录在 authors
表中有一个相关记录。
如果一本书有 10 位作者,它将有一个书记录,10 个作者记录和 10 个映射条目。关系代数非常酷!它是当今对象关系映射器(ORMs)世界中被低估的技能,ORMs 将所有这些抽象成常规对象结构。今天的开发者往往失去了这种技能。如果你失去了它,你将失去很多。你可以在 DDL 中进行精细的修改,这将为你的应用程序带来巨大的性能提升。
说到这个,让我们来了解一下 PyCharm 的数据库开发工具。
数据库术语使用简单的英语复数形式
随着我们讨论数据库讨论中使用的术语,我感到有必要指出困扰我很久的一件事。在讨论数据库时使用的许多术语都源自拉丁语根词。例如,索引是表格的一个附加项,可以加快数据检索速度,但会牺牲插入新数据的速度。它也是一个指代指针、指示器或路标的拉丁语单词。模式指的是在数据库中将表和其他结构划分开来的方式。单词模式起源于希腊语单词σχῆμα (skema),在两种语言中均意为形状、形式或计划。
当谈到复数形式时,如果你像我一样接受了传统教育,你可能会发现单词的复数形式并不像你预期的那么简单。如果你来自一个以拉丁语为基础的语言国家,你也可能会注意到这个问题。
我原本预期 schema 的复数形式应该是 schemata,而 index 的复数形式应该是 indices。如果你预期这样,你总会感到失望。该行业已经标准化了简单的复数形式,如 schemas 和 indexes。
PyCharm 中的数据库工具
PyCharm 中的数据库工具功能全面但通用。我的意思是,PyCharm 试图支持所有数据库,因此它通常支持所有数据库共有的功能。你可能会发现自己有时会依赖更具体的工具,例如用于 SQL Server 的 SQL Server Management Studio(SSMS)。然而,对于一般开发工作,PyCharm 中的工具已经足够。在 PyCharm 中处理数据库的起点是打开数据库工具并创建一个连接。为此,你还需要一个数据库。PyCharm 支持数十种最受欢迎的数据库服务器。由于这个原因,我无法预测你更喜欢哪一个,所以我将退而求其次,选择我熟悉的:MySQL。无论你更喜欢哪一个,PyCharm 中的工具都是通用的,只要你选择一个符合标准的关联数据库,过程都是相同的。
使用 Docker 设置 MySQL 数据库服务器
尝试任何数据库系统,或者几乎所有服务器技术的最简单方法就是使用 Docker。我在本书的后面部分还有更多关于 Docker 的计划,我将使用 Docker Desktop 和命令行。我喜欢桌面版的图形用户界面来查看正在运行的内容,但你应该掌握 Docker 命令行技能,以便保持竞争力。如果你没有安装 Docker Desktop,你可以在 www.docker.com/products/docker-desktop/
找到安装说明。当然,还有其他选项,比如在你的电脑上安装数据库服务器。我个人反对这样做,因为数据库服务器非常复杂。安装像 SQL Server 或 Oracle 这样的东西会在操作系统级别进行修改,使得这些软件包难以卸载。过去有一个规则,如果你在安装数据库服务器时犯了错误,最明智的选择是擦除操作系统并重新开始。我相对确信这不再是规则,但我仍然因为你的解决方案中所有移动部件,这个是最复杂的。你最不希望的事情就是在开发史诗级项目的过程中,你的笔记本电脑上的数据库服务器出了问题。所以,我推荐使用 Docker。如果出了问题,你可以删除容器并创建一个新的。
同样,你可以使用 VMware Workstation 或 Oracle VirtualBox 等产品创建虚拟机。这是另一种很好的工作方式,尽管它比 Docker 占用更多的空间和资源,你必须记住保持你的虚拟机更新。
另一个很好的选择是在你最喜欢的云中启动你选择的数据库服务器。我推荐 DigitalOcean,因为他们的定价和设置都极其容易理解。我用这个服务来托管这本书的配套网站。如果你的电脑无法运行数据库服务器、VMware 或 Docker,使用云服务提供商是你的最佳选择。
所有这些选项都很好,但我需要选择一个,所以我将使用 Docker。记住,这不是一本关于 Docker 的书。我的介绍将是隐晦的。你的目标是让数据库服务器运行起来,以便你可以进行实践。如果你可以用除了 Docker 之外的东西做到这一点,请直接跳到下一节。
安装和运行 MySQL 容器
我假设你在你的电脑上运行了 Docker,并且你的 Docker 命令在你的 PATH
中可用。我们可以通过打开我们的终端并输入以下命令来确认:
docker ps -a
此命令将列出当前正在运行或已停止的所有容器。如果你刚刚安装了 Docker,你应该看到一个空列表,也就是说,什么都没有。这个测试实际上是为了确保命令可以运行并且不会抛出任何错误。如果它没有,你就可以使用以下命令来获取 MySQL:
docker pull mysql
您将看到安装的动画显示,如图 图 11.1 所示。
图 11.1:用于拉取 MySQL 所需镜像的 Docker 命令
此命令拉取了运行一个或多个 MySQL 容器所需的所有要求。接下来,我们需要使用以下命令创建并运行一个容器:
docker run --name pycharm-mysql -e MYSQL_ROOT_PASSWORD=P@ssw0rd –p 3306:3306 -d mysql
此命令将创建并运行一个名为 pycharm-mysql
的容器。它将 MySQL 数据库的根密码设置为 P@ssw0rd
。-p
标志将端口 3306
(MySQL 的标准端口)映射到容器和主机之间的相同值。这将使您感觉就像是在计算机上直接运行 MySQL 服务器一样。-d
标志告诉 Docker 以后台进程的方式运行 MySQL,而不是等待其退出。这对于服务器软件来说是常见的。我希望我不需要提醒您,这并不是生产就绪的。如果您在一个小团队中,开发者负责建立生产环境,不要简单地将您的开发环境复制到对互联网开放的服务器上。您至少应该将数据库映射到更健壮的永久存储,以及加固 MySQL,为您的应用程序使用非特权账户,并使用一个不明显的主机密码。
我们现在正在运行 MySQL 服务器。当您运行 Docker 命令时,您的输出有些晦涩且令人不满意。您可以在 图 11.2 中看到我的输出。
图 11.2:一串看似随机的字母和数字是 Docker 表示“我爱你”的方式,或者至少表明您的容器正在运行
有句老话说,告诉一个人银河系中有万亿颗星星,他会相信你。告诉他他的容器在 Docker 中成功运行,他会运行 docker ps -a
*来确认。实际上,我可能只是编造了这个。无论如何,让我们确保:
docker ps -a
您应该看到像我一样的证据,在 图 11.3 中。CONTAINER ID
的值对于每次运行都会不同,所以不要期望您的与我的匹配。
图 11.3:我可以看到名为 pycharm-mysql 的容器正在运行,并已暴露端口 3306
停止和启动容器
当您准备好退休时,您可能想停止容器:
docker stop pycharm-mysql
这将停止容器。您可以使用我们一直在使用的相同的 docker ps -a
命令来确认状态已从 Up 更改为 Stopped。明天早上,当您回来时,键入以下内容:
docker start pycharm-mysql
这将启动一切,以便您可以从上次离开的地方继续。
使用 PyCharm 连接到数据源
打开 PyCharm 专业版并创建一个名为 database_fun
的新 Python 项目。现在,定位数据库工具。您可以在右侧工具栏中通过看起来像三层蛋糕(美味!)的数据库图标找到它们。或者,您可以通过点击视图 | 工具窗口 | 数据库的汉堡菜单(美味!)来找到它。两种选项都在图 11.4中显示。
图 11.4:打开数据库工具的两种选项 – 一种来自菜单,另一种通过点击数据库工具图标
在打开数据库工具后,您需要创建一个新的数据源。注意这个术语的通用性。PyCharm 支持关系型数据库以及非关系型数据库,所以“数据源”这个术语只是一个通用的方式来指出这一点。点击图 11.5中显示的+图标,然后悬停在数据源上。您将看到一个支持的数据源的长列表。找到MySQL并点击它。
图 11.5:PyCharm 支持的数据源
您将看到图 11.6中像我一样的配置窗口。
图 11.6:MySQL 数据库的配置窗口
每个数据库服务器可能有略微不同的设置,但本质上它们归结为 IP 地址(或 DNS 名称)、端口、安全凭证,以及非常常见的是默认数据库,这可能还不存在。我们为我们的 MySQL 服务器定义了根密码为 P@ssw0rd
,并且我们知道由于我们在 Docker 中运行,我们的 IP 地址将是 localhost
。您可能也记得我们之前运行的 docker ps -a
命令中显示的端口是 3306
。前一个图中的箭头指向 PyCharm 数据库工具的一个重要方面。
PyCharm,就像大多数 JetBrains IDE 一样,是用 Java 编写的。因此,PyCharm 需要依赖Java 数据库连接(JDBC)驱动程序才能工作。大多数 JDBC 驱动程序是由发布数据库的同一公司或团队编写的,这意味着如果没有律师介入,JetBrains 通常不能将那些驱动程序捆绑到 PyCharm 中。没有人想那样做!JetBrains 做了下一件最好的事情。IDE 可以自动下载和安装驱动程序,但您必须通过点击此屏幕上的下载缺失的驱动文件链接来启动此操作。这只需要在您第一次使用数据库驱动程序时完成。一旦安装了驱动程序,图 11.6中箭头所示的选项就不再出现。您可以通过点击测试连接链接来测试您的连接。如果一切正常,您将收到一条确认消息,表明您的数据库连接成功。点击确定以关闭连接对话框。
关闭连接对话框后,您将在数据库面板中看到您项目的数据源连接列表。数据库面板顶部有一个可见的小工具栏,如图 11.7 所示。
图 11.7:数据库面板顶部有一个小菜单栏
与往常一样,我在图中对选项进行了编号。让我们回顾一下:
-
我们已经看到的添加数据源按钮允许您将新的数据源添加到您的项目中。
-
复制按钮允许您快速复制,可能进行一些小的调整。我的许多项目涉及一个服务器上的多个数据库。我只需设置第一个数据库,然后复制连接并更改数据库名称。此选项使这个过程变得快速且简单。
-
刷新按钮用于重新加载您连接的元数据。请记住这一点。PyCharm 不会自动跟踪您所有的数据库更改,尤其是如果您在 PyCharm 之外进行更改。您需要定期点击刷新按钮,以确保您查看的是数据源的最新信息。
-
数据源属性按钮将显示一个对话框,允许您更改数据源配置的设置。
-
断开连接按钮将使您从数据库服务器断开连接。
-
编辑数据按钮允许您使用图形化、类似电子表格的用户界面直接在数据库中的表中编辑数据。这对于快速添加或更改测试数据来说很方便。
-
转到 DDL按钮将带您到当前所选内容的 SQL 定义。您需要有一个 DDL 映射才能正确工作。
-
比较结构允许我们比较两个数据库结构。这通常用于在正常开发过程中进行更改后,帮助将一个数据库结构迁移到另一个数据库。PyCharm 2023 的迁移技术仅部分完成,因此这个功能在你阅读这本书的时候可能已经有所变化。
-
跳转到查询控制台基本上是您在命令行与数据库交互的常用工具。当您第一次连接到数据库时,会自动打开查询控制台,但如果您已关闭它,此按钮将打开它并将其置于焦点。
-
过滤器按钮允许您过滤您在已定义的数据源中看到的内容。默认情况下,所有选项都处于开启状态,这可能对大多数不习惯如此明确地看到数据库所有细节的开发者来说有点多。
除了这些工具之外,还有一些与设置您如何查看和使用数据源相关的其他工具。一旦我们有一个数据库可以操作,这些工具的解释将会更容易。让我们花点时间创建一个数据库。
创建新的数据库
在我们做任何事情之前,我们需要一个新的数据库。大多数数据库服务器只是将这个称为“一个新的数据库”。MySQL 稍有不同。他们将一个新的数据库称为新的 @localhost
) 并点击 新 | 模式,如图 图 11.8 所示。
图 11.8:通过右键单击服务器(箭头所示),然后选择“新”|“模式”来创建一个新的数据库
当您这样做时,系统会提示您命名模式,如图 图 11.9 所示。我将我的模式命名为与 PyCharm 中的项目匹配。我将它命名为 database_fun
。
图 11.9:使用此对话框命名您的模式
在幕后,PyCharm 正在生成和执行 DDL 语句。这就是它能够对许多数据库选项保持如此无差别的理由。您可以在 图 11.9 中看到它将运行的命令预览。点击 确定 执行命令,数据库窗口将更新以显示新的模式。
您刚刚创建了一个新的数据库!使用 PyCharm 支持的任何其他数据库服务器软件同样简单。
在我们开始处理数据库结构之前,还有一些其他设置选项需要考虑。
设置 SQL 方言(这是至关重要的)
由于 PyCharm 支持数十种不同的数据库,每种数据库都有自己的 SQL 方言,因此可以理解您可能需要告诉 PyCharm 您打算使用哪种 SQL 方言。您不会忘记这样做,因为 PyCharm 会一直催促您,直到您填写设置。
在您的项目窗口中,右键单击项目并创建一个名为 test.sql
的新文件。这与我们一直在使用的 Python 文件的过程相同,只是列表中没有模板。右键单击项目,然后点击 新建文件,如图 图 11.10 所示。
图 11.10:没有特定的 SQL 文件列表,所以只需右键单击并选择“新”|“文件”
将会弹出一个小的对话框。将文件的名称键入为 test.sql
。您这样做的时候,就会开始催促。您会看到一个消息指出 SQL 方言尚未配置,如图 图 11.11 所示。
图 11.11:PyCharm 会一直催促您,直到您配置 SQL 方言
如果 PyCharm 是我,而 PyCharm 的用户是我的 13 岁女儿,那么会有很多翻白眼,一个沮丧的哼声,然后说,“好吧!我会设置 SQL 对话框!但我的朋友们都没有这么做!”然后我会说,“如果你的朋友们都在他们的电脑上安装了 Windows 7,你会跟随他们的例子吗?”然后她会垂下头来说,“不,当然不会。”
没有人想看到这个对话框播放出来,所以我们最好让我们的 IDE 君主开心。点击 图 11.11 中的链接来设置方言。你需要本地和全局地设置它。配置如 图 11.12 所示。
图 11.12:全局和本地设置 SQL 方言
全局设置适用于所有项目,所以如果你像我一样,你只使用一种数据库服务器类型,你可以在全局这里设置它,它将为所有你的项目设置。项目方言稍微复杂一些。如果你重视你的理智,你真的需要为你的 SQL 文件创建一个文件夹,然后为文件夹设置方言。在这里,我只是向你展示设置在哪里。请先选择 MySQL 作为你的全局和本地方言,我们稍后再讨论整个文件夹的想法。
对数据源进行分组和颜色编码
我的工作是创建我雇主销售的服务软件产品。这意味着在项目的头七年里,我只处理一个数据库。随着产品功能的增长,我们向项目中添加了两个额外的 SQL 数据库、一个 MongoDB 数据库和几个 Redis 数据缓存。与我所参与的一些项目相比,我的项目仍然相当温和。如果你要处理很多数据库,PyCharm 允许你以几种不同的方式组织它们。
通过文件夹组织
你可以通过将数据源分组到文件夹中来组织你的数据源。我们只有一个,但我们仍然会通过这个过程。点击 图 11.8 中箭头所示的数据源。上次我们是右键点击;这次,只需点击数据源并在键盘上按 F6 键。这将弹出一个对话框,如 图 11.13 所示。
图 11.13:为你的数据源创建一个新文件夹
通过文件夹组织可以很有用,如果你在项目中有很多数据库,或者你可能为开发、用户验收测试(UAT)和生产有单独的连接集。
使用颜色编码数据库
我很喜欢这个功能,因为我有四个环境:
-
一个本地数据库,就像我们现在这样
-
一个中央开发测试数据库,在产品团队被允许看到我们的更改之前,开发人员会验证他们的设计
-
一个用于 UAT 的与我们的应用程序预发布版本连接的预发布数据库
-
一个生产数据库
我总是对一切进行颜色编码!你可以通过打开 数据源属性 窗口来为数据源设置颜色。为此,请点击工具栏上的 数据源属性 按钮。我之前在 图 11.7(4)中展示了这个。点击此按钮将弹出一个与之前创建数据源时使用的对话框相似的对话框。看看我的 图 11.14。
图 11.14:在属性窗口中点击数据源名称旁边的无害点,为您的数据源设置颜色
您可能从这里就能理解。一旦关闭对话框,您在 IDE 中的数据库元素就会变得五彩斑斓。我个人将我的本地开发环境设置为紫色,开发环境设置为绿色,预发布设置为黄色,生产设置为红色。不是从选择中选出的玫瑰色,而是一种鲜艳、刺眼、过分、像消防车一样的红色,我从列表底部的自定义对话框中挑选出来的。作为项目开发负责人,有时我需要查看生产环境中的项目。我想确保我能记住我在哪里!颜色编码有助于!
在项目之间共享数据源
如果您有多个使用相同数据源的项目,每次都要为每个项目重复设置相同的内容将会很繁琐。幸运的是,您不必这样做。PyCharm 允许您将数据源设置为全局,这意味着它可以在 PyCharm 的所有项目中使用。在 图 11.15 展示的数据源属性窗口中,找到箭头指示的按钮。
图 11.15:您可以将数据源配置设置为全局,这样它就可以在所有项目中使用
如果您点击它,对话框会非常微妙地改变,以表明数据源现在是全局的。如果您想检查,只需创建一个新项目,就像我们之前做的那样。打开数据库工具,您会发现数据源已经在那里了。我创建了一个名为 more_database_fun
的项目,如图 11.16 所示,数据源确实被继承了下来。
图 11.16:我创建了一个新项目并打开了数据库工具。全局数据源就在那里
由于这个第二个项目中没有代码,我没有将其包含在章节源代码中。
另一个有趣的功能是,当您导出 IDE 设置时,全局数据源也会被导出。如果您需要复习导入和导出设置,请参考 第二章,安装和配置,但我将在这里指出 图 11.17 中提到的导出设置。
图 11.17:您可以将全局数据源导出到设置导出 ZIP 文件中,允许其他人简单地导入相关设置
如果你是一名开发主管,你可能考虑创建一个不包含颜色和字体大小等个人偏好的 IDE 设置导出。这些可能更适合在笔记本电脑和工作站之间复制设置时使用。但是,导出更多与工作相关的设置,如数据源,以便你的团队能够轻松导入它们是有意义的。如果你这样做,请记住你也在复制凭证!你应该只将开发数据源作为全局导出,以免你的settings.zip
文件落入错误的手中。在越来越多的人使用云数据库服务器而不是本地安装的服务器的时代,这一点尤为重要。
如果你需要严格控制凭证,PyCharm 可以与名为KeePass的产品集成。由于这相当专业,我这里不会介绍,但我会在这个章节的进一步阅读部分留下一个包含更多详细信息的链接。
使用 SSL 和 SSH 选项进行连接
为了获得更高的安全性,特别是针对云数据源,PyCharm 支持 SSL 和 SSH 配置选项。请参阅图 11.18以获取你的数据源安全设置选项。
图 11.18:SSH 和 SSL 安全选项可以在 SSH/SSL 选项卡的数据源设置中找到
我在这里不会深入探讨,但你需要知道这些设置在 IDE 中的位置,因为你在使用像 Microsoft Azure 这样的云服务提供商时需要它们。
数据库设计和操作
让我们继续到你可能一直在等待的部分:构建数据库的部分!我们已经创建了架构,但到目前为止,我们还没有任何表。让我们先解决这个问题!
创建一个表
右键单击我们之前创建的database_fun架构,然后点击新建 | 表,如图图 11.19所示。
图 11.19:右键单击箭头指示的架构,然后点击新建 | 表
你将得到一个新窗口,如图图 11.20所示。
图 11.20:新窗口
我不得不稍微拉伸一下才能看到全部内容,如图所示。接下来的几个步骤将构建我们的表。我在顶部的名称字段中输入了表名。你可以看到这里发生了什么。和之前一样,在架构创建 DDL 中,PyCharm 正在窗口底部的预览中构建 DDL 脚本。目前,我们在分号下有一个红色的波浪线,因为我们还没有添加任何字段,所以这个 DDL 是无效的。
在窗口的左上角有一个带有+图标的按钮。点击它以查看可以添加到表中的元素列表,如图图 11.21所示。
图 11.21:你可以通过点击+按钮向表中添加内容
从这里,你可以选择你需要添加的任何内容。我们将首先添加我们的主键字段。如果你是关系数据库设计的新手,并且需要一个好的入门书籍,Michael Hernandez 的《Database Design for Mere Mortals》是必读之作。我已经在我的课堂上使用了 20 多年!Hernandez 还有一本关于 SQL 查询同样有影响力的书,另一位作者 Ben Forta 也是如此。我将细节留在本章的“进一步阅读”部分。
简而言之,我们创建的每个表都应该有一个字段可以唯一标识单个记录。它不应由与表中包含的数据域相关的数据组成。我的意思是,我们正在创建一个表来存储关于图书作者的信息。我们不应使用与作者相关的任何字段作为主键,即记录的唯一标识符。相反,我们应该使用一些无关的东西。在 MySQL 中,通常使用自动增长的整数字段。
自动增长的整数字段指的是一个字段,它将自动填充一个从 1 开始的序列号,并在每次记录插入时自动增加。这个增加不是你应该在代码中做的事情。这是数据库本身的一个特性。这很重要。关系数据库被设计成原子的,这意味着所有事务都是隔离的。这很重要,因为它意味着即使两个记录插入在彼此微秒之内发生,数据库也不会为自动增长的键生成相同的值。这是你不能通过自己的代码保证的事情。你需要依赖服务器来做这件事。
使用图 11.21 中所示的菜单,添加一个列并设置如图 11.22 所示。
图 11.22:添加具有此处所示设置的第一个列
在这里,我们创建了一个名为author_id
的自动增长的整数字段。我在这里使用的是蛇形命名法,但这不是必需的。使用你项目需要的任何命名约定。注意,我还没有正式将其设为主键。那即将到来。首先,让我们完成其余的列。
如果你选中author_id
字段,然后点击加号按钮三次,这可以快速完成。当你选中一个列,或者甚至当你在窗口中选中columns
文件夹时,PyCharm 会假设当你点击+按钮时,你想要一个列。同样,你也可以选择其他文件夹,比如主键、外键和索引。PyCharm 会简单地创建它,而不会让你使用我们之前看到的菜单。
按如下方式配置三个字段:
-
first_name
:varchar(30)
-
last_name
:varchar(30)
-
email
:varchar(255)
not null
图 11**.23显示了最后一个字段配置。我给这个字段添加了not null
约束,这将防止在email
字段留空时记录插入完成。这是通过点击标题为非空的复选框来完成的,如图图 11**.22所示。
图 11.23:按图所示配置电子邮件字段
它开始看起来像一张表,不是吗?我们还需要几样东西。让我们继续配置author_id
字段,使其成为一个合适的主键。
设置主键
点击keys
文件夹,然后点击+按钮。你会被问是否要创建主键或唯一键,如图图 11**.24所示。
图 11.24:确保已选择 keys 文件夹,如图所示,然后点击+图标并选择主键
这将弹出一个不同的窗口,如图图 11**.25所示。
图 11.25:添加主键需要点击+图标,然后从下拉菜单中选择字段名
要添加键,点击author_id
字段将其添加为主键。关系理论允许创建复合键,这就是为什么你可以添加多个字段。在一般实践中并不常用,因为如果你为键使用自增整数,创建复合主键就不必要了。实际上,我认为复合键是一个代码异味。
添加唯一键约束
接下来,让我们约束email
字段,以确保任何插入的记录的值必须是唯一的。这可以防止由随意插入引起的数据毒性问题。每个电子邮件地址应该只有一个记录。让我们通过唯一键正式实施它。
这个过程与设置主键相同,只是这次我们将选择唯一键而不是主键,并指定email
字段。但是有一个技巧。这次,你需要右键点击keys
文件夹来选择唯一键。如果你只点击+图标,它将添加另一个主键字段,这不是我们想要的。图 11**.26显示了你要做什么。
图 11.26:右键点击 keys,然后点击新建 | 唯一键以避免创建第二个主键
右键点击前一个图中的箭头所指的keys
文件夹,然后点击email
字段。我还会将名称从生成的authors_pk2
值更改为authors_uq
。这将让你一眼就能在 DDL 中知道它是一个唯一字段约束。
添加索引
向表中添加索引将使过滤读取操作更高效。预期应用程序允许用户通过电子邮件地址搜索作者是一个合理的要求。将索引添加到 email
字段将使此搜索更快,但会稍微减慢新记录的插入速度。插入性能的损失很小,但如果您要将索引添加到每个字段,这将变得明显,所以请仔细选择要索引的字段。
创建键的过程与此相同。请参阅 图 11.27。
图 11.27:右键点击索引文件夹并添加如图所示的索引
这次,我们右键点击 indexes
文件夹,然后点击 新建 | 索引。从这里,对话框看起来很熟悉,如图 图 11.28 所示。
图 11.28:我们已经为电子邮件字段添加了一个索引
到目前为止,我们的表代码无法放入屏幕截图,所以这里是我们的内容:
create table authors
(
author_id int auto_increment,
first_name varchar(30) null,
last_name varchar(30) null,
email varchar(255) not null,
constraint authors_pk
primary key (author_id)
);
create unique index authors_email_uindex
on authors (email);
alter table authors
add constraint authors_uq
unique (email);
当您对表结构满意时,点击 确定,PyCharm 将将生成的 DDL 代码应用到数据库中。您可以在数据源中查看结果。我的结果如图 图 11.29 所示。
图 11.29:我们的辛勤工作成果显示在数据源面板中
更改现有结构
这些年来,我见过很多工具擅长让您图形化地创建数据库结构,但当涉及到更改现有结构时,它们却无能为力。微软 SSMS 就是一个例子!它让您愉快地设计,但当您尝试提交更改时,它会告诉您无法执行。
PyCharm 并非如此。PyCharm 处理变更的方式与您公司数据库管理员(DBA)希望您执行的方式一致。如果您是新手,DBA 负责数据库管理。他们是老板。如果您有幸获得在他们的数据库中创建任何内容的特权,您将遵循他们的规则。他们希望您使用 SQL 的 alter
语句来更改结构。
右键点击 authors
表,然后点击 修改表…,如图 图 11.30 所示。
图 11.30:您可以通过右键点击表来更改现有表
当您这样做时,您会看到一个如图 图 11.31 所示的窗口。
图 11.31:您在这里所做的更改将生成一个 alter 语句,这是数据库开发中的最佳实践
在图 11.31中,我添加了一个名为date_of_birthd
的列,其类型为日期。正如你在预览窗口中可以看到的,PyCharm 正在生成一个alter
语句,而不是像许多数据库编辑器那样尝试删除并重新创建表。如果你想要删除表,菜单中有一个选项,如图 11.30所示。
生成脚本
PyCharm 有一个非常强大的工具,可以生成各种 SQL 脚本。由于我们在这里讨论的是 DDL,因此学习如何生成创建我们一直在工作的表的完整 SQL 脚本是有意义的。
右键单击数据库,如图 11.32所示,然后点击SQL 脚本 | SQL 生成器…菜单项。
图 11.32:SQL 生成器生成数据库的创建脚本
左侧的面板允许你设置一些有趣选项。当你点击它们时,如果适用,生成的 SQL 将会改变。我说“如果适用”,因为并非每个数据库平台都支持列表中的每个语法选项。我们使用的是 MySQL,它支持Use CREATE IF NOT EXISTS
语法,但不支持在 Postgres 或 Oracle 中找到的CREATE OR REPLACE
语法。点击不支持的语言不会改变预览窗口中的语法。
当 DDL 脚本满足你的需求时,你可以使用最右侧的按钮(如图 11.32中圆圈所示)来复制、保存或在查询窗口中运行脚本。
生成数据库图表
非常传统的数据库实践通常包括一个设计文档,如数据库模式图。在那些美好的日子里,我们会使用专门的工具来绘制图表。PyCharm 内置了一个图表工具,最好的部分是图表是根据数据库的结构生成的,而不是从头开始绘制。
要生成一个图表,只需右键单击你的数据库,找到图表菜单项,然后选择图 11.33中显示的任一选项。
图 11.33:显示生成的图表的菜单项
这两个选项之间的唯一区别是一个直接在 IDE 的内容区域绘制图表,而另一个在弹出窗口中生成它。弹出窗口对于在另一个显示器上查看图表很有用。你可以使用缩放工具导航图表,以及通过在图表内右键单击并拖动来平移。
你也会在顶部工具栏上找到导出选项,可以将你的图表导出为图像文件,以及可以导入到专用图表工具中的数据文件。
使用 SQL 查询数据源
查询数据库可能是将数据库工具直接构建到 IDE 中的第二大有用功能。与 PyCharm 中的许多功能一样,这个功能确保你永远不需要离开 PyCharm 来完成工作。
有几个地方允许你运行查询。你可以在查询控制台中运行临时查询,或者你可以直接从.sql
文件中运行查询。
临时查询
临时查询只是针对即时目的的查询。临时查询有几个特点:
-
非计划性:临时查询不是预定义查询集的一部分。它们是即兴编写的,以解决特定的需求。
-
临时性:它们用于检索特定任务或情况的数据,并且不会保存供将来使用。尽管经过一些实验和调整后,你可能会在代码中将它们正式化,但它们不是你应用程序的一部分。
-
无优化:临时查询可能没有针对效率进行优化,因为它们是迅速组合而成的,没有时间进行细致调整。
-
可变性:临时查询的语法和结构可能因用户的知识和经验而异。
-
不存储:与存储过程或视图不同,临时查询不会像视图或存储过程一样存储在数据库中。这意味着除非你稍后将其保存到文件中,否则它们不能被再次调用或执行。
临时查询是在数据库控制台中执行的。当你完成与数据源的连接时,会自动出现控制台窗口,但在界面的许多地方你都可以创建一个新的控制台视图。看看图 11.34,你会看到数据源窗口。一个标记为QL(查询语言)的按钮被圈出。它没有说SQL,因为,记住,PyCharm 也支持 NoSQL 数据库,如 MongoDB、Apache Cassandra 和 Redis。
图 11.34:在任何地方看到这样的 QL 图标,你都可以启动控制台窗口并使用临时查询查询数据源
QL按钮对数据源窗口中选择的数据库敏感,所以如果你有多个数据源,你可以通过在数据源窗口中选择它们并点击QL按钮来为任何一个打开查询源。实际上,我在创建数据源时自动启动的控制台已经关闭了。要创建一个新的控制台,我会点击QL按钮并选择新控制台。
你首先打开的控制台将成为默认控制台。PyCharm 在Scratches and Consoles文件夹中跟踪你的控制台,如图图 11.35所示。
图 11.35:PyCharm 在 IDE 中一个专门的文件夹中跟踪控制台和临时文件
如果你已经在控制台中输入了任何 SQL,PyCharm 将在会话之间保持内容不变。当你退出 PyCharm 时,空的控制台将消失。这对于跟踪你的临时工作非常有用。
生成 SQL 语句
我们需要创建一些种子记录,这些记录最终将被保存为一个种子脚本。种子脚本是一个可以用来通过在数据库中填充一些初始测试数据来测试应用程序的脚本。我们正在远离我们迄今为止使用的严格 DDL 语句。我们将使用更多的 DML,这些是用于处理数据而不是数据库结构的语句。种子脚本对于填充很少改变的数据表中的静态数据也非常有用。
我们将使用 PyCharm 为我们的 authors
表生成一些记录,但我们将让 PyCharm 为我们生成大量的 SQL。如果你还没有打开控制台,请先创建一个。右键单击 authors
表,将鼠标悬停在 SQL 脚本 上,然后找到 向表中插入行。查看 图 11.36 以了解其操作。
s
图 11.36:PyCharm 将会自动为您生成 DML 查询!
一旦你点击了选项,你就会发现一个 insert
语句已经被直接插入到控制台(哦,是的!)中。你可以在 图 11.37 中看到我的示例。
图 11.37:PyCharm 为我们选定的表生成了一个 insert 语句
注意文本周围的边界框。我们之前见过这个。你正在看一个模板!这意味着我们可以从模板文本的一个地方跳到另一个地方,这就是为什么你不应该像我一样乱动文本。
复制粘贴警告!
如果你有这本书的电子版,允许你从电子书本身复制粘贴,请注意任何依赖于单引号的代码。编辑书籍的过程常常将它们转换为特殊字符,这些字符在粘贴到控制台时将无法正常工作。请确保单引号确实是单引号!
insert
语句可以分为两部分:
insert into authors (first_name, last_name, email)
values ('Bruce', 'Van Horn', 'bruce@test.com');
我所说的两部分是指语句中不是模板的部分——两对括号中的部分。第一部分指定了你将填充哪些字段,第二部分是值。当你为每个字段输入值时,你必须匹配第一部分中字段的数量和类型。如果你按下 Tab 键,你将在 insert
语句的两部分之间移动,允许你根据需要修改它。
非常规生成的 SQL
PyCharm 默认不会将 SQL 关键字全部大写,这是常规做法。你可能注意到我输入的语句都是大写的,但生成的语句不是。我不得不抵制修改它的诱惑,为了这本书,但我也需要解释为什么存在不一致性。
PyCharm 为主键生成了一个占位符,这是一个你通常不会填充的字段,因为数据库会自动填充它。按照我在前面代码中所做的那样,将第一个字段从第一个括号中的字段集中移除。
接下来,切换到 values
部分,并开始填写值。记住,在 SQL 中,varchar (字符串)
值必须用单引号括起来。Python 允许你使用单引号或双引号,但 SQL 不允许。当你输入值时,PyCharm 会给你关于你正在匹配的字段的提示。它是通过在编辑器中的文本上显示工具提示来做的,但它也在语句的上半部分对当前字段进行着色。参见 图 11.38 了解我的意思。
图 11.38:PyCharm 为你在查询的第二部分中正在填写的字段和类型提供工具提示
这太棒了!你有多少次写了一个长的 insert
语句,结果发现你有的值比字段多?或者更糟糕的是,你发现你颠倒了几个字段,你错误地输入了一堆垃圾数据?PyCharm 通过显示包含字段和类型的工具提示来给你一个视觉指示,确保你知道你正在输入哪个字段。它还在 SQL 语句的上半部分对字段进行着色。
运行查询
你可能不需要我告诉你这一点,因为在这个时候,你可能已经习惯了在适当运行的地方看到绿色的运行箭头。你可以通过点击 IDE 中控制台标签页顶部的绿色箭头来运行控制台的内容。你也可以突出显示控制台内容的一部分,并运行查询的一部分。
将你的控制台内容更改为与此代码匹配:
insert into authors (first_name, last_name, email)
values ('Bruce', 'Van Horn', 'bruce@test.com');
insert into authors (first_name, last_name, email)
values ('Kinnari', 'Chohan', 'kinnari@test.com');
insert into authors (first_name, last_name, email)
values ('Prajakta', 'Naik', 'prajakta@test.com');
insert into authors (first_name, last_name, email)
values ('Jubit', 'Pincy', 'jubit@test.com');
我所做的只是将相同的 SQL 重复四次,然后我更改了名称以增加一些变化,并向我的 Packt 团队致敬(嗨!)。接下来,突出显示第一个语句并运行查询。服务面板将打开以显示查询结果。如图 11.39 所示,只插入了一条记录。
图 11.39:你可以通过选择语句并点击运行按钮来执行单个语句
如果你选择剩余部分并再次运行,你会找到三个插入点。
现在我们有一些数据了,让我们用 SELECT
语句进行查询。将以下代码添加到你的控制台:
SELECT * FROM authors;
我本可以生成 select
语句,但这个查询很短。我想向您展示 PyCharm 如何显示结果。请看 图 11.40。
图 11.40:默认情况下,选择语句产生一个很好的表格输出
这是一个密集的面板!您在这里可以做很多事情。默认情况下,您会看到数据的表格视图(1)。您可以双击单元格并就地编辑数据。您还可以使用此 UI 添加和删除行(2)。在您编辑完数据后,您可以使用工具栏(2)中的小绿色向上箭头提交您的更改 图 11.40。
如果您想将输出视图从表格视图更改为纯文本或树视图(这对于查询层次数据更有意义),您可以在 图 11.41 中的工具栏(2)中点击眼睛图标。
导出查询结果
您可以使用导出按钮导出 select
语句的结果 图 11.41。图 11.41 展示了其外观。
图 11.41:在 PyCharm 中导出数据非常简单
默认的导出格式是 逗号分隔值(CSV),这是一种常见的表格数据交换格式。PyCharm 支持数十种其他有用的格式,您将在 提取器 下拉列表中找到。将输出文件更改为您喜欢的名称,然后点击 导出到 文件 按钮。
与 SQL 文件一起工作
PyCharm 支持具有 .sql
或 .ddl
文件扩展名的文件。与其他已识别的文件类型一样,您将获得自动完成、语法高亮等功能。您还可以直接在 SQL 文件中运行语句,就像我们在控制台中一样。
让我们将到目前为止的控制台工作保存为一个合适的脚本。在 PyCharm 中创建一个名为 seed.sql
的新文件。接下来,将此行添加到脚本文件顶部:
USE database_fun;
TRUNCATE TABLE authors;
USE
语句是 MySQL 特有的,确保在执行 SQL 语句之前已经选择了 database_fun
数据库。TRUNCATE
语句将删除 authors
表中的所有记录,并将表上的自增序列重置为其初始值。这对于开发测试很有用,因为现在种子脚本可以重置到一组干净的测试值。只是要小心,永远不要在生产环境中运行种子脚本!
接下来,将控制台的内容剪切并粘贴到 TRUNCATE
语句下方。注意,我说的是剪切。您想要控制台为空。脚本应该看起来像这样:
USE database_fun;
TRUNCATE TABLE authors;
insert into authors (first_name, last_name, email)
values ('Bruce', 'Van Horn', 'bruce@test.com');
insert into authors (first_name, last_name, email)
values ('Kinnari', 'Chohan', 'kinnari@test.com');
insert into authors (first_name, last_name, email)
values ('Prajakta', 'Naik', 'prajakta@test.com');
insert into authors (first_name, last_name, email)
values ('Jubit', 'Pincy', 'jubit@test.com');
SELECT * FROM authors;
保存您的种子脚本并运行它。它应该会成功执行。
摘要
我有一种可怕的感觉,觉得自己没有涵盖到所有内容。这是因为我没有。我可以写一本关于 PyCharm 数据库特性的整本书,但如果我这样做,我可能不如给它起个类似 使用 DataGrip 进行数据库编程实践 的标题。别忘了,我们看到了 PyCharm 中挤满了另一个 IDE,就像我们在 第七章,使用 JavaScript、HTML 和 CSS 进行 Web 开发 中所做的那样。功能集真正令人印象深刻,我没有涵盖一些真正有趣的功能,要么是因为这本书的空间有限,要么是因为一些真正酷的功能仍在开发中。
例如,你可以将代码设置为数据源,并将代码同步到数据库的结构中。目前,它还不是 100% 完成,这就是为什么我将其排除在外。作为一名数据库开发者,我期望能够有一个往返体验,即代码的更改反映在数据库中,数据库的更改复制回代码。我认为我们已经走上了正轨。
尽管如此,我们所涵盖的内容对于全栈 Web 开发或需要访问多个数据库平台和数据存储范式的数据科学从业者来说是非常全面的。
我们从一点历史开始。我喜欢在本章和前几章中介绍历史,因为我有丰富的职业生涯作为后盾。我见证了所描述的大部分内容,并且是第一排的观众。当我上一年级时,E. F. Codd 正式化了关系代数。我的一部分希望我能吹嘘使用他的早期工作,但事实上,我更热衷于穿着睡衣在周六早上观看电视节目 拉西和救援小队,而不是 SELECT
语句。当我长大后,我开始在 IBM 主机上工作,所以我最终赶上了。
进入更实际的问题,我们了解到 PyCharm 可以通过单独安装的 JDBC 驱动程序连接到数十种不同的数据库平台。我们了解到它不仅支持关系数据库,还支持 NoSQL。在我们学会了如何打开数据库工具并连接我们的数据源之后,我们学会了如何使用一系列为我们生成 DDL 的工具来设计数据库。
然后我们转向使用 PyCharm 生成 DML,一旦我们在表中获取了一些数据,它就能为我们的工作提供动力。最后,在控制台中创建我们的初步工作后,我们将 SQL 保存到了一个 .sql
文件中。
这本书中这一章节的位置是非常有意的。我克制自己没有在上一章中涵盖的完整栈框架中进行大量编码。在现实生活中,你肯定会将这些数据库附加到这样的项目上。虽然许多开发者依赖 ORM 来处理他们的数据库工作,但至少能够直接使用控制台检查数据库及其内容是有益的。
如果你像我一样,完全放弃了 ORM,你现在有了拼图缺失的一块。你可以在不离开 PyCharm 的情况下,使用一套出色的工具来创建你的数据库代码。
本章也进行了逻辑划分,因为接下来的几章开始介绍 PyCharm Professional 的强大数据科学功能集。在介绍这些最普遍的数据存储和检索引擎之前进行概述是有意义的。
在下一章中,我们将开启科学模式!去干洗店取回你的白大褂,给自己倒一大杯 C8H10N4O2(可能是指某种化学物质),我们下一章见!
进一步阅读
请务必查看本书的配套网站:www.pycharm-book.com
。
-
Docker Desktop 安装说明:
www.docker.com/products/docker-desktop/
。 -
在 PyCharm 中使用 KeePass:
www.jetbrains.com/help/pycharm/reference-ide-settings-password-safe.html
。 -
Codd, E. F. (1983). 大型共享数据库的关系数据模型。ACM 通讯, 26(1), 64-69。
-
Forta, B. (2013). 10 分钟学会 SQL. Pearson Education。
-
Hernandez, M. J. (2013). 数据库设计入门:关系型数据库设计实战指南. Pearson Education.
-
Pettit, T. 和 Cossetino, S. (2022). MySQL 实战. Packt Publishing.
-
Poulton, N. (2023). Docker 深度探索 – 第二版. Packt Publishing。
-
Viescas, J. L. 和 Hernandez, M. J. (2014). SQL 入门:SQL 数据操作实战指南. Pearson Education.
第四部分:使用 PyCharm 进行数据科学
与前一部分类似,本书的这一部分专注于 Python 编程的特定应用,这次是数据分析与数据科学。读者将能够使用 PyCharm 及其功能高效地处理他们的数据科学和科学计算项目。
本部分包含以下章节:
-
第十二章, 开启科学模式
-
第十三章, 使用 SciView 和 Jupyter 动态数据查看
-
第十四章, 在 PyCharm 中构建数据管道
第十二章:开启科学模式
欢迎来到本书的第三部分。我最喜欢的视频游戏系列之一是 Sid Meier 的 文明。完成一场游戏需要很多天,游戏开始时你作为历史黎明时期一个文明的首领。你将穿越史前时代,经过中世纪、文艺复兴和工业时代。随着每个新时代的到来,玩家们为了争夺全球统治的各个方面而竞争。一旦玩家们达到信息时代,游戏速度实际上会加快。我都能听到我的编辑在说,“放下你的视频游戏,打字快点!”好吧,我会的。谈到这本书,我觉得我们通过本章已经达到了一个类似的里程碑。自从这本书的前几章中在 main.py
中的“hello world”消息以来,我们已经走了很长的路。
PyCharm 面向 Python 开发者,但 PyCharm Professional 实际上针对的是两组人群:Web 开发者和数据科学家。后者似乎更难以界定,因为 JetBrains 为数据科学提供了多个产品。除了 PyCharm Professional,JetBrains 还制作了一个名为 DataSpell 的独立 IDE,它被誉为 专业数据科学家的 IDE。这是怎么回事?
我也有同样的疑问。根据公司针对 DataSpell 的常见问题解答,PyCharm 应该支持那些将数据科学活动作为软件开发工作一部分的软件开发者。DataSpell 是面向那些不是开发者,但仅仅进行数据分析的专业人士。在功能方面有一些重叠,但与完全嵌入 PyCharm 的 WebStorm 和 DataGrip 不同,DataSpell 拥有一些 PyCharm 没有的功能,而且没有计划改变这一点。他们针对的是两组不同的专业人士。
支持科学和数据分析工作流程意味着在 PyCharm 中开启科学模式。这会打开你通常在科学模式关闭时不会看到的新的 UI 布局和工具面板。在本章中,我们将涵盖以下主题:
-
在 PyCharm 中启动科学项目
-
PyCharm 科学项目的高级功能
到本章结束时,你将了解这些功能如何提高科学计算项目中的生产力。本章将作为一个通用的、高级的讨论,涉及 PyCharm 提供的各种工具,并帮助你理解科学计算以及这些工具是如何集成和相互协作的。
技术要求
为了继续本章,以及本书的其余部分,你需要以下内容:
-
由于我们将转向数据科学的话题,我将把我的首选 Python 发行版切换到 Anaconda,这是一个针对数据科学工作负载定制的 Python 发行版。你可以在
anaconda.com
找到它,以及针对你操作系统的安装说明。 -
同样,我将使用
conda
而不是通常的pip
,因为conda
是 Anaconda 的包管理器。它与 Anaconda 一起安装。 -
安装并运行良好的 PyCharm 版本。安装过程在 第二章**,安装和配置 中有介绍,如果你是在书中间开始阅读的话。
本书从 GitHub 上的示例源代码。我们在 第二章**,安装和配置 中介绍了代码克隆。你可以在 github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-12
找到本章的相关代码。
在 PyCharm 中创建科学项目
到现在为止,你对 PyCharm 中的新建项目对话框已经很熟悉了。让我们创建一个科学项目!点击 文件 | 新建项目,你将找到一个名为 科学 的模板,如图 图 12.1 所示:
图 12.1:退后!我们即将开始做科学实验!
我之前提到,我将使用 Anaconda 来编写这本书的这一部分,因为这是大多数数据科学专业人士所使用的。除了更改解释器之外,唯一有趣的设置是在 更多设置 下的 数据文件夹 设置。它是在我们即将创建的项目中设置一个文件夹,该文件夹将包含,你猜对了:数据。在创建项目之后我们会详细讨论这一点。请点击 创建。
一旦创建过程完成,你会在 图 12.2 中找到类似我这样的设置:
图 12.2:看哪!我们在 PyCharm 中创建了一个科学项目!
让我们谈谈刚刚发生的事情。我们像往常一样创建了一个项目。我们得到了一个名为 data 的文件夹,它之所以特别,是因为它的颜色与其它文件夹不同。我们还得到了更多用于笔记本和模型的空文件夹。我们得到了一个 main.py
文件,一个 requirements.txt
文件,以及一个名为 README.md
的 Markdown 文件,它们都是空的。我们通常会有一些样板代码,但这次没有。
每个文件和文件夹都有其用途。正如我提到的,数据文件夹之所以特别,是因为它的颜色不同。我的文件夹被高亮显示为黄色。这个文件夹很重要,因为它旨在包含用于你科学分析的数据文件。由于它不是代码,而且你的数据文件可能非常大,PyCharm 将这个文件夹标记为项目的特殊文件夹。你可能已经注意到 Flask 和 Django 应用中的模板文件夹也与其他文件夹颜色不同。许多项目类型都有特殊文件夹。
我们可以在项目的属性中找到一些线索。我在 图 12.3 中打开了项目属性窗口:
图 12.3:在 PyCharm 的许多项目类型中,包括科学项目,您可以设置几种特殊文件夹
在这种情况下,数据文件夹被排除在项目之外。PyCharm 将不会以任何方式索引或扫描此文件夹。我们需要这个数据文件夹,因为您的数据文件非常大是很正常的。这个文件夹中没有代码,所以实际上,没有必要扫描它以提供您从代码文件中期望的通常的帮助和见解。您甚至可以考虑让您的版本控制系统忽略这个文件夹,因为出于许多原因,包括数据性质导致的隐私问题,以及文件大小,将数据检查到存储库中可能并不合适。Git 对它可以处理的文件大小有限制,GitHub 也会限制您存储库的大小。数据文件夹是一个明显的冗余,可能不需要在存储库中。
解开这个谜团后,让我们看看requirements.txt
文件。我们之前见过这个文件。无论您使用哪个 IDE,Python 项目都会使用这个文件来跟踪项目的库需求。每次您从 PyPi 使用第三方库时,都需要将其包含在requirements.txt
中。这个文件应该被检查到您的存储库中,以便其他开发者可以复制您的虚拟环境。这个文件是空的,我们在第三章中看到了围绕requirements.txt
的工具。
这里有一些我们之前没有见过的东西:README.md
文件。.md
扩展名表示这是一个 Markdown 文件。Markdown 是 HTML 的替代品,而 HTML 是一种标记语言。他们在这里做了什么?Markdown 允许您创建一个格式化的文本文件,而不需要 HTML 的所有开销。虽然您去掉了开销,但也失去了很多功能。Markdown 仅用于创建文档文件。如果您曾经浏览过 GitHub 上的存储库,并且项目有一个漂亮的着陆页,那么您实际上看到的是 GitHub 和您的浏览器将README.md
文件渲染为 HTML 的结果。图 12.4展示了我的一个存储库中一个很好的README.md
页面的例子:
图 12.4:README.md 文件在 GitHub 上被渲染为着陆页,并用于记录存储库的内容
对于这个项目,PyCharm 已经生成了一个空的README.md
文件。main.py
文件只是一个普通的 Python 文件。实际上,这是一个非常简单的项目模板。
PyCharm 中科学项目的附加配置
PyCharm 的默认配置通常非常完整。然而,对于科学项目来说,您可能还想添加一些插件。本书后面将有一整章介绍一些惊人的插件,但您现在就需要它们,所以让我们来看看。
插件是通过 PyCharm 市场安装的。您可以通过通常用于访问设置的齿轮图标进入市场。您可以在图 12.5 中认出它:
图 12.5:插件菜单选项将带您进入包含 PyCharm 市场的插件窗口
打开插件窗口。您可以在图 12.6 中看到我的:
图 12.6:市场允许您找到 PyCharm 和其他 JetBrains IDE 的所有类型的插件
注意它会直接带您进入市场标签页。这让我想起了我参加过的每一次博物馆之旅结束时的不可避免的小商店。右侧有一个标签页,让您可以看到已经安装的内容。但这不是很无聊吗?浏览我们还没有的东西更有吸引力。这里的大多数插件都是免费的,有些则不是。同样,这里的大多数插件都有良好的文档,并由精心制作市场提供的开发者支持。有些只是简短的描述和标题。让我们去购物吧!
Markdown 插件
您的第一步是找到一个好的 Markdown 插件。尽管我们在科学项目中发现了 Markdown,但它并不完全是火箭科学。然而,一个好的 Markdown 插件会渲染您的 Markdown,这样您就可以看到 Markdown 代码转换为格式化文本后的样子。
要找到 Markdown 插件,您需要使用图 12.7 中指示的搜索框进行搜索:
图 12.7:在此处搜索插件,避免被帮助您找到设置的搜索框所诱惑
事实上,JetBrains 现在在默认安装中捆绑了一个 Markdown 插件。他们并不总是这样做。您可以看到有其他选择可以探索。我只会使用捆绑的插件。如果您有较旧的 PyCharm 版本,您可能需要安装此插件。在这种情况下,将会有一个安装按钮,就像您看到的其他插件一样。
让我们试试。退出设置窗口,打开README.md
文件。将以下代码添加到您的README.md
文件中:
# The Science Project
Welcome to my science project!
您应该看到左侧的 Markdown 代码,以及右侧的渲染预览,如图 12.8 所示:
图 12.8:Markdown 代码位于左侧;渲染结果位于右侧
你可以使用 Markdown 编辑器右上角的按钮更改预览布局,或者完全隐藏它,如图图 12.9所示:
图 12.9:你可以调整预览窗口的布局
点击左键隐藏预览。按右键隐藏代码。你可以点击中间的按钮一次来切换预览和代码。再点击一次,你将得到堆叠在一起的代码和预览。
添加图片
一个好的 Markdown 编辑器所需的一个特性是能够轻松添加图片。在 PyCharm 中,如果你需要在 Markdown 中添加图片,首先在你的项目中创建一个名为 assets 的文件夹。将你的图片文件复制到这个文件夹中。然后,只需拖动图片并将其拖放到编辑器中。在图 12.10中,我正在从我的 assets 文件夹中拖动一个图片并将其拖放到我的 Markdown 编辑器中:
图 12.10:从 PyCharm 外部拖动图片并将其拖放到代码中以将其包含在 Markdown 中
结果可以在图 12.11中看到:
图 12.11:拖动的图片出现在你的代码和预览中
总的来说,默认插件相当不错。其他一些插件有更高级的功能,但真正我想要的只是一个可以将 Markdown 转换为其他格式的插件。如果你需要这个功能,市场上的一些插件可以做到这一点。
安装 CSV 插件
数据存储的常见格式是逗号分隔值(CSV)格式。这是一个被所有电子表格程序支持的格式。市场上有一个 CSV 功能,允许你在类似电子表格的界面中打开 CSV 和类似文件,如制表符分隔值(TSV)。你可以在图 12.12中看到它的市场页面:
图 12.12:你可能会在某些时候使用 CSV 文件。幸运的是,有一个插件可以处理这个
在你点击 CSV 编辑器插件的安装按钮后,你将被提示重新启动 IDE。这是非常常见的。重启后,你可以用你自己的 CSV 文件尝试它,或者你可以使用示例代码数据文件夹中的 FDA 研究数据样本。CSV 插件以全彩色的形式渲染我的数据,如图图 12.13所示:
图 12.13:CSV 编辑器插件以不同颜色渲染列中的数据。这就像在你的 IDE 中嵌入了一个漂亮的电子表格
自然地,你可以编辑数据,但这不是一个电子表格。不要期望你能在单元格中添加公式和表达式。在底部,你会看到你可以切换到表格编辑器视图或文本视图。
你肯定还想安装的一个是单元格模式插件。
安装单元格模式插件
我现在不会深入探讨单元格模式插件的功能。当你说它在处理 PyCharm 代码单元格时非常方便时,请相信我。我们将在稍后讨论这个问题。既然我们已经进入了 Marketplace,让我们现在就把它抓取出来。图 12.14 显示了单元格模式插件的 Marketplace 页面:
图 12.14:单元格模式插件将在本章稍后使与代码单元格一起工作变得容易
通过点击安装按钮来安装它。此时,你已经将 PyCharm 准备得相当适合数据工作了。
安装包
我们准备做一些科学工作,但首先,我们需要为我们的项目设置一些要求。我们已经在前面的章节中讨论过这个问题,但我们将在这里快速回顾。
向任何 Python 项目添加包的最新方法是使用图 12.15 中显示的Python 包面板:
图 12.15:使用 Python 包面板管理 Python 包。如果它不在右侧工具栏上,你可以添加它
如果你之前使用过该面板,它将在右侧工具栏上。如果你之前没有使用过,你可以通过点击三个点,然后选择Python 包选项来添加它。一旦你找到了打开面板的按钮,你将找到图 12.16 中显示的搜索框:
图 12.16:你可以从这个面板中轻松搜索 PyPI 的包,并使用 conda 添加它们
我们将向我们的项目中添加 numpy
和 matplotlib
。将每个输入到图 12.17 中指示的搜索框中,然后使用使用 conda 安装按钮安装包。如果你选择坚持使用纯 Python,按钮将显示使用 pip 安装。
回填 requirements.txt 文件
现在你已经安装了这两个包,打开 requirements.txt
文件。PyCharm 会告诉你,在 conda 环境中安装的要求与空文件不匹配。图 12.17 显示了将我们刚刚安装的包自动添加到 requirements.txt
文件的选项。
图 12.17:PyCharm 帮助你保持 requirements.txt 文件更新
点击链接按钮来更新你的 requirements 文件。
添加一些科学代码
我的编辑器一直说“sciency”不是一个词。一位智者曾经说过:“科学不是关于为什么,而是关于为什么不!?”我会继续使用它,如果你真的在读这篇文章,这意味着我侥幸逃脱了。
我们已经设置了 IDE,并安装了所需的包。让我们打开main.py
并添加一些代码,这样我们就可以看到 PyCharm 展示其功能!在main.py
中添加以下代码:
import numpy as np
import matplotlib.pyplot as plt
这两个初始导入只是引入了numpy
和matplotlib
,并使用了别名。结果发现,科学家比普通开发者更讨厌打字:
N = 100
x = np.random.normal(0, 1, N)
y = np.random.normal(2, 3, N)
#%% plot data in histograms
plt.hist(x, alpha=0.5, label='x')
plt.hist(y, alpha=0.5, label='y')
plt.legend(loc='upper right')
plt.show()
使用 NumPy,我们只是从正态分布中创建了两个 100 个元素的样本数据集。x
的值来自均值为 0、标准差为 1 的分布,而y
来自均值为 2、标准差为 3 的分布。然后,我们使用 Matplotlib 绘制它们相应的直方图。按照惯例运行程序,并在图 12.18 中注意结果:
图 12.18:点击运行按钮(1),matplotlib 图形图表在 SciView 中显示
我们的 Python 脚本运行了,但这样做触发了 SciView 的开启。SciView 是 PyCharm 数据分析功能集的核心。它允许你在 NumPy、pandas 和 PyTorch 等库的复杂 API 文档旁边查看图表。
我在图 12.18 中突出了运行按钮(1),以防你来得晚,错过了之前关于在 IDE 中运行代码的章节。(2)处的图标表示 SciView 已开启。你可以像切换任何其他面板一样切换它开或关。当 SciView 开启时,你会得到一个用于可视化的面板(3)和一个用于文档的面板(4)。我们将在接下来的几章中更多地使用这些工具。
切换科学模式
我之前提到过几次科学模式;现在,我们将看到这个模式在 PyCharm 项目中的重要性。
科学模式由多个组件组成,我们将在本章和即将到来的章节中探讨,最显著的是SciView和文档面板。重要的是要注意,PyCharm 中的这种特殊模式并不等同于拥有科学项目。此外,它更像是一种配置设置,其中各种支持科学计算的 PyCharm 功能更容易访问和使用。
有许多程序根据你打算如何使用软件而具有不同的用户界面配置。Adobe Photoshop 为摄影师和网页设计师提供了不同的视图集。许多浏览器都有无铬选项或模式,优化了无干扰阅读。PyCharm 本身有一个禅模式,它移除了除了编辑器之外的所有用户界面。你可以在图 12中看到它。19*:
图 12.19:在禅模式拥抱下。PyCharm 的代码在和平中起舞。线条清晰
如果您对禅模式感兴趣,您可以通过点击主菜单中的查看 | 外观 | 进入禅模式来找到它。这很好,但我们在这里是为了科学模式,我想要说明的是,这种模式更多的是 PyCharm 外观的配置,而不是针对科学项目的独特工具面板。您可以在任何项目类型中启用科学模式。要打开它,请点击查看 | 科学模式,如图图 12.20所示:
图 12.20:您可以在视图菜单中切换科学模式。当科学模式开启时,您可以使用屏幕右侧工具栏上的按钮切换两个面板
PyCharm 也有点爱管闲事。如果您在使用 NumPy,它会在 IDE 的右下角弹出一个提示,建议您开启科学模式。见图图 12.21:
图 12.21:您的 Python 大兄弟在看着您,但至少它在尽力帮忙
总体而言,科学模式提供了一个直观的界面,可以提高您在科学计算项目中的生产力。在下一节中,我们将更详细地检查科学项目中的其他高级功能,特别是文档面板和 PyCharm 的代码单元格。
理解 PyCharm 科学项目的高级功能
配备了我们之前章节中讨论的功能,您可以高效且富有成效地导航和使用 PyCharm 的科学项目。然而,PyCharm 还提供了其他一些微妙的功能,这些功能在此背景下可能非常有用。首先,我们将考虑文档面板及其用法。
文档查看器
文档是编程和软件开发的重要组成部分,PyCharm 提供了最强大且最直观的功能来支持在 Python 中处理文档的任务。我们首次在第四章中看到了它的实际应用,在 PyCharm 中轻松编辑和格式化。
在科学项目中,正如我们所见,文档面板被固定为项目窗口的主要面板之一。这个文档查看器以动态的方式显示实时文档数据。具体来说,当您将光标移至编辑器中的特定方法或函数调用时,文档面板将显示与该方法或函数对应的官方文档。
回到我们之前编写的代码,并将光标放在第 10 行。将光标放在单词 legend 上,文档窗口将立即显示该方法的文档,该方法属于 Matplotlib 库。你可以在图 12.22中看到这一点:
图 12.22:点击几乎任何内容,文档面板将显示任何属性或方法的文档
这些在数据科学中流行的科学库的问题在于,它们可以使用复杂的数据结构作为参数和返回类型。拥有实例参考资料非常有用。我建议使用更大的屏幕才能真正利用它。我的截图图 12.22略显拥挤,仅仅是因为它是在一个较小的屏幕上,字体被调整到清晰以便于打印书籍。
除了显示你点击处的文档外,文档面板还会在你输入代码时更新。你不仅得到通常的自动完成,还能同时看到完整的文档。文档面板有一些配置选项,通过点击面板顶部的三个垂直点来访问,如图 12.23所示:
图 12.23:在面板顶部的菜单下方可以找到一些文档面板的配置选项
接下来,我们将探讨 PyCharm 在执行 Python 代码时的一个独特功能,即实现代码单元格。
在 PyCharm 中使用代码单元格
PyCharm 的代码单元格是一种将大型 Python 程序的不同部分按顺序分离和执行的方法。如果你曾经使用过Jupyter Notebook,你会认出这个功能。PyCharm 中的代码单元格基本上是这个工具的简化版。PyCharm 还直接支持 Jupyter 笔记本,但我将这部分内容留到下一章。
数据科学的需求与一般软件开发者的需求不同。数据科学家不会担心编写具有相同架构和扩展约束的可用代码。他们也不处理你典型的变量,这些变量大多是原始类型,或者是一些具有少量属性和方法的小对象。他们处理的是大量数据,他们编写的程序是算法性的。不要过分关注“算法”这个词。算法只是一系列构成过程的步骤。当然,推动谷歌搜索业务、PageRank 的算法是一项惊人的专有工程壮举。它是一个算法,但一个美味的花生酱果酱三明治的食谱也是一个算法。复杂性不是必需的,事实上,最好的算法通常是简单的。
数据科学家通过收集、过滤和处理大量数据,使用一系列聚合步骤来研究问题。每个步骤都非常离散,有输入和输出。代码单元格使得构建和使用这些离散步骤变得容易,而不是像常规软件开发人员那样处理复杂的代码结构。
使用 PyCharm 代码单元格
PyCharm 中的代码单元格由以下字符开始的代码行定义:#%%
。这些行在 Python 的低级执行中被视为标准注释,但 PyCharm 会在其编辑器中识别它们作为代码单元格分隔符。让我们看看这个功能是如何工作的。
让我们回到之前演示的代码,做一些小的修改:
import numpy as np
import matplotlib.pyplot as plt
#%% generate random data
N = 100
x = np.random.normal(0, 1, N)
y = np.random.normal(2, 3, N)
#%% plot data in histograms
plt.hist(x, alpha=0.5, label='x')
plt.hist(y, alpha=0.5, label='y')
plt.legend(loc='upper right')
plt.show()
注意到添加了以#%%
开始的两个行。这些行定义了我们的代码单元格。此外,它们还作为常规注释,解释代码块正在做什么。现在看看 PyCharm UI 的变化。您现在应该看到一些运行按钮出现在页边空白处,如图图 12.24所示:
图 12.24:响应特殊注释格式的运行按钮出现在页边空白处
现在,我们可以运行每个单元格,并在运行下一个单元格之前查看结果。如果我们需要在中间单元格中进行调整,我们可以重新运行该单元格及其后续单元格。当您点击运行按钮时,代码将在 PyCharm 控制台中执行。如果您的控制台不可见,您可以通过点击视图 | 工具窗口 | Python 控制台来访问它。在Python 控制台面板打开的情况下,您可以在代码运行时检查它,如图图 12.25所示:
图 12.25:您可以看到代码正在控制台中运行。有一个检查面板,允许您查看单元格执行后的情况
每当每个步骤运行时,您都可以在控制台窗口中看到代码的执行情况。记住,控制台正在访问 Python REPL,它不是一个终端窗口。我们已经在第三章中讨论过这一点。Python 控制台窗口有一个检查面板,允许您检查状态,就像您在调试时一样。
由于是 PyCharm 控制台在执行您的代码,如果您需要从头开始,只需终止控制台并再次运行步骤即可。这将启动一个新的控制台。
单元格模式插件
在之前讨论的一些可用于数据科学工作的酷插件时,我们安装了一个名为单元格模式的插件。单元格模式插件扩展了代码单元格的功能。
首先,使用单元格模式插件时,你需要使用双井号/哈希符号(##
)而不是通常的 #%%
。当你这样做时,插件就会接管并提供一些额外的运行选项。看看 图 12.26:
图 12.26:这次当你点击时,你会得到一些额外的选项
你的运行按钮现在允许你运行单元格并立即跳转到下一个,而不仅仅是停止在你刚刚运行的单元格底部。图 12.27 显示你在菜单级别有一些额外的控制:
图 12.27:单元格模式插件向代码菜单添加了额外的菜单项
当你点击 代码 | 单元格模式 时,你会得到针对你的代码单元格的更多粒度的运行选项。总的来说,我们可以看到这个插件提供了使用 Jupyter 笔记本的所有优点,而无需实际切换到 Jupyter 应用程序。
摘要
在 PyCharm 中创建的科学项目具有与现实生活中项目常见的通用结构,包括诸如从版本控制中排除的数据文件夹、README.md
文件和 requirements.txt
文件等良好实践。正如你可以想象的那样,必须为每个项目手动创建这种设置可能会变得困难且耗时。此功能帮助 PyCharm 用户在项目创建后立即进入开发过程,这样他们就不必担心处理杂项细节。这将使我们能够更快、更高效地完成开发工作流程。
此外,PyCharm 的科学模式包括各种支持科学计算或数据科学项目开发过程的功能,例如文档和 SciView 面板。结合此模式,你还可以利用其他强大的功能,如代码单元格和 CSV 插件,以简化各种任务并有效提高你在数据科学项目中的生产力。
然而,这些功能只是 PyCharm 在帮助我们进行数据相关项目时所能提供的开始。在此基础上,在下一章中,我们将探讨 SciView 面板和 Jupyter 笔记本的使用,它们是 PyCharm 中 Python 数据科学生态系统中重要的一部分。
一定要查看本书的配套网站 www.pycharm-book.com
。
问题
-
Markdown 语言是什么?GitHub 仓库中的
README.md
文件有什么作用? -
为什么 PyCharm 中科学项目的数据文件夹被排除在版本控制之外?
-
你如何在 PyCharm 中打开和关闭科学模式?这将对特定项目窗口产生什么影响?
-
PyCharm 的 文档 面板中可用的功能有哪些?
-
PyCharm 中的代码单元格是什么?你如何实现它们?
-
PyCharm 中 CSV 插件有哪些可用功能?
第十三章:使用 SciView 和 Jupyter 进行动态数据查看
在本章中,我们将继续我们的科学之旅,通过探索 PyCharm 的两个重要功能:SciView和与Jupyter 笔记本的集成。这两个功能都提供了集成和可用的界面,使我们能够查看和在我们的科学项目中处理数据和变量。
我们将从讨论 SciView 面板开始,该面板在上章中隐晦地介绍。在这里,我们将通过使用 NumPy 数组和 pandas DataFrame 进行工作,更深入和现实地探讨。
之后,我们将进一步发展我们的工作流程,包括在 PyCharm 科学项目上下文中处理交互式 Python 计算工具(如 Jupyter 笔记本)的覆盖。
到本章结束时,你应该对以下领域有所了解:
-
在 PyCharm 的 SciView 面板中查看和交互数据
-
PyCharm 中交互式 Python(IPython)的集成
-
在 PyCharm 科学项目中使用 Jupyter 笔记本支持进行交互式编程
到你翻到下一章时,你将拥有掌握驯服数据战争中两个最重要的武器的知识。
技术要求
由于我们已经转向数据科学主题,我将我的首选 Python 发行版切换到 Anaconda,这是一个针对数据科学工作负载定制的 Python 发行版。你可以在anaconda.com
找到它,以及适用于你操作系统的安装说明。
同样,我将使用conda
而不是通常的pip
,因为conda
是 Anaconda 的包管理器。它与 Anaconda 一起安装。
你需要一个已安装并正常工作的 PyCharm 副本。如果你是在书的中间部分开始阅读,安装部分已在第二章,“安装和配置”中介绍。
你还需要从 GitHub 获取这本书的示例源代码。我们已在第二章,“安装和配置”中介绍了如何克隆代码。你可以在这个章节的代码中找到github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-13
。
使用 PyCharm 的 SciView 面板轻松查看数据
我们在第第十二章中从 10,000 英尺的高度概述了 SciView 面板,因此我们知道接下来会发生什么。我们知道 SciView 是一个面板,允许我们以图形方式可视化我们的数据,同时还有一个面板,允许我们轻松且集成地访问典型科学项目中使用的更复杂的数据科学库的文档。
为了看到这个魔法的效果,我们将回顾我们在第十二章中编写的某些代码,并进行一些小的改进。而不是让你回到第十二章的代码中,我已经将项目复制到存储库中的第十三章文件夹中,以便所有内容都在一起。你可以在chapter-13/sci_view_panel
项目中的main.py
文件中找到它。别忘了你需要在虚拟环境中安装requirements.txt
文件中的要求才能使用示例项目。如果你需要复习如何做这件事,请参阅第三章。
import numpy as np
import matplotlib.pyplot as plt
N = 100
for _ in range(5):
x = np.random.normal(0, 1, N)
y = np.random.normal(2, 3, N)
plt.hist(x, alpha=0.5, label='x')
plt.hist(y, alpha=0.5, label='y')
plt.legend(loc='upper right')
plt.show()
你可以从上一章中识别出这个功能。代码生成两组样本数据,然后以直方图的形式绘制结果。区别在于我们添加了for _ in range(5)
。这是相同的代码;我们只是连续运行了五次,这将生成五个不同的直方图。
查看和使用图表
对于这个例子,你应该利用在控制台中运行代码的能力。这个设置可能已经勾选了,但你可以在默认运行配置中再次检查,如图图 13.1所示。
图 13.1:指向的设置表示此代码将在控制窗口中运行,这在科学项目中经常使用
你在上一章的图表中看到了这个图表,但看看这个运行的结果在图 13.2中。
图 13.2:SciView 窗口显示最新的图表,同时也允许我们选择其他运行中的图表
你已经可以看到区别了:这次有五个图表。点击每个图表将改变在查看器中变化的直方图。这比正常的 Python 运行有所改进,因为通常在显示直方图时,图表视图会阻塞执行。你通常只能一次查看一个,下一个只有在按Q键退出当前图表时才会生成。
与默认功能不同,PyCharm 抓取了所有图表以节省你手动逐个导出每个图像的时间。这是一个巨大的时间节省!你还可以使用面板顶部的工具栏进行缩放和缩小,使用其图标右侧的X按钮将其从视图中移除,将图表保存到图像文件,或者通过在图表图标上右键单击来移除所有图表,如图图 13.3所示。
图 13.3:PyCharm 为你提供了许多处理图表的选项
除了能够保存您感兴趣的数据图之外,您的查看器还有一些查看选项,例如缩放功能,如前图中的箭头所示。
热图和相关性数据
PyCharm 能够将热图图例着色,以帮助您轻松地在一个相关性矩阵中找到相关性。为了看到这一点,让我们生成一些数据,其中将有一些相关数据点,也有一些不相关。您将在本章源文件中找到一个名为 correlation_heatmap.py
的文件。代码的实质如下:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#%% generate sample data
# x and z are randomly generated
# y is loosely two times of x
x = np.random.rand(50,)
y = x * 2 + np.random.normal(0, 0.3, 50)
z = np.random.rand(50,)
df = pd.DataFrame({
'x': x,
'y': y,
'z': z
})
#%% compute the correlation matrix
corr_mat = df.corr()
#%% plot the heatmap
plt.matshow(corr_mat)
plt.show()
注意,代码中包含由 #%%
字符分隔的单元格分隔符。我们在 第十二章 中介绍了这些字符在 PyCharm 中的特殊含义。
在这个程序中,我们创建了一个 x
、y
和 z
()的随机生成。我们调整这些随机数在 x
、y
和 z
之间,以便它们之间将存在某种程度的相关性。然后,我们使用 corr()
方法计算这个数据集的相关矩阵。最后,我们使用 Matplotlib 的 matshow()
方法将这个相关性矩阵显示为热图。
在理论方面,一个相关性矩阵告诉我们每个数据集中一个属性与另一个属性的相关程度。数值越高,表示一对属性之间的相关性越高。通常,了解哪些属性彼此高度相关将为数据科学项目的数据集提供有价值的见解。
为了说明这一点,我们生成 y
属性的大小大约是 x
属性的两倍,从而在这两个属性之间创建相关性。另一方面,z
属性是独立于 x
和 y
随机生成的,因此 z
与其他任何属性之间不应有高度的相关性。
项目应该有一个名为 correlation
的运行配置。运行配置的设置如图 13.4 所示。
图 13.4:这是 correlation_heatmap.py 文件的运行配置
记住,检查 使用 Python 控制台运行 复选框非常重要。继续运行文件并查看绘图结果。我的结果在 图 13.5 中。由于数据是随机的,您的结果可能与我的不同。
图 13.5:相关性热图使用颜色来表示相关性矩阵中相关性的程度
由于这本书是黑白印刷的,我尝试在这里为你做点好事,通过添加字母来指示颜色。这不是图表的一个特性。标记为Y的单元格是黄色。G代表绿色,P代表紫色。第一和第二个属性高度相关,因此相关矩阵中相应单元格(第 1 行第 2 列和第 2 行第 1 列)的颜色明亮(黄色或绿色,亮度相似)。第三个属性与其他两个属性的相关性较低,用深紫色表示。自然地,每个属性与其自身完全相关,因此对角线单元格中的颜色是明亮的黄色或绿色。
这里的视觉影响非常有帮助。当处理相关矩阵时,我们可以立即看到变量之间的关系。
SciView 中的图表选项卡为我们提供了许多我们可以在数据科学项目中查看和管理图表的强大工具。注意,在 SciView 面板顶部也显示了两个标签页,如图 13.2(圆圈所示)。接下来,我们将讨论数据选项卡。
查看和使用数据
当你点击数据选项卡时,你可能会感到有些失望。如图 13.6*所示,真正的操作在调试窗口的下方。
图 13.6:在数据选项卡中,直到你选择一个样本集以数组形式查看之前,都不会显示任何内容
将你的注意力转向N
,这个面板就足够了。然而,如果你像我们一样使用 NumPy 数组,在像这样的正常调试窗口中查看数组是有问题的。
如果你点击以数组形式查看链接,也可以通过右键单击变量激活,你可以在数据面板中看到一个类似电子表格的表格。查看图 13.7*。
图 13.7:在下方面板中选择 X 变量后,数据选项卡现在显示 X 中的 NumPy 数组元素
如你所见,X
,因此我看到X
以一长串彩色数据的形式显示。我可以向右滚动并检查所有 100 个元素。高值单元格填充了较暖的颜色,而低值单元格填充了较冷的颜色。这为你提供了对比度,你可以通过视觉检测值之间的差异,而无需眯着眼睛看小数点。
图 13.7 中最左侧的箭头显示,上方的文本框中的X
与下方的窗口中的X
相关。你可以将文本框中的值更改为Y
,或任何在下方控制台中的有效变量名,并看到上方更图形化的数据表示。这里的关键词是有效。N
变量将在数据选项卡中显示错误。
文本框的右侧是格式化工具。这允许你指定数据标签页中数字的显示方式。格式化字符串使用与正常 Python 字符串格式化相同的格式。
当你在控制台视图中选择多个变量时,每个变量都会在自己的标签页中显示,如图13.8所示。
图 13.8:在控制台中选取多个变量将在数据标签页中为每个变量打开一个标签页
只需记住,这仅适用于 NumPy 数组和 pandas DataFrame。由于只有一行,所以这些数据有点无聊。让我们尝试一些更有趣的东西,这样我们就能在数据标签页中看到更多功能。
在章节代码中,你会找到一个名为meteor.py
的文件,里面只有两行:
import pandas as pd
meteor_data = pd.read_csv("./data/Meteorite_Langings.csv")
这份数据来自美国国家航空航天局(NASA)。你可以在data.gov
找到它,以及成千上万的免费数据集,这都是我个人的功劳,因为我交了很多税。当然,其他人也帮忙了,但至少每年一次,感觉这一切都是我的责任。
数据文件包含 NASA 关于陨石降落的资料。具体信息可以在catalog.data.gov/dataset/meteorite-landings
找到。
从仓库的项目中,应该有一个名为meteor_data.py
的运行配置,但以防万一,我的设置在图 13.9中。如果你需要关于运行配置的复习,我们已经在第三章中讨论了这个主题。
图 13.9:这是流星.py 文件的运行配置设置对话框。请确保已选中“使用 Python 控制台运行”
如前所述,关键是要确保文件在Python 控制台中运行,这样你就能得到 SciView 所需的面板视图。现在运行文件。你可以在图 13.10中看到我运行的结果。
图 13.10:我的流星数据运行需要被压缩和拉伸,这样你才能看到所有内容
我不得不认真地拖动面板,才能让所有内容都适合截图。注意,这次,箭头所指的按钮上写着以 DataFrame 查看,因为我们这次使用的是 pandas 而不是 NumPy。我点击了它,数据标签页已经加载了 DataFrame 以供查看。
在数据标签页中进行过滤
流星数据集更接近现实。它包含很多值,并且可能对某些字段进行过滤是有意义的。PyCharm 允许你使用通配符或表达式来过滤数据标签页中显示的数据。
使用通配符进行过滤
在图 13.11中,你可以看到我在数据选项卡上悬停在name
列上。
图 13.11:将鼠标悬停在列上会显示一个过滤器图标,它类似于漏斗
当我这样做时,会出现一个过滤器图标。点击该图标即可显示你的过滤选项,如图 13.12所示。
图 13.12:你可以通过表达式或子串(通配符)进行过滤
点击Bal
。你可以在图 13.13中看到我的。
图 13.13:我们正在过滤名称列,以显示所有名称中包含字符 Bal 的名称
注意,垃圾桶图标允许你移除过滤器。虽然子串过滤器可能很有用,但表达式过滤器更强大、更精确。
使用表达式进行过滤
使用表达式,你可以有更多的选择性。流星很酷,但没有人会对一块小石子印象深刻。例如,让我们在图 13.14中过滤掉大于 10kg 的流星。14*。
图 13.14:在质量列上过滤,其中 x(列中的值)大于 10,000
有很多!现在让我们通过过滤只显示从 2010 年开始发生的更近期的违规事件。我的在图 13.15中。
图 13.15:限制数据仅限于 2010 年和之后的流星撞击
幸运的是,它们并不多,数据在 2013 年结束时只有一次违规。碰巧的是,那是我最后一次按时缴纳所得税的年份。我告诉你,这都是我自己的事。
亲爱的国内税务局
那最后一行就是我们写作界所说的“笑话”。我知道这对你们所有人来说都是一个外国概念,但请相信我,其他人都在笑。
我肯定会被审计。
无论如何,通过我们的过滤实验,我们还可以看到reclat
、reclong
和GeoLocation
字段中存在缺失数据。毫无疑问,这些数据是在地理定位数据非法的 51 区记录的。尽管如此,这可以是一个快速检查数据异常的方法,作为创建数据管道的一部分,我们将在下一章中介绍。
导出到文件或新标签页
看看图 13.16中数据选项卡的顶部。
图 13.16:你可以将数据导出到文件或在新标签页中弹出
.csv
或 .tsv
文件。这很简单,所以我认为你可以没有截图也能过得去。你还可以将 SciView 中的数据标签内容弹出到编辑器视图中的常规标签中。我在 图 13.17 中做了这个操作。
图 13.17:我已经将 SciView 数据标签的内容弹出到主编辑窗口的一个标签中
总的来说,SciView 中的 数据 标签包含了许多有用的功能,用于对数据进行探索,无论是原始数据,如气象数据,还是通过计算生成,如我们之前的随机集。当你将这种图形方式查看输入和输出数据与上一章中描述的 PyCharm 的单元格模式结合起来时,你开始看到 PyCharm 中数据科学工具的力量。它们只是几个简单的面板,但我们可以用它们做很多事情!
理解 IPython 和魔法命令
IPython 是 Python 编程语言的增强型交互式外壳。它提供了一个比默认 Python 交互式解释器更丰富、更用户友好的环境。IPython 旨在使交互式计算和数据分析任务更加方便和高效。
当你在 PyCharm 之外使用 IPython 时,你会发现一些你不会在任何标准控制台或 REPL 环境中找到的功能。所提供的 shell 经验被大大增强,以支持你期望在 PyCharm 中找到的功能,例如自动完成、语法高亮和历史导航。
工具提供了一系列丰富的显示选项。想象一下,一个命令行界面,它可以直接在交互式环境中处理图像、视频、音频和交互式小部件。显然,你可以以片段、表达式或整个脚本的形式执行 Python 代码,而你所有这些操作都是交互式的。
IPython 还使得在分布式计算环境中工作变得更加容易。正如你可能知道的,由于 全局解释器锁(GIL),线程计算在大多数主流 Python 解释器中被有效禁用。GIL 是 CPython 解释器中的一个机制,它是 Python 最广泛使用的实现。GIL 是一个互斥锁(或锁),一次只允许一个线程在解释器中执行,即使在多核系统上也是如此。这意味着在多线程 Python 程序中,在任何给定时刻,只有一个线程可以执行 Python 字节码,无论有多少 CPU 核心可用。
GIL 的引入是为了简化内存管理,并避免由于多个线程同时访问和修改 Python 对象而可能出现的潜在冲突。虽然 GIL 确保了 Python 内存管理保持简单和安全,但它对多线程程序也有影响,因为 Python 程序往往难以真正利用支持多线程的系统上的所有计算资源。
虽然 IPython 没有任何可以克服 GIL 限制的秘密配方,但它确实提供了一个名为 ipython.parallel
的模块,这使得与并行计算资源一起工作变得容易。你可以创建 IPython 引擎的集群,这些集群可以在多个进程甚至多台机器上并行运行代码。
考虑到它显然是一个强大的工具,能够更充分地实现现代数据科学工作流程中富步骤数据交互的模式,IPython 似乎是我们应该非常感兴趣的东西。那么,问题来了,我们如何在 PyCharm 中利用 IPython?
安装和设置 IPython
安装 IPython 的方式与其他 Python 库类似。你可以从 PyCharm 的终端使用 pip install ipython
,或者你可以使用我们从 第三章 (Chapter 3) 开始使用的包管理屏幕。图 13.18* 显示了我准备使用 PyCharm 的 Python 包 面板进行安装。
图 13.18:我在 Python 包面板中搜索了 ipython,并准备使用 conda 将它安装到我的项目中
如果你经常使用 IPython,你可能会考虑全局安装它,以便它对所有项目都可用。你可以通过在虚拟环境之外使用 pip
、conda
或你喜欢的任何包管理工具来安装 IPython 来完成这项工作。
一旦 IPython 可用,PyCharm 将根据 IDE 的 构建、执行、部署 设置中的默认设置使用它,如 图 13.19* 所示。
图 13.19:默认情况下,如果可用,PyCharm 将使用 IPython 作为其控制台
我们在第 第二章 (Chapter 2) 中详细介绍了设置,所以如果你跳到了这一章并且不确定如何访问设置,请回顾前面的章节。一旦你的项目中可用 IPython,你需要关闭或循环任何可能打开的控制台。到目前为止,我们已经运行了三个程序,我们故意将它们设置为在控制台中运行,所以你不会看到任何变化,因为它们在我们安装 IPython 之前就已经在运行了。图 13.20* 显示了 IPython 提示符的外观。
图 13.20:关闭或重启现有控制台后,IPython 提示符出现
在 图 13.20 中,我画了一个箭头来提醒你如何打开控制台,如果它还没有打开。如果你看不到那个图标,尝试点击箭头上方的三个点图标。如果你仍然找不到它,请回顾 第二章,我们在那里讨论了第一次与控制台一起工作。
正如我提到的,如果你已经打开了控制台,它们需要被重启。我在 图 13.20 中画了一个圆圈来标记这个按钮。当控制台重启后,你应该会看到一个以单词 In
开头的不同提示符,与显示 >>>
符号的正常提示符不同。在 In
之后,你会看到一些包含数字的方括号。在 图 13.20 中,它显示的是 2
。这个数字表示迄今为止输入命令的顺序。如果你查看新的 IPython 控制台顶部,你会看到一些命令被自动输入,这些命令导入 sys
库,打印出 Python 版本,并扩展我们的路径。这是三个命令,但请记住我们是从 0
开始的。你输入的下一条命令将是第四条命令,并标记为 3
。
如果 PyCharm 是一款视频游戏,安装 IPython 就像是解锁超级枪、剑或魔法力量,你可以用它来击败那些毫无防备的对手。如果你是第一次使用,你可能迫不及待想要消灭一些数据僵尸,那么让我们来看看 IPython 的魔法命令。抛开夸张的说法,魔法命令确实就是它们的名字!
介绍 IPython 魔法命令
在 IPython 中,一个 %
(用于行魔法)或 %%
(用于单元格魔法)符号,在 IPython 会话或 Jupyter 笔记本单元格中使用。这些魔法命令提供了执行各种任务的快捷方式,从使用特定选项运行代码到与环境交互或控制 IPython 的行为。魔法命令是执行常见操作而不必编写大量代码的便捷方式。
魔法命令有两种类型:
-
%
符号用于单行。它们通常接受参数和选项来修改其行为。例如,%run script.py
可以用来在 IPython 会话中运行外部脚本。 -
%%
和它们被用于单元格的开始处。它们允许你影响整个单元格的内容。例如,%%time
可以用来测量整个单元格代码的执行时间。
这里有一些你每天会使用的常见魔法命令的例子:
-
%run script.py
:在 IPython 会话中运行外部 Python 脚本。 -
%timeit
:测量 Python 语句或表达式的执行时间。 -
%matplotlib inline
:配置matplotlib
以在笔记本中直接显示图表。 -
%%writefile filename.txt
:将笔记本单元格的内容写入名为filename.txt
的文件。 -
%load_ext extension_name
:加载特定的 IPython 扩展。 -
%%html
:将单元格的内容渲染为 HTML。 -
%reset
:通过删除用户定义的所有名称来重置命名空间。 -
object_name?
:在这个问题上,不要字面理解。你不需要输入 object_name?。相反,你输入你实例化的对象的名字。也许我们稍后再做这个,这样你就能理解了。
到现在为止,你开始闻到我们在这里煮的东西了。我们看到了 第十二章 中的单元格模式(Chapter 12),但现在,有了 IPython,我们可以使用这些魔法命令对单元格做更多的事情。就像电影中的任何好魔法一样,有足够的神秘但简短的语法可以学习,这增加了神秘感,让你感觉比那些不使用魔法的同事更精英一些。
让我们试几个,好吗?
在你的 IPython 控制台中,输入 a = 1
。现在,让我们尝试 object_name?
魔法命令。你不需要输入 object_name?;相反,你输入 a?
。对象的名字是 a
,你后面跟着一个问号,就像你是加拿大人或澳大利亚人一样。无论你使用什么对象名,假设它是有效的,你都会得到关于该对象的信息。你可以在 图 13**.21 中看到我的结果。
图 13.21:a 怎么样?
我们对 a
了解了很多,对吧?好吧,嗯,让我们再试一个。输入 %precision 4
。结果应该是 '%.4f'
。这告诉我们我们已经将我们的会话设置为四位小数的精度。让我们通过输入以下代码来测试一下:
import math
math.e
小写 e
是对 e
到 10 位的参考,即 2.7182818285。然而,我们设置为四位小数的精度,所以 IPython 的结果应该显示 2.7183
,如图 图 13**.22 所示。
图 13.22:将精度设置为四位小数后,我们可以正确地看到常数 e 的表示
再试一次。%%timeit
魔法在创建算法时非常有用。算法只是可重复的指令集;然而,为了有用,算法需要高效。效率由两个向量判断:完成所需的时间和完成工作所需的空间。例如,英国的国家卫生系统(NHS)设计了一个算法,根据许多复杂因素将器官捐赠者与接受者匹配。它的目的是以最小化组织排斥风险等方式将器官与捐赠者匹配。正如你可以想象的那样,这是一个复杂的算法,必须在一个包含数千名注册捐赠者和数千名注册接受者的数据集上运行。当器官可用时,它有有限的保质期。如果匹配算法花费太长时间来生成匹配,器官将无法用于移植,该算法实际上就变得毫无用处。我们在第六章中介绍了分析,但这实际上是为了帮助软件工程工作流程找到执行瓶颈。
在数据科学中,我们的操作超出了典型软件项目的限制,因此对单个步骤进行计时至少可以让我们对计算步骤可能如何扩展有所了解。即使我们的目标不是创建算法,我们仍然需要在常规的探索性工作中处理时间和计算空间(如内存和存储)的实际限制。
%%timeit
魔法随后成为我们工作的一部分。现在我已经花了很多时间构建了一个巨大的示例,让我们用图 13**.23中的可笑的琐事来试试。
图 13.23:我们尝试使用%%timeit 进行简单的排序,以找到操作大约在 318 纳秒内完成
这确实是一个琐事。首先在 IPython 控制台中输入%%timeit
。当你按下Enter键时,它会带你到下一行。在第二行中,我们创建了一个包含 0 到 9 的数字的列表,顺序相反。在最后一行,我们将它们按正常顺序排序。这太无聊了!在最后一行空白处按Enter键来运行代码。看看%%timeit
做了什么!它本可以只启动一个时钟,运行脚本,然后停止时钟并报告差值。相反,%%timeit
反复运行此代码以获得一个样本大小,其中它可以计算平均值和标准差。这使我们能够更好地了解这段代码将需要多长时间运行。当然,不同的计算机性能会有所不同。对我来说,它运行了 318 纳秒加减 3.44 纳秒。
在这里,我们考虑了 IPython 中最常见的三个魔术命令。当然,还有很多其他有用的命令可以利用,这些可以在 IPython 的官方文档中找到:ipython.readthedocs.io/en/stable/index.html
。
话虽如此,IPython 的主要目的并不仅仅是利用方便的 API 来简化特定任务,如变量检查、格式化或性能分析——IPython 实际上使用这些功能来支持其底层的交互特性。在数据科学项目的背景下,当在 PyCharm 中使用时,IPython 为我们提供了一个很好的方法来检查和测试在大型程序中使用之前的小块代码。
既然如此,让我们继续到下一节,我们将考虑 PyCharm 为科学计算提供的其他显著支持——Jupyter 笔记本。
利用 Jupyter 笔记本
Jupyter 笔记本可以说是 Python 科学计算和数据科学项目中用得最多的工具。在本节中,我们将简要讨论 Jupyter 笔记本的基本知识以及为什么它们是数据分析的绝佳工具。然后,我们将考虑 PyCharm 如何支持这些笔记本的使用。
我们将在章节源中与jupyter_notebooks
项目合作。别忘了,你需要在一个虚拟环境中安装requirements.txt
文件中的要求,以便使用示例项目。如果你需要复习如何做这件事,请参阅第三章。
尽管我们将在 Jupyter 笔记本中编写代码,但首先考虑一个传统的 Python 脚本中的裸骨程序是有益的,这样我们才能充分欣赏使用笔记本的优势。让我们看看main.py
文件,看看我们如何与之合作。我们可以看到这个文件包含与上一节相同的程序,其中我们随机生成一个包含三个属性(x
、y
和z
)的数据集,并考虑它们的相关矩阵:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Generate sample data
x = np.random.rand(50,)
y = x * 2 + np.random.normal(0, 0.3, 50)
z = np.random.rand(50,)
df = pd.DataFrame({
'x': x,
'y': y,
'z': z
})
# Compute and show correlation matrix
corr_mat = df.corr()
plt.matshow(corr_mat)
plt.show()
这基本上是我们在本章早期想要在 PyCharm 中查看相关矩阵时使用过的相同代码。在这里,我们添加了最后两行,它们是不同的。这次我们不是绘制热图,而是绘制散点图。我之前解释过,我们故意并且人为地在我们随机生成数据中引入了相关性。检查我们设置y
的地方,你会看到我们将x
的矩阵乘以 2,然后添加了一些来自另一个随机样本生成的小数。这将使y
看起来大致但不是完全与x
相关,从而让我们看到一个合理但人为的相关矩阵。当我们运行文件时,这得到了证明。我的结果是图 13**.24所示。别忘了,我们的数据是随机的,所以你的数据不会精确匹配我的。
图 13.24:这是我们的代码生成的散点图
就像我说的一样,这里除了从热图到散点图的转变之外,没有什么是新的或惊人的。我们运行这个作为我们讨论 Jupyter 笔记本的比较点。
理解 Jupyter 基础知识
Jupyter 笔记本建立在迭代开发的理念之上。在任何开发工作中,无论你正在创建什么,将大型项目分解成更小的部分总是会有回报。福特汽车公司的任何人都不只是建造一辆汽车。他们建造了数百万个零件,这些零件最终被组装成汽车。每个零件都可以单独设计、生产、测试和检查。
同样,通过将给定的程序分解成可以独立编写和运行的各个部分,程序员和特别是数据科学家可以以增量方式工作在程序的逻辑上。
迭代开发的想法
世界上软件开发领域的多数从业者已经习惯了敏捷方法背后的理念。有数十个敏捷框架旨在帮助你管理软件,或者实际上,任何旨在创建有用产品的项目。它们一致共有的一个理念就是迭代开发。开发工作被分解成更小、更简单的任务,称为迭代。在每次迭代的结束时,我们应该有一些可用的产品。这很重要!
你可能会考虑制造一辆汽车,但真正思考一下最初汽车的研究和开发过程。当时我们主要关注的是制造一种车辆,它能以比使用驯养动物更快、更有效的方式,将我们从 A 点带到遥远的 B 点。
我们已经了解了轮子,那么让我们以某种有些滑稽的水平来娱乐一下,如果他们要创造世界上第一辆汽车,平均水平的软件开发者可能会使用的过程。记住,每次迭代都必须产生某种可用的旅行方式。我们的开发团队可能从滑板开始。这是我们可以在一个短暂的迭代中制作的东西。下一个迭代可能会产生一辆滑板车,下一个是一个自行车,下一个可能是一辆带动力的三轮车,然后最终是一辆原始的汽车。参见图 13.25。
图 13.25:逐步过程一步一步实现目标
每次迭代都会产生一个可用的结果,我们可以将其视为连续的进步。在每次迭代的结束时,你应该反思下一步,因为随着每次迭代,你对最终目标的了解会变得更加清晰。你从每次迭代中学到很多东西,如果你保持迭代的小规模,当你发现需要改变方向时,你有灵活性去调整。
这在传统的软件开发和数据科学工作中经常发生。你可能一开始就有一个特定的研究目标,但最终,你必须跟随数据指引的方向,而不是屈服于自己的偏见。迭代过程使得这一点成为可能。
我们可以将这一点应用到 Jupyter 笔记本背后的理念上,因为这些理念涉及一次构建一个单元格或迭代的工作。在每次迭代中,你会在读取数据集的代码单元格中添加或做出适当的更改,然后重新运行后续的单元格,而不是重新运行之前的代码。作为对用户的致敬,Jupyter 笔记本以三种最常用的科学编程语言命名:Julia、Python和R。
Jupyter 笔记本的另一个重要部分是对 Markdown 语言的支撑。正如我们之前提到的,在上一章的开头,Markdown 是一种常用于 GitHub 中README.md
文件的标记语言。此外,由于它能够与 LaTeX(通常用于撰写数学方程和科学论文)协同工作,Markdown 在数据科学社区中受到了极大的青睐。
接下来,让我们看看如何在常规的 PyCharm 项目中使用 Jupyter 笔记本
PyCharm 中的 Jupyter 笔记本
对于这个任务,我们将把main.py
文件中的程序翻译成 Jupyter 笔记本,以便我们可以看到 Jupyter 提供的界面与传统的 Python 脚本相比。我将利用我们之前开始的jupyter_notebooks
项目。你可以在本章的示例代码中找到它。如果你没有那个仓库,我们在第二章中介绍了如何克隆它。如果你想从头开始,你可以简单地创建一个新的科学项目,这在第十二章中有介绍。
创建笔记本并添加我们的代码
要在 PyCharm 项目中添加一个新的 Jupyter 笔记本,就像创建一个文件一样创建它。如图 13.26 所示,点击文件 | 新建 | Jupyter 笔记本。
图 13.26:使用文件 | 新建菜单选项创建一个新的 Jupyter 笔记本
你会立即被提示为你的笔记本命名。我把它命名为basic.ipynb
。文件创建在我的项目的根目录中,但我把它拖到了notebooks
文件夹中。你可以在图 13.27中看到我的起点。
图 13.27:我的新笔记本准备好了!
让我们从输入一些文档和一些已知的导入开始。在提示符下,输入以下代码:
### Importing libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#%%
当你输入最后一个#%%
时,你会发现 PyCharm 为你启动了一个新的单元格。在第二个单元格中,让我们输入我们之前在示例 Python 脚本中的一些代码:
x = np.random.rand(50,)
y = x * 2 + np.random.normal(0, 0.3, 50)
z = np.random.rand(50,)
df = pd.DataFrame({
'x': x,
'y': y,
'z': z
})
#%%
我在本章中已经解释了这段代码,所以不会再重复了。和之前一样,最后的 #%%
将创建一个新的单元格。查看 图 13.28 看看我现在有什么。
图 13.28:我现在有两个单元格,包含我们之前程序中的代码
到目前为止,一切顺利!我已经有了导入,还有一个为我生成数据集的单元格,就像我在 Python 脚本中做的那样。我已经将我的程序分解成小的、迭代的步骤。首先,我的导入,然后是我的数据集。接下来,我可以查看如何处理我的数据。从之前的讨论中我们知道这将是一个相关矩阵。如果这是一个大学项目,而我们的教授希望我们记录我们使用的公式,这可能是一个好主意,即使你已经离开了学校。让我们花点时间看看我们可以使用的酷炫文档功能。我们知道我们可以有代码单元格。我们还可以有利用不仅 Markdown 而且是 LaTeX 的文档单元格。
使用 Markdown 和 LaTeX 进行文档编写
Markdown 是我们已经见过的一种东西。它是一种简单的标记语言,允许你创建类似 HTML 的文档,但与丰富的标签集不同,你使用符号来标记你的文档。Markdown 只涵盖基本功能,如标题、列表和简单的图片。我们在 第十二章 中介绍了 PyCharm 集成 Markdown 插件的使用。
在我们的笔记本中使用 Markdown 允许我们进行一些简单的格式化,以增强笔记本的外观和可读性。
LaTeX,根据你的数学教授在哪里学习,发音为“LAY-tech”或“LAH-tech”,是一种在科学和数学领域使用的排版约定。这种东西在学术期刊中是必需的,因为你不太可能在键盘上轻松地输入复杂的公式,而且很少有数学教授能熟练使用 Adobe Illustrator 并进行自己的插图工作。他们没有使用图形工具,而是发明了一种简短、难以理解且难以记忆的约定,用于在他们的期刊文章中标记公式。然后他们给这个约定起了一个常见的英语单词,但坚持要求我们错误地发音。
让我们试试吧!
首先,我还没有完全向你坦白如何创建单元格。当然,你可以直接开始输入内容,使用 #%%
来分隔单元格效果也很好。你也可以悬停在单元格之间的空间,或者如果你在笔记本的顶部或底部,悬停在分隔处。查看 图 13.29。
图 13.29:你可以在单元格之间的空间悬停以获得添加新单元格的简单 GUI 帮助
当你在单元格之间点击这些按钮时,会触发一个称为有丝分裂的细胞分裂过程。等等,不,那是生物学。它确实通过在两个已有的细胞之间添加一个新的细胞来分裂细胞。我将通过将鼠标悬停在最后一个细胞下方并点击添加 Markdown 单元格来在我的最后一个单元格下方添加一个单元格。见图 13**.30。
图 13.30:将鼠标悬停在最后一个空单元格上方并点击添加 Markdown 单元格
这将创建一个不带 IPython 提示的浅蓝色单元格。由于它是一个 Markdown 单元格,PyCharm 不期望代码,因此不需要提示。现在,在单元格内,输入以下绝对废话:
### Pearson's correlation
$r_{XY}
= \frac{\sum^n_{i=1}{(X_i - \bar{X})(Y_i - \bar{Y})}}
{\sqrt{\sum^n_{i=1}{(X_i - \bar{X})²}}\sqrt{\sum^n_{i=1}{(Y_i - \bar{Y})²}}}$
这是一种 LaTeX 标记语法,它将传达皮尔逊的相关公式。相信我,当我们运行笔记本时,它将非常令人印象深刻。让我们继续添加我们之前的两张图表。
添加我们的图表
在最后一个单元格下方添加一个新的代码单元格,并添加以下代码用于我们的热图:
# Compute and show correlation matrix
corr_mat = df.corr()
plt.matshow(corr_mat)
plt.show()
接下来,为我们的 scattelot 添加另一个代码单元格:
# Scatterplot
plt.scatter(df['x'], df(['y']))
plt.show()
我们将代码作为 Jupyter 笔记本实现完成。
执行单元格
你可以通过点击笔记本顶部的运行按钮来执行笔记本,如图图 13**.31所示。
图 13.31:使用双绿色箭头按钮运行笔记本中的所有单元格
这会在 PyCharm 中触发一个奇妙的变化。图 13**.32是一个很好的起点。
图 13.32:我们已经运行了笔记本,这导致 PyCharm UI 发生了大量变化
首先,注意左侧边栏上有一个新的 Jupyter 工具。我们可以看到我们已经开始了一个在端口 8888
上运行的 Jupyter 服务器。如果你在没有 PyCharm 的情况下使用 Jupyter,这将是一种正常的操作模式。你会从命令行运行 Jupyter 服务器,并在浏览器中导航到笔记本。PyCharm 在 IDE 中替换了这种体验,但我们仍然需要运行服务器以获取结果。
如果你向上滚动,你会看到 LaTeX 标记语法已经在我们的 Markdown 单元格中渲染,如图图 13**.33所示。
图 13.33:现在的废话看起来绝对惊人!
如果你向下滚动以检查散点图,我们会看到……唉,出错了!见图 13**.34。
图 13:34:错误信息显示出了 exactly what is wrong
我搞砸了。我在 DataFrame 对象 df
旁边加了括号。我需要去掉它们,让它看起来像是一个邻居:
# Scatterplot
plt.scatter(df['x'], df['y'])
plt.show()
现在,我可以重新运行整个笔记本,但我不需要这样做。问题出在最后一个单元格中,所以我只需将光标放在最后一个单元格中,然后点击单个绿色运行箭头。见图图 13.35。
图 13.35:是的,这要好得多!
让我们用一些零散的内容来完成对 Jupyter 的介绍。
零散的内容
目前,我们正在运行一个 Jupyter 服务器。我们刚才看到这在新工具图标上创建了一个左侧菜单。让我们点击它,看看发生了什么。见图图 13.36。
图 13.36:Jupyter 面板打开,我们可以在服务器选项卡上看到运行服务器的输出
将滚动条滚动到窗口顶部,如果你想在浏览器中与你的笔记本一起工作,将显示 Jupyter 服务器的本地 URL。但为什么要在 PyCharm 如此出色的前提下这样做呢?
注意允许你停止正在运行的 Jupyter 服务器的停止按钮。在服务器选项卡旁边是显示在图 13.37中的变量选项卡。
图 13.37:Jupyter 面板中的“变量”选项卡允许深入检查笔记本中的变量
在变量选项卡中,你可以获得允许你深入到笔记本中任何变量的检查。请注意,以数组查看和以 DataFrame 查看选项也在那里,这样你还可以使用 SciView 面板查看和过滤你的数组内容。
我们并没有太多讨论笔记本标签页顶部的工具栏,如图图 13.38所示。
图 13.38:操作笔记本中单元格的控制按钮
有一些工具用于剪切、复制和粘贴你的单元格(1)。在(2)处的箭头允许你通过上下移动来重新排列笔记本中的单元格。在(3)处有一系列运行和调试工具,你无疑会将其视为一个常见主题。
扫帚图标将清除单元格的输出,如果你正在处理基于你即将提出的加薪的薪资预测,而你的老板正好从你身后走来,这将非常有用。垃圾桶将完全删除单元格。这里还有一个标记为代码的下拉菜单,允许你更改单元格的类型,比如从代码单元格更改为标记单元格,反之亦然。
我们已经在 Jupyter 笔记本的上下文中介绍了 PyCharm 的主要功能。总的来说,使用传统 Jupyter 笔记本的一个最大的缺点是在单独的代码单元格中编写代码时缺乏代码补全。具体来说,当我们在我们浏览器中的 Jupyter 笔记本中编写代码时,这个过程与在有限支持的简单文本编辑器中编写代码非常相似。
然而,当我们直接在 PyCharm 编辑器中处理 Jupyter 笔记本时,我们会看到所有可用于常规 Python 脚本的代码编写支持功能也在这里可用。换句话说,当使用 PyCharm 编写 Jupyter 笔记本时,我们得到了两者的最佳结合——来自 PyCharm 的强大、智能支持以及来自 Jupyter 的迭代开发风格。
摘要
Python 程序员通常以两种方式在数据科学项目中工作——编写传统的 Python 脚本或使用 Jupyter 笔记本,这两种方式都得到了 PyCharm 的大力支持。具体来说,PyCharm 中的 SciView 面板是一种全面且动态的方式来查看、管理和检查数据科学项目中的数据。它为我们提供了一个很好的方式来显示由 Python 脚本生成的可视化,以及检查 pandas DataFrame 和 NumPy 数组中的值。
另一方面,Jupyter 笔记本是促进 Python 中迭代开发的强大工具,使用户能够逐步分析并从他们的数据集中提取见解。Jupyter 笔记本也得到了 PyCharm 的良好支持,可以直接在 PyCharm 编辑器中进行编辑。这使得我们能够在利用 PyCharm 提供的强大代码编写支持功能的同时,跳过使用网络浏览器运行 Jupyter 笔记本的中间步骤。
通过深入了解 PyCharm 在数据查看和操作过程中的帮助,无论是通过 SciView 面板还是使用 Jupyter 笔记本,我们已经学会了如何使用 PyCharm 在 Python 中促进各种数据科学任务。有了这些,我们已经拥有了足够的知识和工具来使用 PyCharm 处理现实生活中的项目。
在下一章中,我们将结合到目前为止我们所学到的关于数据科学和科学计算的所有知识,并在 PyCharm 中逐步构建数据科学管道的过程。
一定要查看本书的配套网站www.pycharm-book.com
。
问题
-
SciView 面板包含哪两个主要功能?
-
当 Python 程序生成多个可视化时,在 SciView 面板中使用绘图查看器的优势是什么?
-
SciView 面板中的数据查看器支持哪些类型的数据结构?
-
迭代开发的概念是什么?Jupyter 笔记本是如何支持这一点的?
-
什么是 Markdown 和 LaTeX?为什么在 Jupyter 笔记本中支持它们是有益的?
-
在 PyCharm 编辑器中,Jupyter 代码单元是如何表示的?
-
在 PyCharm 编辑器中编写 Jupyter 笔记本有哪些好处?
第十四章:在 PyCharm 中构建数据管道
“数据管道”这个术语通常表示一个逐步的过程,包括收集、处理和分析数据。在行业中,这个术语被广泛用来表达对可靠工作流程的需求,该工作流程将原始数据转换为可操作的见解。一些数据管道在巨大的规模上工作,例如一家营销技术(MarTech)公司从 Kafka 流中摄取数百万数据点,将它们存储在像Hadoop或Clickhouse这样的大型数据存储中,然后清洗、丰富和可视化这些数据。有时,数据量较小但影响更大,例如我们将在本章中工作的项目。
在本章中,我们将学习以下主题:
-
如何处理和维护数据集
-
如何清理和预处理数据
-
如何可视化数据
-
如何利用机器学习(ML)
在本章中,您将能够将迄今为止关于科学计算主题所学的知识应用到 PyCharm 的实际项目中。这作为了一个动手讨论,以结束与科学计算和数据科学项目合作的主题。
我要特别指出,我大量借鉴了第一版中的文本、代码和数据,该版由不同的作者 Quan Nguyen 编写。在第二版中,我的主要工作是更新现有内容。Quan 在本章中的处理非常出色,因此我更新本章的大部分工作都是使用 PyCharm 的新版本,更新到最新版本的库,然后用我自己的话重写本章,以便写作风格与本书的其他部分相匹配。没有 Quan 的原始作品,我无法完成这项工作,我想向这位原始的 Python 数据科学功夫大师致敬。
技术要求
要继续阅读本章,您需要以下内容:
-
Anaconda,这是一个针对数据科学工作负载定制的 Python 发行版。您可以在
anaconda.com
找到它,以及适用于您操作系统的安装说明。 -
同样,我将使用 Anaconda 的包管理器 conda,而不是通常的
pip
。conda 是与 Anaconda 一起安装的。 -
安装并运行 PyCharm 的副本。如果您是在本书的中间部分开始阅读,其安装已在第二章,“安装和配置”中介绍。
-
本书样本源代码来自 GitHub。我们在第二章,“安装和配置”中介绍了如何克隆代码。您可以在
github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-14
找到本章的代码。
处理数据集
数据集是任何数据科学项目的骨架。有了良好、结构化的数据集,我们就有机会从数据中探索、构思,并发现重要的见解。术语“良好”和“结构化”是关键。在现实世界中,这种情况很少是偶然发生的。我是每天进行数据科学项目的首席开发者。我们从各种硬件平台(如存储阵列、交换机、虚拟化节点(如 VMware)、备份设备等)收集诊断、利用和性能数据。我们收集整个企业的数据;每个数据中心中的每个设备。然后我们的软件将原始数据转换为可视化,提供见解,使组织能够通过整合健康监控、利用和性能报告以及容量规划,有效地管理其 IT 资产。
我已经从事这个领域 10 年了,我们一直在寻找支持新的设备和系统。然而,我们的挑战是获取所需的数据。10 年前,从 NetApp 存储阵列中获取数据非常困难,因为它的诊断数据以非结构化文本的形式被丢弃。与之相比,更现代的阵列将数据以 XML 或 JSON 格式丢弃,或者更好的是,它们有自己的 SDK,用于与硬件接口并提取我们所需的数据。
从各种来源收集数据并努力将原始数据塑造成有用的东西需要投入大量的努力。有时这很容易,有时却非常困难。格式不佳的数据可能导致错误的结论和错误的见解。
一个令人深思的例子来自一家大型鞋制造商。大约 20 年前,我在一家销售用于管理工厂生产的软件的公司工作。我们与鞋厂进行了咨询,并告诉他们如何建模他们的数据以获得最佳结果。他们没有听取我们的意见,而是选择了另一条路。我们告诉他们这不会奏效。他们感谢我们的建议。他们的预测完全错误,因此他们像任何有董事会和股东的大公司一样做了——他们责怪软件。我们的首席执行官在商业节目中四处奔波,但损害已经造成。我们的公司股价暴跌,那一年很多人失去了工作,包括我。时至今日,我都不会穿他们的鞋。糟糕的数据可能会危及生计、声誉,甚至在鞋之外,甚至生命。我们必须拥有工具和流程,帮助我们确保一切正确。
让我们回顾一下这个过程的一些步骤。
从一个问题开始
科学中的每一件事都是从一个问题开始的。就我们的目的而言,我们将考虑两种可能的场景:
-
我们心中有一个具体的问题,我们需要收集和分析适当的数据来回答这个问题
-
我们已经有了数据,在探索过程中,一个问题已经出现
在我们的案例中,我们将重新创建医学诊断领域一个可能的重要突破的数据分析阶段。我将展示一个来自 Kaggle 的例子,该例子取自一篇题为《利用打字时手指运动的多项特征高精度检测早期帕金森病》的论文,该论文由沃里克·亚当斯于 2017 年进行。你可以在本章的“进一步阅读”部分找到完整的研究论文和数据集链接。
注意
Kaggle 是一个为数据科学家和机器学习工程师设计的在线数据社区。该网站提供竞赛、数据集、游乐场和其他教育活动,以促进数据科学在学术界和工业界的增长。更多关于网站的信息可以在其主页上找到:www.kaggle.com/
。
帕金森病(PD)是一种影响大脑并导致运动问题的疾病。它是一种进行性疾病,这意味着随着时间的推移会变得更糟。全世界有超过 600 万人患有这种疾病。在 PD 中,一种产生多巴胺这种化学物质的特定类型的大脑细胞开始死亡。这导致了一系列的症状,包括运动困难和与其他非运动相关的问题。
在撰写本文时,医生们还没有一个明确的测试来诊断 PD,尤其是在早期阶段,症状可能不是很明显。这导致诊断疾病时出现错误,高达 25%的病例被非 PD 专科医生误诊。有些人可能患有 PD 多年,直到被正确诊断。
这引出了一个问题……
我们如何有效地使用某些测试、指标或诊断数据点来诊断 PD,而不需要专业的临床培训?
亚当斯提出了一种测试方法,该方法使用收集到的随时间推移的计算机打字数据。由于打字涉及精细运动,而这种精细运动是 PD 早期发病时首先出现的问题,亚当斯希望利用日常的打字任务作为诊断工具。研究人员在 103 人身上测试了这种方法;其中 32 人患有轻度 PD,其余的为对照组,没有 PD。对他们的打字模式进行计算机分析能够区分早期 PD 患者和无 PD 患者。这种方法在检测 PD 患者时准确率达到 96%,在正确识别非 PD 患者时准确率达到 97%。这表明这种方法可能擅长区分这两组人群。让我们看看是否可以根据他们研究的数据得出相同的结论。
存档用户数据
在本章的源代码中,你可以找到一个名为pipeline
的数据科学项目。该项目包含一个数据文件夹,其中包含我们的数据集,分为两个文件夹:Archived users
和Tappy Data
。
在“存档用户”文件夹中的数据是文本文件格式,看起来是这样的:
BirthYear: 1952
Gender: Female
Parkinsons: True
Tremors: True
DiagnosisYear: 2000
Sided: Left
UPDRS: Don't know
Impact: Severe
Levodopa: True
DA: True
MAOB: False
Other: False
为了更好地沉浸其中,让我们稍微揭开一些神秘的面纱。以下是每个记录中包含的字段:
-
出生年份:1952
:这个人出生于 1952 年。 -
性别:女性
:这个人自认为是女性。 -
帕金森病:是
:这个人已被诊断为 PD。 -
震颤:是
:这个人有震颤,这是一种不由自主的颤抖运动。震颤是 PD 的常见症状。 -
诊断年份:2000
:这个人于 2000 年被诊断为 PD。 -
侧:左
:在这个上下文中,“侧”这个术语可能指的是症状更明显的身体部位。在这种情况下,症状在身体的左侧更为明显。 -
UPDRS:不知道
:统一帕金森病评分量表(UPDRS)是一种用于评估 PD 严重程度的工具。在这种情况下,不知道这个个体的具体 UPDRS 评分是多少。 -
影响:严重
:PD 对这个人生活的影响被认为是严重的,这表明症状对其日常活动和生活质量有显著影响。 -
左旋多巴:是
:左旋多巴是一种常用的药物,用于管理 PD 的症状。这个人正在接受左旋多巴作为其治疗的一部分。 -
DA:是
:多巴胺激动剂(DAs)是另一种用于管理 PD 症状的药物。这个人正在接受多巴胺激动剂作为其治疗的一部分。 -
MAOB:否
:单胺氧化酶 B 抑制剂(MOABs)是一种可以增加大脑多巴胺水平的药物,有助于管理 PD 的症状。在这种情况下,这个人没有服用 MAOBs。 -
其他:否
:如果我要为真实的研究重新创建这个研究,我可能会联系原始研究者,如果这个数据点没有在出版物中直接解释。由于我没有这样做,我会猜测这不会影响我们的项目。这很可能指的是 PD 的其他特定药物或治疗方法,表明这个人除了左旋多巴和 DAs 之外没有接受任何其他特殊治疗。
总结来说,这位个体在研究时是一位 65 岁的女性,于 2000 年被诊断为 PD。她有震颤,尤其是在身体的左侧。疾病对她的生活影响严重。她正在接受左旋多巴和 DAs 的治疗来管理她的症状,但她没有使用 MAOBs 或任何其他特殊治疗。根据 UPDRS 测量的具体严重程度,在提供的信息中没有提供。
文件夹中的文件名很重要。发布User_0EA27ICBLF.txt
文件是不道德的。
Tappy 数据
研究方法使用一个名为 Tappy 的应用程序,该应用程序在 Windows 上运行,并记录每个受试者的按键时间和每个键的位置数据。如果你还记得我们之前关于用户数据的讨论,侧向性是一个因素。运动皮层是大脑中负责计划、控制和执行自愿运动的部分。它位于大脑皮层,即大脑的最外层。
运动皮层,连同大脑的大部分区域,分为两个半球:左半球和右半球。每个半球控制身体对侧的自愿运动。换句话说,运动皮层的左半球控制身体右侧的运动,而右半球控制身体左侧的运动。由于这一点是真实的,知道按键数据来自键盘的哪一侧可能具有潜在的诊断重要性。
让我们打开一个 Tappy 数据集,看看里面有什么:
图 14.1:我已经打开了 Tappy 数据文件夹中的第一个文件,并且可以看到它是制表符分隔的数据
我可以看到顶部有一个警告,说明文件很大,按照代码编辑器的标准,代码洞察不可用。这是无谓的,因为 PyCharm 中科学项目的数据文件夹本来就不被索引和代码洞察。你可以安全地忽略这个警告。
我还可以看到文件是制表符分隔的,这将很好地在数据管道中运行。看到你的数据以易于解析的格式到来总是令人鼓舞。这实际上是适合导入电子表格或数据库表的结构化数据。这并不一定是我们将要处理的数据,但如果我们可以用给定的数据文件进行这些类型的导入,我们几乎可以处理任何数据。
和之前一样,文件名是有意义的。文件的第一部分,由下划线分隔,是来自存档用户
文件夹的受试者 ID。我们将能够将Tappy 数据
文件夹中找到的每个受试者的表现数据与存档``用户
文件夹中找到的他们的人口统计数据相关联。
Tappy 数据文件中的字段如下:
-
患者 ID
-
数据收集日期
-
每个按键的时间戳
-
哪只手执行了按键操作(L代表左手,R代表右手)
-
保持时间(按下和释放之间的时间,以毫秒为单位)
-
最后一个按键的转换
-
延迟时间(从按下前一个键到的时间,以毫秒为单位)
-
飞行时间(从释放前一个键到的时间,以毫秒为单位)
我们已经确定我们拥有可工作的原始数据格式。说实话,这真是个好日子。它并不完全完美;我们仍然需要进行一些整理,但这是一个非常好的起点。
术语警告 - 整理
Munging是一个在计算机编程和数据处理中使用的俚语,用来描述将数据从一种格式转换到另一种格式的过程。这通常涉及改变数据的结构或内容,使其更适合特定目的,例如分析、存储或展示。Munging 可能包括以下活动:
-
数据清洗:从数据集中删除错误、不一致或不相关信息
-
数据转换:改变数据的格式、结构或表示,以适应特定要求
-
数据解析:从更大的数据集中提取特定的信息
-
数据聚合:将多个数据集合并成一个单一的数据集
-
数据过滤:根据某些标准选择或排除数据
-
数据格式化:改变数据展示或编码的方式,以适应特定的系统或软件
术语munging是非正式的,来源于mangle和modify的混合。它通常用于需要准备或调整数据以进行分析、集成或其他数据相关任务的环境中。
我们的项目有一个良好的开端,但我们有一个问题:我们能否通过打字测试来检测早期帕金森病?我们有一项研究实施的此类打字测试的原始数据。我们准备好卷起袖子大干一场了!
数据收集
我们很幸运。我已经找到了我们的数据,并包括了它供您考虑。在现实世界中,我们本需要执行数据收集的正常步骤。虽然关于这个主题有整本书的论述——大多数 4 年的科学大学学位课程都高度重视这个主题——但我并不打算在这里深入研究。然而,如果您对我们试图完成的事情不熟悉,我至少会提供一个概述。
从外部来源下载
在我们的示例数据集中,情况就是这样,因为我从 Kaggle 下载了它。当使用从互联网下载的数据集时,我们应始终确保检查其版权许可。大多数情况下,如果它在公共领域,我们可以自由使用和分发,无需担忧。我们正在使用的数据集就是这种情况的一个例子。另一方面,如果数据集受版权保护,我们可能仍然可以通过请求数据集的作者/所有者的许可来使用它。我发现,通过电子邮件与他们联系并详细解释他们的数据集将如何被使用后,数据集所有者通常愿意与他人分享他们的数据。
手动收集/网络爬虫
如果我们想要的数据在线上有,但不是以表格或 CSV 文件格式,大多数情况下,我们需要自己收集它并手动将其放入数据集中。最多,我们可以编写一个网络爬虫,可以向包含目标数据的网站发送请求并解析返回的 HTML 文本。当你必须以这种方式收集数据时,确保你没有非法行事也很重要。例如,从某些网站上抓取数据是违法的;有时,你可能需要设计爬虫,以便在特定时刻只发送一定数量的请求。一个例子是 LinkedIn 在 2016 年对许多匿名抓取其数据的人提起诉讼。因此,始终寻找你试图以这种方式收集的数据的使用条款是一个好习惯。
通过第三方收集数据
学生和研究人员在他们的研究中发现,他们寻找的数据无法在线收集时,通常会依赖第三方服务来帮他们收集这些数据(例如,通过众包)。Amazon Mechanical Turk (MTurk) 就是这样的服务之一——你可以输入任何类型的问题来制作调查问卷,MTurk 将该调查问卷介绍给其用户。参与者可以通过完成调查获得报酬,这笔费用由调查的所有者支付。这个选项再次特别适用于当你想要一个在线任何地方都不可获得的代表性数据集时。
数据库导出
如果你正在处理公司或组织的数据,这很可能是这种情况。幸运的是,PyCharm 在处理数据库及其数据源方面提供了许多有用的功能。这个过程在第十一章中进行了讨论,如果你还没有看过,我强烈推荐你查看。
数据集的版本控制
由于我们刚刚简要地讨论了数据收集,我希望在谈论使用数据在版本控制系统如 Git 中时,你能再次宽容我。早些时候,我们打开了一个数据文件,PyCharm 立即对文件的大小提出了抱怨。按照现代标准,一个 8MB 的文件并不大。然而,考虑到大多数代码文件,PyCharm 的宗旨,平均大小都在 100K 以下。如果你的文件非常大,那可能是一个代码问题,你应该找出你做错了什么。
在这里,我们向 PyCharm 提供了一个大约比其通常处理的文件大 8,000% 的文件。Git 也主要用于处理来自 IDE 的小文件。我提这一点是因为数据科学和科学计算社区中存在某种程度的可重复性问题。这就是一个数据团队能够从一个数据集中提取特定的见解,但其他人即使使用相同的方法也无法做到。许多这种情况的发生是因为不同团队之间使用的数据不兼容。有些人可能使用的是相同的但过时的数据集,而其他数据集可能来自不同的来源。
数据集的版本控制是一个需要考虑的重要话题。Git 通常对任何文件都有 100 MB 的硬限制,我可以根据经验告诉您,GitHub 上您项目的总大小有一个上限。在其他版本控制系统中也存在相同的限制。我过去曾用名为 Unity 3D 的工具教授游戏开发,我们总是因为这些限制而苦苦挣扎,因为视频游戏项目通常包含非常大的资源,这些资源不一定是代码,但可以从版本控制中受益。
使用 Git 大文件支持
由于问题普遍存在,Git(以及其他)添加了通过 Git 大文件支持(Git LFS)跟踪大型资源的能力。当我们使用 Git LFS 添加文件时,系统会用一个简单地引用它的指针来替换该文件。当文件被置于版本控制之下时,Git 只会保留对实际文件的引用,该文件现在存储在外部文件系统中,可能位于另一台服务器上。Git LFS 允许我们使用 Git 对大型文件(在这种情况下,是数据集)进行版本控制,而实际上并不存储这些文件在 Git 中。
此功能通常与现代 Git 安装程序一起安装。图 14.2 展示了我安装 Git for Windows 的过程,其中 LFS 是默认安装的一部分:
图 14.2:在 Windows 中,LFS 默认已安装
您可以使用命令行检查您的安装,无论您使用的是哪种操作系统:
git lfs version
我在 Windows 11 的 GitBash 中运行此命令的结果如 图 14.3 所示:
图 14.3:如果已安装 LFS,它应该会告诉您版本号
我拥有 Windows 的唯一原因(除了一般的 Ghost Recon 和 Steam)是,我可以使用 Microsoft Word 来编写这本书。这不是我的主意。我本来打算用原始的 LaTeX 和 vi 编写整个内容。不是 vim。不是 neovim。原始的 gangsta vi,我会自然地从源代码编译它。我的编辑器说不行。她非常礼貌!如果我们的角色互换,谁知道会说什么呢?无论如何,我剩下的真正工作是在 Pop_OS 上完成的,这是一个 Ubuntu Linux 的变种。当我把命令扔进那个环境时,我得到了一个不那么友好的回答,如 图 14**.4 所示:
图 14.4:我的安装程序不够现代,无法预装 LFS
我没有!我必须使用以下命令来安装它:
sudo apt update
sudo apt install git-lfs
完成这些后,我可以再次测试:
图 14.5:成功!如果你使用的是其他 Linux 分发版,如果你的安装中没有 git-lfs 软件包,请检查你的包管理系统
适用于你 Linux 分发的特定 git-lfs
软件包。
使用 Git LFS
我们可能有点过于急切了。如果你打算跟随这个小小的旁路练习,最好在本书代码库之外创建一个新的文件夹。让我们假设你在操作系统的终端中已经有了类似这样的文件夹:
cd ~/
mkdir git-lfs-test
cd git-lfs-test
git init
这一系列命令可以在任何流行的操作系统(Windows、macOS 或 Linux)中运行。如果你使用 Windows,这一系列命令可以在 PowerShell 中运行,并假设你已经安装了 Windows 的 Git 客户端。安装程序可在 git-scm.com/downloads
获取。
第一个命令将你带到你的 home
文件夹。第二个命令创建一个名为 git-lfs-test
的新文件夹。接下来,我们将目录更改为我们刚刚创建的 git-lfs-test
文件夹,并初始化一个新的仓库。现在,我们已经准备好设置 Git LFS 的支持。
不要忘记章节文件已经在 Git 仓库中
如果你正在跟随本章的源代码,别忘了文件已经在一个 Git 仓库中。在现有仓库内创建第二个仓库是不行的。如果你想练习,请在这个书的仓库之外创建一个完全独立的文件夹,并将项目文件复制到你的文件夹中。当你复制时,你特别想避免将 .git
文件夹复制到你的目标文件夹中。
在我们的项目中,我们将使用 Git LFS 来跟踪具有特定扩展名的文件,特别是具有 .txt
扩展名的文本文件。鉴于这些文件自然是纯文本,你可以对扩展名进行一些创意性的处理,而不会影响它们的使用方式,但我们将坚持使用 .txt
。我将在我的终端窗口中运行这个命令:
git lfs track "*.txt"
你可以在 图 14**.6 中看到我的测试运行:
图 14.6:Git LFS 现在正在跟踪所有.txt 扩展名的文件
为了完成我的 LFS 测试,我将把之前检查过的文件0EA27ICBLF_1607.txt
从Tappy Data
文件夹复制到我们用于实验的git-lfs-test
文件夹。为了清楚起见,图 14**.7显示了我的文件夹。我们不会在本书代码存储库的任何子文件夹内进行此操作,因为在一个存储库内部创建存储库是大忌:
图 14.7:我已经将 0EA27ICBLF_1607.txt 复制到 git-lfs-test 文件夹
现在,让我们将新复制的文本文件添加到存储库中:
git add 0EA27ICBLF_1607.txt
git commit -m "adding big file"
git lfs ls-files
我们在第五章中详细介绍了前两个 Git 命令。最后一个命令将列出在这个存储库中由 LFS 跟踪的所有文件。您可以在图 14**.8中看到我的输出:
图 14.8:我可以看到我的文本文件正在被 LFS 跟踪
您现在已经了解了如何使用 Git LFS 跟踪大文件。如果这是一个我们感兴趣的真正存储库,我们还需要做最后一件事。当我们命令 Git 跟踪我们的文本文件时,代表我们创建了一个名为.gitattributes
的特殊文件。我们应该添加并提交该文件:
Git add .gitattributes
Git commit -m "Added .gitattributes to repo"
您已经准备好了!让我们继续到数据分析流程中的下一个正式步骤,这包括数据清洗和预处理。
数据清洗和预处理
如我之前提到的,我们非常幸运。我们团队处理的一些数据可能非常糟糕。当我们使用“脏”、“污秽”和“清洗”等术语来描述数据时,我们谈论的是解决数据的格式问题,以及数据适合处理的情况。如果数据格式是我们能处理的形式,那么数据才有用。结构化数据是我们始终首选的。
结构化数据指的是分割成可识别字段的数据。我们已经看到了逗号分隔和制表符分隔的文本。结构化数据的其他示例包括 XML、JSON、Parquet 和 HDF5 等格式。前两种,XML 和 JSON,非常常见,并且具有文本格式的优势。后两种,Parquet 和 HDF5,是二进制文件,专门用于存储比文本处理更舒适的大数据集。正如我们所见,包括 PyCharm 在内的大多数工具在尝试读取非常大的文本文件时都会崩溃。如果您想就地查看或编辑这些文件,则需要专门用于处理大文件的工具。
当我谈论脏数据与干净数据时,我寻找的是诸如缺失或无效字段数据等问题。回想我们之前的数据样本:
BirthYear: 1952
Gender: Female
Parkinsons: True
Tremors: True
DiagnosisYear: 2000
Sided: Left
UPDRS: Don't know
Impact: Severe
Levadopa: True
DA: True
MAOB: False
Other: False
UPDRS
字段标记为未知。这并不理想。如果包含该字段,我希望看到那里的值。在这种情况下,无法进行回填,但在一个完美的世界里,这可能是一个数据清洗练习的候选者。
一个涉及忍者的有毒数据示例
我遇到的最相关的例子是来自企业界,而不是来自数据科学实验中的脏数据——或者在这个案例中,是有毒的数据。我的公司正在为一家大型航空公司提供咨询服务,该公司也是美国国防部的承包商。在这里我不会提及真实姓名,因为我通常不喜欢政府在凌晨 2 点踢开我的门,或者更糟糕的是,因为我在这里写的内容而被标记为税务审计。所以,我们将保持这个理论上的讨论。
这家航空公司与许多供应商做生意,当你大规模地与供应商做生意时,根据购买量对所购买的商品应用折扣并不罕见。如果你或我去 Hammers R Us 买一把锤子,我们可能为这把锤子支付 12.95 美元。但是,如果航空公司在一个季度内通过多个订单购买了 5,000 把锤子,他们可能会获得高达 60%的折扣。航空公司的任务是跟踪他们购买的商品和供应商,以便他们可以兑现公司与供应商协商的任何批量购买协议。
当是时候运行折扣报告时,一个会计分析师可能会查询一个由数百甚至数千人在代表航空公司工作的领域输入的数据数据库。由于这些操作员是真人,他们在没有任何验证的情况下将干净、标准化的数据输入到一个设计不良的系统中的能力几乎为零。在这种情况下,用于订单输入的软件允许用户在一个文本字段中输入公司的名称,而这个字段从未与任何批准的供应商名单进行过验证。
有一个人将采购记录为“Hammers R Us”的供应商。另一个人将其输入为"HRUS"(自然地,那是股票代码),还有一个人将其输入为"H.R.U.S。"另一个人将其拼写错误为“Hammers Are Us”,还有另一个人将其拼写为“Hammers-R-Us。”现在,我们对同一家公司有五种不同的引用,这削弱了我们确定我们可以要求多少折扣的能力。如果有五种拼写,并且采购数量在五份订单中平均分配,那么每份订单将只有 1,000 把锤子,我们的折扣只有 20%,而不是 60%。我们的有毒数据问题正在使公司损失大量金钱!
飞机制造公司雇佣了我的公司进行数据清洗。我们的任务是清理所有数据并标准化所有对 Hammers R Us 的引用。对我们来说,这个项目是成功的,因为我们只需向客户收取比他们损失少几美元的费用,而这笔费用相当可观。然后,我们帮助他们修复软件,使其之后无法输入有毒数据。这对每个人都是个胜利!我甚至从 Hammers R Us 得到了一把免费的锤子,至少在我的故事版本中是这样的,这个版本意味着我没有被审计或遭到忍者的袭击。
在 PyCharm 中进行探索性分析
虽然在数据科学项目中数据清洗通常不会带来经济上的利润,但它是一个非常必要的步骤。当你开始第一次检查你的数据时,你经常会听到这个过程被称为探索性数据分析,在这个过程中我们同时探索和分析数据。但我们实际上是在盘点,看看我们能用我们的数据做什么。如果没有首先确保所有必要的数据都存在并且以可用的数值格式,那么执行诸如计算总和、平均值和标准差之类的制表操作将会非常困难。我们可能还会寻找异常值。也许一个锤子订单被误输入,我们有一个取消的订单,该订单为 100 万把锤子。这些类型的异常值在我们真正开始分析之前可能需要被移除。
在我们数据的情况下,有几件事情让我感到烦恼:
-
研究称它检查了 103 名受试者;然而,在
存档用户
文件夹中有 277 个用户文件。我怀疑并非每个用户都有匹配的收集数据。我们需要一种方法来检查存档用户
文件夹中的每个用户在Tappy
数据
文件夹中都有一个相关的数据集。 -
我们的数据是纯文本的,这意味着当我们通过读取文件将其导入 Python 时,数据将以字符串的形式表达。这对于数据分析来说并不理想。我希望数字能转换为数字类型,日期转换为日期类型,布尔值转换为布尔值,等等。
-
Impact
列应该完全标准化,以处理数据中的缺失值。自然地,这也适用于任何我可以看到或怀疑可能包含缺失值的列。 -
我们可以将用户数据集中的某些字段转换为二进制格式,以使分析更容易。具体例子包括
Parkinsons
、Tremors
、Levadopa
、DA
、MAOB
和Other
。 -
我们可以使用一种称为独热编码的过程来更容易地处理标记为
Sided
、UPDRS
和Impact
的字段。一旦我们准备好执行这个过程,我会详细介绍独热编码。
这只是我第一眼看到的。一旦我们开始着手,可能会有其他清洁的机会出现。
从文本文件中读取数据
让我们看看我们需要对数据进行预处理的工作。如果你打开 data_clean.py
文件,你会看到我们的清理脚本,该脚本使用了在 第十二章 中讨论的单元格模式。我们的第一个单元格处理我们的导入:
import pandas as pd
import numpy as np
import os
import gc
如果你正在跟随本章的代码,别忘了使用 requirements.txt
文件创建一个虚拟环境。在这里,我们导入了一些老朋友。numpy
和 pandas
是标准的分析库。os
包将用于处理文件目录,而 gc
包允许我们控制 垃圾回收(GC)过程。如果你之前从未听说过这个,那是因为大多数编程语言,包括 Python,都自动处理 GC。GC 的一个常见发生情况是当一个变量,它将分配内存来存储其值,超出作用域并且不再需要时。在 C 编程语言中,你需要在能够使用变量之前自己分配该内存。当你完成变量后,你需要“手动”释放该内存。如果你不这样做,你会使用比所需的更多内存,这就是你被邀请参加 Pi Day 披萨派对的原因。
大多数现代语言在称为 GC(垃圾回收)的过程中自动处理这种分配和释放。然而,有时,尤其是在你加载和处理大量数据时,当垃圾被清理出来时,采取更积极的角色是有意义的,这可以释放内存以供进一步使用。
我们的导入完成之后,让我们使用以下单元格读取一些数据:
#%% Read in data
user_file_list = os.listdir('data/Archived users/')
user_set_v1 = set(map(lambda x: x[5: 15], user_file_list)) # [5: 15] to return just the user IDs
os.listdir
方法接受我们的 data/Archived users/
文件夹,并给我们一个来自该文件夹的文件可迭代列表。这很重要,因为我们需要一个包含在每个用户文件名中的 ID 列表。
我们创建了一个名为 user_set_v1
的变量,并实例化了一个集合。在 Python 中,set
是一个内置的数据类型,它表示一个无序的唯一元素集合。这意味着集合不能包含重复的值,并且存储元素的顺序不一定与它们被添加的顺序相同。
我们使用 map
语句填充这个 set
,该语句遍历 Archived users
文件夹中的文件列表。对于 map
的每次迭代,我们使用一个 lambda 函数从 user_file_list
中的每个文件名中提取一部分。具体来说,它从每个文件名的第 5 个到第 15 个字符提取子字符串。这是从文件名中提取用户 ID 的目的。接下来,我们还需要对 Tappy
数据文件做类似的事情:
tappy_file_list = os.listdir('data/Tappy Data/')
user_set_v2 = set(map(lambda x: x[: 10], tappy_file_list)) # [: 10] to return just the user IDs
现在,我们有两个集合,一个来自用户文件,一个来自 Tappy 数据文件。我们需要找到这两个集合的交集。
在 集合论 中,术语 交集 指的是一种操作,它将两个集合组合起来创建一个新集合,该集合只包含两个原始集合共有的元素。两个集合的交集,通常用 ∩ 符号表示,代表集合之间的重叠或共享元素。
从数学上讲,如果你有两个集合,A 和 B,A 和 B 的交集是一个新集合,它包含所有同时存在于集合 A 和集合 B 中的元素。
我知道你们这些数学爱好者都喜欢你们的符号,我也知道你们的头脑被编程去寻找模式而不是逐字阅读,所以我会帮你们。从符号上讲,它表示为 A ∩ B = {x ∣ x ∈ A and x ∈ B}。
在 Python 编程的背景下,set
类型的 intersection()
方法执行这个数学运算。给定两个集合,它返回一个新集合,只包含两个集合中都存在的元素。
例如,假设你有两个以下集合:
-
集合 A =
-
集合 B =
集合 A 和 B 的交集将是 A ∩ B = {3, 4},因为 3 和 4 都在两个集合中。在我们的情况下,获取交集很重要,因为研究文本指出它考察了 103 个受试者,但在“存档用戶”文件夹中列出了 227 个受试者。我可以制作一个列表,然后逐一检查Tappy Data
文件夹的内容,以确保每个人都已计入,但这会很无聊,耗时且容易出错。我将让 Python 帮我完成:
user_set = user_set_v1.intersection(user_set_v2)
你难道不喜欢 Python 的单行代码吗?当然,有一些设置(嘿嘿,intersection
方法,我们得到了一个新集合,它既有趣又无杂乱!让我们通过打印长度来看看我们得到了什么:
print(len(user_set))
我将使用 图 14**.9 中指示的绿色箭头运行前两个单元格:
图 14.9:我正在使用每个单元格顶部的绿色箭头运行到目前为止我们所覆盖的前两个单元格
运行结果如 图 14**.10 所示:
图 14.10:在数据清理的第一步之后,我得到了一个相对干净的用戶列表
我们得到了两个集合之间相关数据的 217 个用戶,所以我们成功地消除了我们不会使用的 60 个用户文件。这个数字与测试中报告的 103 个受试者不符,但没关系——日子还很长,我们可能会稍后消除更多。即使我们不会,也可能有其他原因稍后消除正确匹配的数据。我们的新集合可以用来遍历任一数据文件夹中的数据,因为两个文件夹中的文件名都使用 ID 作为文件名的主要部分。这将在我们数据准备过程的下一步中非常有用。
在 图 14**.10 中,我点击了查看链接来查看带有 SciView 面板的我的列表。它并不特别令人兴奋,因为它只是一个 ID 列表,但能够在不执行额外打印的情况下轻松检查我们正在工作的内容是非常有用的。
将数据导入 pandas DataFrame
我们下一个单元格中的代码是为了将加载的数据集拉入 pandas DataFrame。pandas 是一个库,它允许轻松分析表格数据,甚至提供了许多非常实用的方法,可以直接将数据加载到 DataFrame 中,DataFrame 是 pandas 中的一个表格结构。DataFrame 对象很像一个没有编辑器的内存中的电子表格。你可以用最小的努力执行各种计算。
让我们检查下一单元格中的代码:
#%% Format into a Pandas dataframe
不要忘记在 PyCharm 中 #%%
是一个特殊的格式化注释。它不是 Python 的一部分。我们之前在 第十三章 中讨论过这一点。这些字符用于分割我们的代码中的单元格,这使得我们可以使用一个脚本,但可以逐步从一个单元格操作到下一个单元格。最终,它仍然是一个注释,因此我们应该包含一些文档来解释单元格中正在发生的事情。
接下来,我们将创建一个函数,从 Archived
users
文件夹中的文件读取数据:
def read_user_file(file_name):
f = open('data/Archived users/' + file_name)
data = [line.split(': ')[1][:-1] for line in f.readlines()]
f.close()
return data
该函数简单地接受一个文件名作为参数并打开文件。然后逐行读取文件。对于每一行,我们使用 split
字符串函数将行分割成块作为列表。这使得我们可以只获取我们需要的部分。如您所回忆的那样,这些文件的一些数据行看起来像这样:
BirthYear: 1952
Gender: Female
Parkinsons: True
字段名和数据之间的分隔符是一个冒号和一个空格(:
)。我们将其用作分隔符,因此如果你使用 "BirthYear: 1952".split(': ')
,你会得到一个列表:["BirthYear", "1952"]
。我们现在不关心字段名,我们关心的是值。为了得到这个值,我们取 [1]
,这给我们 "1952"
,这是值,但每行末尾都有一个换行符,并且这个换行符被包含在我们的分割中。在我们继续下一个迭代之前,我们最后做的事情是使用 Python 的分割操作符 [:-1]
清除换行符,这实际上意味着“到达字符串的末尾”,正如事实所证明的那样,数字在冒号之后,并且“从末尾切掉一个字符”,正如负数所表示的那样。我们不是使用循环,而是使用了列表推导,这是一种迭代列表的替代方法。这些通常比普通的 for
循环更高效。列表推导的结果是一个新列表,它只包含我们想要的数据。
接下来的几行代码是为了填充 pandas DataFrame 而设置的。首先,我们获取 Archived
users
文件夹中的文件列表:
files = os.listdir('data/Archived users/')
接下来,我们创建一个字段列表。我们已经设置了一个函数,可以从文件中提取数据而不包括字段名。同时提取名称可能会增加很多时间,因为这是重复的操作;这只是一个更有效的方法:
columns = [
'BirthYear', 'Gender', 'Parkinsons', 'Tremors', 'DiagnosisYear',
'Sided', 'UPDRS', 'Impact', 'Levadopa', 'DA', 'MAOB', 'Other'
]
接下来,我们使用columns
列表创建一个空的 DataFrame 作为起点。想象一下,就像创建一个新的电子表格,并在你的工作表的第一行填写列名:
user_df = pd.DataFrame(columns=columns) # empty Data Frame for now
接下来,让我们遍历user_set
,这是我们之前单元格中创建的。记住,这是包含Tappy Data
文件夹中数据的用户 ID 列表。回想一下,这个文件的文件名结构是单词User
后跟一个下划线,然后是用户 ID,最后以.txt
文件扩展名结尾:
for user_id in user_set:
temp_file_name = 'User_' + user_id + '.txt'
接下来,我们确保文件存在。既然我们之前已经做了set
操作,它应该存在,但检查一下是个好主意。如果文件不存在,我们的分析集将会崩溃。对于几百个文件来说,这不算什么大问题,但如果你要处理成千上万的数据,那就可能让人心碎。假设文件存在,我们使用之前创建的函数将其读入一个名为temp_data
的变量中。记住,那个函数返回的数据值看起来就像电子表格中的一行单元格。然后,我们使用用户 ID 作为行的索引,将数据插入 DataFrame 中:
if temp_file_name in files:
temp_data = read_user_file(temp_file_name)
user_df.loc[user_id] = temp_data
当然,我们想检查,但我们不想检查每一行——我们只想检查前几行,以确保它们格式化得符合我们的预期:
print(user_df.head())
当我运行这个单元格时,我得到以下输出:
图 14.11:我们最新单元格的运行结果显示我们有一个填充的 pandas DataFrame
记住,你可以通过点击图 14.11中箭头所指的以 DataFrame 查看按钮在SciView中查看 DataFrame。我的显示在图 14.12中:
图 14.12:在 PyCharm 中查看我在上一步创建的 DataFrame 既简单又多彩
从这里我可以看出,我还有很多工作要做。诊断年份很混乱,其他一些字段也是如此。让我们继续一点一点地解决它。
数据清洗
现在我们已经可以看到我们的数据以表格格式呈现,有一些方法可以帮助我们改进数据的格式,以便在可能选择的任何维度上执行数值分析。
将数值数据转换为实际数字
我们下一个单元格包含几行代码,用于将数值值转换为数值类型。记住,所有数据都是以文本形式进入的,并且直到你告诉 pandas 否则,它们都被当作字符串处理。以下是该单元格的代码:
#%% Change numeric data into appropriate format
# force some columns to have numeric data type
user_df['BirthYear'] = pd.to_numeric(user_df['BirthYear'], errors='coerce')
user_df['DiagnosisYear'] = pd.to_numeric(user_df['DiagnosisYear'], errors='coerce')
应用程序程序员可能会倾向于逐行处理数据,逐字段处理类型转换。pandas 的好处在于,一旦你的数据在 DataFrame 中,你就可以对整个行和列进行操作。
在这段代码中,我们正是这样做的。BirthYear
和 DiagnosisYear
正在使用 pd.to_numeric
方法转换为数字。第二个参数 errors='coerce'
将尝试强制数据转换为数值类型。如果不可能,例如,当值是 “-------
”(一串破折号),就像我们在 NaN
中看到的那样,或者 “不是数字”。虽然 NaN
在计算上没有价值,但它至少将所有非数值值标准化为这个值,这将使我们在选择忽略这些行时更容易。
提到 NaN
也意味着是时候在你妈妈的坦度炉里烤一些美味的面包了。一些作者做 Patreon,我烤面包,但必须是你的妈妈的食谱。这意味着你必须给她打电话,告诉她你爱她。现在就做,即使她没有坦度炉,不能烤面包!我会等着。
当你在电话上时,我运行了单元格;我的结果显示在 图 14**.13 中:
图 14.13:年份字段实际上不是数字。无论在哪里有无效数据,我们现在都看到一个标准化的 nan 值
二值化数据
我们可以在任何可以将数据转换为本质上二进制的地方进行转换。在我们的数据中,性别以两种可能值报告,即男性和女性,表示可以用二进制格式表示的可能性。同样,许多字段都呈现为二进制,如下所示:
Parkinsons: True
Tremors: True
Levodopa: True
DA: True
MAOB: False
Other: False
在这些情况下,我们只需要将值标准化为实际的二进制值,这可能会导致重命名或扩展我们的字段名称列表。让我们看看单元格代码:
#%% "Binarize" true-false data
user_df = user_df.rename(index=str, columns={'Gender': 'Female'})
user_df['Female'] = user_df['Female'] == 'Female'
user_df['Female'] = user_df['Female'].astype(int)
在前面的代码中,我们将 DataFrame 中的列名从 Gender
改为 Female
。第二行将新列中每行的值更改为与单词 Female
进行比较的表达式的结果。它要么是 Female
,要么不是,因此我们得到 True
或 False
的值。第三行将布尔类型转换为整数,使其更适合分析。
接下来,我们将关注之前列出的列,并执行相同的转换。这次,我们在表达式中检查单词 True
。值要么是 True
,要么不是,这会产生布尔值:
str_to_binary_columns = ['Parkinsons', 'Tremors', 'Levadopa', 'DA', 'MAOB', 'Other'] # columns to be converted to binary data
for column in str_to_binary_columns:
user_df[column] = user_df[column] == 'True'
user_df[column] = user_df[column].astype(int)
运行此代码会导致我们的 DataFrame 发生变化,如图 图 14**.14 所示:
图 14.14:我们已经成功地将字段二值化
我们可以看到,我们的字段现在都是二进制数字!这将使后续工作更容易!
让我们跳到下一个单元格,因为代码的第一部分正在进行一些清理工作,类似于我们之前所做的那样。在单元格的第一部分,我们正在清理Impact
字段。我们将任何不是Mild
、Medium
或Severe
的值标准化为None
:
# prior processing for `Impact` column
user_df.loc[
(user_df['Impact'] != 'Medium') &
(user_df['Impact'] != 'Mild') &
(user_df['Impact'] != 'Severe'), 'Impact'] = 'None'
接下来,在保持同一单元格的同时,我们将探索一个强大且流行的技术,该技术被机器学习算法在准备数据分析数据时使用。
独热编码
由于某种原因,当我第一次听到独热编码这个词时,我立刻想到了热狗,还有我多么希望能在美味的蒸面包上用芥末和甜美酱编码一个,或者也许是我要发送给你的 NaN。记录在案,我知道面包的拼写不是这样的,我也不在乎。只有当我拼错时,这个笑话才有效。我不知道为什么我要告诉你这些,但就是这样。
独热编码是一种技术,它允许你将不是固有的布尔值的数据转换为布尔值。当我正在寻找一辆新的 Jeep Wrangler 时,我只考虑了少数几种颜色:
-
爆竹红
-
海洋蓝金属色
-
莫吉托!
-
Hellayella
还有更多的颜色,但它们都是黑色、白色或灰色的无聊变体。我无法得到一辆橙色的吉普车,因为人们会认为我去了俄克拉荷马州立大学,我们不能有那样的事情。我可以忽略那些颜色,留下一个可以放在页面上的列表。现在,让我们对这个列表进行独热编码:
Color_Firecracker_Red | Color_Ocean_Blue_Metallic | Color_Mojito | Color_Hellayella |
---|---|---|---|
1 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 0 | 1 |
你可以轻松地看到如何进行独热编码——它将字段旋转并使它们变为二进制。如果你是关系型数据库的大师,你可能刚刚失去了你的午餐。数据科学家做事的方式略有不同。在独热编码表示中,每个观察值在其类别对应的列中得到一个“1”,在其他所有列中得到一个“0”。这种编码确保了分类信息以机器学习算法可以理解和有效使用的方式被保留。记录在案,我选择了Hellayella,基于这样的想法:如果我的吉普车被卡在某个无法到达的地方,比如大弯国家公园的沙漠中,或者东德克萨斯州深处的松树林地区,救援直升机会很容易找到我的尸体。
独热编码通常用于诸如分类变量等特征,这些特征不能直接用作许多机器学习算法的数值输入。它是数据预处理的重要步骤,将此类变量转换为适合训练模型的合适格式。
让我们回到当前单元格的代码。我们已经解释了前几行,所以让我们继续设置对多个字段进行独热编码:
to_dummy_column_indices = ['Sided', 'UPDRS', 'Impact'] # columns to be one-hot encoded
我们将要编码这三个列。考虑中的一个列是Impact
列,我们刚刚将其标准化作为这一步的引导。我们将在这里对这三个列执行 one-hot 编码:
for column in to_dummy_column_indices:
user_df = pd.concat([
user_df.iloc[:, : user_df.columns.get_loc(column)],
pd.get_dummies(user_df[column], prefix=str(column)),
user_df.iloc[:, user_df.columns.get_loc(column) + 1 :]
], axis=1)
print(user_df.head())
在循环中,代码执行以下步骤:
-
user_df.iloc[:, : user_df.columns.get_loc(column)]
:选择当前正在处理的列左侧的列。这保留了正在 one-hot 编码之前的列。 -
pd.get_dummies(user_df[column], prefix=str(column))
:使用pd.get_dummies()
方法对当前列进行 one-hot 编码。它创建一个具有二进制列的 DataFrame,代表列中的不同类别。prefix
参数为列名添加前缀,以指示它们是从哪个原始列派生出来的。 -
user_df.iloc[:, user_df.columns.get_loc(column) + 1 :]
:选择当前正在处理的列右侧的列。这保留了正在 one-hot 编码之后的列。
当输入到pd.concat
方法中时,这些步骤有效地将三个分类列替换为 one-hot 编码的二进制列,同时保持 DataFrame 的其余部分完整。当您运行单元格时,您应该看到像我一样的结果,如图 14.15所示:
图 14.15:我已经向右滚动,以便您可以看到新添加的 one-hot 编码列
One-hot 编码将在 DataFrame 中添加许多新列,因此您可能需要向右滚动才能看到它们全部。
探索第二个数据集
在我们的用户数据被相当干净地整理并存储在 pandas DataFrame 中后,我们现在准备处理 Tappy 数据。为了使内容更具相关性,我将任意选择Tappy Data
集中的一个文件。让我们看看下一个单元格中的代码:
#%% Explore the second dataset
file_name = '0EA27ICBLF_1607.txt'
正如我说的,我随机选择了一个文件来检查。我们之前打开了一个这样的文件,并注意到它们都是制表符分隔的格式。pandas 有一个方法可以直接将此类文件直接读入 DataFrame。尽管该方法被称为read_csv
,但您可以指定一个分隔符,它不必是逗号。该方法将读取任何类型的分隔文件:
df = pd.read_csv(
'data/Tappy Data/' + file_name,
delimiter = '\t',
index_col = False,
names = ['UserKey', 'Date', 'Timestamp', 'Hand', 'Hold time', 'Direction', 'Latency time', 'Flight time']
)
对于我们的目的,我们不需要UserKey
字段:
df = df.drop('UserKey', axis=1)
print(df.head())
当我们运行这个单元格时,我们创建了一个名为df
的新 DataFrame。请确保从图 14.16中显示的控制台变量面板中选择它:
图 14.16:可以通过点击“查看为 DataFrame”按钮来查看我们的新 DataFrame
格式化日期时间数据
下一个单元格修复我们的datetime
数据:
#%% Format datetime data
这第一行尝试强制Date
列中的值成为日期。如果强制转换不起作用,我们将看到NaT
(不是一个时间),这很令人失望,因为没有食物玩笑可以讲。接下来,我们将在Hold time
、Latency time
和Flight
time
字段上进行更多的强制转换:
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', format='%y%M%d').dt.date
# converting time data to numeric
for column in ['Hold time', 'Latency time', 'Flight time']:
df[column] = pd.to_numeric(df[column], errors='coerce')
任何缺少时间数据的观察结果都应该被删除:
df = df.dropna(axis=0)
让我们打印出结果以供检查:
print(df.head())
让我们运行它!我的单元格运行结果显示在图 14**.17中:
图 14.17:我们的日期时间数据现在是数值型,任何缺少时间数据的观察结果,因为无用,已被删除
洗手并确定方向
下一个单元格清理了手和方向列:
# cleaning data in Hand
df = df[
(df['Hand'] == 'L') |
(df['Hand'] == 'R') |
(df['Hand'] == 'S')
]
这段代码使用逻辑“或”来过滤掉任何不具有L
、R
或S
值的值。由于它被表示为“或”,任何超出三个期望可能性的值都将返回为false
,并被排除。
让我们用同样的方法处理方向,它有更多的可能性:
# cleaning data in Direction
df = df[
(df['Direction'] == 'LL') |
(df['Direction'] == 'LR') |
(df['Direction'] == 'LS') |
(df['Direction'] == 'RL') |
(df['Direction'] == 'RR') |
(df['Direction'] == 'RS') |
(df['Direction'] == 'SL') |
(df['Direction'] == 'SR') |
(df['Direction'] == 'SS')
]
当然,我们会打印出结果:
print(df.head())
好的,运行这个单元格。所有包含无效数据的行都已删除。这个结果不像大多数那样直观,所以我认为我们不需要为这个结果截图。
总结数据
我们下一个单元格提供了一个如何总结我们数据的例子,这是我们一直在努力设置以进行分析的。我们已经准备好了!让我们尝试一些简单的东西。像往常一样,单元格中的第一行代码只是标记单元格的开始:
#%% Group by direction (hand transition)
回想一下,我们到目前为止一直在处理的是特定时间特定主题的打字速度数据。一个主题(User
)只是我们第一个数据集中单个数据点,我们希望以某种方式将两个数据集结合起来,因此我们需要一种方法来聚合我们当前的数据到一个单一的数据点。
由于我们正在处理数值数据(打字时间),我们可以通过对不同列的时间数据进行平均(平均值)来总结给定用户的数据。我们可以使用 pandas 的groupby()
函数来实现这一点:
direction_grouped_df = df.groupby('Direction')[numeric_columns].mean()
当然,我们应该打印它:
print(direction_grouped_df)
运行结果显示在图 14**.18中。代码将结果放入一个新的 DataFrame 中,名为direction_group_df
,所以请确保您按照图中的方式选择它:
图 14.18:太好了!我们得到了第一个计算洞察!
这很令人兴奋!我们已经让机制工作起来,但现在,我们需要专注于让它能够处理多个数据文件,而不仅仅是单个文件。
为扩展进行重构
我们对 Tappy 数据的探索主要集中在单个文件上,以便以易于验证的方式确认我们的代码正在正常工作。我们已经确定它是可行的,因此现在,我们应该重构我们的代码,以便我们可以处理成千上万的文件。为此,我们应该将一些单元格合并成一个函数。下一个单元格中的代码虽然很长,但很熟悉,因为它只是我们将到目前为止所写的所有代码合并成一个函数的结果。如果你是一个应用程序开发者,并且理解被称为单一职责原则(SRP)的设计原则,你知道这是一个反模式。然而,请记住,这并不是应用程序代码。没有人会运行这段代码来执行分析之外的操作,因此通常适用于软件开发中的 SOLID 原则的严格性在数据科学工作中并未得到体现。
使用一个函数处理 Tappy 数据
这里是函数:
#%% Combine into one function
def read_tappy(file_name):
在这里,我们正在读取作为函数参数传递的 CSV 文件名。我们使用硬编码的字段名丰富数据:
df = pd.read_csv(
'data/Tappy Data/' + file_name,
delimiter='\t',
index_col=False,
names=['UserKey', 'Date', 'Timestamp', 'Hand', 'Hold time',
'Direction', 'Latency time', 'Flight time']
)
我们删除了不需要的列:
df = df.drop('UserKey', axis=1)
我们修正了日期:
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', format='%y%M%d').dt.date
# Convert time data to numeric
for column in ['Hold time', 'Latency time', 'Flight time']:
df[column] = pd.to_numeric(df[column], errors='coerce')
df = df.dropna(axis=0)
总是通过去除无效值来洗手:
# Clean data in `Hand`
df = df[
(df['Hand'] == 'L') |
(df['Hand'] == 'R') |
(df['Hand'] == 'S')
]
对方向数据值也做同样的处理:
# Clean data in `Direction`
df = df[
(df['Direction'] == 'LL') |
(df['Direction'] == 'LR') |
(df['Direction'] == 'LS') |
(df['Direction'] == 'RL') |
(df['Direction'] == 'RR') |
(df['Direction'] == 'RS') |
(df['Direction'] == 'SL') |
(df['Direction'] == 'SR') |
(df['Direction'] == 'SS')
]
我们正在做数学运算!这是手动 GC 过程介入的地方。我们洗手洗得很好,对吧?在以下代码中,我们正在进行计算。结果作为新的 DataFrame 返回,因此为了节省内存,我们在进行过程中删除旧的 DataFrames。这释放了内存,因为这种工作对内存密集型:
direction_group_df = df.groupby('Direction')[numeric_columns][numeric_columns][numeric_columns] direction_group_df = df.groupby('Direction')[numeric_columns].mean()
del df
gc.collect()
使用我们新的结果,我们重新索引然后排序:
direction_group_df = direction_group_df.reindex(
['LL', 'LR', 'LS', 'RL', 'RR', 'RS', 'SL', 'SR', 'SS'])
direction_group_df = direction_group_df.sort_index() # to ensure correct order of data
这行代码返回一个展平的 NumPy 数组,其中包含分组数据的均值。.values.flatten()
方法将 DataFrame 转换为二维 NumPy 数组,然后将其展平为一维数组,以便于使用:
return direction_group_df.values.flatten()
使用函数处理用户
在同一个单元格中还有一个第二个功能:
def process_user(user_id, filenames):
running_user_data = np.array([])
这行代码初始化了一个名为 running_user_data
的空 NumPy 数组。这个数组将在函数遍历文件名时累积数据,这正是以下代码块所做的工作:
for filename in filenames:
if user_id in filename:
running_user_data = np.append(running_user_data, read_tappy(filename))
这个循环遍历文件名列表。如果提供的用户 ID 在文件名中找到,它将调用 read_tappy()
函数(该函数返回包含均值值的展平 NumPy 数组)并将内容追加到 running_user_data
数组中。
在遍历文件名并追加数据后,以下行将 running_user_data
数组重塑为二维数组,每行包含 27 列。这种时间数据的展平允许进行进一步的分析:
running_user_data = np.reshape(running_user_data, (-1, 27))
最后一行使用 np.nanmean()
函数计算 running_user_data
数组沿行(axis=0
)的均值。np.nanmean()
函数在计算均值时忽略 NaN
值:
return np.nanmean(running_user_data, axis=0)
总结一下,process_user
函数通过迭代相关文件名,使用read_tappy
函数聚合数据,重塑数据,并计算平均值(忽略NaN
值),来处理特定用户的数据。最终结果是数据每一列的平均值数组。
处理所有数据
这是为了所有宝石!下面的单元处理所有可用用户的数据,通过聚合和基于 Tappy 数据计算平均值。首先,有一些家务要做。我们将忽略任何警告:
#%% Run through all available data
import warnings
warnings.filterwarnings("ignore")
我们将再次访问Tappy
数据文件夹:
filenames = os.listdir('data/Tappy Data/')
接下来,我们将为最终的 DataFrame 创建一些列名:
column_names = [first_hand + second_hand + '_' + time
for first_hand in ['L', 'R', 'S']
for second_hand in ['L', 'R', 'S']
for time in ['Hold time', 'Latency time', 'Flight time']]
user_tappy_df = pd.DataFrame(columns=column_names)
接下来,让我们遍历用户索引并使用我们的process_user
函数:
for user_id in user_df.index:
user_tappy_data = process_user(str(user_id), filenames)
user_tappy_df.loc[user_id] = user_tappy_data
接下来的几行通过确保任何 NaN 值被替换为零,以及任何负数值也被归一化到零,进行了一些中间清理:
user_tappy_df = user_tappy_df.fillna(0)
user_tappy_df[user_tappy_df < 0] = 0
然后,我们将像从未打印过一样打印!好吧,这不是真的——我们经常这样做:
print(user_tappy_df.head())
保存处理后的数据
最后一个代码单元可能不需要太多解释:
#%% Save processed data
首先,我们将两个 DataFrame 连接起来:
combined_user_df = pd.concat([user_df, user_tappy_df], axis=1)
print(combined_user_df.head())
最后,我们将其保存到 CSV 文件中:
combined_user_df.to_csv('data/combined_user.csv')
这通常在给定的数据管道中是一个好的实践。保存数据集的处理和清理版本可以在过程中出现问题时为数据工程师节省大量精力。它也提供了灵活性,如果我们想改变或进一步扩展我们的管道,它也提供了灵活性。
在我们开始真正的分析工作之前,我将在 PyCharm 中打开 CSV 文件进行最后一次查看。你可以看到我的图 14.19:
图 14.19:我们的辛勤工作得到了回报!我们的数据已准备好分析
这样,我们就准备好探索我们的数据集并寻找洞察了。
数据分析和洞察
记得我们之前提到的,在开始数据科学项目时,心中要有问题意识的重要性吗?这一点在这个阶段尤其正确,在这个阶段,我们将探索我们的数据集并提取洞察,这些都应围绕我们的初始问题展开——打字速度与患者是否患有 PD 之间的联系。
在本节中,我们将使用位于当前项目notebooks
文件夹中的EDA.ipynb
文件。在接下来的子节中,我们将查看这个notebooks
文件夹中包含的代码。请打开这个 Jupyter 笔记本到你的 PyCharm 编辑器中,或者,如果你正在跟随我们的讨论并输入自己的代码,创建一个新的 Jupyter 笔记本。
启动笔记本并读取我们的处理数据
记住,当你用 Python 打开一个 Jupyter 笔记本时,你可以看到代码,但除非你点击运行按钮,否则 Jupyter 不会运行。你可以在图 14.20中看到 PyCharm 已经准备好了:
图 14.20:笔记本已打开,我已点击第一个单元格(In 1),现在我将点击箭头所示的运行按钮
一旦你点击运行按钮,Jupyter 服务器将启动并运行笔记本中的第一个单元格,该单元格处理我们的导入并读取我们的清洗后的数据集:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
combined_user_df = pd.read_csv('../data/combined_user.csv', index_col=0)
combined_user_df.head()
由于最后一行让我们打印输出前五行,你将看到它们出现在代码下方,并紧挨着一个标记为Out 2的标记,如图图 14.21所示:
图 14.21:In 2 中的头部语句输出显示在 Out 2 中,并且可以水平滚动
现在我们已经加载了清洗后的数据,我们可以继续进行数据分析技术。
使用图表和图形
可视化通常是大多数工作的最终目标,因此对我来说,这是一个自然的下一步。我将首先创建一个条形图,以显示数据中唯一值的计数分布。我认为这可能会让我们对哪个因素会影响本研究中的因变量有所了解,这个因变量是是否患有早发性 PD。然而,仍然存在问题。如图图 14.21所示,在开始认真分析之前,我需要考虑数据中的一些空白。
我将要做的第一件事是创建一个条形图来可视化我们的缺失数据。以下代码单元格处理此操作:
#%%
missing_data = combined_user_df.isnull().sum()
g = sns.barplot(x=missing_data.index, y=missing_data)
g.set_xticklabels(labels=missing_data.index, rotation=90)
plt.show()
运行此代码将产生如图图 14.22所示的可视化:
图 14.22:缺失数据在柱状图中进行了可视化
幸运的是,我们的图表非常稀疏。只有少量数据缺失或不完整。对于BirthYear
和DiagnosisYear
有一些缺失值。你甚至可以在图 14.21的预览中看到其中一个。分析缺失值很重要,我们将在稍后回到填充这些值的流程。但现在,让我们继续可视化过程。
Matplotlib 的一个很棒的功能是子图,它允许我们并排生成多个可视化。在下面的代码单元格中,我们使用此功能创建多个可视化,以突出显示患有和未患有帕金森病的患者之间的潜在差异:
#%%
f, ax = plt.subplots(2, 2, figsize=(20, 10))
sns.distplot(
combined_user_df.loc[combined_user_df['Parkinsons'] == 0,
'BirthYear'].dropna(axis=0),
kde_kws = {'label': "Without Parkinson's"},
ax = ax[0][0]
)
sns.distplot(
combined_user_df.loc[combined_user_df['Parkinsons'] == 1,
'BirthYear'].dropna(axis=0),
kde_kws = {'label': "With Parkinson's"},
ax = ax[0][1]
)
sns.countplot(x='Female', hue='Parkinsons', data=combined_user_df, ax=ax[1][0])
sns.countplot(x='Tremors', hue='Parkinsons', data=combined_user_df, ax=ax[1][1])
plt.show()
运行此代码单元格后,将生成如图图 14.23所示的可视化:
图 14.23:从前一个单元格中绘制出的四个图表
顶部两个可视化表示有帕金森病(右上角)和无帕金森病(左上角)的人的出生年份分布。我们可以看到这些分布大致遵循正态钟形曲线。如果你遇到偏斜或形状奇怪的分布,可能值得进一步挖掘那些数据。注意,我们也可以将相同的可视化应用于诊断年份
列。
在左下角的视觉图中,我们有一个柱状图,表示男性患者(左侧两根柱状图)和女性患者(右侧两根柱状图)的数量。帕金森病患者用橙色柱状图表示,无帕金森病患者用蓝色柱状图表示。在这个可视化中,我们可以看到虽然患病的患者比无病患者多,但两性之间的分布大致相同。
另一方面,右下角的可视化展示了患有震颤(右侧两根柱状图)和无震颤(左侧两根柱状图)的患者的差异。从这个可视化中,我们可以看到震颤在帕金森病患者中显著更常见,这一点相当直观,也可以作为我们到目前为止分析的合理性检查。
接下来,我们将转向箱线图。具体来说,我们将使用箱线图来可视化有和无帕金森病的患者在不同的时间数据(保持时间
、延迟时间
和飞行时间
)中的分布。再次使用子图功能来同时生成多个可视化:
#%%
column_names = [first_hand + second_hand + '_' + time
for first_hand in ['L', 'R', 'S']
for second_hand in ['L', 'R', 'S']
for time in ['Hold time', 'Latency time', 'Flight time']]
f, ax = plt.subplots(3, 3, figsize=(10, 5))
plt.subplots_adjust(
right = 3,
top = 3
)
for i in range(9):
temp_columns = column_names[3 * i : 3 * i + 3]
stacked_df = combined_user_df[temp_columns].stack().reset_index()
stacked_df = stacked_df.rename(
columns={'level_0': 'index', 'level_1': 'Type', 0: 'Time'})
stacked_df = stacked_df.set_index('index')
for index in stacked_df.index:
stacked_df.loc[index, 'Parkinsons'] = combined_user_df.loc[index,
'Parkinsons']
sns.boxplot(x='Type', y='Time',
hue='Parkinsons',
data=stacked_df,
ax=ax[i // 3][i % 3]
).set_title(column_names[i * 3][: 2], fontsize=20)
plt.show()
在这个代码单元格中,每个子图将可视化特定方向类型(LL
、LR
、LS
等)的数据,并将包含表示有病和无病患者的不同分割。你应该获得图 14.24所示的可视化。
图 14.24:前一个运行单元格的图表
从这个可视化中我们可以得出,令人惊讶的是,无帕金森病患者打字速度的分布可以跨越更高的值,并且比帕金森病患者有更多的变异性,这可能与一些人的直觉相矛盾,即帕金森病患者按键盘键需要更多的时间。
总体而言,柱状图、分布图和箱线图是数据科学任务中最常见的可视化技术之一,主要是因为它们既易于理解,又足够强大,能够突出我们数据集中的重要模式。在接下来和最后的关于数据分析主题的子节中,我们将考虑更高级的技术——即属性之间的相关矩阵和利用机器学习模型。
基于机器学习的见解
与之前分析的方法不同,本小节中讨论的方法和其他类似方法基于更复杂的数学模型和 ML 算法。鉴于本书的范围,我们不会深入探讨这些模型的具体理论细节,但仍然值得通过将它们应用于我们的数据集来观察它们的一些实际应用。
首先,让我们考虑我们的数据集的特征相关矩阵。正如其名称所暗示的,该模型是一个矩阵(一个二维表),其中包含我们数据集中每对数值属性(或特征)之间的相关性。两个特征之间的相关性是一个介于-1 和 1 之间的实数,表示相关性的大小和方向。值越高,两个特征的相关性越强。
要从 pandas DataFrame 中获得特征相关矩阵,我们必须调用corr()
方法,如下所示:
corr_matrix = combined_user_df.corr()
我们通常使用热图来可视化相关矩阵,如同一代码单元格中实现的那样:
f, ax = plt.subplots(1, 1, figsize=(15, 10))
sns.heatmap(corr_matrix)
plt.show()
此代码将产生图 14.25所示的可视化:
图 14.25:热图非常适合可视化相关矩阵
接下来,我们将尝试将 ML 模型应用于我们的数据集。与普遍看法相反,在许多数据科学项目中,我们并不利用 ML 模型进行预测任务,即训练我们的模型以预测未来的数据。相反,我们将数据集输入到特定的模型中,以便我们可以从中提取更多见解。
在这里,我们使用 scikit-learn 中的线性支持向量分类器(SVC)模型来分析我们的数据,并返回特征重要性列表:
#%%
from sklearn.svm import LinearSVC
combined_user_df['BirthYear'].fillna(combined_user_df['BirthYear'].mode(dropna=True)[0], inplace=True)
combined_user_df['DiagnosisYear'].fillna(combined_user_df['DiagnosisYear'].mode(dropna=True)[0], inplace=True)
X_train = combined_user_df.drop(['Parkinsons'], axis=1)
y_train = combined_user_df['Parkinsons']
clf = LinearSVC()
clf.fit(X_train, y_train)
nfeatures = 10
coef = clf.coef_.ravel()
top_positive_coefs = np.argsort(coef)[-nfeatures :]
top_negative_coefs = np.argsort(coef)[: nfeatures]
top_coefs = np.hstack([top_negative_coefs, top_positive_coefs])
注意,在我们将数据输入到 ML 模型之前,我们需要填写我们在之前确定的两个列中存在的缺失值——BirthYear
和DiagnosisYear
。大多数 ML 模型都不能很好地处理缺失值,这取决于数据工程师选择如何填充这些值。
在这里,我们使用模型之后的coef_
属性。
此属性包含特征重要性列表,该列表通过代码的最后部分进行可视化:
plt.figure(figsize=(15, 5))
colors = ['red' if c < 0 else 'blue' for c in coef[top_coefs]]
plt.bar(np.arange(2 * nfeatures), coef[top_coefs], color=colors)
feature_names = np.array(X_train.columns)
# Make sure the number of tick locations matches the number of tick labels.
plt.xticks(np.arange(0, 2 * nfeatures), feature_names[top_coefs], rotation=60, ha='right')
plt.show()
运行此代码将产生图 14.26所示的可视化:
图 14.26:特征重要列表的图表显示了在训练 ML 模型时广泛使用的特征
从特征重要性列表中,我们可以识别出在训练过程中被 ML 模型广泛使用的任何特征。具有非常高的重要性值的特征可能与目标属性(某人是否患有帕金森病)以某种有趣的方式进行关联。例如,我们可以看到震颤
(我们知道它与我们的目标属性高度相关)是我们当前 ML 模型的第三重要特征。
那就是关于分析我们数据集的最后一个讨论点。在本章的最后部分,我们将简要讨论如何为 Python 数据科学项目编写脚本。
数据科学中的脚本与笔记本
在前面的数据科学流程中,有两个主要部分:数据清洗,其中我们删除不一致的数据,填补缺失数据,并适当地编码属性;数据分析,其中我们从清洗后的数据集中生成可视化和洞察。
数据清洗过程是通过 Python 脚本实现的,而数据分析过程则是使用 Jupyter 笔记本完成的。一般来说,在数据科学项目中,决定是否应该在脚本或笔记本中完成 Python 程序是一个相当重要但常常被忽视的方面。
如我们在上一章中讨论的,Jupyter 笔记本非常适合迭代开发过程,在这个过程中我们可以边走边转换和操作我们的数据。另一方面,Python 脚本则没有这种动态性。我们需要在脚本中输入所有必要的代码,并作为一个完整的程序运行。
然而,如数据清洗和预处理部分所示,PyCharm 允许我们将传统的 Python 脚本划分为独立的代码单元格,并使用SciView面板在执行过程中检查我们的数据。Jupyter 笔记本提供的动态性也可以在 PyCharm 中找到。
现在来看,常规 Python 脚本和 Jupyter 笔记本之间的另一个核心区别是,笔记本中包含了打印输出和可视化结果,以及生成它们的代码单元格。从数据科学家的角度来看,我们可以看到这个特性在制作报告和演示时非常有用。
假设你在一个公司项目中负责从数据集中找到可操作的见解,并且需要向团队展示你的最终发现以及你是如何找到它们的。Jupyter 笔记本可以有效地作为你演示的主要平台。人们不仅能够看到用于处理和操作原始数据的特定命令,你还可以包括 Markdown 文本来进一步解释任何细微的讨论点。
常规 Python 脚本可以简单地用于已经达成共识的通用工作流程的低级任务,你不需要将其展示给其他人。在我们的当前示例中,我选择使用 Python 脚本清洗数据集,因为我们应用的大多数清洗和格式化更改并没有生成任何可以解决我们初始问题的可操作见解。我只使用了笔记本进行数据分析任务,其中有许多值得进一步讨论的可视化和见解。
总体而言,决定使用传统的 Python 脚本还是 Jupyter 笔记本完全取决于你的任务和目的。我们只需要记住,对于我们想要使用的任何工具,PyCharm 都提供了令人难以置信的支持,可以简化我们的工作流程。
摘要
在本章中,我们介绍了数据科学管道的动手操作过程。首先,我们讨论了为什么需要对代码和项目相关的文件以及数据集进行版本控制的重要性;然后我们学习了如何使用 Git LFS 对大文件和数据集进行版本控制。
接下来,我们研究了针对示例数据集的各种数据清洗和预处理技术。使用 PyCharm 中的 SciView 面板,我们可以动态检查我们数据的状态和变量,并查看它们在每次命令执行后的变化。
最终,我们考虑了几种生成可视化并从我们的数据集中提取洞察的技术。使用 PyCharm 中的 Jupyter 编辑器,我们能够避免与 Jupyter 服务器交互,并在 PyCharm 内部完全处理我们的笔记本。经过这个过程,你现在已经准备好使用我们迄今为止讨论过的相同工具和功能来解决现实生活中的数据科学问题和项目。
因此,我们已经完成了在科学计算和数据科学背景下使用 PyCharm 的讨论。在下一章中,我们最终将考虑我们在前几章中多次提到的主题——PyCharm 插件。
问题
回答以下问题以测试你对本章知识的掌握:
-
对于数据科学项目,有哪些主要的数据集收集方式?
-
Can Git LFS be used with Git? If so, what is the overall process?
-
哪种类型的属性可以使用平均值来填充其缺失值?对于众数呢?
-
What problem does one-hot encoding address? What problem can arise from using one-hot encoding?
-
哪种类型的属性可以从条形图中受益?对于分布图呢?
-
为什么考虑数据集的特征相关性矩阵很重要?
-
除了预测任务之外,我们还能用机器学习模型做什么(就像我们在本章中做的那样)?
进一步阅读
一定要查看本书的配套网站:www.pycharm-book.com
。
更多信息可以在以下文章和阅读材料中找到:
-
Adams, W. R. (2017). High-accuracy detection of early Parkinson’s Disease using multiple characteristics of finger movement while typing. PloS one, 12(11), e0188226。
-
由 Patrick DeKelly 上传的 Tappy Keystroke Data with Parkinson’s Patients 数据:
www.kaggle.com/valkling/tappy-keystroke-data-with-parkinsons-patients
。 -
从零开始构建数据管道,作者 Alan Marazzi:
medium.com/the-data-experience/building-a-data-pipeline-from-scratch-32b712cfb1db
。 -
设计企业级数据科学管道的商业视角,作者 Vikram Reddy:
www.datascience.com/blog/designing-an-enterprise-level-data-science-pipeline
。 -
《初创公司数据科学:数据管道》,作者 Ben Weber:
towardsdatascience.com/data-science-for-startups-data-pipelines-786f6746a59a
。 -
pandas 库的文档:
pandas.pydata.org/pandas-docs/stable/
。
第五部分:插件和结论
本部分将向读者介绍 PyCharm 插件的概念,并指导他们下载插件并将其添加到 PyCharm 环境中。它还将详细介绍最受欢迎的插件以及它们如何进一步优化程序员的效率。我们还将简要回顾书中前几章讨论的重要主题,并全面展示 PyCharm 最受欢迎的功能。
本部分包含以下章节:
-
第十五章,使用 PyCharm 插件拓展更多可能性
-
第十六章,未来发展趋势
第十五章:插件带来的更多可能性
任何值得拥有的 IDE 都可以通过插件架构进行扩展。来自 Microsoft、Eclipse 和 JetBrains 的大牌支持插件,同样,来自 Geany 和 Ninja IDE 等较小、免费和开源的 IDE 项目也支持插件。即使是没有任何 GUI 的 vim,也有大量的插件可供选择,这些插件允许你超越随产品提供的功能来自定义你的体验。在 JetBrains IDE 的案例中,插件架构是他们构建一切的核心。
考虑到 JetBrains 是从 IntelliJ 开始,并在此基础上构建了十几个左右的专业 IDE。之所以能够做到这一点,是因为它们的架构基于插件。PyCharm Professional 包含了其他两个 IDE(WebStorm 和 DataGrip)的所有核心功能,因为这些功能仅仅是插件集合。
在本章中,我们将讨论一些来自 JetBrains 和第三方开发者的重要插件。到本章结束时,你将了解以下内容:
-
如何利用 JetBrains 的远程开发功能,允许你在本地笔记本电脑上工作,同时利用远程环境,如虚拟机、工作站或服务器
-
如何使用 JetBrains Code With Me 进行协作工作
-
如何使用 JetBrains Docker 插件与 Docker 一起工作
-
如何在 PyCharm 中使用 HashiCorp Vagrant 利用代码化开发基础设施
-
我还将介绍一些较小的、提升生活质量的插件,包括主题、专业高亮显示、额外语言,当然还有 Black 代码格式化器。
技术要求
为了继续阅读本章,以及本书的其余部分,你需要以下内容:
-
安装并运行 PyCharm 的副本。安装过程在第二章**,安装和配置中已有介绍,如果你是在书的中间部分开始阅读。
-
本章中的许多部分都需要连接到互联网上的服务。对于某些演示,需要高速连接或大量耐心。
-
对于远程开发部分,你需要一台可以通过 SSH 访问并运行 Linux 操作系统的远程计算机。这可以是一台物理机器,也可以是通过云服务提供商运行的虚拟机。我只介绍如何连接到远程计算机。我不会深入介绍设置 Linux 计算机或虚拟机作为远程服务器的过程。
-
对于 Docker 部分,我将使用 Docker Desktop。你可以在
www.docker.com/products/docker-desktop/
找到适用于你计算机操作系统的安装说明。 -
本书从 GitHub 提供的示例源代码。我们在 第二章 安装和配置 中介绍了代码克隆。你可以在
github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-15
找到本章的相关代码。
打包插件和 JetBrains 市场插件
在本书中,我已经指出了一些有用的插件。我实在等不及了,向你展示的插件要么与正在讨论的特定章节高度相关,要么真的很酷、很吸引人。为了成为一个负责任的 PyCharm 指导者,我会控制自己的情绪(尽可能做到),假装你直接跳到了这个章节,这可能是本书中最好的章节。或者不是,由你决定。
在 JetBrains IDE 产品中,许多功能都是作为插件构建的。你将遇到两种类型的插件:由 JetBrains 制作的插件和可在 JetBrains 市场中获取的第三方插件。许多由 JetBrains 制作的插件都包含在他们制作的各个 IDE 中,但有些需要添加或激活。让我们首先探索 IDE 设置中可用的 插件 面板。
插件窗口
有几种方法可以进入 插件 窗口。可能最简单的方法就是关闭你在 PyCharm 中可能打开的任何项目,这会给你我们 15 章前在 图 15.1 中开始的窗口。1.
图 15.1:插件窗口包含你在 PyCharm 中管理插件所需的所有工具
在 插件 窗口的顶部,你可以找到两个重要的标签页:一个显示你已安装的插件(2),另一个带你去市场,你可以在那里购买和安装新的插件(1)。市场中的许多插件是免费的,但许多更好的插件则不是。
螺丝刀图标(3)允许你配置额外的插件仓库。我从未需要过这个功能,但如果你需要,它就在那里。你还可以使用螺丝刀下找到的相关菜单从本地文件夹安装插件。
搜索框(4)允许你在列表中查找插件。当你选择一个插件时,你通常会在右侧(5)获得一些关于它的信息。插件可以被启用或禁用(6),所以如果你的 IDE 在安装插件后表现不稳定,你可以暂时关闭它或完全卸载它。
打包插件
如果您研究 图 15.2 中的列表,您会发现此屏幕上的大多数插件都捆绑在 PyCharm 中。这些是由 JetBrains 创建并包含在正常 PyCharm 安装中的插件。检查这个列表并禁用您不太可能使用的插件是个不错的主意。例如,我使用 Terraform 和 Ansible 进行 DevOps 和 基础设施即代码(IaC)。我不太可能使用 Puppet,所以我应该禁用该插件。
在您这样做的时候,您可能会发现 PyCharm 警告您有关依赖插件的警告。例如,有一个 Less 插件,它是一个 CSS 元框架。我不使用它,所以禁用它很有诱惑力。然而,我在 React 项目中使用的 JSX 样式组件插件依赖于 Less。参见 图 15.2。
图 15.2:当我尝试禁用另一个插件所需的插件时,系统会发出警告
在这种情况下,我最好保持 Less 启用。
JetBrains 商场
如果您想要一些零售 IDE 治疗,您可以点击 Marketplace 选项卡并开始购物。一些插件是免费的,一些则不是。您可以从此屏幕轻松搜索和安装来自 MarketPlace 的插件。如果您购买了一个插件,您可能会被引导到 JetBrains 商场网站,以便您控制您的许可证。
制作您自己的插件
任何人都可以使用 Java 编程语言和 IntelliJ Community 或 Ultimate 版本制作 IDE 插件。我不会介绍如何制作插件,但至少我会展示如何开始。在 图 15.3 中,我打开了 IntelliJ Community Edition 的新项目对话框。鉴于您在 PyCharm 中创建项目有丰富的经验,我会告诉您所有 IDE 都是相同的。IntelliJ 是 JetBrains 的原始旗舰 IDE 产品。
图 15.3:IntelliJ Community Edition 提供创建自定义 IDE 插件的项目模板
除了项目模板外,还有一个链接到构建您的插件项目的教程。
为您的项目要求插件
正如我们有一个 requirements.txt
文本文件来列出特定 Python 项目所需的包的名称和版本一样,我们也可以指定特定的插件作为 PyCharm 项目的需求。这将确保其他开发者能够访问到您认为对他们在您的项目上协作成功至关重要的任何插件功能。图 15.4 展示了如何为您的项目设置所需的插件。
图 15.4:要求插件的操作步骤
首先,进入你的项目设置,找到构建、执行、部署,找到必需插件菜单项(1)并点击它。然后,点击+图标(2),从出现的对话框中选择你的必需插件(3)。点击确定。
有用的杂项插件
我想在本章的大部分内容中介绍一些非常有影响力的插件,它们包含了许多复杂的功能。不过,在这样做之前,我们先解决一些简单的问题。我会介绍那些你在搜索 IDE 插件时最可能想到的小而有趣的插件。其中一些我们在其他章节中已经见过:
-
CSV 插件在第十三章中有介绍。
-
Markdown 插件。这个插件以前没有捆绑提供,但现在有了,而且它绝对值得拥有。
-
单元模式插件,我们也在第十三章](B19644_13.xhtml#_idTextAnchor318)中介绍过。
让我们再谈谈几个,我将根据它们的功能将它们分组。
主题插件
你每天花费 8 到 16 个小时盯着 IDE。你不妨花点时间让它看起来很酷。有很多颜色主题可供选择。图 15.5*显示了我当前最喜欢的:深紫色主题。
图 15.5:深紫色主题是我最喜欢的之一,因为它与标准的 VS Code 深色主题不同
当人们走过并看到 IDE 打开时,他们会立刻知道你不是那些拥有无处不在的微软深色主题的 VS Code 奴隶。你可以使用 PyCharm 附带的默认 Darcula 主题,但像我这样的俄克拉荷马大学毕业生,那个主题中橙色太多。选择一个你平时看不到的主题,很快,在格子间里的人都会知道你是真的。
我也喜欢那种漂亮的太阳色蓝色的 Material Theme UI。这个插件有免费(轻量版)和付费版。如果你四处看看,你会找到一个适合你的颜色主题,因为可供选择的有许多。找到你新喜欢的主题的简单方法是,在图 15.6*中显示的搜索框中输入一个标签。
图 15.6:你可以使用“主题”等标签过滤插件搜索
我建议你试试这个,因为无论什么原因,留空搜索框不会得到一个非常长的选项列表。
文件处理器插件
这类插件为我们每天使用的某些非代码文件提供了彩色高亮显示。
.ignore 插件为常见的忽略文件(如.gitignore
和.dockerignore
)提供了一些不错的格式化。
工具集成插件
PyCharm 的开发者倡导者之一保罗·埃弗雷特(Paul Everitt)经常提醒我们,IDE 中的 I 代表的是集成。你永远不应该因为任何原因而离开 PyCharm。除了编写代码,开发者还需要与问题跟踪器、版本控制系统(VCS)、持续构建环境以及可能的大量基础设施工具进行交互。因此,有插件来帮助这些类型的集成并不奇怪。图 15.7展示了用于查找这些插件的标签。
图 15.7:使用工具集成标签查找你最喜欢的基础设施和 DevOps 工具的集成
我曾经是 TeamCity 的重度用户。如果你使用 TeamCity,或者任何其他 JetBrains 团队工具,你将找到插件,允许你从 IDE 中监控你的构建、处理问题等。记录在案,TeamCity 仍然是我最喜欢的持续集成(CI)服务器。
生产力插件
开发者生产力是首先使用集成开发环境(IDE)的主要原因。在这个标签下,你会发现许多额外的功能和集成。图 15.8展示了这个标签的使用情况。
图 15.8:使用生产力标签查找提高生产力的工具
到目前为止,我最喜欢的 DevOps 插件是 GitToolbox。看看我在 PyCharm 中打开并启用了 GitToolbox 的一个项目。
图 15.9:GitToolbox 插件提供了关于你的项目仓库的大量信息
一眼就能看到我当前正在使用的分支,以及相对于远程仓库我领先或落后的提交数量。插件将帮助你清理长时间未使用的旧分支,在定时间隔内执行常规获取,并验证你的提交信息。我最喜欢的功能可能是你可以看到每一行的git blame
信息。我不是那种指责别人的人,但作为主开发人员,我发现它非常有用,可以知道我的同事在哪个特定文件上工作。这无疑有助于代码审查更快地进行。
完成插件
人工智能(AI)已经成为软件开发中一个颠覆性的重要组成部分。使用/tag: Completion
标签将列出数十个传统和 AI 驱动的代码完成插件,如图 15.10所示。
图 15.10:包括最流行的 AI 实现的完成插件
目前,GitHub Copilot 和数十个 ChatGPT 插件都是非常受欢迎的自动补全插件。但在您支付费用之前,请查看我在第十七章中关于 JetBrains AI 助手插件的讨论。它目前处于封闭测试阶段,所以我无法在这里真正包括它,但它真的很好,并且比这些插件更紧密地集成。
与我一起编码(从此不再孤独)
配对编程是一种多年前通过名为极限编程(XP)的敏捷方法引入的流行实践。虽然不如 Scrum 或 Kanban 知名,但 XP 有许多相同的实践和目标,但将配对编程作为一个强制性组件。在 XP 中,两名程序员坐在一台电脑前一起编写代码。一个人输入,另一个人观察、指导、查找信息,并通过帮助提供一般性贡献。在固定的时间间隔内,程序员交换位置。该实践的拥护者对其深信不疑,声称它消除了代码审查的需要,并且通常导致更好的代码。批评者,包括所有在世的非技术经理,认为让两个昂贵的、专业的工人只做一半的工作是没有意义的。
无论您喜欢还是讨厌,您都必须承认直接协作有很大的实用性。真正的问题是,在新冠后的世界里,许多开发者远程工作,两个开发者共用一个屏幕的想法不再可行,至少直到现在。
JetBrains 有一个名为与我一起编码的集成工具。这个工具允许多个开发者加入一个在线共享会话,每个人都在一台电脑上同时为一个项目工作。这个插件包含在所有 JetBrains IDE 中,包括 PyCharm。您可以通过点击图 15.11中显示的图标来激活它。
图 15.11:要开始“与我一起编码”会话,点击箭头所示的图标
您可以为您的访客设置权限。请记住,您正在授予对您电脑的访问权限!您应该只与您信任的人进行会话,同样,也只将您的会话链接发送给您信任的人。
一旦您点击开始会话,PyCharm 会创建一个您可以根据需要分享的超链接。您可以通过电子邮件发送它,但我最常见的是通过 Slack 或类似的私人聊天服务发送给其他开发者。任何拥有链接的人都可以点击加入会话。除了共享 IDE 之外,您还有正常的会议工具。参与者可以像在 Zoom 或 WebEx 这样的产品上一样看到和听到对方。图 15.12显示了我在没有人与我进行会议的情况下的样子,但它能给您一个预期的概念。
图 15.12:正在进行中的“与我一起编码”会话。不幸的是,因为我把所有时间都花在写书上,我没有朋友可以打电话,所以只有我一个人。
这个功能在许多场景中都很有用,而不仅仅是明显的结对编程用例。你可以为每一节课使用它,或者使用比 Google Doc 更真实的东西进行编码面试。
如果你打算经常使用这项服务,你需要订阅,因为会话长度和你可以邀请的参与人数都有限制。你可以在www.jetbrains.com/code-with-me/buy/#commercial
了解更多信息。
远程开发
在我的办公室里,我有一台大型、强大的 60 磅全塔式 HP 工作站,配备双核 Intel Xeon 处理器(总共 48 个核心),128 GB 的内存,专业级的 Nvidia GPU,以及 24 GB 的 GPU 加速器!它甚至还有一个串行连接的小计算机系统接口(SAS),用于运行企业级存储!你真的无法在笔记本电脑上获得这种性能,而且有时候我会把它用到极致。
我们这个行业喜欢字母汤!
我只是想为所有那些试图弄清楚三字母缩写(TLA)SAS代表不少于六个单词的工程类型的人澄清一下。这是因为缩写SAS中包含的一个字母本身又代表另一个缩写。它是串行连接的 SCSI(SAS)。SCSI代表小型计算机系统接口。SAS 驱动器是旋转磁盘,其转速比标准的串行高级技术附件(SATA)驱动器要高,但它们仍然远远不如固态驱动器(SSD)或具有NOT-AND或NAND内存的非易失性内存表达(NVME)驱动器快。然而,SAS 驱动器是设计用于冗余磁盘阵列(RAID)的,这使得它们在以容错方式低成本存储非常大的文件时很有用。
所有这些缩写和术语都是行业内部阴谋的一部分,旨在让你那些对你所从事的工作一无所知的亲戚在家庭聚会和节假日时既感到自豪又感到不舒服。
但像其他许多开发者一样,我也希望能在家工作。显然,我不愿意带着 60 磅的工作站四处奔波。即使我能,我们的安全团队也不会喜欢我们的源代码离开大楼。确实,我可以使用 VNC Viewer 或 Microsoft Remote Desktop 这样的远程技术,但它们可能会出现延迟,而且缺乏我习惯的四显示器等舒适功能。
我也可以通过 SSH 使用neovim进行工作。但我想你和我,我们已经过了那个阶段,对吧?如果你认为这是个好主意,就回到第一章,我在那里谈到了这是一个坏主意。那么,我该怎么办呢?
幸运的是,JetBrains 为我提供了支持。使用其预安装在 PyCharm Professional 上的远程插件,我可以在家里的电脑上运行我的 IDE,但在办公室的工作站上操作。这是通过 安全外壳(SSH)实现的,它是一种加密通信协议,通常用于管理基于 Linux 和 Unix 的服务器。
简而言之,你可以配置 PyCharm 使用安装在远程 PC 上的虚拟环境(venv
),以及远程硬盘上的代码。除了 IDE 本身之外,所有操作都在远程进行。你的运行配置将在远程执行你的代码。当你进行调试时,调试器是在远程而不是本地机器上运行的。这相当无缝,但也有一些缺点。
首先,你必须对网络有一定的了解,才能配置项目进行此操作。你需要远程的 IP 地址,并且需要有效的访问凭证,以便访问远程计算机。在我的情况下,我还需要一个 虚拟专用网络(VPN)客户端才能访问办公室的网络,而这个客户端是 PyCharm 外部的。虽然 VPN 从技术上讲不是必需的,但在大多数情况下,它应该是。
第二个权衡是性能。PyCharm 在好日子里也不算特别快。在我们能够使用书中所讨论的任何那些令人惊叹的功能之前,我们必须等待索引完成。从 PyCharm 获取内存不足错误并不罕见,这需要你增加 PyCharm 被允许使用的内存量。更新或安装插件需要你重新启动 IDE,甚至初始加载界面挂起的时间也比我们许多人希望的更长。总的来说,有时 PyCharm 可能有点笨拙,但我们忍受它,因为它带来的价值有助于简化我们的整体工作流程。
我倾向于将 PyCharm 比作柴油卡车。这样的卡车不可能在四分之一英里短跑比赛中战胜保时捷或特斯拉 Model 3。来自俄克拉荷马州的农村地区,我可以告诉你,在我所在的地方没有人拥有跑车,因为我们的柴油卡车不仅仅是交通工具,它们是我们生计的一部分。我无法用特斯拉拖运一车牛去拍卖,也无法用保时捷拉运干草到后 40 英亩去喂马。同样,我们使用重型 IDE,因为我们愿意为了 PyCharm 的索引提供的某些极端价值而放弃最小编辑器(如 VS Code)的“速度”。能够找到用法、直接跳转到函数定义,以及拥有从编辑到数据库管理的一整套紧密集成的工具,这些都是值得一点延迟的。
话虽如此,当你在一个远程项目中工作时,这种延迟可能会变得更加明显。你可以预期你的索引时间会更长,因为索引是在远程进行的,然后传输回你这里。网络延迟和 VPN 总是会使事情变慢,但这不是 PyCharm 的错。我们忍受它,因为你可以在穿着睡衣的情况下在家工作,音乐声调很高,靠近自己的厨房和零食,你可以限制与其他人的互动,这通常只会让我们这样的人变慢。这是值得的。现在你的期望已经设定,让我们看看如何让这一切工作。
图 15.13:远程开发选项清楚地显示在欢迎窗口的右侧
在我所有的书籍和课程中,我自豪地保持着尽可能真实的内容。例如,在第七章中,我遇到了 PyCharm 的一个功能问题,这个功能本应自动为我下载 Node.js。我没有隐藏它,因为这种情况确实发生在我身上,这意味着它也可能发生在你身上。学习如何绕过这类问题是有价值的。
到现在为止,你可能已经注意到这一章的屏幕截图与之前章节的不同。我是在一个我在家工作的日子里写这一章的,我正在使用一台今天之前从未配置过远程工作的 Windows 11 PC。由于这是我的家用电脑,截图可能看起来与之前章节的略有不同,尤其是在深色模式方面。我关闭了大多数书籍的深色模式,因为印刷书籍中的浅色模式更容易阅读,而且我在墨水上也节省了出版商的费用。但今天不是这样。因为我正在保持真实。
我要做的第一件事是安装并打开 PyCharm。显然,我们已经在第二章中介绍了这一点,所以没有必要再次做这件事。欢迎屏幕提供了打开远程项目的选项,如图15.13所示。
这里有几个选项,包括 SSH,我将使用它。JetBrains Space 是一个独立的产品,我不拥有它,所以我真的不能涵盖它。"WSL",即"Windows Subsystem for Linux",可能只会在你使用 Windows 时出现。WSL 允许你在 Windows 中直接运行 Linux 虚拟机,而无需使用像 VMware 或 Virtual Box 这样的明显虚拟机管理程序。"明显",是因为当然这里有一个虚拟机管理程序在起作用——它是微软的HyperV,它是 Windows 11 和 Windows Server 产品的专业版中内置的。Windows 11 通过允许你通过 Windows 11 中新的 Windows Terminal 程序选择一个或多个 Linux 安装来使其无缝。
配置 PyCharm 中的远程开发
由于 WSL 在技术上是一个虚拟机,尽管它发生在你的本地计算机上,但它仍然算作远程开发。目前,我将专注于通过 SSH 访问我的工作站进行真正的远程开发,我的工作站目前大约 12 英里远。为了清楚起见,我的本地计算机运行 Windows 11,但我的工作站运行Pop_OS! 22,这是 Ubuntu Linux 的一个变种。你可以在全书中的屏幕截图中看到它。这增加了神奇之处!我可以用 Windows 或 macOS 这样的消费级操作系统连接到专业的 Linux 工作站。
在你跳上 Twitter(现在称为 X)来对我侮辱你最喜欢的操作系统进行抨击之前,请意识到这是唯一可能的组合。你必须连接到 Linux 远程服务器,因为 Windows 和 Mac 目前还不支持。我不确定 macOS 的延迟是什么,因为它预装了 SSH 服务器。对于你在工作中有一台 Mac Pro 而家里有其他设备的情况,这种工作流程是有意义的。接下来的设置屏幕确实表明这项功能是为未来版本准备的。
图 15.14首先展示了当我们点击图 15.13中显示的 SSH 选项时会发生什么。我们得到了一个屏幕,允许我们在远程创建一个新的项目,但前提是我们首先配置我们的连接。这就像我们创建用于发布 Web 项目的 SSH 连接一样,我们在第七章中讨论了这一点。
图 15.14:远程配置对话框只能通过在其上方堆叠另一个配置对话框来改进
在你能够做任何事情之前,你需要通过点击图 15.14中箭头所指的齿轮图标来配置 SSH 连接。这会带我们进入另一个对话框,如图 15.5所示。
图 15.15:在这里,我正在创建一个用于远程设置的 SSH 配置
我已经点击了22
,所以我已经保留了默认设置(6)。
如果你回到第一个位置(1),你可以点击那个铅笔图标,如果你不喜欢用户名和 IP 地址的美观,可以给它一个不同的名字。我在图 15.16中将我的名字重命名为更符合其硬件规格的名称。
图 15.16:The Beast 是我远程工作站的名称
其余的选项对我来说并不重要,因为我不需要通过代理才能到达我的工作站。我继续展开图 15.15中所有关闭的选项,以便你可以看到你可能需要的任何附加设置。
点击确定,这将带您回到之前的对话框。您需要从下拉菜单中选择您刚刚创建的连接。对我来说,重命名没有保留,所以又回到了显示在图 15.17 中的用户名和 IP。
图 15.17:创建连接后,您需要在这里选择它,用户名和主机字段将为您自动填写
注意,您在这里可以选择无密码登录并使用私钥。使用 SSH 密钥可能限制了密码的需求,但您仍然可能需要解锁密钥。在任何情况下,如果您愿意,PyCharm 都可以为您记住密码或 SSH 密钥短语。如果您在一家注重安全的公司工作,您可能需要检查您的指南,以了解在哪些情况下,应用程序存储您的密码和密钥短语被认为是合适的。
点击标有检查连接并继续的按钮。假设您获得了访问权限,PyCharm 会将一些软件和设置复制到主机上,然后向您展示原始的对话框,如图图 15.18 所示。
图 15.18:我们现在有一个连接的系统,有三个选项!
从这里,我们有三个选项。您可以通过点击显示在(1)位置的图标来使用终端会话连接到主机。点击(2)位置的+图标允许您在远程创建或打开项目。齿轮(2)允许您删除主机或管理主机上的后端。后一个选项是由我之前的评论解释的。当我们第一次连接时,我告诉您 PyCharm 将一些软件复制到了主机上。所涉及的软件很像我们的工具箱应用程序。它将帮助我们管理作为项目创建过程的一部分上传的 IDE 后端。您还可以使用其他几种 IDE 以这种方式使用,并且您有在这里管理这些后端的能力。如果您现在点击它,将不会有任何,因为我们还没有创建任何项目。让我们纠正这一点。
创建远程项目
点击图 15.18 中标记为(2)位置的+按钮。您将看到图 15.19 所示的屏幕。
图 15.19:我们即将创建一个新的远程项目,这需要下载 IDE 后端并在远程选择一个项目文件夹
远程项目创建需要你指定你想要使用的 IDE 以及你想要在远程硬盘上存储代码的位置。鉴于我们正在调用 PyCharm 来创建项目,它要求我们选择所需的 IDE 可能看起来有些奇怪。记住,这实际上并不是 PyCharm。它是一个可以被许多 JetBrains IDE 利用的插件。现在,你实际上正在与另一款软件交互,它恰好托管在 PyCharm 中。
我们确实得到了 PyCharm 作为默认选项,所以这也是一件事情。请注意版本。我的默认设置为 EAP 版本,这可能不是你想要的。我个人只有在测试向后兼容性时才使用 EAP。我在日常工作中使用稳定版。从下拉菜单中选择合适的条目,如图图 15.20所示。
图 15.20:在远程选择你想要使用的 IDE
另一个可能明显的考虑因素是,你需要为所选的任何 IDE 拥有一个许可证。接下来,我们需要选择一个位置。这可能有点令人困惑,因为 Linux 文件系统的布局与你在 Windows 中遇到的不同。Linux 不使用驱动器字母,它使用正斜杠来分隔路径元素,而 Windows 使用反斜杠。幸运的是,我可以通过点击项目目录设置旁边的三个点来浏览。查看图 15.21。
图 15.21:点击三个点以浏览远程文件系统以选择你的项目目录
默认情况下,目录浏览器将在远程显示你的主文件夹。你可以浏览到你选择的文件夹。不幸的是,目前,一个很大的缺失功能是创建新文件夹以供项目使用的选项。我相信这将会到来,但现在它还不存在。我们将不得不绕过这个问题。点击取消,然后点击后退返回到图 15.5所示的屏幕。点击终端按钮以在主机上启动 SSH 终端会话,如图图 15.22所示。
图 15.22:使用终端会话创建你的文件夹
使用显示的命令创建一个新文件夹。我在远程主文件夹中已经有一个PyCharmProjects
文件夹。这是因为 PyCharm 实际上安装在了远程上,因为它是我的日常使用电脑。你可以在远程主文件夹中的任何位置创建任何适合你的文件夹。你可以通过输入exit
退出终端会话。
让我们再试一次
如前所述,点击+图标,从下拉菜单中选择你的 IDE,并浏览到你刚刚创建的文件夹。
就把它想象成没有 UI 的 PyCharm。我们正在将我们电脑上的前端与远程上传的 PyCharm 后端连接起来。
让我们把注意力重新集中在远程项目上。点击图 15.23中显示的下载 IDE 并连接按钮。
图 15.23:点击按钮在远程创建项目
接下来的这部分需要几分钟时间。在您点击下载 IDE 并连接按钮后,一系列命令将在您的远程服务器上运行。结果是运行中的 IDE 后端,如图图 15.24所示。
图 15.24:现在您已经在远程运行了一个 IDE 后端服务器
点击图 15.11中显示的链接来连接到您的远程 IDE。您可能会看到密码挑战,也许会有 Windows 的提示,询问是否允许流量通过防火墙,但最终,您将像我在图 15.25中一样在远程实时工作。
图 15.25:神奇!看起来和本地工作一样!
从这里开始,您基本上知道该做什么。您的 IDE 将正常工作,除了一个被跳过的步骤:您从未被提示创建虚拟环境。
在远程创建虚拟环境
我们之前已经做过这件事了,在第三章,所以在这里我不会再次深入探讨,但有一个点可能会让人困惑。让我们来看看。
要创建一个虚拟环境,您需要进入项目设置并找到项目解释器的设置。我的设置如图 15.26所示。
图 15.26:当您添加解释器时,请记住,虽然它是远程的,但它被视为本地
解释器默认为/usr/bin/python3.9
,这是全局实例,不是我想要的。当我点击添加解释器按钮时,会有些困惑。您可能会倾向于使用通过 SSH,因为,嗯,我们正在使用 SSH 连接到远程。然而,我们已经在远程连接上了,所以 PyCharm 在远程运行,这使得它成为一个本地解释器。当您点击添加本地解释器时,您会注意到,就像图 15.14中显示的那样,路径是类似 Linux 的,而不是 Windows 路径。对于 macOS 用户来说,这可能会不太明显,因为路径看起来是一样的。
其他注意事项
现在您有了这个设置,您实际上是在远程计算机上运行 PyCharm。所有文件路径都将显示为 Linux 路径。Git 和其他版本控制客户端与远程文件夹一起工作。如果您使用 SSH 密钥通过 GitHub 或您的版本控制主机进行身份验证,您需要从远程配置密钥,而不仅仅是使用您本地的公钥。本质上,完全忘记您的本地计算机。您完全在远程操作。
图 15.27:尽管我们选择了本地选项,但所有操作都在远程进行
点击venv
已为项目创建。现在您可以开始工作在您的项目上了!
使用 Docker
容器技术是自虚拟机以来 DevOps 技术最重要的进步之一。Docker和类似的容器技术彻底改变了软件开发、部署和管理的方式。想象一下容器作为轻量级、独立的单元,封装了运行应用程序所需的所有必要组件,包括代码、运行时、系统库和设置。这种封装确保了应用程序在不同环境中的一致运行,从开发者的笔记本电脑到生产服务器。
Docker 特别是一个广泛用于构建、运输和运行容器的平台。它提供了一种标准化的方式来打包应用程序及其依赖项,将它们与底层主机系统隔离开来。这种隔离使得开发者能够避免臭名昭著的“在我的机器上它运行正常”问题,因为容器在各种环境中表现一致。
容器的另一个主要优点是它们的可移植性。开发者可以将应用程序及其所有依赖项打包到容器镜像中,然后可以轻松与他人共享。这使得协作更加高效,并减少了兼容性问题的可能性。此外,容器具有高度的扩展性。它们可以快速复制以处理增加的工作负载,为现代、动态和资源密集型应用程序提供了一种出色的解决方案。
Docker 还带来了资源利用率的提高。由于容器共享主机操作系统的内核,它们比需要单独操作系统的传统虚拟机更轻量。这转化为更快的启动时间、更低的内存开销以及单个主机上容器化应用程序的更高密度。
预装的 Docker 插件
PyCharm Professional 已经预装并启用了 JetBrains Docker 插件。您无需进行任何其他操作即可使用 Docker 插件。然而,您确实需要安装 Docker。在我的开发计算机上,我通常运行 Docker Desktop,因为我发现产品的图形用户界面很有用。今天它将很有用,因为我将向您展示您可以在 PyCharm 中使用 Docker 完成多少工作。
让我们创建一个 Flask 项目并在 Docker 中运行它。
创建项目
在 PyCharm Professional 中创建一个新的 Flask 项目。如果你跳过了,我在 第八章 中展示了如何做。你可以在 图 15.28 中看到我的项目设置。
图 15.28:创建一个 Flask 项目以便我们在 Docker 中运行
我们不会对生成的项目进行任何修改,除了添加一个 Dockerfile。图 15.29 展示了我这样做的过程。
图 15.29:右键单击项目,然后点击“新建”|“Dockerfile”
在前面的图中,我正在右键单击项目,悬停在“新建”上,并点击“Dockerfile”菜单选项。这为我生成了一个包含以下内容的 Dockerfile 模板:
FROM ubuntu:latest
LABEL authors="BruceVanHorn"
ENTRYPOINT ["top", "-b"]
第一行指示哪个 Linux 内核将作为我们的容器镜像的基础。我非常喜爱 Ubuntu,但也有其他可能性。Alpine Linux 同样非常受欢迎。
第二行正如其名:为包含作者名字的镜像添加标签。我们实际上并不需要这个,也不需要最后一行,至少在目前的格式下是这样。让我们用更有用的内容替换生成的模板。Docker 背后的公司维护着一个庞大的预制作容器注册库,你可以将其作为工作的起点。其中就包括已经安装了任何给定版本的 Python 3 的容器。我将使用 Python 3.9,尽管这并不是最新的版本,因为当我生成示例项目时,我恰好注意到我的电脑上安装的是这个版本:
# Use the official Python 3.9 image as the base image
FROM python:3.9
在下一行,我将设置容器内的工作目录。容器有一个最小化的文件系统,空间有限。我们的应用程序代码将存放在这里:
WORKDIR /app
接下来,我需要指示 Docker 复制我的 requirements 文本文件,并使用 pip
安装我的项目需求:
# Copy the requirements file and install the necessary packages
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
接下来,我将把我的代码复制到容器中:
# Copy the Flask application code into the container
COPY . .
COPY
指令将复制我的项目文件夹的全部内容,包括 venv
文件夹。由于 pip
命令将安装我们的需求,我们不需要 venv
文件夹。我将在稍后解决这个问题。我在这份文件中的下一步是暴露容器防火墙上的端口 5000
,这样我就可以访问应用程序:
# Expose the port on which the Flask app will run
EXPOSE 5000
最后,我将定义容器启动时运行的 Flask 应用程序的命令:
CMD ["python", "app.py"]
为了解决复制问题,我将在项目文件夹中创建一个名为 .dockerignore
的文件。这和 .gitignore
的工作方式相同,但显然它是用来为 Docker 设置文件排除的。.dockerignore
文件的内容很简单:
venv
顺便说一句,你可以在 Marketplace 中找到一个名为 .ignore
的插件,它为一系列忽略文件添加格式。
添加 Docker 运行配置
我们在第三章中介绍了创建和使用运行配置,所以这里不再赘述。我只会指出如何使用 Docker 创建运行配置,如图 15.30 所示。
x
图 15.30:使用 Dockerfile 选项创建使用 Docker 的运行配置
您的许多运行配置将自动填写。PyCharm 会看到您的 Dockerfile 并将其用作默认设置。我的设置如图 15.31 所示。
图 15.31:我的 Docker 配置
如果我们运行这个,Docker 将拉取 Python 3.9 镜像,创建一个容器并运行应用。如果我们实际上想使用应用,我们还需要做一件事。图 15.31 中的箭头指向一个菜单,允许您定制您的docker run
命令。根据您的应用,这里通常有一些您想做的事情,例如映射持久存储卷,或者在我们的情况下,将容器中的端口映射到本地计算机的端口。
如图 15.32 所示,点击修改 | 绑定端口。
图 15.32:为了从浏览器中看到应用运行,您必须为 Docker 容器添加一个绑定端口
一旦您点击绑定端口选项,PyCharm 会为绑定端口添加一个配置参数。奇怪的是,图 15.33 中由右箭头指示的文件夹图标。
图 15.33:点击文件夹以打开端口绑定对话框并设置端口映射
这很奇怪,因为我们不会浏览文件或文件夹。当您点击它时,您得到一个5000
,所以您只需要指定容器中的5000
与您计算机上的5000
之间的绑定。点击确定直到您离开运行配置。运行运行配置。我们早在第三章中解释了运行配置,所以如果您是后来加入的,请返回并复习。
当 Docker 将数据块下载到容器镜像中,构建容器并最终运行应用时,您将看到一个文本光秀。您可以在图 15.34 中看到我的应用正在运行。
图 15.34:我的 Flask 应用正在 Docker 容器中运行
如果您想比较 Docker 与 Docker Desktop 的用户界面,您会发现许多相同的功能。快速浏览如图 15.35 所示。
图 15.35:PyCharm 拥有来自一个有能力的 Docker UI 的许多你期望的功能
在位置(1)我们可以看到三个标签页。构建日志允许我们检查当 Docker 从我们的 Dockerfile 构建容器时生成的输出。我们在(2)处有一些基本控制来停止和重新启动容器。当然,当容器停止时,启动它的选项也会出现在这里。然后可能是最有用的,你可以使用终端按钮(3)在容器内启动一个终端会话。
在左侧(4)我们可以看到一个全屏显示,显示了所有我们的运行容器、下载的镜像、网络和挂载的卷。
总结来说,你几乎可以在 PyCharm 中做任何你可以在 Docker Desktop 中做的事情。再次强调,PyCharm 的使命是为你提供一种与当今使用的重要技术之一集成的体验,这一使命已经完全实现!
摘要
PyCharm 插件是定制附加组件,可以在使用 PyCharm 时进一步添加可利用的功能和功能列表。我们已经看到了如何在 PyCharm 环境中浏览、下载和管理不同的插件。通过利用这些插件,我们可以进一步定制我们的工作空间并提高我们的生产力。在 PyCharm 中的插件管理可以在设置中的插件标签页中完成。
我们查看了一些简单的插件,以增强你在编码时的生活质量,例如主题插件和一些有用的文件处理器。我们查看了我最喜欢的一个,GitToolbox,它允许你在你的 IDE 窗口中看到更多来自你的 Git 仓库的详细信息。
我们随后扩展了范围,查看了一些在 PyCharm 2023 中相对较新的具有很多功能的插件。在疫情之后的世界里,越来越多的人远程工作,远程开发非常有用。即使你不是远程工作,你也很可能发现自己越来越多地与云服务器和虚拟机一起工作,而不是总是使用本地笔记本电脑上的硬盘。
另一个重大进步是 PyCharm 对 Docker 的支持。捆绑的 Docker 插件提供了完整的集成,从在 Dockerfile 和 composer 文件中提供自动完成和语法高亮,到管理你的容器的完整 GUI。你会发现你永远不需要离开 PyCharm 跳转到 Docker Desktop 来处理你的 Docker 操作。当然,如果你愿意,你将发现这两个环境完全同步,你也可以根据你的偏好,在 PyCharm 的终端窗口中与你的容器进行交互。
插件负责为我们 IDE 添加许多非常酷且非常有用的功能。有一些插件我没有涉及,因为坦白说,它们还没有完全准备好。我有一些访问一些封闭的测试功能和一些隐藏的宝石,我想要在下一章中介绍。我迫不及待地想向你展示一些即将在未来的版本中出现的功能!
第十六章:使用 PyCharm 的下一步
PyCharm 的功能非常广泛,JetBrains 的改进又如此迅速,以至于很难跟上。
在这个,我即将告别的章节中,我想做几件事情,以公正地评价这款令人惊叹的软件。在本章中,我们将涵盖以下内容:
-
我将进行例行总结,简要总结并评论我们迄今为止的旅程。
-
我将介绍一些有助于提高你生产力的酷炫功能,这些功能之前没有很好地融入章节主题。
-
我将介绍一些在撰写本书的 8 个月期间引入的酷炫新功能。我一直在介绍 2023.1 版本,为了避免与书中使用的 UI 截图不一致,我避免更新它。2023.2 版本已经发布,其中有一些惊人的变化,包括新 AI 助手 的测试版!
-
当我们分别,我会举杯畅饮,泪眼朦胧,直到下一本书出版。
我会尽力不让你感到伤感。我们已经在一起有一段时间了,知道这本书即将完成,和你一起庆祝这个胜利的时刻将会很难。我想到这里,眼眶都有点湿润了,所以我们还是继续前进吧。
为了继续本章的学习,你需要以下内容:
-
安装并运行良好的 Python 解释器。我将使用来自
python.org
的最新版本。 -
安装并运行良好的
pip
和virtualenv
。当你将 Python 安装在 Windows 上时,你会自动获得这些,macOS 在每个系统上都会包含它们。如果你使用 Linux,你需要单独安装包管理器(如pip
)和虚拟环境工具(例如virtualenv
)。我们的示例将使用pip
和virtualenv
。 -
安装并运行良好的 PyCharm 版本。安装过程已在 第二章 中介绍。本章中的截图来自 2023.2.1 版本。
本书从 GitHub 上的示例源代码可以在 github.com/PacktPublishing/Hands-On-Application-Development-with-PyCharm---Second-Edition/tree/main/chapter-16
找到。我们已在 第二章 中介绍了代码的克隆,安装 和配置。
PyCharm 中的杂项主题
如我所说,这个 IDE 的功能数量令人震惊。在本节中,我将介绍一些没有很好地融入其他章节的内容。我将更多地以“嘿,看看这个!”的精神来展示它们,而不是为每个功能提供完整的教程。
远程虚拟环境
我们已经拥有这个功能一段时间了,我认为它可能被我们在第十五章中介绍的新远程开发功能所超越。这个功能允许您通过 SSH 访问另一台计算机上的解释器进行工作。这个功能有几个用例。我曾使用这个功能来调试一个在生产服务器上使用虚拟环境的应用程序副本。我在我的电脑上有一个生产代码的副本,但虚拟环境在生产系统上。这让我能够在它通常运行的环境中重现错误并本地修复它。当我在使用 Mac 和 Windows 笔记本电脑作为主要开发环境,而生产环境在 Linux 上运行时,这种方法特别有效。正如我们指出的,许多软件包根据操作系统部署方式不同。我一直在NumPy、pandas和pymssql(Microsoft 支持的 SQL Server 驱动程序)上遇到挑战,这些软件包在这些不同的环境中工作方式非常不同,尤其是在 Windows 上。要构建利用 C 代码的第三方软件包,您需要一个编译器。在 Linux 上,它是cmake。在 Windows 上,它是 Microsoft C++编译器。两者之间有很大的区别,仅在对 Windows 进行应用程序测试对我来说是不够的。我想要一个更真实的测试,使用这个功能,我可以在它将运行的确切环境中使用。
您可以通过设置到远程计算机的 SSH 连接来创建或使用一个虚拟环境。我们之前在第十五章中介绍了如何创建 SSH 连接,所以这里不再重复说明。使用远程环境的选项与项目解释器设置中的常规选项并列,如图 16.1所示。
图 16.1:使用解释器设置对话框以及之前配置的 SSH 连接来在远程 Linux 系统上设置您的虚拟环境
自然地,以这种方式工作可能会带来相当大的性能损失;至少对我来说是这样的。对于我的用例来说,这非常值得!
您会注意到还有一个在 Vagrant 上的选项,这实际上是一个相同的选项,但它不是在随机的远程服务器上,而是在由HashiCorp 的 Vagrant管理的本地虚拟机上。您可能会问,Vagrant 是什么?
使用 HashiCorp Vagrant
在 DevOps 领域,一个名为基础设施即代码(IaC)的潮流变得非常重要。其理念是,没有人再在裸金属上运行任何东西;所有东西都是在虚拟机上运行的。尽管如此,多年来我面临的一个挑战是,当我去设置我的测试和生产环境时,我必须全部手动完成,而且需要相当长的时间。我的目标是设置至少两个或三个几乎完全相同的环境。
假设我正在使用 Flask 创建一个产品,该产品使用 React 作为前端,并使用 Microsoft SQL Server 作为数据库。为了按照最佳实践运行,我需要几个服务器:
-
我需要一个像 Nginx 或 Apache 这样的 Web 服务器来为我的 React 应用提供服务,以及可能需要的任何静态资源。
-
我需要一个应用服务器。Flask 的开发服务器在生产环境中是不够用的。我需要一个运行类似绿色独角兽(简称gunicorn)的服务器,这是一个能够处理生产负载的生产级 WSGI 服务器。WSGI代表 Python 开发领域的Web 服务器网关接口。它是一个规范,用于定义 Web 服务器和 Python Web 应用或框架(如 Flask、FastAPI 和 Django)之间的标准化接口。WSGI 定义了一套规则和约定,允许 Web 服务器以一致和互操作的方式与 Python 应用通信。
-
我需要一个专用的数据库服务器来运行 SQL Server。
简而言之,这是三个服务器,每个服务器复制三次:一个用于开发测试,一个用于预发布或用户验收测试(UAT),一个用于生产。对于每个服务器,我需要安装、打补丁和加固操作系统。然后我需要设置每种类型的服务器。Web 服务器很简单,我只需要安装 nginx,并配置它使用 SSL,重定向任何不使用 HTTPS 的请求,缓存静态内容,并将 API 请求反向代理到运行 Flask 的 API 服务器。
Flask 服务器更为复杂,因为最佳实践要求在该系统上也要有 nginx,将流量转发到 Green Unicorn。我通常会在该服务器上设置防火墙规则,拒绝来自 Web 服务器的任何流量,并封锁任何不需要的端口。
数据库服务器是最困难的一个。理想情况下,我需要为操作系统、日志和数据分别设置分区,以防止 I/O 性能瓶颈。总计,仅设置和验证一个小型环境就可能需要一周时间。
如果我要使用 IaC,我可以编写一个脚本来自动设置和拆除三个环境中的每一个。编写脚本所需的时间与手动设置所有内容所需的时间一样长,但到了扩展或更新我的基础设施的时候,将其以代码的形式存在将非常有帮助。由于它在代码中,所以一切都有文档记录。如果我想更新操作系统,比如从 Ubuntu 20 更新到 22,我只需更改一个变量,就可以重建所有新的服务器或更新现有的服务器。在所有超出一次性原型的情况中,IaC 都是值得投入时间的。
更受欢迎的 IaC(基础设施即代码)框架之一是HashiCorp 的 Terraform。你可以在 JetBrains 市场找到针对HashiCorp 语言(HCL)的插件,这些插件允许你使用常规的语法高亮和自动完成功能来处理 Terraform 文件。Terraform 旨在通过提供程序来帮助你配置虚拟机。有针对 VMware 的提供程序,这就是将你的 IaC 转换为实际虚拟机的方式。还有针对云服务的提供程序,包括 Azure、AWS 和 Digital Ocean。
HashiCorp 还生产一个名为Vagrant的产品。Vagrant 旨在为你的本地计算机提供 IaC。我一直在说,如果你打算在 Linux 服务器上运行最终应用程序,你应该使用 Linux 进行开发。但大多数人宁愿不放弃在 Windows 或 Mac 上工作。由于 Mac 基于类似 Linux 的操作系统,Mac 至少很接近,但如果你在 Ubuntu、Red Hat 或 Fedora 上运行工作,它不会完全相同。
Vagrant 是一个程序,当与本地虚拟机管理程序(如 VMware Workstation、Oracle VirtualBox 或 Microsoft 的 Hyper-V)结合使用时,允许你创建一个脚本,该脚本可以生成一个本地虚拟机,称为box,它可以匹配你的生产环境。本质上,你正在以与使用 Terraform 为服务器的方式相同的方式自动化你在本地计算机上创建和维护虚拟机。Vagrant 做一些很酷的事情,比如自动将你的项目文件夹映射到虚拟机,这样你就不需要不断手动将代码复制到虚拟机中。你的 PyCharm 终端会自动通过 SSH 连接到虚拟机,所以当你执行命令时,你实际上是在 IDE 中使用本地终端,但虚拟机正在处理执行。你可以使用Windows Subsystem for Linux(WSL)来做这件事,但会涉及更多的努力,而且与为每个项目创建单独的虚拟机相比,WSL 的使用非常有限。
虽然 Vagrant 非常有用,但大多数开发者正在转向 Docker 来处理这类工作流程,但我仍然看到开发者,尤其是在网络安全领域和教育环境中,仍在使用虚拟机。Vagrant 的优势在于 Docker 容器是不可变的,这意味着一旦创建就无法更改。虽然这在操作上更优越,因为部署应用的不可变性提供了一个非常稳定的系统,但对于日常开发来说可能不太理想,因为开发者需要能够不受限制地调整环境。
Vagrant 创建一个盒子,它只是一个虚拟机,代表了一个完整的带有完整操作系统的服务器体验。你可以对它做任何你想做的事情,而且因为它在你的笔记本电脑上,如果你的操作破坏了系统,你只需杀死盒子,然后在几分钟内将其重置到原始状态。
Vagrant 允许你使用真实 IP 地址而不是使用回环地址(也称为 localhost)来构建 Web 应用程序。这提供了本地测试的优势,因为通常在 localhost 上运行的应用程序的行为与应用程序绑定到真实 IP 地址时的行为不同。此外,你可以选择使你的 Vagrant 盒子的 IP 地址不仅可在本地网络中共享,也可在公共互联网上共享。当你准备好展示你的进展时,你可以邀请他人访问你的共享 Vagrant 盒子,获取反馈,然后关闭共享。
你可以在显示在 图 16.2 中的 工具 菜单中找到 Vagrant 的工具。
图 16.2:PyCharm 可以直接通过菜单选项支持所有 Vagrant 命令
为了使用 Vagrant,你需要安装它以及一个受支持的虚拟机管理程序。你可以在 developer.hashicorp.com/vagrant/tutorials/getting-started/getting-started-install
找到安装说明。至于虚拟机管理程序,默认是 Oracle VirtualBox。你可以在 www.virtualbox.org/wiki/Downloads
找到更多关于 VirtualBox 的信息。
一旦安装了这些程序,你就可以使用显示在 图 16.2 中的 在项目根目录中初始化 选项。
这将提示你通过选择你的 Vagrant 盒子的名称并提供镜像 URL,在项目的根目录下创建一个名为 Vagrantfile
的文件。对话框显示在 图 16.3 中。
图 16.3:你被提示创建一个带有非常旧默认设置的 Vagrant 盒子
很明显,这是一个非常旧的默认设置,因为 lucid32
指的是 Ubuntu 10。在撰写本文时,Ubuntu 的当前版本是 23,而长期支持版本是 22。
如果你认真想要使用更先进的虚拟机,你可能需要找到一个更新的 box 定义。你可以在app.vagrantup.com
找到 box 定义。我在图 16.4中这样做,我搜索了Jammy Jellyfish
,这是Ubuntu 22的项目代号,我目前所有生产项目都在使用它。
图 16.4:你可以在 Vagrant Cloud 注册表中找到各种预定义的 box
一旦找到你想要使用的,点击它。在图 16.5中,我正在点击列表中的第二个条目。
图 16.5:这是 Ubuntu Jammy 64 的 box 定义
现在我们有了 box 定义,请参考图 16.3。在第一个字段中,你需要输入图 16.5中由深色箭头指示的 box 名称,即ubuntu/jammy64
。接下来,复制 box 定义 URL 的名称,由图 16.5中的浅色箭头指示,在我们的例子中是app.vagrantup.com/ubuntu/boxes/jammy64
。点击确定将下载 box 镜像并创建虚拟机。一旦准备好,你会在菜单中看到 Vagrant 的其他工具选项不再被灰色显示,如图 16.6所示。生成的 Vagrantfile 是打开的(不是自动的,是我手动做的),你甚至可以看到 PyCharm 在提示我添加新文件到 Git 仓库。
图 16.6:Vagrant 工具选项现在可用
如果你更深入地使用 Vagrant,你会发现,就像 PyCharm 的许多集成一样,IDE 提供了一个图形界面来执行原本可能需要命令行工作流程的操作。你可以使用图 16.6中的菜单运行命令,或者你可以在终端中输入命令以实现相同的结果。Packt 有一些关于使用 Vagrant 开发的优秀书籍。我会在本章的进一步阅读部分留下一些建议。
使用 Vagrant 有一些显著的优点。你可以让整个开发团队使用相同的开发环境来开发和测试你的应用程序,这个环境就是你将在生产中使用的环境。不再有“在我的机器上它运行正常”的情况,这暗示代码是好的,配置问题是罪魁祸首。由于环境配置是标准化的,并且作为代码库的一部分进行维护,只要代码和任何外部配置文件(如.ini
或.env
文件)或环境变量也是相同的,项目应该在任何地方都能正常工作。
在 Vagrant 中使用 IaC 可以保护那些不知道如何设置虚拟机的经验不足的开发者,并且它还标准化了构建,使得每个人的虚拟机都是相同的。配置在版本控制系统中跟踪,因此它就像应用程序代码一样跟踪和更新。
跟踪你的时间
在我漫长的职业生涯中,我多次为美国政府工作,并且我经营着自己的自由职业实践。在每种情况下,我都需要一种跟踪时间的方法。在政府合同的情况下,我必须将一组计费代码跟踪到 7 分钟间隔。时间跟踪也可以在更合理的环境中应用。我试图将时间跟踪数据与敏捷开发方法(如 Scrum)中使用的难以捉摸的故事点与完成任务所需的时间相关联。根据大多数敏捷方法,人们真的很擅长估计相对大小。但在图 16**.3中,你很难准确地告诉豆浆盒相对于咖啡杯的高度,是英寸还是厘米。
图 16.7:豆浆盒有多高?
它是 8 英寸高还是 20 英寸?但如果尝试使用咖啡杯作为你的度量单位来估计大小,你可以更加精确。纸箱大约有二又四分之一咖啡杯高。故事点也是如此,但最终,老板真正想知道某件事需要多长时间。你不能告诉她你将在两杯咖啡后完成她的功能,并期望她对此感到高兴。这可能会引起明显的回应,“太好了,那么它将在我的第二杯咖啡之后完成!”
我先进行正常的故事点估计,然后跟踪我的时间,看看实际花了多长时间,这样我既可以给老板一个答复,也可以在下次估计时做得更好。
要使用 PyCharm 中的时间跟踪功能,你需要连接到服务器。在这里,服务器指的是 PyCharm 支持的项目跟踪服务之一,如图 16**.4所示。
图 16.8:点击“配置服务器”菜单项来配置你的项目跟踪器
当你点击这个菜单项时,你应该会看到图 16**.5所示的屏幕。
图 16.9:从列表中选择你使用的任何项目跟踪服务
一旦你的服务器注册成功,你将能够选择分配给你的待办事项或问题。图 16**.6展示了我的 YouTrack 服务器与PyCharm Book中的问题编号DEMO-20: 完成第十六章的集成。
图 16.10:我在这件事上花费的时间会自动跟踪
在你一天中切换任务时,你可以在 IDE 中切换,它会跟踪你在每个任务上花费的时间。然后你可以将你的工作时间同步到服务器,以保持你时间花费的准确统计。
TODO 列表
这听起来可能和时间跟踪功能有关,但实际上并没有。TODO 列表面板以列表形式显示你代码中的所有 TODO 注释。你可以在图 16.7 中看到一个示例。
图 16.11:PyCharm 中的 TODO 面板列出了你代码中的所有待办事项,并在你双击时直接跳转到指定行
你可以双击 TODO 项直接跳转到代码中的该行,这样你可以快速找到你之前暂时放弃的地方。
宏
宏是一系列记录的按键脚本,可以按顺序回放以创建有用的自动化。在我看来,学会使用这些是软件开发丛林中的必要生存技能。
假设我们需要一个不改变的物品列表来填充 HTML 中的下拉列表框。在这个例子中,我将创建一个包含美国所有 50 个州的SELECT
标签。自 1959 年以来,这并没有改变,尽管如果我当选皇帝,加拿大和墨西哥将是我名单上的第一个,他们永远不会想到这一点。
在现实中,我需要完成这项工作,而且我绝对不想手动输入所有这些。我向我的好朋友 ChatGPT 请求给我一个按字母顺序排列的 50 个州的列表。结果在章节源代码的us-states.txt
文件中。
你也会在源代码中找到一个名为states.html
的 HTML 文件。我已经包含了以下代码:
<form>
<p>Pick a state:</p>
<select id="state" name="state">
</select>
</form>
如你所见,<select>
标签中没有内容,它旨在在由该文件中其余代码定义的 HTML 页面中渲染下拉选择列表。我需要添加州名,使它们看起来像这样:
<option>Alabama</option>
<option>Arkansas</option>
对于这 50 个州,我可以使用宏来完成。当然,我也可以让 ChatGPT 为我生成它,但那样就没有悬念了,因为这是一本关于 PyCharm 的书,而不是关于 ChatGPT 的书。
打开章节源代码中的feature_demo
项目,然后打开united-states.txt
文件。首先,将光标放在united-states.txt
文件中的Alabama
前面,如图 16.9 所示。
图 16.12:将光标放在阿拉巴马州前面
接下来,点击编辑 | 宏 | 开始宏录制,如图 16.10 所示。
图 16.13:开始录制宏
使用宏录制时,输入 <option>
。然后按键盘上的 End 键(在 Mac 上为 Cmd + 右箭头)到达行尾,然后输入 </option>
。按键盘上的 下箭头 键,接着按 Home 键(在 Mac 上为 Cmd + 左箭头)。此时光标应位于第二行开头,Arkansas
。回到 图 16**.10 中显示的相同菜单项,但请注意 option-list
。
再次回到 图 16**.10 中显示的菜单,您将看到保存的宏名称列表,或者只是一个 Arkansas
到 option
标签的 Arkansas
。
您可以将宏映射到键盘快捷键组合,并通过键盘快捷键重复执行操作,这使得重复执行变得非常容易。
如果我坦白的话,PyCharm 中的宏功能还不错,但并不像某些其他工具那样出色。虽然 Ultraedit 和 Notepad++ 都不是 IDE,但它们都拥有非常复杂的宏功能,例如能够回放宏直到文件末尾,或者运行特定次数的宏。PyCharm 的宏对于小任务来说足够好了,但对于更大的任务,您可能需要考虑其他工具。
通知
有时 PyCharm 会在屏幕右下角以托盘的形式显示通知。这些消息通常会在几秒钟内消失,但您可以在通知面板中看到它们。您可以在右侧工具栏中找到这个面板,如图 图 16**.11 所示。
图 16.14:通知面板
您可以通过点击右侧工具栏上的 铃铛 图标来切换面板。面板有一个 时间线 选项,允许您按时间顺序列出通知。您可以选择删除不再需要的任何通知,或者清除所有通知。
2023.2 的新功能
在撰写本书的过程中,许多事情都发生了变化,尤其是在 UI 方面。我使用了构建号 #PY-231.8109.197,或者像我喜欢叫的,战斗的 197 号。我整个过程中都保持了相同的版本,没有安装任何更新,因为我希望保持一致性。大约在我开始编写 第十三章 的时候,JetBrains 举办了一场在线活动,公布了新的点版本,2023.2。这个新版本包含了大量新功能,但今天当我写 第十六章(本章)时,又推出了两个点版本。这种速度可能很大程度上归因于 JetBrain 对 UI 的重新设计。如果你记得本书开头,我们不得不在设置中打开“新 UI”。等你读到这本书的时候,新 UI 将成为默认设置。JetBrains 没有止步于此。UI 一直都可以高度自定义,我们在本书的前几章中对此进行了详细阐述。
新用户界面努力的主要推动之一是简化开发过程。大多数 IDE 都会经历一个增长周期,它们从一个简单且专注的 UI 开始,集成了被视为 IDE 而非增强型文本编辑器所需的最小功能。就在前几天,我突然想到,在我 100%专注于 Python 开发,或者甚至是 JavaScript、Web 或数据库开发的日子里,我真正需要的可能只是一个轻量级的操作系统和 PyCharm。甚至不需要比运行 PyCharm 所需的更多的窗口管理器!
这样一个工具的丰富性带来的问题是,它对于新用户来说可能令人望而生畏,对于老用户来说则显得杂乱无章。我们在第十五章中学到,任何 JetBrains IDE 的新功能都是作为插件实现的。这可能导致 UI 杂乱无章,并使 IDE 感觉像是一堆无关的碎片拼凑在一起,用户体验不佳。
新的 UI 旨在通过压缩 UI,展示最常用的工具,并隐藏(但不是删除)更复杂的选项来解决这个问题。我用于书籍的构建版本做得非常好。随着新的点版本发布,我注意到进一步的努力来隐藏复杂性。看看图 16.13。
图 16.15:JetBrains 已经将许多设置移动到标签中,而不是有几十个复选框
这是从最近几天发布的最新更新中提取的运行配置,标记为构建号#PY-232.9559.58,构建于 2023 年 8 月 22 日。有两个明显的不同之处。底部有一组标签,以及一个修改选项下拉菜单,两者都在图 16.13中用箭头指示。与图 16.14进行比较。
图 16.16:这是第三章的旧对话框
所有那些选项仍然存在,但它们已经被整合到修改选项下拉菜单中。你实际使用的设置作为标签出现在图 16.13的底部。仅仅几个月的变化量是惊人的!
除了产品变化之外,还有一些有趣的新功能添加和改进。
Black 集成
Black 首次在 PyCon 2019 上推出,是一个不妥协的代码格式化器。自从发布以来,它已经获得了极大的关注和人气,JetBrains 也为 PyCharm 添加了支持。为了使此功能正常工作,需要安装 Black。你可以在github.com/psf/black
上找到有关安装的详细信息。如果你使用 Linux,检查你的包管理器。我使用基于 Ubuntu 的 Pop_OS。图 16.15显示我已经找到了 Black 作为包,并且我更喜欢以这种方式安装它。
图 16.17:在 Linux 中,你很可能会使用你的包管理器来安装 Black
一旦安装了 Black,你需要根据每个项目来开启它。当你搜索图 16.16 中显示的设置对话框中的 Black 时,你会找到这个选项。
图 16.18:在设置对话框中搜索 Black,你会在工具下找到它
你的格式化选项是开发
。在我的团队中,我们每个人都会从开发
分支创建特性分支。如果我正在开发一些新功能,我会从开发
分支分出一个新分支,命名为feature/amazing-thing
。接下来,我将创建我的神奇事物,但在这个过程中,我需要修改其他开发者编写的一些文件。自然地,在我提交之前,肌肉记忆会自动使用Ctrl + Alt + L(重新格式化代码)命令来重新格式化。Black 会重新格式化整个文件。当你去合并时,你可能会发现其他开发者已经更改了你重新格式化的某些文件,但他们不太注意重新格式化。即使两个开发者之间实际上只更改了几行,你仍然可能会面临一个丑陋的合并挑战。
这并不是 Black 格式化器的错!如果你使用 PyCharm 的默认代码格式化器,同样的事情也会发生。然而,Black 格式化器是一个更好的解决方案,因为它旨在实现确定性,这是一个复杂的程序员术语,意味着操作的结果总是产生相同的输出。一个不妥协的确定性格式化器应该每次使用时都产生非常一致的结果。
因此,如果你想要开始使用 Black,我的建议是作为一个团队一起行动。让每个人都推送他们的工作,将其合并到一个分支中,然后对整个项目应用 Black。在我的例子中,我会解决我的冲突,然后合并回开发分支。然后我会让所有同事都这样做。一旦开发阶段包含了每个人的更改,冻结代码,并在所有源文件上应用 Black。在冻结期间,不允许任何人单独工作,直到格式化被应用并推送。如果你有一个非常大的代码库,你可能需要分阶段组织你的工作。结果将是值得的!你的代码将符合 PEP-8 标准,你应该会看到更少的合并冲突,尤其是那些由格式化引起的。
Black 可以配置在保存时运行,并且超出 PyCharm 的范围,Black 可以作为你的版本控制或 CI/CD 流程中的一个钩子。
GitLab 集成
GitHub 是管理代码最流行和最广泛使用的基于云的解决方案。它的阿喀琉斯之踵在于它是基于云的。对于像那些要求其贸易伙伴获得认证(如 服务组织控制 2(SOC2)认证)的严肃组织来说,他们的知识产权托管在公开可用的平台上通常是不满意的。虽然你可以在 GitHub 上托管私有仓库,但仅仅因为文件不在由认证组织控制的服务器上,就可能成为一个问题。
幸运的是,你可以使用 GitLab 来托管自己的私有 GitHub 服务器。你可以在防火墙后面运行 GitLab,同时对你的安全和基础设施拥有完全的控制权,同时仍然享受到在所有其他方面都相同的 GitHub 体验。你将在 图 16.17 中的 版本控制 设置中找到 GitLab 集成。
图 16.19:GitLab 设置可以与其他 VCS 设置一起找到
集成相当深入。除了我们在 第五章 中介绍的基本内容之外,你还可以直接从 IDE 中处理所有你的拉取请求、审批等。
运行任何地方
我们已经有一段时间的 全局搜索 功能了。如果你错过了它,你可以双击 Shift 键,打开一个对话框,让你在项目中找到任何地方的内容。即使你要找的东西不在代码中,这也同样适用。它可能是在文档、设置或其他地方。
虽然 PyCharm 对于新手来说可能不是新手,但在对话框中按 ?
) 是一个新功能。
图 16.20:双击 Ctrl 键以打开“运行任何内容”对话框
说出“你可以运行任何东西!”已经足够好了。但所有那些批判性思考者会立即追问,“你说的‘任何东西’是什么意思?”在这里,我对问号的回答告诉我可以运行什么。我可以看到,我可以通过简单地输入其名称来运行项目中的任何 Python 脚本。我可以看到,我也可以运行pip
和conda
命令。Web 开发者可以运行我们在第九章中介绍的 HTTP 请求测试代码。他们还可以通过 npm 运行所有流行的包管理器,例如package.json
文件,这是明显支持的。当然,我们也可以像运行 Python 一样轻松地运行任何Node进程。
AI 助手
在最近许多 PyCharm 相关活动中,抢尽风头的无疑是 AI 助手。当我写这篇文章时,该功能处于封闭测试阶段,这意味着您必须注册并获得批准才能尝试该功能。请记住,这里展示的任何内容都可能发生变化。
最容易实现的事情,而且有众多插件可以做到这一点,就是为某些在线 AI API 提供一个集成的前端体验。
相比之下,您会发现 AI 助手已集成到 PyCharm 中。要使用 AI 助手,请使用图 16.19中显示的右侧菜单。
图 16.21:可以通过右侧菜单上的按钮激活 AI 助手
前面的对话框总结了可用的功能。让我们看看。
此功能不是本地的
如果您的公司或环境对将代码发送到 API(如 ChatGPT)有限制,您需要知道这个功能正是这样做的。该功能不是在 PyCharm 内部自包含的,您输入的任何代码或信息都将发送到各种外部第三方 API。这一点在您盲目滚动并忽略的协议中有记录,以便您可以使用 PyCharm 和酷炫的新 AI 功能。
解释代码
在许多情况下,您可能会遇到不理解代码。我通常用相当生动的语言来描述这些情况,尤其是在截止日期前一天凌晨 2 点。我将努力保持专业:
-
这是由其他人编写的,显然他们不知道自己在做什么。
-
这是由其他人编写的,显然他们比您更擅长。有时这可能是同一个人,具体取决于时间。
-
这是由一个喜欢简洁代码的人编写的,尽管这在 Python 中是一种反模式。
-
代码中包含一个您没有记住的正则表达式,因为即使人们假装了解,也没有正常人类能理解正则表达式。
在这种情况下,帮助就在鼠标点击之间。突出显示有问题的代码,右键点击,并选择AI 助手操作 | 解释代码。几秒钟后,AI 将为您提供所选代码的分解。
注意
请记住,AI 助手并不总是正确的,也不总是给出完全合理的建议。
回答编程问题
事实上,AI 助手可以回答你提出的任何问题。我曾就 nginx 配置、Docker 和一般网络问题向它提问。当然,它也能回答编程问题。
创建提交信息
这个功能真的很棒!我是 XKCD 在线漫画的超级粉丝,他们在许多漫画中对 Git 的处理非常到位。现在我想起了一个:xkcd.com/1296/
。
由于 IDE 可以看到你的更改列表,它可以为你生成提交信息,如图 图 16.20 所示。
图 16.22:生成的提交信息比“我改了一堆东西”更深入和有意义
也许你对提交信息并不太重视。我曾参与过美国政府项目,那里的提交信息需要接受审计。开发团队并不普遍知道这一点,当他们得知后,选择重置并重新创建每个提交,以移除提交信息中的更生动的语言。审计并不是真的在寻找那个;它被用来验证变更控制流程。我的团队目前正在通过 SOC2 认证,我可以亲身体验到完整且描述性的提交信息非常有帮助。随着网络安全继续侵入开发工作流程,你可以确信这个功能迟早会证明它的价值。
生成文档
你讨厌编写文档字符串吗?这是一个陷阱问题。如果你是软件开发人员,你可能认为代码本身就是任何人可能需要的所有文档。但你也曾见过在使用主界面和 SciView 中的文档功能时,良好文档的代码的力量。如果你的函数也能得到良好的文档,而不需要你自己编写这些文档,那岂不是很好?图 16.21 显示了我正在为我项目中的一个函数生成文档字符串。
图 16.23:不要编写文档,生成它
在这种情况下,文档实际上只是另一种形式的模板。你应该让 IDE 为你生成这些文档,然后如果需要的话,再对其进行改进。
Polars 对 Jupyter Notebook 的支持
虽然 pandas 很强大,但对于大数据来说,它太慢了。Spark 和其他大数据框架可以提供帮助,但只能通过横向扩展来实现,这涉及到在你的网络上设置多个计算节点。Polars 是一个数据科学库,旨在允许你无需使用多台计算机即可处理大量数据,并且长期以来一直是 PyCharm 中的一个请求功能。如果你的 DataFrame 可以适应你的计算机内存,你就可以在 Polars 中使用它,并在 PyCharm 中查看,就像使用任何其他 DataFrame 一样。
此外,PyCharm 支持 Plotly 库进行可视化,该库独特地与 Polars 数据帧一起工作。当你结合 PyCharm 对这些高级库的支持时,你可以克服许多通常被列为使用 Polars 的障碍。
摘要和结束语
我们在短时间内覆盖了大量的内容!至少对我来说是这样的,因为在许多这些章节中,我非常想跳入我们正在讨论的主题的教程。例如,在 Flask 和 Django 章节中,我觉得没有给你 Flask 和 Django 的教程是“错误的”。我没有这样做,因为那些书实际上本身就是一本,无论是在 Packt 图书馆内部还是外部。
我们了解到 IDE 的目的是为你提供一套完整的工具,以应对你每天可能执行的所有开发任务。从这个角度来看,PyCharm 是一个惊人的 IDE,它实际上几乎没有竞争对手。
我们看到了如何通过自动化创建虚拟环境等过程来简化设置简单 Python 项目的基礎。PyCharm 专业版具有许多用于项目创建的附加功能,主要围绕通用软件开发、全栈 Web 开发和数据科学领域中最流行的项目类型的项目模板。甚至还有一个专门用于教育用途的版本,它允许你创建直接在 IDE 中播放的交互式课程。我们没有讨论这一点,但如果你对它感兴趣,我将在 进一步阅读 部分留下一个链接。
全栈 Web 开发者拥有 PyCharm 工具集中很大一部分是专门针对他们的工作。我们看到了所有主要框架的支持,而且支持并不局限于 Python。PyCharm 可以做到 WebStorm 可以做到的任何关于 JavaScript 开发的事情,以及与现代 JavaScript UI 框架(如 Angular、Vue、Vite 和 React)一起工作。
PyCharm 确实为与关系型和非关系型数据库交互提供了完整的工具集。这个功能集主要来自 DataGrip,这是 JetBrains 的一个专注于数据库的 IDE,它完全专注于创建和开发关系结构。它对 MongoDB 和 Redis 提供了全面的支持,这两个都是我每天都会使用的。在我通常需要切换到 Studio 3T 或 Redis Insights 等工具的地方,我可以在 PyCharm 中停留。支持的平台多样性令人印象深刻!有数十种支持的数据库,有时只是看看列表,然后查找你从未听说过的那些数据库,也是一种乐趣。
在介绍了数据库之后,我们学习了 PyCharm 为数据科学领域带来的好处。你可以通过利用 IPython 中的代码单元格来使用我所说的 Jupyter Notebooks 的轻量级版本。我在工作中并不是每天都会做数据科学,但有时我喜欢假装自己是。在第十二章和第十三章中涵盖的代码单元格功能对于集成多个 API 或微服务之类的任务非常有用。我可以创建一个原型脚本,使用从不同 API 或微服务中提取数据的单元格,将它们扔进 pandas DataFrame 中,即使我不需要在我的实际程序中保留 DataFrame 调用,我也可以使用 SciView 来查看结果。仅仅能够将数据以几个并排的简单表格可视化,就可以有很大的帮助。
如果你是一名数据科学从业者,随着 Jupyter Notebooks 集成等工具的支持增长,PyCharm 成为你整个工作流程的一站式工具。这就是这个工具针对其三个不同受众的目的。
当 Packt 出版社邀请我写这本书时,我既兴奋又感到畏惧。我之所以兴奋,是因为我刚刚得知 LinkedIn Learning 已经下架了我的 PyCharm 课程,而这正是我个人的最爱之一。这本书给了我一个机会,可以比在 2 小时的视频课程中做得更深入。如果你喜欢 2 小时的视频课程,我还在 LinkedIn 上留有一门关于使用 Flask 开发 API 的课程。我怀疑他们不会保持太久,所以如果你感兴趣,请确保在他们下架之前查看。我会在进一步阅读部分放一个链接。我的新视频课程可以在我的课程网站上找到,maddevskilz.com
。自然地,关于 Python 的任何内容都会以 PyCharm 作为 IDE。
写这本书的想法令人畏惧,因为在这个 IDE 中包含的内容如此之多(Packt?),实际上我也知道自己不知道什么。我并不全知。当我开始时,我确实发现了一些我不知道的功能。我可能做了一些错误的事情。没关系(我希望)。结束这本书只是我在一条旅程上的一个时间点,对我来说,这条旅程始于七年前的某个时刻,当时我对 Visual Studio 和 C#的 Web 开发感到厌倦。当我看到在 Flask 中创建应用程序有多容易,然后又看到 PyCharm 如何让像我这样的初学者也能真正完成工作,我就上瘾了。写这样一本书是一项巨大的责任,可能产生巨大的影响。编码是一种真正的超级力量。与电影中的大多数超级力量不同,这是一种可以教授的超级力量。没有必要来自遥远的星球,被放射性蜘蛛咬伤,或者被注射实验血清。虽然这些可能有所帮助,但你真正需要的是毅力、好奇心和付出努力。如果你在这方面哪怕只有一点点的掌握,你也能做大事。
我曾有机会开发一款帮助选择前列腺癌患者治疗方案软件。这个项目最终没有取得进展,但在我搁置项目一年后,我的父亲打电话告诉我,他刚刚被诊断出患有前列腺癌。这个故事的一个额外寓意是永远不要删除你的旧代码库!我的父亲本身也是一位病理学家,在算法创建者的帮助下,我们用程序运行了我父亲的检查数据,最终的治疗方案让我又多陪了他 8 年。
我还参与过旨在帮助保护我的国家、在美国海军陆战队训练飞行员以及管理 IT 资产的项目。当我参与这些项目时,它们只是项目。它们是我必须完成的截止日期,也是压力的来源。但每一款发布的软件,实际上我们写的每一行代码,都有潜力让周围的世界变得更好!有时好处是显而易见的,就像癌症治疗软件那样。即使是具有平凡目的的软件,比如企业存储容量规划,也能产生深远的影响,因为这也是一个让他人能够做他们想做的事情、建造他们想建造的东西的工具。
还要考虑到,我们的超级力量可以大规模运作。超人只能在一个地方出现一次;这是一个被各种超级恶棍频繁利用的事实。我们在这方面没有限制。有多个应用商店中的应用程序可以帮助阅读障碍儿童学习阅读。那个下载了数万次的单一应用程序可以教育那些在大多数国家的公共教育体系中可能会被边缘化或忽视的几代人,这些国家更重视考试成绩和资金而非结果。
你拥有超能力!利用它!这些工具使它变得更容易!如果我已经吸引到哪怕一个人,并且他们从我所得到的这个工具和这种语言中受益,那么对我来说,这本书就是一个巨大的成功。
进一步阅读
一定要查看本书的配套网站 www.pycharm-book.com
,以及以下有用的资源:
-
布朗顿,A. (2018) 《使用 Vagrant 的实践 DevOps》。Packt。
-
使用 Python 3 和 Flask 构建 RESTful API(linkedin.com):
www.linkedin.com/learning/building-restful-apis-with-flask/restful-apis-with-python-3-and-flask-4
-
Polars 库:
www.pola.rs/
-
Python 的 Plotly 库:
plotly.com/python/
-
PyCharm 教育版:
www.jetbrains.com/pycharm-edu/
-
HashiCorp Vagrant:
www.vagrantup.com/
-
HashiCorp Terraform:
www.terraform.io/
-
Docker 桌面:
www.docker.com/products/docker-desktop/
-
Oracle VirtualBox:
www.virtualbox.org/
-
JetBrains 人工智能助手:
blog.jetbrains.com/idea/2023/06/ai-assistant-in-jetbrains-ides/