PythonLand-中文系列教程-一-
PythonLand 中文系列教程(一)
原文:PythonLand
免费的 Python 初学者教程:学习 Python
你想学习 Python 吗?不要再看了。这个免费的 Python 教程包含 100 多篇精心制作的免费 Python 文章,其中充满了信息、实用建议和 Python 实践!我们将深入基础知识,然后逐步学习高级概念。我会给你提供许多例子来解释清楚所有的概念。
目录
- 急着学 Python?
- 为什么要这个免费的 Python 教程?
- 互动示例代码
- 你将从这个免费的 Python 教程中学到什么
- 我是谁?
- 关于 Python
- Python 的主要特性
- Python 历史
- 浏览免费 Python 教程
- 你能帮我什么?
急着学 Python?
如果你急着学习 Python,我现在就给你一些捷径,让你快速入门。
首先,如果您需要安装 Python,请查看关于安装 Python 的部分。
安装 Python 后,您可以做两件事:
- 直接进入Python 简介开始学习这门语言吧!从那里,您可以使用导航链接和菜单来学习免费的 Python 教程。
- 支持我的工作,参加高级课程 Python 基础 I 。它旨在快速而正确地学习 Python,没有干扰,有许多测验、练习和可以添加到简历中的结业证书。
为什么这个免费的 Python 教程?
这就是为什么你应该读这本书而不是其他的:
- 这个免费的 Python 教程易于阅读,对于初级程序员来说非常理想。我尽最大努力用简单的语言解释事情,让每个人都容易理解。
- 我是一名经验丰富的作家兼导师,我非常注意学习材料及其呈现的顺序。
- 本教程包含交互式示例代码您可以编辑和运行。这很有趣,而且可以帮助你更快地学习概念。
- 本教程是 实用 。我将枯燥的理论保持在最低限度,而是专注于在现实世界中完成事情。但与此同时,我试着解释事情是如何运作的,而不是教你技巧。
- 我在大多数页面上为你提供经过仔细审查的链接来加深你的知识。
- 我有没有提到它是完全免费的,没有任何附加条件?我确实为那些寻求优质体验、额外练习和良好结业证书的人提供了高级 Python 课程。
互动示例代码
这里有一个例子,说明我是如何在整个教程中包含交互式的、可运行的代码的。请随意使用这个“Hello World”示例。您可以编辑并运行它:
https://crumb.sh/embed/6FPiVq8p6J2
Python 的“Hello world”示例。像这样的代码遍布整个教程。
你将从这个免费的 Python 教程中学到什么
你将学习使用世界上最流行的编程语言进行计算机编程。我的目标是让你理解语言和生态系统。读完这篇教程,你可以继续自己探索 Python。你不会再感到失落;相反,当你试图解决一个问题时,你会知道去哪里找。
这篇 Python 初学者教程涵盖了广泛的主题,可以让你很快学会 Python。我不会只教你基础知识,但我们也会尝试一些高级主题,比如部署你的代码,正确使用虚拟环境和包管理。
我是谁?
让我自我介绍一下。毕竟,你应该问自己这样一个问题:是什么让我有资格通过这个教程教你 Python?
我是 Erik,作为一名专业软件工程师,我已经工作了超过 25 年。在我的职业生涯中,我使用了许多编程语言,但 Python 是我的绝对最爱!我喜欢编程和构建复杂的系统,但我也喜欢写作。这就是为什么我决定将这两者结合起来,为初学者编写这个免费的 Python 教程。之后,我开始学习我的高级课程:Python 基础 I 和 II。他们相当成功,到目前为止,已经获得了一些好评!
最终,我受够了有限的复制和粘贴代码示例,并希望示例代码可以在页面中编辑和运行。它产生了一个附带项目( crumb.sh ),提供了一种通用的方法来实现这一点。它还在开发中,但是教程和课程已经有很多有用的代码了!
如果你在 Twitter 上,你可以关注我(@erikyan) 获取新内容的更新。如果你喜欢电子邮件,也可以试试我的 Python 时事通讯!我试着定期分享有趣的代码片段、练习和测验。
关于 Python
让我们从定义 Python 到底是什么开始。Python 是一种计算机编程语言。或者,换句话说,用于指示计算机执行任务的词汇和语法规则集。它的原创者吉多·范·罗苏姆以英国广播公司电视节目“巨蟒剧团的飞行马戏团”命名因此,您会发现 Python 书籍、代码示例和文档有时会引用这个电视节目。
Python 被认为是容易学习的,它是围绕一组明确定义的原则(Python 的禅)设计的,这些原则鼓励 Python 核心开发人员开发一种明确且易于使用的语言。
Python 是用来做什么的?
人们在很多地方使用 Python。其丰富的基础库使其非常适合各种小助手脚本。但是它同样适用于大型系统。举例来说,YouTube 最初的创建者大部分都使用了 Python!据我所知,Dropbox 主要是用 Python 写的。而且你知道 Instagram 的整个后端和网站也是用 Python 写的吗?
您可以使用 Python 来自动化任务、执行计算、创建用户界面、创建网站后端、访问数据库、从互联网下载信息等。它是一种通用的语言,易于学习和编写,虽然对于初级程序员来说非常完美,但对于经验丰富的专业人员来说也同样有用和强大。
Python 在一个快速发展的专业领域非常受欢迎,这个领域叫做数据科学。许多数据科学家在日常工作中使用 Python。这些只是几个例子。如果你开始仔细观察,Python 是非常普遍的。
很多人说 Python 自带电池。这是一种有趣的方式来说明它包括一个全面的基础库。除此之外,您还可以找到由庞大的社区贡献的成千上万的外部软件包。您会发现支持您想要完成的几乎任何事情的基础库和包。
Python 的流行
很难衡量编程语言的受欢迎程度,并且有许多不同的顶级列表和排名系统。例如,自 2021 年 1 月以来,Python 在著名的流行编程语言排行榜上名列第二。由于其作为 web 开发语言的优势,Javascript 经常是第一名。这与您在浏览器语言方面几乎没有选择的事实有关。
Python 的流行是一个很大的优势。有大量的教程、书籍、 Python 课程、示例代码和帮助可用。Python 已经存在,所以如果你想学习 Python:这是一个安全的赌注!Python 的工作在薪酬等级中排名很高。这个免费的 Python 初学者教程将为你彻底学习 Python 和推进你的职业生涯提供一个良好的开端。
Python 的主要特性
那么是什么让 Python 成为如此受欢迎的编程语言呢?当你阅读这个 Python 初学者教程时,你会发现,但是我已经可以向你展示一些好处来吊你的胃口了!
读和写都很容易
Python 最显著的特性之一是为了可读性而强制使用缩进的方式。没有适当的缩进,你的代码甚至不能运行。我们需要将 Python 中的所有代码块缩进到相同的级别。这是一个工作中的例子。如果您还不理解代码,不要担心:
def bigger_than_five(x):
# The contents of a function are indented
if x > 5:
# This is another, even more indented block of code
print("X is bigger than five")
else:
# And one more!
print("x is 5 or smaller")
因为需要缩进,所以 Python 语言不需要花括号来像 Java、C 和 C#一样对代码块进行分组。这个事实本身就消除了代码中的许多混乱和符号。虽然很主观,但人们普遍认为它让 Python 看起来更舒服。
你不需要手动编译你的程序
许多语言在运行程序之前需要一个手动编译步骤,而其他语言是所谓的解释语言:它们可以由解释器直接运行。Python 介于这两个世界之间。
创建 Python 程序时,可以直接运行它,无需手动编译步骤。所有的解释型语言都是如此,这也是为什么大多数人会告诉你它是一种解释型语言。然而,Python 在内部将代码编译成低级代码,称为字节码。这段代码并不特定于某个系统架构(比如 Intel vs. ARM),但是它的读取和解析速度比纯文本文件要快。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
在某些情况下,这个字节码缓存在磁盘上以*结尾的文件中。pyc 扩展。但是当作为脚本语言使用时,Python 不会缓存字节码。对我们来说,这无关紧要。我们可以直接运行我们的代码,而不用担心字节码,因为 Python 会自动处理这一切。这有几个优点:
- 您可以在文本编辑器中编写代码并直接执行。不需要像编译和链接这样的额外步骤。
- 因为它是纯文本,你可以简单地打开一个程序并检查它的内容。相比之下,编译后的代码是不可读的。你将不得不查找源代码(如果有的话)。
- 它是平台无关的。只要平台有 Python 解释器,你的代码就能工作。编译后的代码通常绑定到特定的平台,如 Windows 和 Linux,以及特定的处理器架构,如 Intel 或 ARM(除非它被编译成中间字节码,如 Java 和。网)。
这些优点中的一些也可能是缺点。如前所述,解释语言不是高性能语言。此外,源代码易于阅读和修改的事实对于想要保护其版权的供应商来说并不是一个优势。
动态类型化
解释语言的另一个优点是它为动态类型打开了大门。那是什么意思?我将用一些简单的代码来演示它。
以下是 Java 中的一些变量声明:
String myName = "Erik";
int myAge = 37;
float mySalary = 1250.70;
在强类型语言中,你需要指定每个变量的确切类型,比如String
、int
和float
。当涉及到物体时,情况变得更糟。
现在我们来看看 Python 变量。在 Python 中,我们可以在没有类型的情况下做同样的事情:
my_name = "Erik"
my_age = 37
my_salary = 1250.70
正如你所看到的,Python 的变体看起来更干净、更舒服!
运行这段代码时,Python 动态地找出变量的类型。比方说,我想知道我的年收入,用我的工资乘以 12。我需要做以下事情:
my_income = my_salary * 12
Python 将查看my_salary
,发现它是一个浮点值,并执行数学运算。如果my_salary
是一个字符串,Python 也不会抱怨。它会检测一个字符串,然后创建一个新的,由该字符串的 12 个重复组成!然而,在这种情况下,Java 会因出错而失败。
动态类型有很多优点。总的来说,这样更容易快速上手。有些人会告诉你这更容易出错。当有类型错误时,像 Java 这样的强类型语言将无法编译。Python 可能会继续运行,但输出将是意外的。根据我的经验,这种事不常发生。此外,您将在测试期间很快发现问题,并在软件投入生产之前修复错误。
Python 确实支持类型化
不过,我并不是说打字是件坏事。从 Python 3.5 开始,Python 就支持类型提示。这是一个可选的特性,但是许多程序员接受它,因为它有几个优点,比如在你的 Python IDE 中更好的自动完成。我喜欢打字,因为它消除了猜测。显式键入是文档的一种形式,我在自己的日常工作中适当的时候使用它。
碎片帐集
Python 有变量的概念。变量允许你存储任何值,比如一个数字,一串文本,甚至更大的对象。
你声明的每个变量都会占用你计算机的内存空间。这可能会很快累积起来,尤其是当您创建长时间运行的程序时。所以你需要一种方法来清理你不再使用的变量。
在某些语言中,您需要显式地执行这种清理。这容易导致一种称为内存泄漏的错误。如果你犯了一个小错误,忘记清理,你的软件就会慢慢地耗尽可用内存。幸运的是,Python 的垃圾收集器会自动清理未使用的变量!
我在这里不打算深入细节,但是你可以放心,Python 会做得很好,绝不会不小心清理掉你仍然需要的变量。
Python 历史
我们已经稍微触及了这个主题,但是让我们探索一下 Python 为什么会出现。这一切开始于 1987 年 12 月一个寒冷多雾的夜晚,一位名叫吉多·范·罗苏姆的荷兰科学家在半夜醒来。他只是做了一个深刻的梦,虽然他当时并不知道,但这个梦最终会改变他的生活和许多其他人的生活。
所以他下了床,穿上了他的童装。在把一些木头扔进几乎窒息的壁炉后,他开始尽可能多地记下这个梦。一种新的编程语言诞生了:Python。
Python 的诞生
好吧,我有点失控了。上述故事的唯一真相是吉多·范·罗苏姆的名字和开始的日期。1987 年,Guido 在荷兰的一个国家数学和计算机科学研究所 CWI 工作,开发一个大型分布式操作系统。在那个项目中,他有一些自由去从事副业。凭借他多年前积累的知识和经验,他开始编写 Python 编程语言。
可扩张的
在 2003 年对比尔·凡纳斯的采访中,圭多提到了这种新语言最大的创新:
我认为我对 Python 的成功最具创新性的贡献是使它易于扩展。这也源于我对 ABC 的失望。ABC 是一个非常单一的设计。有一个语言设计团队,他们就是上帝。他们设计了每一个语言细节,没有办法再添加了。你可以写你自己的程序,但是你不能轻易地添加底层的东西。
Guido van Rossum
他认为应该能够以两种方式扩展这种语言:通过编写 Python 模块或完全用 c 编写模块。这很成功,因为他在 CWI 的同事、用户和 Guido 本人立即开始编写他们自己的扩展模块。扩展模块允许您做各种事情。只是目前存在的一小部分模块:
从一开始,Guido 就积极参与 Python 的开发,直到今天。短暂退休后,他又回来工作了。微软目前雇佣了他,他的主要工作是提高 Python 的速度。
时间表
下图显示了 Python 历史上最具定义性的版本的全球时间表:
Python 的历史
Python 2 对 Python 3
正如您在 Python 历史时间表中看到的,Python 2 和 3 已经并行开发和维护了很长一段时间。主要原因是 Python 3 代码并不完全向后兼容 Python 2 代码。这种不兼容性导致了长时间的采用率。许多人对版本 2 很满意,也看不出升级的理由。最重要的是,Python 3 最初比 Python 2 慢。随着 Python 3 不断改进和接受新特性,最终,它开始腾飞。
本指南主要关注 Python 3,因为它现在是默认且唯一受支持的版本。在现实世界中,你可能会遇到 Python 2 代码。我在章节从 Python 2 迁移到 3 中分享了一些从这些代码中迁移的技巧。如有疑问:如果你想学 Python,就去学 Python 3。
浏览免费 Python 教程
我尽我所能让浏览免费 Python 教程变得尽可能简单!首先,您可以使用菜单。此外,在每个页面的顶端和末端都有导航链接,引导您进入下一个主题或返回上一个主题。我也链接到相关网页来加深你的知识。
教程主题经过仔细排序,以便您可以从头开始,一步步学习。不过,随便逛逛吧!
你能帮我什么?
你,对,就是你,可以帮我完善一下这个 Python 初学者教程。你可以做几件事来帮忙。
1.如果你……请联系我
- 发现任何错误,
- 认为有些事情可以改进,
- 或者有些事情你不清楚。
2.捐赠或购买课程
我已经在这个网站上工作了大约三年,大部分业余时间都在这里全心全意地工作。我希望它显示出来,并且我真诚地希望你在这里学习 Python 有很多乐趣。
支持我工作的最好方式是购买课程,但我能理解不是每个人都有这个预算。如果你还想表达你的感激,你可以请我喝咖啡。到目前为止我得到的所有支持都是鼓励我继续写作和不断更新内容的东西!
3.禁用广告拦截器
如果你屏蔽广告,你不想捐赠或购买课程,我恳请你在这个免费的 Python 教程上禁用你的广告屏蔽器。你可能认为这不会有什么帮助,但请意识到,作家们正因为如此而努力工作。我只投放值得信赖的广告,这些广告通常也非常针对网站的主题。
4.跟我来
您可以关注我(@erikyan) 获取新内容更新。我也倾向于偶尔发布有趣的代码片段和测验。该网站还有一个专门的邮件列表;点击这里订阅我的简讯。它的容量很小,主要包含我写的新文章,有趣的阅读链接,以及我的高级课程的偶尔折扣!
if ready:
print("Let's learn Python programming!")
安装 Python:windows、Mac 和 Linux 的详细说明
在本章中,您将学习如何在 MacOS、Windows 和 Linux 上安装 Python。我们将查看每个平台的多种安装方法,并讨论我认为的最佳选择。
大多数时候,使用 python.org 网站上的官方安装程序是不明智的。相反,最好使用操作系统打包的版本。操作系统提供的版本的优势是你将获得自动更新。
目录
在 Windows 上安装 Python
在 Windows 上有三种方法可供选择。
使用微软商店
微软在微软商店中托管了 Python 3 的社区版本。这是在 Windows 上安装 Python 的推荐方式,因为它可以自动处理更新,也可以轻松卸载。
要使用这种方法:
- 打开微软商店,搜索 Python
- 选择最新版本并安装它
使用官方安装程序
你也可以从官方的 Python 下载网站下载 Python 安装程序。这种方法不会给你自动更新,只有当你没有访问微软商店的权限时,我才推荐它。当您使用此安装程序时,请确保选中“将 Python 添加到路径”复选框:
确保选中复选框“将 Python 添加到路径
WSL 内部
如果您熟悉 Linux 的 Windows 子系统,您也可以考虑这个选项。这是我自己用的,我真的很喜欢它。它为我提供了 Windows 必须提供的优势(主要是强大的硬件支持),同时仍然喜欢 Linux,在我看来,Linux 是 Python 开发的最佳平台。
要在 WSL 中安装,您首先需要安装 WSL 本身。如果可以,请使用 WSL2。好多了。之后,只需按照下面的 Linux 安装说明!
在 MacOS 上安装
在 Catalina 之前的大多数 MacOS 版本中,已经包含了 Python 的发行版。不幸的是,它几乎肯定是一个旧版本,Python 2.7。幸运的是,有两种方法可以轻松地在 Mac 上安装 Python 3。
公司自产自用
首先也是最重要的一点,我建议调查一下家酿。它允许你轻松地安装几乎任何东西。额外的好处:
- 自制软件包通常是最新的。
- 以后升级到新版本也很容易。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
然而,要使用自制软件,你需要熟悉使用命令行 shell 。如果这对你来说是全新的,我现在推荐下一个选择:使用官方安装程序。
如果您选择安装 Homebrew,那么在 MacOS 上安装 Python 非常简单:
$ brew install python
官方安装人员
或者,你可以从 Python 下载网站下载一个安装程序。这很简单,就像安装任何其他 MacOS 软件程序一样。这种方法的缺点是你不会得到自动更新。就像 Windows 一样,您应该确保将 Python 添加到您的系统路径中。
在 Linux 上安装 Python
在 Linux 上安装 Python 有几种方法,如果你需要安装的话!
先检查安装了什么
大多数 Linux 发行版都包含 Python。许多将包括 Python 2 和 Python 3。
如果你在命令行输入python --version
,你会看到版本号。大概是 2.7 版本:
$ python --version
Python 2.7.16
你不想要 Python 2,但不幸的是,一些操作系统仍然附带它。
现在试试python3 --version
。如果你得到一个“找不到命令”,你需要安装 Python 3。如果您的输出如下所示,那么您很幸运:
$ python3 --version
Python 3.8.5
使用包管理器
根据您运行的 Linux 发行版,您可以使用默认的包管理器来安装 Python:Yum、APT 等等。您需要找出哪个包管理器用于您的特定 Linux 发行版,以及如何使用它。
如果你使用的是 Ubuntu、Linux Mint 或者 Debian,你可以使用 apt 来安装:
$ apt install python3 python-is-python3
这也安装了一个名为python-is-python3
的包,使命令python
指向python3
。相信我,我说这会让你以后省去很多麻烦。
公司自产自用
Linux 的另一个有趣的选择是使用自制。没错,MAC 版的包管理器也能在 Linux 上工作。
使用自制软件的主要优势:
- 您将获得最新版本的 Python,而不是您的操作系统附带的版本
- 您不需要 root 用户访问您的系统。所有随家酿一起安装的软件都安装在您的主目录中
我发现自己在 Linux 下工作时越来越多地使用自制软件——试试吧!
浏览器中的 Python
如果你不想安装 Python,或者因为某种原因无法安装,我也可以提供一个替代方案:你可以直接从浏览器使用Python;无需安装!
了解更多信息
所有 Python 安装方法还会安装一个名为 pip 的工具。Pip 用于安装默认 Python 安装中没有的 Python 包。在本教程的后面,我们将广泛地研究如何使用 Pip 和一些提供更多功能的替代品,如 Pipenv 。
在线 Python 解释器:在浏览器中运行 Python
原文:https://python.land/installing-python/python-in-the-browser
就像现在的任何事情一样,你也可以在浏览器中运行 Python。在这个页面上,你可以使用我的在线 Python 解释器。它允许您输入 Python 代码,运行它,并查看结果。所有这些都不需要安装 Python 并自己启动一个 Python REPL 。你可以使用这个在线 Python 解释器来学习 Python 初学者教程。
目录
在浏览器中运行 Python
简言之,如果您:
- (还)不想安装 Python,
- 想要快速开始,
- 首先想试试 Python 是否适合你,在安装之前,
你很幸运!不需要安装任何东西就可以马上上手。
请注意:
- 这是一个测试产品
- 这个在线 Python 解释器故意限制了资源(详见下文)
- 这不是 REPL,也不是互动的。不能用
input()
。更多信息见下文。
说了这么多,实验学习应该够了。既然你是第一批用户之一,我很想听听你的评论和问题(或者咆哮)。
在浏览器中使用 Python 对于学习和探索来说是很好的。然而,如果你开始认真对待 Python 开发,你会发现,在某些时候,你会想要一个合适的 IDE(集成开发环境)和 Python 的本地安装。如果可以的话我推荐你在 PC 上安装 Python 它将提供更好的体验,并且允许你离线工作。前往我们的 Python 安装指南,学习如何在你的电脑上安装 Python。
在线 Python 解释器
https://crumb.sh/embed/F65R4QZRHmo
这是一个在线 Python 解释器。试试看!
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
不是 REPL
请注意,这不是一个 REPL 或交互式终端。在这个游戏中,您创建一个真实的 Python 文件 (playground.py),您在 Python Land 的真实计算机上执行该文件。所以如果你想看到输出,你需要使用 print 语句来打印它。这里有几个例子可以帮助你开始:
print(1 + 3)
# will print 4
print('Hello world')
# will print 'Hello world'
a = 6 + 9
print(a)
# will print 15
资源限制
在我们的教程中使用这个在线 Python 解释器时,您不应该遇到资源限制。如果有,可能是由于以下限制之一造成的:
- 输入的大小,
- 程序的运行时间,
- 内存使用情况,
- 流程分叉的数量
- 网络不起作用
使用在线 Python 解释器中的模块
你可以用这个在线 Python 解释器导入 Python 模块。包含在 Python 标准库中的任何东西都应该是可用的,比如 os 模块、线程、多重处理、 JSON 模块、CSV 等等。
但是,您不能安装需要您使用 pip install 的外部软件包。我确实安装了一些最常用的模块,尤其是本教程中提到的那些。您可能会看到以下软件包:
- Numpy,scipy,交响乐
- 皮亚米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅米雅
- 熊猫
- jmespath
- python date util
- pytest(测试)
疑问?
如果您有任何问题或意见,请在这里联系我。
如何在 Windows、Mac、Linux 上打开 Python
现在,您将学习如何在 Linux、Windows 和 MacOS 上打开 Python。首先,你要知道使用 Python 有两种方式:
- 启动一个交互式 shell,也称为 REPL,是读取-评估-打印-循环的缩写。
- 使用启动存储在一个或多个文件中的 Python 程序。py 扩展名。
在本教程中,我们将从交互式 shell 开始,因为它是探索语言的理想选择。但是在某种程度上,使用 REPL 不再有用,你必须开始创建 Python 文件。
如果您在本地机器上安装了 Python ,您首先需要启动一个终端或命令提示符,然后才能启动 Python 交互式 shell。在所有平台上,您应该能够用命令python3
(或者有时只是python
)启动 Python 3。只是确保你运行的是 Python 3,而不是 2 ,因为有些系统可以安装两个版本。
目录
如何在 Windows 上打开 Python
在 Windows 上,可以从终端启动 Python。例如,要启动 PowerShell,只需点击 Windows 键并开始键入“PowerShell”。如果您没有 PowerShell,可以使用“命令提示符”程序。在 shell 或命令提示符下,输入以下命令之一(按给定顺序尝试):
- 巴拉圭
python3
- 大蟒
第一个命令(py
)是一个包装器脚本,允许您启动最新版本的 Python。如果成功了,那太好了。只要记住我会在教程中经常提到python
或python3
。在这些情况下,您需要使用py
。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
Python 从 Windows PowerShell 开始
如何在 Mac 上打开 Python
在 MacOS 上,搜索一个名为 terminal 的程序。你可以通过按下命令键(⌘) + 空格键来完成。这将打开 Spotlight 搜索栏,您可以在其中输入单词“终端”。
启动终端后,输入python3
打开 Python REPL。如果不起作用,试着输入python
(不带 3)。
如何在 Linux 上打开 Python
在 Linux 上,首先需要启动一个终端。这通常可以通过快捷键ctrl
+alt
+T
+来实现。或者,您可以在开始菜单中搜索终端程序。每个发行版的名称和位置都不一样。一旦运行了终端,输入python3
启动 Python REPL。如果不起作用,试试python
(不带 3)。
Python 在 Linux 终端上运行
如何关闭 REPL
既然你知道了如何打开 REPL,那么正确地关闭它也是很好的。如果您简单地关闭终端窗口,您也可以关闭 REPL。然而,这不会是一个干净的出口,你的终端通常也会警告你。那么,如何干净利落地退出 REPL 呢?
在每个操作系统上起作用的是下面的命令:exit()
如果你在 Linux 或 MacOS 上,你也可以使用一个小技巧。按下 control + d ,REPL 将立即退出。Control + d 将“文件结束”字符发送到终端,REPL 将此解释为退出 Python 的请求。在 Windows 上,你可以通过按下 control + z 然后按 enter 来做类似的事情。
Python 简介
现在,您已经准备好开始学习 Python 的基础知识了。确保你的系统上安装了 Python,或者使用 Python 的在线版本。
即使您以前没有编程经验,您也应该能够从这个 Python 介绍中获得良好的开端。我们会慢慢来,但在您了解之前,您将对重要主题有一个坚实的基础知识:
这篇 Python 介绍不会深入这些主题的本质细节,而是触及表面,向您介绍基本概念。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
在本教程的后面,我们将深入探讨一些特定的主题:
- 例如,在我们的 Python 函数深入研究中,我们将学习关于函数的更高级的东西
- 我们还涵盖了虚拟环境(简称 venv)
- 使用 Pip 安装进行软件包安装
- 我们的部署部分涵盖了像容器化您的 Python 项目这样的事情
- 我们关于 Python 数据处理的章节将进入用 Python 处理 JSON】和解析 YAML
- 我们提供了使用 Python 进行数据科学的学习途径
如果你遇到困难,有任何问题,请不要犹豫联系我。有时候写下问题就足以自己回答了,但是我很乐意尽我所能提供帮助。我们走吧!点击这里开始了解 REPL 。
蟒蛇 REPL
我们将从 Python REPL 开始我们的 Python 学习之旅。这是一个交互式 shell,允许您输入 Python 命令并直接查看结果。这是修补和学习的好方法!我们将使用 REPL 作为计算器,并探索 Python 的操作符。
目录
探索 Python REPL
打开终端,启动 Python 交互式 shell】,您会看到一个由三个箭头(>>>
)组成的命令提示符。要绝对明确的是,你不要输入三个箭头,只输入后面的内容。
现在输入数字 10:
>>> 10
10
发生了什么事?记住我们是在 REPL,这是读-评估-打印-循环的缩写:
- ead: Python 读取 10
- 评估:Python 评估这个输入并决定它是一个数字
- print:打印出被评估的内容
- oop:为下一次输入做好了准备
让我们给它一些更有挑战性的东西:
>>> 10 + 10
20
这一次,Python 识别了两个数字和一个所谓的运算符,即加号,并将其计算为 20。是的,Python 可以用作计算器。
算术运算符
好的,Python 非常擅长数学。事实上,它可以很容易地取代你的计算器。一个小小的坦白:我一直使用 Python REPL 作为计算器!
我们已经看到了如何使用+运算符。就像普通的数学一样。让我们来看看你可以使用的其他算术运算符。有的会看着眼熟;其他的可能看起来有点奇怪。你会很快习惯的,大多数操作符在其他编程语言中都是一样的,所以学好它们是值得的。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
在 REPL 继续玩这个:
操作员 | 名字 | 例子 |
---|---|---|
+ | 添加 | 2 + 2 |
– | 减法 | 3 – 1 |
* | 增加 | 5 * 3 |
/ | 分开 | 5 / 2 |
你们大多数人都知道的基本操作符
如果你懂数学,你可能也想试试:
操作员 | 名字 | 例子 |
---|---|---|
% | 系数 | 5 % 2 |
// | 楼层划分 | 9 // 2 |
** | 指数的 | 2 ** 4 |
一些更高级的运算符
运算符优先级
运算符优先级,即 Python 处理运算符和数字的顺序,与数学中的相同。例如,乘法和除法先于加法和减法。如果你对操作符的优先级有疑问,你可以使用括号。或者,你可以在 REPL 试试,看看会发生什么。
让我们试一些例子:
>>> 2 + 3 * 3
11
>>> (2 + 3) * 3
15
>>> 1 + 2 ** 2
5
>>> 2 / 2 * 8
8.0
使用下划线获取之前的结果
既然我们变得越来越先进,我想向你展示一个可以节省你时间的小技巧。
您可以使用下划线运算符在 Python REPL 中获得最后一个表达式的结果,例如,在 Python REPL 中如下所示:
>>> 3 * 3
9
>>> _ + 3
12
使用历史记录
你注意到 Python 也保存了命令的历史吗?通过按向上和向下箭头,可以在以前的命令之间来回切换。Python 将这个历史保存在一个文件中(在~/.python_history
中的大多数操作系统上),所以它甚至在会话之间保持不变。
存储结果
太棒了,我们已经可以用 Python 做一些数学计算了,我们甚至可以使用以前的结果。但是如果我们能够存储我们的计算结果,那就更棒了。为此,Python 允许我们定义变量,这是本教程的下一个主题。
Python 变量:存储信息以备后用
在上一节中,我们通过利用REPL将 Python 用作计算器。存储这些计算的结果不是很好吗?为此,我们使用 Python 变量。在本文中,您将学习什么是变量以及如何声明变量。我们还将了解创建变量的规则和最佳实践。
目录
什么是 Python 变量?
让我们从更正式地定义什么是变量开始:
Variable
A variable is used to store information that can be referenced later on.
所以变量就是我们用来命名结果的东西,比如说,我们做的一个计算。或者,换句话说,我们可以将计算结果赋给一个变量。我们可以创造无限量的变量;我们只需要确保给它们起一个唯一的名字。
声明一个 Python 变量
我们将在 REPL 中创建一个名为result
的 Python 变量(正式名称为声明变量)。但是在我们这样做之前,我们将尝试看看 Python 是否已经知道结果是什么:
>>> result
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'result' is not defined
这是 Python 让您了解错误的方式。忽略前两行,关注实际的错误。Python 报道:name 'result' is not defined
。如果你知道去哪里找,Python 错误往往会很有帮助。所以我想给你看一个。最终,您将需要自己编写代码,不幸的是,出错是这项工作的一大部分。能够破译错误将是一个有用的技能!
现在让我们声明变量名result
并再次尝试:
>>> result = 3 * 5
>>> result
15
这是逐步发生的事情:
- Python 看到一个所谓的赋值:我们把 3 * 5 的结果赋给一个叫做
result
的变量。赋值是用“=”字符完成的,这个字符方便地称为“is”。所以我们只是告诉 Python:我声明result
是表达式 3 * 5 的结果。 - 接下来,我们输入
result
。 - Python 并不认为这是一个命令,所以它尝试看看是否有一个同名的变量。有,我们给它分配了 15 分。因此,这一行的结果是数字 15,显示在屏幕上。
变量命名
在本例中,我们选择了通用名称result
,但是您可以选择任何您认为合适的名称。作为一般规则,总是选择一个最能描述其内容的变量名。这种做法使您的代码可读性更强,更容易理解。例如,如果我们在这里计算购物车的总价,一个好的名字应该是shopping_cart_total
。
不要吝啬变量名中的字符数。最好使用像shopping_cart_total
这样清晰易读的名字,而不是像sct
这样的缩写。您很快就会了解到,一个好的代码编辑器会自动完成诸如变量名之类的事情,所以您不必完全键入它们,如果这是您所担心的。
在表达式中使用变量
Python 变量是该语言的重要组成部分,因为您也可以在其他表达式中使用它们:
>>> 4 * result
60
>>> result - result
0
>>> _
我在没有解释表达式是什么的情况下使用了表达式这个词,让我们来修正一下:
Expression
An expression is anything that Python can evaluate to a value
这些都是有效的表达式,因为 Python 可以对它们求值:
# Evaluates to 9:
3 * 3
# Evaluates to 19 if the result is 15:
result + 4
# Simply evaluates to 2:
2
你在表情上面看到的,叫做注释。任何跟在散列符号(#)后面的东西都被视为一个 Python 注释,并被 Python 解释器忽略。
变量类型
顺便说一下,赋给变量的值不一定是数字。Python 除了数字还有几个数据类型,它们都可以是表达式的结果(值)。一种这样的数据类型是 Python 字符串,这也是本教程下一篇文章的主题!但是也有其他的,像布尔和元组。
Python 有一个名为type()
的内置函数,我们可以用它来确定变量或表达式类型。以下是一些例子:
>>> my_number = 4
>>> type(my_number)
<class 'int'>
>>> my_string = 'Hello'
>>> type(my_string)
<class 'str'>
首先,我们创建了一个值为 4 的变量。当被问到时,Python 告诉我们这个变量属于 int 类,这是整数的缩写。如果你愿意的话,可以通过链接获得关于整数的更全面的解释。
接下来,我们创建一个字符串。当被问到时,Python 确实告诉我们它属于 str 类,string 的缩写。差不多是时候学习更多关于 Python 字符串的知识了,但是我想先讨论最后一个话题。
有效的 Python 变量名
变量名中不允许使用某些字符;我们需要遵守几条规则。让我们从可以出现在变量名中的有效字符的完整列表开始:
- 小写和大写字母:a-z 和 A-Z
- 数字:0-9
- 下划线:_
此外,还有以下两条规则:
- 变量名必须以字母或下划线字符开头,不能以数字开头。
- 名称区分大小写
下面是一些有效的变量名:
- name_1,
- 姓名 _2,
- _ 数据库 _ 连接
这些是无效名称:
- 1 托曼尼(不要以数字开头)
- 我的号码(-不允许)
- 我的号码(不允许有空格)
由于区分大小写,这些变量不同于:
- 购物车总计
- 购物车总计
关于骆驼案的一个注记
这是为那些来自另一种编程语言的人准备的,比如 C#或 Java。许多编程语言利用 camel-case 来命名变量。使用 camel-case,我们使用大写字母来更清楚地分隔单词。
在 Python 中,我们可以使用 camel-case,但是我们更喜欢使用下划线作为变量名,而 camel-case 是类名的标准。所以我们 Pythonistas 不用shoppingCartTotal
,而是用shopping_cart_total
。然而,我们确实对类名使用了 camel-case,您很快就会了解到。
继续学习
我们见过数字,我们知道如何在变量中存储数字。但是文字呢?进入下一部分,学习关于 Python 字符串的所有知识。
Python 字符串:使用文本
所以我们看到了数字,但是文字呢?这个页面是关于 Python 字符串的,它是在 Python 中存储和使用文本的首选 Python 数据类型。所以,在 Python 中,一段文本叫做字符串,你可以对字符串进行各种操作。不过还是先从基础做起吧!
目录
什么是 Python 字符串?
以下是字符串的正式定义:
String
A string in Python is a sequence of characters
更简单地说,字符串就是一段文本。字符串不仅仅是 Python 的事情。这是计算机科学领域中一个众所周知的术语,在大多数其他语言中也有同样的意思。现在我们知道了什么是字符串,我们将看看如何创建一个字符串。
如何创建 Python 字符串
Python 字符串需要引号才能被识别,如下所示:
>>> 'Hello, World'
'Hello, World'
因为有引号,Python 认为这是一个字符序列,而不是一个命令、数字或变量。
就像数字一样,我们之前学过的一些运算符也适用于 Python 字符串。用下面的表达试试看:
>>> 'a' + 'b'
'ab'
>>> 'ab' * 4
'abababab'
>>> 'a' - 'b'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'str' and 'str'
这是上面代码中发生的情况:
- 加号运算符将两个 Python 字符串粘合在一起。
- 乘法运算符将我们的 Python 字符串重复给定的次数。
- 减号运算符不适用于 Python 字符串,会产生错误。如果你想删除一个字符串的一部分,还有其他的方法,稍后你会学到。
单引号还是双引号?
我们使用了单引号,但是 Python 也接受字符串两边的双引号:
>>> "a" + "b"
'ab'
请注意,这不是两个相邻的单引号。该字符通常位于键盘上的回车键旁边(美国键盘布局)或 2 键上(英国键盘布局)。其他国家可能会有所不同。你需要同时按 shift 和这个键才能得到双引号。
从它的回答可以看出,Python 本身似乎更喜欢单引号。它看起来更清晰,Python 尽量做到清晰易读。那么为什么两者都支持呢?因为它允许你使用包含引号的字符串。
在下面的第一个例子中,我们使用双引号。因此,单词中的单引号没有问题,它是。然而,在第二个例子中,我们尝试使用单引号。Python 看到单词中的引号是,并认为这是字符串的结尾!下面的字母“s”导致语法错误。语法错误是命令或指令中错误放置的字符或字符串,会导致执行失败。
换句话说,Python 不理解该处的 s,因为它认为字符串已经结束,并因错误而失败:
>>> mystring = "It's a string, with a single quote!"
>>> mystring = 'It's a string, with a single quote!'
File "<stdin>", line 1
mystring = 'It's a string, with a single quote!'
^
SyntaxError: invalid syntax
Python 用^
符号指出遇到错误的确切位置。Python 错误往往非常有帮助,所以仔细观察它们,您通常能够指出哪里出错了。
如果你注意到了,这个网站上的语法高亮器甚至会因为无效的语法而变得混乱!
逃避
这个问题其实还有另外一个办法,叫做逃避。您可以用反斜杠对特殊字符进行转义,如引号:
>>> mystring = 'It\'s an escaped quote!'
>>> mystring
"It's an escaped quote!"
也可以对双引号字符串中的双引号进行转义:
>>> mystring = "I'm a so-called \"script kiddie\""
>>> mystring
'I\'m a so-called "script kiddie"'
这里,您再次看到 Python 对单引号字符串的偏好。尽管我们使用了双引号,Python 还是使用单引号将字符串返回给我们。它仍然是同一个字符串,只是表示方式不同。一旦你开始打印字符串到屏幕上,你就会看到证据。
那么应该用哪一个呢?很简单:总是选择需要最少转义的选项,因为这些转义会降低 Python 字符串的可读性。
多行字符串
Python 也有使用三重引号创建多行字符串的语法。我指的是三个双引号或三个单引号;两者都可以,但我将用双引号来证明:
>>> """This is line 1,
... this is line 2,
... this is line 3."""
'This is line 1,\nthis is line 2,\nthis is line 3.'
如您所见,Python 将字符串作为常规的单行字符串回显。在这个字符串中,您可能已经注意到了\n
字符:这是 Python 和许多其他编程语言对特殊字符(如换行符)进行转义的方式。下表列出了几个你会遇到的最常见的转义序列:
转义序列 | 什么是做 |
---|---|
\n | 换行符(换行符是用 return 键生成的)。前进到下一个 |
\r | 回车:将您带回到行的开始,而不前进到下一行 |
\t | 制表符 |
\ | 斜杠字符本身:因为它被用作转义序列的开始,所以我们也需要转义这个字符。但是,如果您忘记对它进行转义,Python 是相当宽容的。 |
常见的转义形式的特殊字符
有趣的事实:像 Linux 这样基于 Unix 的操作系统使用\n
作为新行,回车被自动包含,而 Windows 使用\r\n
。这已经并将成为许多 bug 的原因。所以如果你在 Windows 上,你会看到很多\r\n
。
使用三重引号进行转义
三重引号的好处是你可以在其中使用单引号和双引号。因此,您可以使用三重引号干净地创建既包含单引号又包含双引号的字符串,而无需进行转义:
>>> line = """He said: "Hello, I've got a question" from the audience"""
字符串操作
字符串附带了几个您可以执行的方便的内置操作。我在这里只给你看几个,因为我不想过多地转移你对教程的注意力。
在 REPL,有时可以使用自动完成功能。这取决于你使用的安装程序和操作系统。我不知道确切的原因,但你最好的办法就是试一试!
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
在下一个代码片段中,我们创建了一个字符串mystring
,在下一行中,我们键入它的名称,然后按两次选项卡键:
>>> mystring = "Hello world"
>>> mystring.
mystring.capitalize( mystring.find( mystring.isdecimal( mystring.istitle( mystring.partition( mystring.rstrip( mystring.translate(
mystring.casefold( mystring.format( mystring.isdigit( mystring.isupper( mystring.replace( mystring.split( mystring.upper(
mystring.center( mystring.format_map( mystring.isidentifier( mystring.join( mystring.rfind( mystring.splitlines( mystring.zfill(
mystring.count( mystring.index( mystring.islower( mystring.ljust( mystring.rindex( mystring.startswith(
mystring.encode( mystring.isalnum( mystring.isnumeric( mystring.lower( mystring.rjust( mystring.strip(
mystring.endswith( mystring.isalpha( mystring.isprintable( mystring.lstrip( mystring.rpartition( mystring.swapcase(
mystring.expandtabs( mystring.isascii( mystring.isspace( mystring.maketrans( mystring.rsplit( mystring.title(
如果一切顺利,您应该得到一个可以在字符串上执行的操作的大列表。如果不行,你可以在官方 Python 手册中查看这些操作的列表(每个都有解释)。你可以自己尝试一下:
>>> mystring.lower()
'hello world'
>>> mystring.upper()
'HELLO WORLD'
对这些操作的解释可以在官方 Python 文档中找到,但是我们也将在这里讨论一些。
获取字符串长度
一个常见的操作是获取字符串长度。与上面的操作不同,这可以通过 Python 的len()
函数来完成,如下所示:
>>> len("I wonder how long this string will be...")
40
>>> len(mystring)
11
事实上,len()
函数可以用在 Python 中的许多对象上,稍后您将会了解到。如果函数对你来说是新的,你很幸运,因为我们的下一页将解释 Python 中的函数是什么,以及你如何自己创建一个函数。
拆开一根绳子
另一个常见的操作是拆分字符串。为此,我们可以使用内置操作之一,方便地称为 split。让我们简单地从两个单词之间的空格字符开始:
'Hello world'.split(' ')
['Hello', 'world']
拆分操作有一个参数:要拆分的字符序列。输出是一个包含所有独立单词的 Python 列表。
按空白分割
一个常见的用例是分割空白符。问题是空白可以是很多东西。你可能已经知道的三个常见的例子是:
- 空格字符
- 制表符
- newlines
但是还有很多,更复杂的是,空白不仅仅指这些字符中的一个,也可以是它们的整个序列。例如,三个连续的空格和一个制表符形成一片空白。
正是因为这是程序员中如此常见的操作,并且因为很难完美地完成它,Python 为它提供了一个方便的快捷方式。调用不带任何参数的 split 操作会在空格处拆分字符串,如下所示:
>>> 'Hello \t\n there,\t\t\t stranger.'.split()
['Hello', 'there,', 'stranger.']
正如您所看到的,无论空白字符是什么以及有多少,Python 仍然可以为我们将这个字符串拆分成单独的单词。
替换字符串的一部分
让我们再来看一个内置的字符串操作:replace 函数。它用于替换一个或多个字符或字符序列:
>>> 'Hello world'.replace('H', 'h')
'hello world'
>>> 'Hello world'.replace('l', '_')
'He__o wor_d
>>> 'Hello world'.replace('world', 'readers')
'Hello readers'
反转字符串
一个常见的赋值是反转一个 Python 字符串。不过,没有相反的操作,你可能已经注意到了,当你用字符串研究像lower() and upper() that comes
这样的操作列表时。这并不完全是初学者的东西,所以如果你按顺序阅读教程,现在可以跳过这一步。
我们可以把一个字符串当作一个列表来高效地反转一个字符串。列表将在本教程稍后介绍(参见 for 循环)。事实上,你可以把一个字符串看作一系列字符。更重要的是,你可以这样对待它。像mystring[2]
这样的列表索引操作就像它们在列表上工作一样:
>>> mystring = 'Hello world'
>>> mystring[2]
'l'
>>> mystring[0]
'H'
注意,在 Python 中,像在所有计算机语言中一样,我们从 0 开始计数。
切片操作符的工作方式与列表中的完全相同。切片的细节可以在 Python 列表页面找到,这里不再赘述。如果您来自其他语言,您可能会将其与 Java 中的substring()
操作进行比较,后者允许您检索字符串的特定部分。
Python 中的切片使用切片操作符,看起来是这样的:mystring[start:stop:step_size]
。我们使用切片的关键特征是步长。我们通过给切片操作符一个-1 的负步长,从末尾到开头遍历字符串。通过将开始和结束位置留空,Python 假设我们想要对整个字符串进行切片。
因此,我们可以使用切片来反转 Python 字符串,如下所示:
>>> mystring = 'Hello world'
>>> mystring[::-1]
'dlrow olleH'
切片在我关于列表的文章中有更详细的解释。
链接呼叫
到目前为止,我们使用的是单一操作。但是,我们也可以将这些操作链接在一起:
>>> 'Hello world'.replace('world', 'student').upper()
'HELLO STUDENT'
这里发生了什么事?第一个操作(替换)产生一个字符串。这个字符串和所有字符串一样,再次为我们提供了相同的操作。所以我们可以直接在结果上调用下一个操作(upper)。这是一个方便的快捷方式,您将在其他代码中经常使用和遇到。
您也可以通过首先将结果赋给一个变量来显式地实现这一点:
>>> student_greeting = 'Hello world'.replace('world', 'student')
>>> student_greeting.upper()
'HELLO STUDENT'
用 f 字符串格式化 Python 字符串
一个常见的模式是需要将一些文本字符串合并在一起,或者在字符串中使用一个变量。有几种方法可以做到这一点,但最现代的方法是使用 f 字符串,格式化字符串的缩写。
在深入细节之前,我们先来看一个例子:
>>> my_age = 40
>>> f'My age is {my_age}'
My age is 40
f 字符串看起来像一个添加了 f 前缀的普通字符串。这个 f 告诉 Python 扫描字符串中的花括号。在这些花括号中,我们可以放入任何我们想要的 Python 表达式。在上面的例子中,我们只是包含了变量 my_age。f-string 提供了一种优雅的方式来将表达式的结果包含在字符串中。
这里还有几个例子,你也可以在 REPL 亲自尝试一下:
>>> f'3 + 4 = {3+4}'
'3 + 4 = 7'
>>> my_age = 40
>>> f'My age is, unfortunately, not {my_age-8}'
'My age is, unfortunately, not 32'
快速打印变量
f-string 允许我们快速打印变量的名称和内容。这在调试代码时非常有用。下面的代码片段展示了这一点:
https://crumb.sh/embed/suDyy7G5k9Y
了解更多信息
我在这里只是触及一些基本问题。如果你是按照教程从头到尾,你可以继续下一个主题,因为你现在知道的已经够多了。如果您想了解更多关于 f 弦的信息,请尝试以下资源:
使用 Python 的 print()函数将数据输出到屏幕
在我们继续之前,我需要告诉你 Python print
函数。到目前为止,我们一直在使用 REPL ,REPL 会自动将任何评估结果打印到屏幕上。但是常规的 Python 程序不会这样做。为此,Python 为我们提供了打印功能。
目录
什么是函数?
我们还没有谈到函数,但是我们很快会深入讨论函数。现在,知道函数是可以调用的就足够了。它会为我们做一些特定的事情,名字往往暗示了它的作用。函数通常接受参数,它们可以用这些参数来完成特定的工作。
Python 打印功能
Python print()
函数可以将文本打印到我们的屏幕上。我们可以输入一个或多个参数,这些参数将打印在屏幕上。让我们尝试一些打印电话。下面的代码示例是交互式的,这意味着您可以编辑和运行它。阅读代码,按下运行按钮,并检查结果:
https://crumb . sh/embed/34ta 26 nf 4 aj
一些你可能已经注意到的观察结果:
- Python 的 print 通过输入其名称后跟括号来调用。
- 可选参数列在括号中,用逗号分隔。
- 您可以打印数字和字符串。事实上,Python 中的大多数数据类型都可以被打印出来。
- 您可以混合参数类型,如数字和字符串。
- 在其最基本的形式,我们可以调用打印没有任何参数。在这种情况下,它打印一个空的新行。
- 函数将把特殊字符转换成有意义的东西。例如,换行符
\n
变成实际的换行符,制表符\t
将被转换成实际的制表符。 - print()不会像 REPL 那样对字符串进行转义,因为我们不想向软件用户显示转义的字符串。这只是 REPL 为我们程序员提供的便利。
您这么早就开始学习 print,因为您会在几乎所有 Python 程序和 web 上的示例中遇到它。另一个原因是交互式代码示例只能通过使用 print 来显示数据,就像上面的例子一样。现在你知道了印刷,我可以包括更多的这些互动的例子,这是一个伟大的学习援助!
Python 布尔和条件编程:如果..其他
原文:https://python . land/introduction-to-python/python-boolean-and-operators
除了数字和字符串,Python 还有其他几种类型的数据。其中之一是布尔数据类型。布尔值非常简单:它们要么为真,要么为假。布尔与布尔运算符相结合,使得创建条件程序成为可能:基于某些条件决定做不同事情的程序。
布尔数据类型是以乔治·布尔的名字命名的,他在 19 世纪中期定义了一个逻辑代数系统。
目录
什么是布尔?
让我们从一个定义开始:
Boolean
A boolean is the simplest data type; it’s either True
or False
.
在计算机科学中,布尔被大量使用。这与计算机内部的工作方式有关。计算机内部的许多操作可以归结为一个简单的“对或错”值得注意的是,在 Python 中,布尔值以大写字母开始:True
或False
。这与大多数其他编程语言形成对比,在其他编程语言中,小写是标准。
在 Python 中,我们结合使用布尔和条件语句来控制程序的流程:
>>> door_is_locked = True
>>> if door_is_locked:
... print("Mum, open the door!")
...
Mum, open the door!
>>>_
下面是相同代码的交互式版本,您可以进行试验:
https://crumb . sh/embed/tfax 2y xobtk
Python if 语句
首先,我们定义一个名为door_is_locked
的变量,并将其设置为True
。接下来,您将找到一个 if 语句。这就是所谓的条件语句。它后面是一个表达式,可以计算为True
或False
。如果表达式的计算结果为True
,则执行后面的代码块。如果评估为False
,则跳过。继续将door_is_locked
改为False
,看看会发生什么。
if 后面可以跟一个可选的 else 块。仅当表达式计算结果为False
时,才执行该块。这样,您可以为这两个选项运行代码。让我们试试这个:
>>> door_is_locked = False
>>> if door_is_locked:
... print("Mum, open the door!")
... else:
... print("Let's go inside")
...
Let's go inside
>>>_
感谢我们的 else 块,如果door_is_locked
是False
,我们现在可以打印一个替代文本。作为练习,尝试修改上面的交互式代码示例以获得相同的结果。
Python 运算符
使用条件的能力是计算机运行的动力;它们使你的软件变得智能,并允许它根据外部输入改变自己的行为。到目前为止,我们已经直接使用了True
,但是更多的表达式评估为True
或False
。这些表达式通常包含一个所谓的运算符。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
有多种类型的操作符,现在,我们只看这些:
- 比较运算符:它们比较两个值
- 逻辑运算符
比较运算符
我们先来看看比较运算符。你可以在 REPL 和它们玩耍:
>>> 2 > 1
True
>>> 2 < 1
False
>>> 2 < 3 < 4 < 5 < 6
True
>>> 2 < 3 > 2
True
>>> 3 <= 3
True
>>> 3 >= 2
True
>>> 2 == 2
True
>>> 4 != 5
True
>>> 'a' == 'a'
True
>>> 'a' > 'b'
False
这就是所有比较运算符的名称:
操作员 | 意义 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
== | 是相等的 |
!= | 不相等 |
Python 的布尔运算符
从示例中可以看出,这些操作符也适用于字符串。字符串按照字母表的顺序进行比较,增加了以下规则:
- 大写字母比小写字母“小”,例如:“M”
- 数字小于字母:“1”
您可能想知道这些规则背后的逻辑是什么。在内部,每个字符在一个表格中都有一个数字。该表中的位置决定了顺序。就这么简单。如果你感兴趣的话,可以在维基百科上查看 Unicode。
逻辑运算符
接下来:逻辑运算符。这些操作符只对布尔值起作用,用于实现逻辑。下表列出并描述了它们:
| 操作员 | 什么是做 | 例子 |
| 和 | 如果两种说法都是真的 | 真与假假
假与假假
真与真真 |
| 或者 | 如果其中一个陈述是正确的 | 真或假真
真或真真
假或假假 |
| 不 | 否定下面的语句 | 不真假
不假真 |
Python 逻辑运算符
以下是 REPL 的一些例子,可以帮助你使用它们:
>>> not True
False
>>> not False
True
>>> True and True
True
>>> True and False
False
比较 Python 中的不同类型
当你试图比较不同的类型时,你经常会得到一个错误。假设你想比较一个整数和一个字符串:
>>> 1 < 'a'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'
>>>
这就是 Python 告诉你它不能比较整数和字符串的方式。但也有可以混搭的类型。我建议不要这样做,因为这会让你的代码难以理解,但是为了便于演示,让我们来比较一下 boolean 和 int:
>>> True == 1
True
>>> False == 0
True
>>> True + True
2
>>> False + False
0
>>> False + True
1
>>> True + 3
4
>>>
可以看出,True
的值为 1,False
的值为 0。这与 Python 中布尔的内部表示有关:它们是 Python 中一种特殊的数字。
Python For 循环和 While 循环
原文:https://python.land/introduction-to-python/python-for-loop
我们学习了如何用条件语句 if 和 else 来改变程序的流程。另一种控制流程的方法是使用 Python for-loop 或 Python while-loop。本质上,循环允许你重复一段代码。
目录
Python For-loop
在 Python 中有两种创建循环的方法。我们先来看看 Python 的 for-loop。for 循环遍历您提供给它的对象的各个元素。如果这听起来很难,一个例子将有望澄清这一点:
>>> for letter in 'Hello':
... print(letter)
...
H
e
l
l
o
我们在这里偶然发现了两个需要解释的概念:可迭代性和对象。
Iterable
An iterable is an object in Python that can return its members one at a time.
正如您在示例代码中看到的,一个文本字符串是可迭代的。大多数 Python 的数据类型在某种程度上都是可迭代的。如果你想知道所有的基本细节,请前往关于迭代器的页面。如果您是编程新手,我建议您继续阅读这里的内容,稍后再讨论迭代器。
我们需要解决的下一个问题是:对象。这是一个很大的主题,在本教程中有自己的章节: Python 类和对象。你现在不需要学习它来理解 Python for-loops。知道 Python 中的一切都是对象就够了,对象有一定的类型和一定的属性。可迭代是这些属性之一。
所以我们知道 for 循环可以循环遍历 iterable 对象。通过逐个返回其成员,使其在一个变量(在上面的例子中是变量letter
)中可用,您可以用 for 语句循环遍历 iterable 的每个元素。
Python 中 for 循环的通用模板是:
for <variable> in <iterable>:
... do something with variable
在每次迭代中,来自iterable
的一个元素被分配给variable
。这个变量存在,并且只能在循环内部使用。一旦没剩下什么,循环就停止,程序继续执行下一行代码。
Python for 循环和列表
这是研究新数据类型的理想时机:列表。Python 列表可以包含零个或多个对象。这是 Python 编程中经常使用的数据类型。在其他编程语言中,通常没有列表这样的东西。大多数语言都提供数组,但是数组只能包含一种类型的数据。
Python 也有数组,但我们不会在本课程中讨论它们。简而言之:数组只能包含一种类型的数据(比如数字),但是它们在存储数据方面更有效。另一方面,列表可以混合存储所有类型的数据,非常灵活,但会牺牲一些性能。关于列表的详细解释,你可以阅读我关于 Python 列表的文章。如果你是初学者,我建议你继续阅读。
列表就像字符串一样,是可迭代的;因此,它们与 for 循环配合得非常好:
>>> mylist = [1, 'a', 'Hello']
>>> for item in mylist:
... print(item)
...
1
a
Hello
让我们再试一次,但这一次是在一个互动的面包屑中,所以你可以玩它:
https://crumb . sh/embed/jcfk 2 va 3 vlb
有几点需要注意:
- 我们用引号创建列表。
- 它的内容是您喜欢的任何类型的对象,用逗号分隔,它们不需要属于同一类型。
- 我们也可以手动访问列表中的单个元素。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
一个列表可以包含我们到目前为止看到的所有类型:数字、字符串、布尔值,甚至其他列表。事实上,如果你愿意,你可以创建一个列表列表!正如您在交互式示例中看到的,我们还可以访问列表中的单个元素。记住在计算机科学中,我们从 0 开始计数。所以mylist[0]
给我们第一个元素,mylist[1]
给我们第二个元素,以此类推。
以下是您可以在 REPL 或上面的交互式代码示例中尝试的一些事情:
>>> mylist = [1, 2, 'Hello', ['a', 'b'] ]
>>> mylist[0]
1
>>> mylist[0] + mylist[1]
3
>>> mylist[2]
'Hello'
>>> mylist[3][0]
'a'
从上一个例子中,您可以看到如何访问嵌套列表。关于 Python 列表的广泛的教程可以在后面的 Python 教程中找到。
Python While-loop
虽然 Python 中的 for 循环有点难以理解,但是因为有了像 iterability 和 objects 这样的新概念,While 循环实际上要简单得多!它的模板如下所示:
while <expression evaluates to True>:
do something
你应该这样理解:“当这个表达式的值为True
时,继续做下面的事情”。
让我们来看一个真实的例子:
>>> i = 1
>>> while i <= 4:
... print(i)
... i = i + 1
...
1
2
3
4
我们看到在while
语句之后有一个表达式:i <= 4
。只要这个表达式的值为True
,while 循环内部的块就会重复执行。
在上面的例子中,我们从i = 1
开始。在循环的第一次迭代中,我们打印i
并增加 1。只要i
小于或等于 4,这种情况就会一直发生。print 语句的输出确认这个循环运行了四次。
这里有一个互动的例子,让你自己尝试一下:
https://crumb.sh/embed/itwRMBZbEoY
Python while 循环
无限循环
有时你希望你的软件继续运行。在这种情况下,无限循环会有所帮助。让我们用 Python 创建这样一个无限循环:
>>> while True:
... print("Help I'm stuck in a loop!")
...
为什么这是一个无限循环?请记住,while 接受一个表达式,只要该表达式的值为True
,就一直重复代码。因为非常简单的表达式'True
'总是True
,这个循环永远不会停止。
走出无限循环
使用 while 循环,很容易出错并发现自己陷入了一个无限的 while 循环。这意味着由于某种原因,表达式永远不会计算为 False。它发生在我们最好的人身上。同时按下 control + c 可以脱离这种情况。既然知道了如何故意制造无限循环,那就人为制造这样一种情况吧。点击控制+ c 停止以下循环:
>>> while True:
... print("Help I'm stuck in a loop!")
...
输出将如下所示:
Help I'm stuck in a loop!
Help I'm stuck in a loop!
Help I'm stuck in a loop!
Help I'm stuck in a loop!
Help I'm stuck in a loop!
Help I'm stuck in a loop!^C
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
KeyboardInterrupt
如果你仔细观察,你会看到错误前的字符^C
,意味着control + c
在该点被按下。这个组合键会让你摆脱程序无限期运行的大多数情况,所以记住它是有好处的!
无限循环在 for 循环中不太常见,因为大多数可迭代对象会在某个时候用完要迭代的元素。然而,如果你发现自己陷入了一个无限的 for 循环,你可以用同样的技巧来摆脱它。
更多类型的循环
在本教程的后面,您还将了解到 Python 列表理解。列表理解是 Python 中一个强大的构造,我们可以用它来基于现有的列表创建一个列表。如果您是编程新手,您应该先学习其他基础知识,不过我建议您继续学习下一部分。
Python 函数:代码重用的基础
函数是编程世界中一个至关重要的概念。在本文中,我们将探索 Python 函数。您将了解为什么它们如此重要,如何用 Python 的 def 关键字定义函数,如何调用函数,并且我们将了解使用函数时出现的一个主题:变量范围。
目录
Python 中的函数是什么?
让我们确切地定义一下什么是函数:
Function
A Python function is a named section of a program that performs a specific task and, optionally, returns a value
函数是任何编程语言的真正组成部分。我们用关键字def
定义了一个 Python 函数。但是在我们开始这样做之前,让我们先回顾一下函数的优点,让我们看看一些您可能已经知道的内置函数。
使用功能的优势
代码重用
Python 函数可以定义一次,使用多次。因此,它有助于代码重用:您不会希望多次编写相同的代码。
函数是保持代码短小、简洁、易读的好方法。通过给函数起一个精心选择的名字,你的代码将变得更加易读,因为函数名直接解释了将会发生什么。这样,其他人(或者将来的你)就可以阅读你的代码,而且不用看全部,也能理解它在做什么,因为它有精心选择的函数名。
稍后将解释代码重用的其他形式。例如,你也可以将代码分组到模块和包中。
因素
函数接受一个参数,稍后您将看到它是如何工作的。这里最大的优点是,你可以通过改变参数来改变函数的行为。
返回值
函数可以返回值。该值通常是某种计算或运算的结果。事实上,一个 Python 函数甚至可以返回多个值。
内置 Python 函数
在我们开始自己定义函数之前,我们先来看看 Python 的一些内置函数。先说最广为人知的内置函数,叫做print
:
>>> print('Hello, readers!')
Hello, readers!
>>> print(15)
15
Print 接受一个参数并将其打印到屏幕上。
正如我们的定义中所述,函数可以选择返回值。但是,print
不返回任何东西。因为它把一些东西打印到屏幕上,看起来好像是这样,但实际上不是。我们可以通过将打印语句的结果赋给一个 Python 变量来检查这一点:
>>> result = print('Hello')
Hello
>>> print(result)
None
None
是 Python 中一种特殊类型的值,基本意思是“什么都不是”
另一个确实返回值的内置函数是len()
。它返回你喂它的任何东西的长度:
>>> mylength = len('Hello')
>>> print(mylength)
5
创建 Python 函数
现在我们知道了如何使用函数,让我们自己创建一个简单的函数。为此,我们使用 Python 的def
关键字:
>>> def say_hi():
... print('Hi!')
...
>>> say_hi()
Hi!
虽然只有几行,但很多事情都在进行。让我们仔细分析一下:
- 首先我们看到关键字
def
,这是 Python 定义一个函数的关键字。 - 接下来是我们的函数名,say_hi。
- 然后我们遇到两个括号,(),表示这个函数不接受任何参数(不像
print
和len
)。 - 我们用冒号(:)结束这一行
- 最后,我们遇到了一个使 Python 区别于许多其他编程语言的特性:缩进。
缩进
Python 使用缩进来区分属于一起的代码块。具有相同缩进的连续行是同一代码块的一部分。
为了告诉 Python 下面几行是我们函数的主体,我们需要缩进它们。你用键盘上的键键键来缩进行。在 Python 中,使用四个空格进行缩进是一种很好的风格。整个 Python 社区都这样做。如果你点击 TAB 键,Python 交互式 shell (REPL)和所有像样的编辑器将自动缩进四个空格。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
回到我们的函数:它的主体只有一行代码:print 命令。在此之后,我们再按一次 enter 键,让 Python REPL 知道这是函数的结尾。这很重要。函数后面必须有一个空行来表示函数的结束。最后,我们可以用say_hi()
调用我们的函数。
Python 函数参数和自变量
我们可以通过将一个参数传递给我们的函数来使这变得更有趣。我们再次用 def 定义了一个函数,但是我们在括号之间添加了一个变量名:
>>> def say_hi(name):
... print('Hi', name)
...
>>> say_hi('Erik')
Hi Erik
我们的函数现在接受一个值,该值被赋给变量名。我们将这样的变量称为参数,而我们提供的实际值(‘Erik’)称为参数。
Parameters and arguments
A Python function can have parameters. The values we pass through these parameters are called arguments.
如您所见,print()
接受多个参数,用逗号分隔。这允许我们打印“hi”和提供的姓名。为了方便起见,print()
会自动在两个字符串之间加一个空格。
具有多个参数的 Python 函数
让我们更进一步,定义一个有多个参数的函数:
>>> def welcome(name, location):
... print("Hi", name, "welcome to", location)
...
>>> welcome('Erik', 'this tutorial')
Hi Erik welcome to this tutorial
没那么难;您只需在函数定义中添加更多的参数,用逗号分隔。
从函数返回
一个函数运行到该函数结束,之后 Python 返回到调用该函数的地方。在下面的示例中,我希望您在运行之前预测输出:
https://crumb.sh/embed/k2pkTYQVGMW
预测这个程序的输出
你的期望与现实相符吗?如果是这样,那么您已经很好地理解了函数调用是如何工作的。在这一点上,有些人希望看到文本“让我们问候整个世界”两次。如果你是其中之一,不要着急,继续读下去。
Python 会跟踪函数的调用点。在我们的例子中,它在第 5 行。一旦被调用,Python 就会一行一行地运行函数中的代码块,直到到达函数的末尾。一旦到达函数的末尾, Python 会跳回到从调用函数的那一行:第 6 行!
简而言之:函数调用不是跳转或“转到”,而是对一段可重用代码的调用,这段代码返回到它被调用的地方。函数调用也是一个表达式:大多数函数都返回值。
返回值
到目前为止,我们的函数只打印了一些东西,没有返回任何东西。使函数更有用的是返回值的能力。让我们看一个 Python 函数如何返回值的示例:
https://crumb.sh/embed/QdrfCWZeXKu
这是从 Python 函数返回值的方式
如您所见,我们使用关键字return
从函数中返回值。返回值的函数可以用在任何我们可以使用表达式的地方。在上面的例子中,我们可以将返回值赋给变量result
。
我们也可以在 if 语句中使用该函数。例如:
if add(1, 1) == 2:
print("That's what you'd expect!")
空 return 语句
如果你的函数不返回任何东西,但是你仍然想从函数返回,你可以使用一个空的 return 语句。这里有一个愚蠢的例子:
def optional_greeter(name):
if name.startswith('X'):
# We don't greet people with weird names :p
return
print('Hi there, ', name)
optional_greeter('Xander')
这是一个有趣的模式;我称之为提前回归。我想早点回来,因为另一种选择是使用 if… else 语句块:
def optional_greeter(name):
if name.startswith('X'):
# We don't greet people with weird names :p
pass
else:
print('Hi there, ', name)
optional_greeter('Xander')
你觉得哪个看起来更干净?我认为是第一种,因为它需要更少的缩进代码。虽然对于这样一个小例子来说差别很小,但是当你有更大的代码块时,这种差别就开始增加了。
变量范围
变量name
只存在于我们的函数内部。我们说变量name
的范围限于函数say_hi
,意味着它不存在于这个函数之外。
Scope
The visibility of a variable is called scope. The scope defines which parts of your program can see and use a variable.
如果我们在所谓的程序顶层定义一个变量,那么它在任何地方都是可见的。
让我们来演示一下:
>>> def say_hi():
... print("Hi", name)
... answer = "Hi"
...
>>> name = 'Erik'
>>> say_hi()
Hi Erik
>>> print(answer)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'answer' is not defined
say_hi
能够像预期的那样使用变量name
,因为它是一个顶级变量:它随处可见。然而,在 say_hi 内部定义的answer
在函数之外是未知的,并且会导致NameError
。Python 给出了一个信息性的详细错误:“名称‘答案’未定义。”
默认值和命名参数
Python 的一个引人注目的特性是能够为参数提供默认值:
>>> def welcome(name='learner', location='this tutorial'):
... print("Hi", name, "welcome to", location)
...
>>> welcome()
Hi learner welcome to this tutorial
>>> welcome(name='John')
Hi John welcome to this tutorial
>>> welcome(location='this epic tutorial')
Hi learner welcome to this epic tutorial
>>> welcome(name='John', location='this epic tutorial')
Hi John welcome to this epic tutorial
或者,如果您愿意,可以使用下面的交互式示例:
https://crumb . sh/embed/3gqc 9 ebqd 3k
默认值和命名参数的演示
因为我们的参数有一个默认值,所以您不必填写它们。如果没有,则使用默认值。如果这样做,您可以用自己的值覆盖默认值。
在显式命名参数的同时调用 Python 函数与我们迄今为止所做的不同。这些参数称为命名参数,因为我们指定了名称和值,而不仅仅是值。由于这些命名的参数,我们提供它们的顺序并不重要。仔细想想,这是让默认值变得有用的自然且唯一的方法。
如果你不想使用命名参数,你可以。当你依赖位置而不是名字时,你提供了我们所说的位置参数。位置很重要,如下所示:
>>> def welcome(name='learner', location='this tutorial'):
... print("Hi", name, "welcome to", location)
...
>>> welcome('Erik', 'your home')
Hi Erik welcome to your home
现在,您已经学习了足够多的函数知识,可以继续学习本教程。如果你想了解更多,请阅读我的 Python 函数深潜。
你的第一个 Python 程序
原文:https://python . land/introduction-to-python/your-first-program
如果你从一开始就遵循了 Python 教程,那么现在你已经学到了很多。我们已经讨论了关键主题,比如布尔和条件编程、字符串和函数。我们还没有做的是创建一个实际的程序。所以,让我们总结一下,把我们学到的东西整合成一个漂亮的小程序。我们将一起创建你的第一个 Python 程序。
目录
- 在 REPL 中输入代码
- 分析你的第一个 Python 程序
- 任务:改编你的第一个 Python 程序
- 现在呢?
在 REPL 中输入代码
我先分享一下节目。请彻底分析后再继续阅读。有一个你还不知道的函数(input ),但是我将很快解释它:
def say_hi(name):
if name == '':
print("You didn't enter your name!")
else:
print("Hi there...")
for letter in name:
print(letter)
name = input("Your name: ")
say_hi(name)
当输入超过几行代码时,必须非常小心缩进。如果您因为格式或缩进而不断得到错误,您也可以尝试完全复制并粘贴代码。在 REPL,它应该看起来像下面这样:
>>> def say_hi(name):
... if name == '':
... print("You didn't enter your name!")
... else:
... print("Hi there...")
... for letter in name:
... print(letter)
...
>>> name = input("Your name: ")
< enter your name at this point >
>>> say_hi(name)
如果你还有问题,这里有我的在线 Python 解释器的一个版本,你可以直接从这个页面运行。注意,input()
在这个系统中不起作用,所以我们只是用一个预定义的字符串调用say_hi()
:
https://crumb . sh/embed/w75 zob 5 wmuh
你的第一个 Python 程序
分析你的第一个 Python 程序
让我们一步一步地检查代码。
使用 Python 请求输入
我设法塞进了一个新东西,内置函数input()
。它完全按照您的期望去做:请求输入并将输入赋给一个变量。如果你给input
一个字符串作为参数,它会把它作为前缀打印出来。在这种情况下,它将打印“您的姓名:”并等待您输入您的姓名。
带有一个参数的 say_hi 函数
我们定义的say_hi(name)
Python 函数接受一个参数,即名称,并将名称打印到屏幕上。该函数不返回任何内容。它不需要,因为它自己完成所有的打印工作。
如果..else 块
我们的函数只在名字不是空字符串时才通知我们。为什么会这样?
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
当有人被要求输入时点击 enter 键,input()
函数返回一个空字符串。你可以在 REPL 亲自查看:
>>> input()
''
当要求输入时,只需按 enter,您将看到对 input()的调用产生一个空字符串。所以为了不让自己出丑,我们不会问候表现出如此粗鲁行为的人。我们通过使用一个 if-else 块来检查输入是否等于一个空字符串。
for 循环
最后,使用 Python 的 for 循环,我们将输入姓名的每个字母打印在新的一行上,因为我们可以。
任务:改编你的第一个 Python 程序
由于我们定义了一个函数say_hi(name)
,我们可以重用这个函数。可以反复问名字,反复打电话 say_hi。这里有一个小任务:
Create an infinite loop that keeps asking for names,
and that keeps greeting us using the entered name.
Hint 1: use the say_hi function from above.
Hint 2: revisit the section about loops if you need to.
def say_hi(name):
if name == '':
print("You didn't enter your name!")
else:
print('Hi there...')
for letter in name:
print(letter)
# This is an infinite loop
while True:
# Ask for the name first using input()
name = input('Your name: ')
# And then call say_hi with that name
say_hi(name)
现在呢?
此时,使用交互式 Python shell 开始对我们不利。您可能已经做了很多工作来让第一个 Python 程序工作,主要是因为缩进问题。幸运的是,我们还可以将 Python 程序存储在文件中,您将在下一节中了解到:创建 Python 程序。但是在我们这样做之前,我们将首先深入了解一下 Python 注释。
目前:恭喜你。如果您一直在学习,那么您应该对使用 Python 编程有一个基本的了解。您创建了您的第一个 Python 程序!我建议你继续在 REPL 里面做实验。您可能需要重读部分或全部章节。这是正常的。
在这一点上,我想给出的最重要的一条建议是,你不能光靠阅读来学习编程,就像你不能光靠阅读来成为一名医生一样。你必须亲自动手练习。
如果你觉得准备好了,继续下一章!
Python 评论:它是什么以及如何创建
原文:https://python.land/introduction-to-python/python-comment
Python 注释是 Python 程序源代码中的解释。除了提供信息之外,它不做任何事情,并且被 Python 解释器忽略。在本文中,您将了解到:
- Python 注释是什么,
- 如何在 Python 代码中添加注释,
- 写评论时常见的陷阱是什么
目录
什么是 Python 注释?
让我们从定义注释的确切含义开始:
Comment
A comment is an explanation in the source code of a Python program. Comments are added with the purpose of making source code easier for humans to understand. They are ignored by the Python interpreter.
因此,简而言之,注释可以帮助你和你的代码的其他读者更好地理解它。
单行 Python 注释
在 Python 中,我们通过以散列符号开始一行来创建注释: # 。这种符号被称为数字符号,哈希,或(在北美的用法)英镑符号。不管你叫它什么,这就是它的样子:
# This is a single-line comment in Python
print('Hello')
# And here's another one!
多行 Python comment
添加多行注释没有特殊的方法,也称为块注释。相反,您只需在每一行的开头使用散列符号,就像这样:
# This is a multi-line comment in Python,
# there's not much difference with single-
# line comments, as you can see!
在 Python(或任何语言,就此而言)中,最好编写清晰易懂的代码。所以如果你做得很好,你不应该经常需要多行注释!
注释掉代码
程序员经常使用的一种模式是临时注释掉代码。一个人想要这样做的原因有无数。这里仅举几个例子:
- 一项功能现在不能工作,但是你计划以后再工作,所以你不想放弃它
- 有一段可供选择的代码,您以后可能会用到,所以您可以把它作为一个提示留在注释中
- 你在重构你的代码,意味着你在清理代码。通常,这是一个创建更多更小的函数的过程,而您还没有完全准备好丢弃旧代码。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
为了注释掉 Python 中的代码,我们再次使用散列符号:
# This code is disabled for now
# if password == 'welcome123':
# allow_access()
提示:在大多数 ide 和编辑器中,您可以通过选择所有行并按下 Ctrl + / 来注释掉多行代码块。您可以通过完全相同的操作来反转操作。
Python 注释的常见错误
很多程序员在注释时都会犯一些错误。所以,我们来探讨这些,希望你不会犯!
使用太多注释
你应该把注释的使用限制在绝对必要的范围内。这是为什么呢?
有一个概念叫做自文档化代码。这意味着你的代码可读性强,易于理解,几乎不需要注释或文档。如果你的代码是可读的,并且不需要注释,那么它会更紧凑,更容易阅读。因此,如果您总是努力编写自文档化的代码,并且只求助于注释来解释更高级或不明显的东西,那将是最好的。
编写自文档化的代码应该有自己的文章,但是有三个要点已经很有帮助了:
- 给你的 Python 变量起个名字,让它们包含的内容一目了然。例如,不要只给你的清单命名为
m
或m_list
,而要像member_list
那样称呼它。 - 用简洁的方式命名您的 Python 函数,清楚地描述它们做什么。
- 创建只做一件事的短函数。如果他们做不止一件事,把他们分成多个功能。一旦开始使用单元测试,短函数也更好。
陈述明显的事实
许多初级程序员倾向于陈述显而易见的事情。换句话说,他们在评论中描述他们将要做的事情。你可能也想这样做,尤其是当一门语言对你来说是新的时候,作为对你自己的一个提示。
以下是一些显而易见的例子:
# Ask for the user's name
name = input('Name: ')
# Call the say_hi function
say_hi(name)
# Now we divide the user input by 100
result = input / 100
没有维护您的 Python 注释
编辑代码时,很容易忘记那里的注释。总是检查它们是否仍然适用,或者可能需要修改,因为您刚刚更改了代码。
忘记描述函数或类的注释是一个常见的错误,因为它们通常与您正在编辑的代码不太接近。重构代码后,总是要看看注释。
注释掉代码而不是移除它
如上所述,有时您需要注释掉代码。但这并不意味着你可以把它留在那里。在我作为程序员的职业生涯中,我见过太多注释掉的代码部分。这个的问题是没人敢碰它。你的同事肯定是有原因的,对吧?
不…你的同事可能只是忘记了。但是你能确定吗?因此,养成清理被注释掉的代码的习惯。
继续学习
你可能也会对我的关于 Python docstrings 的教程感兴趣。
创建 Python 程序
到目前为止,在阅读完 Python 教程之后,你应该对 Python 和 REPL 有了基本的了解。尽管后者可能非常有用,但在上一个例子中,您可能已经注意到 REPL 有其局限性。这对于快速实验来说很好,但是:
- 输入几行以上真的很难
- 很难回到以前的路线
- 没有语法突出显示
- 我们不能存储我们的程序,以后再使用它们
如果我们想存储我们的程序并使用一个像样的编辑器,我们只需要简单地:
- 创建一个文件,
- 输入该文件中的代码,
- 省省吧,
- …并执行它!
“等等,什么?怎么样!”
别担心,我们会一步一步来。
目录
1。创建一个 Python 文件
我们需要创建一个所谓的纯文本文件,这意味着这个文件没有什么特别之处。只是文字而已。听起来很简单,其实不然。例如,如果你启动 Word 或类似的程序,输入一些文本,并保存它,你不会得到纯文本。
像 Word 这样的文本处理软件会添加各种额外的代码来定义标记,允许包含图像等等。看起来有点像 HTML,其实是用来创建网站的(介绍见本 HTML 教程)。
那么我们应该用什么呢?
如果你用的是 Windows,试试记事本。这是一个不添加任何标记的纯文本编辑器。这是创建 Python 程序的一种可怕方式,但是我们将在以后探索更好的选择。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
如果你在 Linux 上,你可以打开一个终端,尝试一个文本编辑器,比如nano
或vim
(如果你没有读过手册,最后一个很难使用)。许多 Linux 安装也包括 GUI 文本编辑器,如 gedit。
MacOS 自带一个名为 TextEdit 的程序。
2。输入代码
现在该输入一些代码了。我们暂时保持简单。只需输入如下内容:
print("Hello world")
在 Windows 上,它应该是这样的:
在新的纯文本文件中输入 Python 代码
3。保存文件
保存文件。最需要注意的是文件扩展名。Python 文件以.py
扩展名结尾。调用你的文件helloworld.py
。在记事本中,您需要单击名为“保存类型”的下拉列表,然后选择All files (*.*
:
用记事本保存 Python 文件(你很快会学到更好的方法)
4。执行一个 Python 程序文件
现在您已经保存了文件,我们可以执行它了。有两种选择,通常你会选择第一种:
- 打开终端或命令提示符,转到文件的目录,用
python3
命令执行它 - 用 Windows 资源管理器找到文件,右键单击,用 Python 3。x '。
如果你尝试选项二,你会发现程序在屏幕上闪了一下,你看不到输出,因为文本打印到屏幕后窗口直接自己关闭了。所以现在你知道你为什么想要第一个选项了吧!如果您创建更高级的程序,使其持续运行而不是直接退出,选项二可能是启动 Python 脚本的一种不错的方式。
注意:我将使用 Windows 进行演示,但这在 Linux 和 MacOS 上是一样的。您打开的不是命令提示符或 Windows 终端,而是 MacOS 终端或 Linux 终端。
- 打开“开始”菜单,键入“命令提示符”如果您的操作系统配置了英语以外的其他语言,它的名称可能会有所不同。提示:用英语设置创建新的配置文件。这可能会有所帮助,因为网上和书中的大多数例子都采用了英语设置。
cd
保存文件的目录。在我的例子中,它是C:\python.land
- 用命令
python3 helloworld.py
运行程序
输出应该如下所示:
从命令提示符或终端运行 Python 程序文件
恭喜,你做到了。你写了一个 Python 程序,保存到一个文件里,像老板一样执行!不过,不要庆祝得太过火。你不想用记事本或其他简单的文本编辑器开发 Python 软件。你真正想要的是一个 Python IDE :一个集成开发环境。听起来很吓人,其实不然!
最好的 Python IDE
原文:https://python . land/creating-python-programs/the-best-python-ide
您学习了如何使用 Windows 记事本保存 Python 文件并从命令行运行它。但是就像我说的,使用记事本绝对是编写 Python 程序最糟糕的方式。那么专业人士用什么呢?任何严肃的 Python 程序员都使用 Python IDE:一个集成开发环境。但是,对于所有可供选择的 Python IDE,什么是最好的呢?
目录
- 为什么要使用 Python IDE?
- 最好的 Python IDE 是什么?
- VSCode 优势
为什么要使用 Python IDE?
让我们先来看看另一个问题:为什么要使用 IDE?一般来说,IDE 可以加快开发速度。IDE 将全程帮助您,从设置项目到调试和运行它。让我们深入了解一下使用 IDE 的一些优势。
语言支持
IDE 提供了强大语言支持:
- 自动完成单词,大大加快开发速度
- 建议(基于上下文)可以使用哪些选项
- 语法突出显示,使您的代码更具可读性
排除故障
IDE 很好地集成了调试过程。调试是我们在本指南中还没有谈到的内容。基本上,它意味着在你的软件中找到错误并修复它们。
基于项目的工作流
IDE 有项目的概念。您可以在 IDE 中打开您的项目,并将其保存在一个窗口中。IDE 会记住您的项目及其所有设置。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
最好的 Python IDE 是什么?
很多人问我:最好的 Python IDE 是什么?尽管每个人的品味不同,但是有几个 IDE 确实很突出。其中一个头肩突出。如果你问我,什么是最好的 Python IDE 的答案是 Visual Studio 代码。
VSCode 优势
在这个 Python 教程中,我将只告诉你 VSCode。如果你强烈喜欢另一个 IDE,比如 PyCharm,那也可以。我不会少喜欢你!要知道这不仅仅是个人喜好。我将给出一些令人信服的理由来说明为什么我认为它是最好的 IDE。
这是真正的免费和开源
其他 IDE 也是免费的,但与付费的“专业版”相比,它们的功能往往有限。VSCode 是完全免费的,甚至是开源的。如果你愿意,你可以破解它的源代码,这些都可以在 GitHub 上找到。
它很快
大多数 IDE 都希望是跨平台的。因此,它们通常是使用 Java 构建的。尽管 Java 自早期以来已经取得了很大的进步,但它仍然让人感觉缓慢和迟钝。VSCode 完全是使用 TypeScript 构建的。它不太需要内存,在所有平台上都感觉非常快。
它是跨平台的
这意味着它可以在所有平台上工作。它是 Windows、Mac 和 Linux 上最好的 Python IDE。它在所有平台上的工作方式几乎都是一样的,当你想要或需要切换平台时,可以缩短学习曲线。
除了 Python 之外,它还支持许多语言
其他特定于 Python 的 ide 只支持 Python。但是任何有经验的程序员都知道这是不够的。例如,当您开始使用像 Django 这样的 Python 框架创建网站时,您也将创建 HTML 和 CSS 文件。
当你的 IDE 可以支持多种语言时,这是非常方便的。有了 VSCode,这完全不是问题。许多 web 开发人员主要使用 VSCode,因此支持非常好。VSCode 还为您经常遇到的文件格式提供了强大的支持,无论您用什么语言编写,例如:
除此之外,还有针对所有流行语言和平台的插件。例如 Java、Kotlin、C#、Javascript、TypeScript、Dart 和 Flutter 等等。
在远程位置运行良好(SSH、Windows WSL、Docker 容器)
VSCode 有一个很好的扩展,可以帮助您连接到远程环境。如果您是 Windows Linux 子系统的粉丝,那么您很幸运:您使用原生的 Windows VSCode 程序并在 WSL 中工作。这提供了完整的 Linux 体验,而无需离开 Windows。没错。你可以在 Windows 上运行 Linux。这个扩展也可以连接到一个码头集装箱。
由微软和许多其他公司支持
VSCode 由微软构建,这意味着有这个大公司支持它,并确保它的开发是专业和稳定的。据我个人所知,在开发人员的带领下,VSCode 有了很大的转变。此外,许多公司和个人接受了 VSCode,并开始为它创建令人敬畏的扩展。它有一个充满活力的社区!
安装 VSCode:如何在 Windows、Linux 和 MacOS 上安装和运行
原文:https://python . land/creating-python-programs/installing-and-starting-vs code
现在你知道了为什么 VSCode 很棒,为什么它可能也是你的最佳选择。你可能等不及要安装 VSCode 了吧?我们将探索安装 VSCode 的不同方法以及如何启动它。
目录
安装 VSCode
就像安装 Python 的时候一样,安装 VSCode 有很多种方法。
从官方网站下载
VSCode 可以从这个网站免费下载。它适用于以下平台:
- Windows 操作系统
- Linux 操作系统
- 苹果
确保为您的平台下载正确的二进制文件。该网站试图自动检测你的平台,并提供正确的链接,所以这应该不会太难!下载后,打开文件并按照步骤将其安装到您的系统上。
使用特定于操作系统的软件包管理器
在 Linux 和 MacOS 上,您可以使用好的替代方法来安装 VSCode。例如,如果您是自制软件用户,您可以安装 vscode 桶,其中包括:
$ brew install --cask visual-studio-code
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
如果你用的是 Ubuntu,你可能想用 snap 来代替。所有这些方法都很好,甚至可能比手动下载 VSCode 更好。我个人在 Ubuntu 上使用 snap 包,它运行得非常完美,并且保持最新版本。
VS 法典
虽然 VSCode 是免费的,但有些人担心,因为它是由微软构建和维护的。据我所知,代码是开源的,但二进制版本不是。微软采用开源软件,并添加了一些额外的东西,比如品牌和遥测技术(你可以把这个关掉)。
如果你是一个开源纯粹主义者,有一个由社区构建的二进制发行版,叫做 vscodium 。我没有亲自尝试过,但它应该和官方发布的完全一样。绝对明确:VSCode 本身是免费的。就我个人而言,我对微软构建二进制文件很满意,甚至不太介意遥测,尽管我通常在安装新操作系统时会关闭它。
启动 VSCode
有两种方法可以启动 VSCode:
- 在菜单中查找并点击它
- 从命令行启动它
从“开始”菜单启动 VSCode
大多数操作系统都有菜单系统,例如:
- Windows 上的开始菜单
- MacOS 上的启动器
- Linux 上类似的功能,例如 Gnome Shell。
如果您想打开 VSCode,只需查找并单击图标。
从命令行启动 VSCode
从命令行启动 VSCode 的一大好处是,可以直接传递路径或文件来打开它。在所有平台上,您都可以在 VSCode 中将当前目录作为项目打开,只需键入:
$ code .
您也可以使用code
命令打开特定文件:
$ code myscript.py
VSCode Python 扩展
原文:https://python . land/creating-python-programs/vs code-python-extensions
现在您已经安装了 VSCode 并知道如何打开它,在您开始创建 Python 程序之前,我们需要讨论最后一个主题:VSCode Python 扩展。您应该将 VSCode 视为一个基本框架。它可以做的事情有:
- 打开、编辑和保存文件
- 允许您处理项目
- 启动应用内终端,将所有东西放在一起
- 管理扩展
目录
关于扩展
正是这些扩展让 VSCode 开始大放异彩。它有你能想到的几乎任何东西的扩展。通过这些扩展,VSCode 支持特定的语言,比如 Python,还有 Java、Kotlin、Javascript、Typescript 等等。但它也提供了如下功能:
- 高级代码完成,它建议了一些有用的方法来继续您正在键入的内容,并考虑了上下文
- 与 Git 等版本控制系统的集成
- 与基于云的服务集成,如 GitHub、Docker Hub 等。
VSCode 的强大之处在于它可以一次完成所有这些工作。它不仅仅是一个 Python IDE,还是一个很棒的 HTML 编辑器,非常擅长编写 Javascript,当然,它对 Python 代码也很棒!最棒的是:你可以决定你希望它能做什么。所以你可以保持精简,或者安装任何你需要的插件。看你的口味了!
如何安装扩展
要在 VSCode 中安装扩展,可以按左边的大扩展按钮,如下图所示:
在 VSCode 中安装扩展很容易
这将打开扩展市场。在那里,在搜索栏中输入 Python 来查找所有与 Python 有关的扩展。您可以点击扩展来阅读更多关于它们的信息,并按下安装按钮来安装扩展。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
最好的 VSCode Python 扩展
计算机编程语言
你至少要为 Python 编程安装官方的 Python 扩展。它提供了许多您需要的功能,如智能感知、林挺、调试、代码导航、代码格式化、重构、变量资源管理器、测试资源管理器、代码片段等等。
索纳林特
Sonarlint 插件检测常见错误和 bug。它检测到许多安全问题和代码质量问题,并且不止一次地拯救了我。我强烈推荐。它只支持 Python,但也支持 Java、JavaScript、TypeScript、PHP 等通用语言。
魔力神话
MagicPython 是 VSCode 中默认 Python 语法高亮器的前沿版本。你不需要它,但我总是安装它只是为了确保安全。
Visual Studio 智能代码
Intellicode 扩展是一个智能的、人工智能辅助的代码完成扩展,由微软构建。它支持 Python、Java、JavaScript 和 TypeScript。
VSCode GUI 教程:探索这个强大的 IDE 的元素
原文:https://python . land/creating-python-programs/vs code-GUI-tour
当您第一次启动 VSCode 时,它可能看起来让人不知所措。让我一步一步地向您介绍 GUI,这样您就可以从这个强大的 IDE 中获得最大的收益。
目录
vs code 工作区的主要部分
VSCode 中的主工作区分为五个主要部分,如数字所示:
1.菜单栏
2.VSCode 侧栏
3.VSCode Explorer
4.调试控制台/终端
5.文档显示/编辑器
VSCode 概述
让我们从详细查看这些部分开始!
菜单栏
菜单来自 VSCode 的菜单栏
菜单栏是用户界面的一部分,但对你的项目至关重要。
- 在“文件部分,您可以保存和打开项目,但是您也可以将文件夹添加到您的工作区。
- “ Edit ”选项卡包含标准的复制/粘贴选项,当您需要对某一行稍加修改进行复制时,这些选项会派上用场。查找和替换选项也隐藏在这里。
- 在“选择下,有一些选项允许您更容易地选择和移动代码。
- “视图”选项卡上的选项可以让您更改 VSCode 的外观,并显示调试控制台等窗口。
- 我个人并不经常使用“ Go ”,但是这一节可以让你更容易地浏览代码。
- “运行选项卡提供了调试代码和添加配置的选项。
- “终端”窗口允许您创建终端窗口和编辑任务。
- 我们工具箱的最后一个工具是“ Help ”窗口,它会引导您找到关于 VSCode 的更多信息和文档。
侧栏
仔细观察侧栏,有五个主要元素。第一个是“资源管理器”选项卡,我将在后面更详细地介绍它,但要知道它是您工作区中组织的基础。
VSCode 侧栏面板
“搜索”选项卡用于搜索任何字符串。最棒的是,您的工作区中的任何文件都将被搜索,这意味着您可以在所有源代码中找到特定的文本。
“源代码控制”选项卡允许您在 GitHub 存储库上发布和维护代码。这使得您的代码可以公开,供其他程序员使用和构建他们的项目。虽然对于经验丰富的程序员来说这是一个很好的特性,但是如果你还在学习 VSCode,你就不需要它了。
最后一个选项卡“扩展”,允许您查看当前安装的扩展,并找到其他可添加的扩展。如您所见,我为 Java 和 Python 编程语言安装了一些。如果您还没有这样做,请确保您已经安装了 Python 的 essential VSCode 扩展。
文件浏览器
文件资源管理器面板
文件资源管理器的工作方式与任何文件资源管理器一样。您可以从这里创建、组织和删除文件。使用上面显示的工具,您可以创建一个新文件夹或刷新目录。对目录的任何更改几乎都会立即显示在这里,但是您可能需要不时地强制刷新。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
最后一个窗口图标允许您一次最小化所有文件夹。当您开始构建更大的项目(如网站)时,您必须同时管理几个文件。这个按钮可以清除混乱,将所有内容折叠成一个简单的视图。
跳过第一个图标并非偶然,因为我想在 VSCode 中展示一个很酷的特性。这个小图标让你在当前目录下创建一个文件。
您可以创建许多包含源代码并将被 VSCode 识别的文件。例如,在。csv 文件扩展名通常用于存储逗号分隔的数据。的。py 文件包含 Python 代码,而。html 文件是网页文件。这个功能中我最喜欢的部分是它根据你的文件类型创建的小图标。它给你视觉提示,让你快速找到你要找的东西。
调试控制台和终端窗口
底部的小窗口通常是为终端窗口保留的。将终端集成到您的工作区可以节省时间和点击。如果你想浏览其他目录或运行一个脚本,这通常是通过终端完成的,这就是为什么在底部有一个嵌套窗口是很好的。
使用终端在 VSCode 中运行脚本非常简单。假设您的目录中保存了一个名为thisisavirus.py
的脚本。如果您想运行它,只需在控制台中键入 python thisisavirus.py
。
您可能希望在不同于工作空间的目录中运行 python 文件。在这种情况下,将终端指向文件名,仍然使用 python 命令,如下所示。如果路径中有空格,使用逗号很重要。这将防止弹出下面显示的错误。
终端还可以很好地查看代码中可能出现错误的地方,如上所示。回溯向您显示哪里出错了,以及导致错误的代码在哪里。
本节中我最喜欢的工具被简单地称为“问题”它会检查工作区中每个打开的文件是否有错误和其他问题。正如这里看到的,我有一个“EOF”错误,这只是一个语法错误,因为命令print(name)
中缺少括号。
文档编辑器
看看那些漂亮的颜色!在我为一个类构建的代码中,大约有六种不同的颜色,每种颜色代表另一种类型的代码。例如,紫色文本突出显示了本机 Python 命令,如“import”和循环中的单词。字符串将突出显示为橙色,以便与其他文本区分开来。
VSCode 的另一个优秀特性是迷你文本覆盖。当处理多行代码时,不可避免地会出现错误。VSCode 在右侧为您提供了一个较小版本的代码,如果发现错误,它会用红色突出显示一行,而不是滚动数百行。
VSCode 允许您通过简单地点击它们,然后点击靠近顶部的这个小图标来查看多个文件。每个文件都是可滚动的,并且依赖于另一个文件。
双屏按钮的左边是一个小小的绿色箭头。我希望经过多年的研究,你会发现这个箭头只是运行代码,事实也的确如此。
最后,想想那些同时打开十个浏览器标签的人。你可能会鄙视他们的方式,但是 VSCode 也没有设置打开标签的限制。现在这确实派上了用场,因为你知道你可以一次分割屏幕和编辑多个文件。
发现并消灭 bug
我们还没有谈到第四个图标。带有一个小 bug 的 play 按钮以调试模式运行您的脚本。进入调试模式时,VSCode 的底部会变成橙色。您的脚本正在调试模式下运行。
使用 VSCode 调试软件
在调试模式下,VSCode 会在运行时检查每一行的错误。如果发现了一个 bug,你会看到一个亮红色的窗口,指出错误发生的位置。这使得查找错误和调试代码比依靠终端告诉您哪一行出错要容易得多。
VSCode 突出显示代码中的错误
调试模式还允许您单步调试代码。这意味着逐行运行它,在每一行之后停止。这使您有机会在代码运行时检查变量和对象的当前状态。这是一个强大的方法来粉碎那些难以发现的错误,并更好地了解正在发生的事情。
要更深入地了解在 VSCode 中运行和调试 Python 代码,请继续阅读下一篇文章。
特别感谢扎克·戈雷兹尼对本文的贡献。你也想为 Python Land 做贡献吗?请联系我们。
VSCode 中的 Python:运行和调试
原文:https://python . land/creating-python-programs/python-in-vs code
本文向您展示了如何在 VSCode 中使用 Python。您将学习如何运行和调试 Python 程序,以及如何充分利用 VSCode 中的命令行。如果您遵循了本教程,那么您已经阅读了许多关于 ide 和 VSCode 的内容。如果没有,您可能希望从这些页面开始:
- 如何用记事本写一个简单的 Python 程序
- 为什么应该使用 IDE 进行 Python 编程
- 如何安装 VSCode
- 写 Python 程序需要哪些 VSCode Python 扩展
- VSCode GUI 之旅
目录
- 在 VSCode 中创建或打开一个 Python 项目
- 在 VSCode 中运行 Python
- 在 VSCode 中调试 Python
- 运行选择或当前行
- 终端运行代码
- VSCode 和 Python Virtualenv
- 在 VSCode 中格式化 Python
- 保存工作空间
- 继续学习
在 VSCode 中创建或打开一个 Python 项目
VSCode 窗口总是显示一个工作区。如果您愿意,一个工作区可以依次显示多个文件夹(或者:项目)。您可以打开多个工作区,每个工作区都有自己的窗口。然而,你通常一次只做一个项目。这样做时,一个窗口一个工作区就足够了。
创建项目很简单;这是一个用 VSCode 打开的目录。如果您在一个终端上,无论是在 Linux、MacOS 还是 Windows 上,您都可以创建一个新项目并用 VSCode 打开它,如下所示:
$ mkdir myproject
$ cd myproject
$ code .
code 命令是打开 VSCode 窗口的便捷快捷方式。如果您愿意,也可以从菜单中打开文件夹:**File -> Open Folder**
。
第一次使用 VSCode 打开项目时,IDE 会使用项目特定的设置创建一个新文件.vscode/settings.json
。如果使用版本控制系统,您可能希望添加。因为您的同事可能有自己的设置和偏好,甚至使用完全不同的 IDE。
在 VSCode 中运行 Python
以下分步指南可帮助您正确设置 VSCode 以运行 Python 代码。
步骤 1:选择 python 解释器
一个系统可以有多个 Python 解释器。为您的项目使用正确的解释器非常重要,因为 VSCode 使用它来运行和调试您的代码,并提供自动完成等功能。VSCode 通常会尽力自动检测可用的 Python 解释器。事实上,VSCode 甚至在你的项目文件夹中检测到一个 virtualenv 。例如,virtualenv 还包含一个 Python 解释器,VSCode 可以使用它。此外,它还支持增强的虚拟环境管理器,如 Pipenv 。
命令选项板
我们将使用一个叫做命令面板的特性来设置解释器。命令面板使您可以快速访问 VSCode 提供的所有功能。它允许你只用键盘做几乎所有的事情。它很棒,而且真正节省时间,所以我建议你尽早习惯它。
快捷键' Control+shift+P '(Windows/Linux)或者 cmd + shift + P (MacOS)可以让你快速打开命令托盘。如果你需要学习一条捷径,那就是这条!或者,您可以使用菜单:“视图->-命令托盘……”
在命令面板打开的情况下,开始键入‘Python:select interpreter’。你会很快发现自动完成功能会帮你解决问题;您不必键入整个文本:
使用命令托盘快速找到您要查找的内容
如果你使用的话,最好从你的虚拟环境中选择解释器。如果没有,选择一个合适的版本。如果不知道选哪个,就选最新版本的 Python 3。
如果你别无选择,确保你的系统上已经安装了 Python,并选择手动输入你的解释器的路径。你不应该有;VSCode 应该能够检测到正确安装的 Python 解释器。
选择适合您项目的 Python 版本
在 Windows 中,这看起来像这样:
在 Windows 中选择 Python 解释器
步骤 2:在 VSCode 中创建新的 Python 项目
当您第一次打开 VSCode 时,您将从一个空的工作区开始。我们先打开一个文件夹,在里面可以开始实验。我事先创建了一个,但是你也可以使用“打开文件夹”对话框来创建一个。具体工作方式因操作系统而异。在 Windows 中,您可以单击“新建文件夹”,例如:
步骤 3:在 VSCode 中创建和运行 Python 文件
配置好解释器后,我们现在可以运行 Python 程序了。让我们创建一个简单的测试程序。通过点击左侧浏览器中的“新建文件”按钮或使用“文件”菜单创建一个新文件。随便你怎么称呼它,我把我的叫做vscode_playground.py
。如果您还没有这样做,VSCode 可能会要求您选择一个 Python 解释器。去选一个吧。
将以下程序复制并粘贴到新创建的文件中:
import sys
if len(sys.argv) == 1:
name = sys.argv[1]
else:
name = 'stranger'
print(f'Hi there, {name}')
这段代码将查看脚本是否收到了一个参数。如果是这样,它将把它赋给name
变量。如果不是,它会叫你陌生人。我故意犯了一个错误,我们稍后将尝试调试。
要运行这个文件,你可以使用菜单 Run - > Run 不调试或者按 Control + F5 (Windows 和 Linux)或者 cmd + F5 (MacOS)。接下来发生的是 VSCode 打开一个运行该文件的集成终端窗口。由于我们故意犯了一个错误,您应该会看到类似于以下内容的错误:
Traceback (most recent call last):
File "vscode_playground.py", line 4, in <module>
name = sys.argv[1]
IndexError: list index out of range
这是它在 Windows 上的样子:
我们的程序在 VSCode 中运行(故意出错)
在 VSCode 中调试 Python
调试是使 IDE 比简单的编辑器更强大的特性之一。这并不难做到,并且可以为您节省大量的时间来疯狂地向代码中添加 print 语句。因此,让我们现在就花一点时间来学习基础知识吧!
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
调试当前文件
我们不再使用“不调试就运行”选项,而是选择“运行- >开始调试”选项。或者,你可以简单地按下 F5 。VSCode 会问你想做什么。因为我们想运行当前文件,选择“Python 文件”。
程序将再次因出错而崩溃。但是调试器没有停止,而是进来并突出显示发生错误的那一行。既然它抱怨列表索引超出范围,让我们更仔细地检查一下sys.argv
列表!
当悬停在文本sys.argv
上时,您会看到一个弹出窗口,允许您详细检查变量。当悬停在sys
上时,您会看到 sys 模块的许多内部组件。当鼠标悬停在argv
上方时,你会看到argv
的内容:
检查 argv 的含量
即使我们没有提供参数,argv
仍然包含一个元素:当前脚本的完整路径。一些我们没有预料到的事情!操作系统总是给我们程序的名字作为 argv 的第一个参数。我们现在需要做的就是将比较改为:if len(sys.argv) == 2。按下右上角的重启按钮或按下 Control + Shift + F5 或 cmd + shift + F5 重启程序。它现在应该输出“你好,陌生人”并正常退出。
创建运行配置
您已经学习了通过运行当前文件开始调试的快速方法。但是,如果需要更多选项,可以创建一个或多个运行配置。这样的配置允许你存储特定的参数等等,所以我们可以完全按照我们想要的方式来启动程序。
要创建运行配置,点击运行- >添加配置。再次选择“Python 文件”选项。VSCode 将在。vscode 文件夹。该文件预填充了一个示例配置。修改 JSON,如下所示:
{
"version": "0.2.0",
"configurations": [
{
"name": "Run with argument",
"type": "python",
"request": "launch",
"program": "vscode_playground.py",
"console": "integratedTerminal",
"args": ["Erik"]
}
]
}
这个配置为脚本提供了一个参数:名称“Erik”。注意,它还专门启动了vscode_playground.py
而不是当前文件。现在,您可以使用此配置启动调试器。但是首先,让我们打开左边面板中的运行/调试视图,通过点击上面有小 bug 的大运行按钮,或者点击 Ctrl+Shift+D 或者 Cmd+Shift+D :
通过单击按钮或按 Ctrl+Shift+D 打开运行和调试视图
在这个视图的顶部,您应该会看到新创建的配置。点击旁边的播放按钮开始播放。现在输出应该是“你好,Erik”。
断点
在某些时候,你想使用所谓的断点:在程序中的某一行,你想显式地暂停执行,或者休息一下,这样你就有机会检查程序在该点的状态。添加断点非常容易。在所谓的“装订线”中,即文件左侧显示行号的地方,您可以在行号前单击鼠标右键。当您悬停在那里时,应该会出现一个模糊的红点,一旦您单击它,它就会变成鲜红色:
断点,执行将在此处暂停
如果您现在运行程序,它将在断点处暂停,允许您在该时间点检查变量。要继续,您可以单击继续按钮或按下 F5 。调试器将继续执行,直到遇到另一个断点或程序完成。
或者,您可以使用“单步跳过”(F10)、“单步进入”(F11)和“单步退出”(F12)按钮从这里开始逐步执行程序。这样,您可以一行一行地执行程序,并有选择地进入和退出 Python 函数调用。
现在,您已经具备了在 VSCode 中开始调试的坚实基础知识。要了解更多细节,我想让您参考 VSCode 文档的调试部分,这应该是我到目前为止所教授内容的一个很好的延续。
运行选择或当前行
另一个有用的特性是运行代码选择或当前行的能力。这并不总是有用的:通常,一行代码或代码选择很大程度上取决于它的上下文。但有时,它会派上用场。要运行代码选择或当前行,按下 Shift+Enter 或使用命令调板并搜索“在终端中运行选择”。
代码在一个 Python REPL 中运行,一旦完成,这个 REPL 就保持打开,这样你就可以实验、检查变量等等。例如,如果您“运行”一个函数,现在您可以从 REPL 调用该函数,因为它是在那里定义的。
一个很酷的特性是,运行选择或当前行的所有后续命令都在同一个 REPL 中执行,保持状态不变。因此,如果您更改函数并再次“运行”它,函数会在 REPL 中重新定义。这是一个测试你刚刚写的代码的好方法,但是它不能代替适当的单元测试。
终端运行代码
VSCode 有一个集成的终端,非常有用。有的人从来不用,有的人一直用。我属于最后一类人:我从命令行运行我的代码,除非我在调试。我也在命令行上做版本控制。我发现熟悉所有命令行工具非常有用。它允许我在没有 IDE 的情况下执行关键任务,例如,在其他人的 PC 上、远程终端上,或者仅仅是快速打开的终端窗口,而不是打开完整的 IDE。
除了我个人的偏好之外,还有几个使用终端比 GUI 更容易的用例。例如:当您使用命令行选项进行测试时。您可以在运行配置文件中添加选项,但是如果这些选项不断变化,使用终端会更快。
因此,让我们也探索一下如何从集成的终端窗口运行您的代码。
第一步:打开内置终端
使用命令面板运行终端:创建新的集成终端,或者使用快捷键 Ctrl+Shift+** (那是一个反勾)。屏幕底部应该会打开一个终端。通过再次按下该组合键,您可以创建更多的终端窗口。重复按 **Ctrl+
可以快速显示和隐藏终端面板。
步骤 2:运行您的代码
不要忘记激活你的虚拟环境,如果你有一个的话。接下来,像处理任何其他文件一样运行 Python 文件:
$ python3 vscode_playground.py Erik
Hi there, Erik
VSCode 和 Python Virtualenv
使用 Python 虚拟环境时,需要让 VSCode 知道。如前所述,这样做的方法是从您的虚拟环境中选择解释器,而不是从系统范围内选择。
为了测试这一点,让我们先创建一个虚拟环境:
python3 -m venv venv
从截图中可以看出,VSCode 几乎立刻就注意到我们创建了这个 venv,并提供使用它:
点击是,就大功告成了!或者,你可以在命令面板(Ctrl+Shift+P)中通过输入“选择解释器”并点击“Python:选择解释器”来手动选择这个 venv
在 VSCode 中格式化 Python
您可以在 VSCode 中格式化 Python,如果您点击:
- Windows: Shift + alt + F
- Mac: Shift + Option + F
- Linux: Ctrl+Shift+I (那是大写的 I)。
- 或者打开命令调板(ctrl + shift + p)开始输入‘格式化文档’。
默认情况下,VSCode 格式化当前文档。如果您还没有这样做,它会询问您是否要安装 autopep8、black 或 yapf 之类的格式化程序。选择一个(默认,如果你不确定),让它安装。
从现在开始,如果您在编辑 Python 文件时按下 format 快捷键,您会注意到您的文件会根据格式化程序的默认规则进行格式化。如果你想定制这些规则,你将需要查找(例如,在 Google 上)如何为你选择的特定格式化程序定制规则。通常,您可以通过在项目的主目录中创建特定文件来添加或修改规则。
保存工作空间
最后,您可能想要保存您的工作空间。不过,这不是必须的。您可以简单地再次打开该文件夹。像启动配置这样的定制保存在(隐藏的)中。vscode 目录,如果打开该文件夹,VSCode 会找到它。然而,如果你在你的工作空间中打开了多个文件夹,并且不想连续重复这些步骤,你可以使用文件- >保存工作空间为..菜单。
继续学习
阅读这些文章以了解有关 Visual Studio 代码及其功能的更多信息:
- 正确安装 Python
- 为什么应该使用 IDE 进行 Python 编程
- 如何安装 VSCode
- VSCode GUI 之旅
- VSCode Python 扩展
以下外部链接可能也有帮助:
Python 中的类和对象
Python 类和 Python 对象是该语言的重要组成部分。不了解 Python 类和对象,就无法正确学习 Python。在本章中,您将学习:
- 在 Python 中一切都是对象
- 定义自己的 Python 类
- 基于类创建对象
- 什么是继承
当您只是创建小脚本时,您可能不需要创建自己的 Python 类。但是一旦你开始创建更大的应用程序,对象和类允许你自然地组织你的代码。对对象和类的良好理解会帮助你更好地理解语言本身。
目录
- Python 对象:引擎盖下的一瞥
- 什么是 Python 对象?
- 什么是 Python 类?
- 创建一个 Python 类
- 创建一个 Python 对象
- Python 中的 self 是什么?
- 创建多个 Python 对象
- 继续学习
Python 对象:引擎盖下的一瞥
在我们深入了解所有细节之前,让我们先来看看引擎盖下的情况。我这样做是因为我相信这会让你更好地理解这些概念。不要因为这一页的长度而气馁。通读一遍并亲自尝试示例后,您应该对 Python 中的类和对象有了很好的理解。
OK;让我们开始吧!你大概知道内置的len()
函数。它只是返回你给它的物体的长度。但是,比如说,数字 5 的长度是多少呢?我们来问问 Python:
>>> len(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
我喜欢错误,因为它们说明了 Python 内部是如何工作的。在这种情况下,Python 是在告诉我们 5 是一个对象,它没有len()
。在 Python 中,一切都是对象。字符串、布尔值、数字,甚至 Python 函数都是对象。我们可以使用内置函数dir()
检查 REPL 中的对象。当我们在数字 5 上尝试dir
时,它显示了一个函数的大列表,这些函数是任何数字类型对象的一部分:
>>> dir(5)
['__abs__', '__add__',
'__and__', '__bool__',
'__ceil__', '__class__',
...
'__str__', '__sub__',
'__subclasshook__', '__truediv__',
'__trunc__', '__xor__',
'bit_length', 'conjugate',
'denominator', 'from_bytes',
'imag', 'numerator',
'real', 'to_bytes']
为了清楚起见,我把列表缩短了一点。
列表以这些名字古怪的包含下划线的函数开始,比如__add__
。这些被称为魔术方法,或 dunder(双下划线的缩写)方法。如果你仔细观察,你会发现对于类型为int
的对象没有__len__
dunder 方法。Python 的len()
函数就是这样知道一个数没有长度的。所有的len()
所做的,就是在你提供给它的对象上调用__len__()
方法。这也是 Python 抱怨“‘int’类型的对象没有 len()”的原因
什么是 Python 方法?
我在这里随便介绍了一下方法这个词。让我更正式地定义它:
Method
When a function is part of an object or Python class, we call it a method.
字符串是有长度的,所以一个字符串必须有一个 len
的方法,对吗?让我们来了解一下!
>>> dir("test")
['__add__', '__class__',
'__contains__', '__delattr__',
'__dir__', '__doc__',
'__eq__', '__format__',
'__ge__', '__getattribute__',
'__getitem__', '__getnewargs__',
'__gt__', '__hash__', '__init__',
'__init_subclass__', '__iter__',
'__le__', '__len__', '__lt__',
'__mod__', '__mul__', '__ne__',
'__new__', '__reduce__',
'__reduce_ex__', '__repr__',
'__rmod__', '__rmul__',
'__setattr__', '__sizeof__',
'__str__', '__subclasshook__',
'capitalize', 'casefold', 'center',
'count', 'encode', 'endswith',
'expandtabs', 'find', 'format',
'format_map', 'index', 'isalnum',
'isalpha', 'isascii', 'isdecimal',
'isdigit', 'isidentifier', 'islower',
'isnumeric', 'isprintable', 'isspace',
'istitle', 'isupper', 'join', 'ljust',
'lower', 'lstrip', 'maketrans',
'partition', 'replace', 'rfind',
'rindex', 'rjust', 'rpartition',
'rsplit', 'rstrip', 'split',
'splitlines', 'startswith', 'strip',
'swapcase', 'title', 'translate',
'upper', 'zfill']
是的,在那里。既然这是一个方法,我们也可以称它为:
>>> "test".__len__()
4
这相当于len("test")
但是少了很多优雅,所以不要这么做。这只是为了说明这东西是如何工作的。
还有一系列其他不那么神奇的方法向我们展示。随意尝试几个,比如islower
:
>>> "test".islower()
True
该方法检查整个字符串是否是小写,如果是,那么 Python 返回布尔值True
。其中一些方法需要一个或多个参数,如 replace:
>>> 'abcd'.replace('a', 'b')
'bbcd'
它用“b”替换所有出现的“a”。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
有时候编程可能有点像变魔术,尤其是当你刚刚开始的时候。但是一旦你在引擎盖下瞥了一眼,看到事情实际上是如何工作的,那种魔力就消失了。让我们继续来看看对象到底是什么,以及它们是如何定义的。
什么是 Python 对象?
既然我们已经使用了对象,并且知道 Python 中的一切都是对象,那么是时候定义什么是对象了:
Object
An object is a collection of data (variables) and methods that operate on that data. Objects are defined by a Python class.
对象和面向对象编程是 20 世纪 90 年代初开始流行的概念。早期的计算机语言,如 C,没有对象的概念。然而,事实证明,对于人类来说,对象是一种易于理解的范式。对象可以用来模拟现实生活中的许多概念和情况。如今,大多数(如果不是全部的话)新语言都有对象的概念。所以你将要学习的东西在概念上也适用于其他语言:这是基础计算机科学。
什么是 Python 类?
因为对象是 Python 语言的组成部分,所以您也可以自己创建对象。如果你想创建你自己类型的对象,你首先需要定义它的方法和它能保存什么数据。这个蓝图叫做类。
Class
A class is the blueprint for one or more objects
所有 Python 对象都基于一个类。当我们创建一个对象时,我们称之为“创建一个类的实例”。字符串、数字甚至布尔也是类的实例。让我们用内置函数type
来探索一下:
>>> type('a')
<class 'str'>
>>> type(1)
<class 'int'>
type(True)
<class 'bool'>
显然,有被称为str
、int
和bool
的类。这些是 Python 的一些原生类,但是,正如我所说的,我们也可以构建自己的类类型!
创建一个 Python 类
没有汽车类比的教程是不完整的,所以让我们创建一个代表汽车的 Python 类。要输入的内容很多,而且每一个错误都要重新开始。尽管试,但如果你想走捷径,我理解。只需将下面的类复制并粘贴到您的 Python REPL 中。粘贴后,确保按两次回车键:
class Car:
speed = 0
started = False
def start(self):
self.started = True
print("Car started, let's ride!")
def increase_speed(self, delta):
if self.started:
self.speed = self.speed + delta
print('Vrooooom!')
else:
print("You need to start the car first")
def stop(self):
self.speed = 0
print('Halting')
创建一个 Python 对象
不要担心,我们将一步一步地检查类定义,但是让我们首先创建并使用一个 Car 类型的 Python 对象:
>>> car = Car()
>>> car.increase_speed(10)
You need to start the car first
>>> car.start()
Car started, let's ride!
>>> car.increase_speed(40)
Vrooooom!
如果你愿意,你也可以使用下面的面包屑来玩我们新创建的汽车类:
https://crumb.sh/embed/GzaEerCX65E
Python 中的对象总是一个类的实例。一个类可以有许多实例。我们刚刚用Car()
创建了类Car
的一个实例,并将它赋给了(小写)变量car
。创建一个实例看起来像调用一个函数;你以后会知道为什么。
接下来,我们调用我们的一个汽车对象方法:试图在它还没有启动的时候增加它的速度。哎呀!只有启动汽车后,我们才能提高它的速度,享受它发出的噪音。
现在让我们一步一步地复习我们的汽车课程:
- Python 中的类是使用 class 语句定义的,后跟类名(Car)。我们以冒号开始缩进的代码块。
- 我们定义了两个变量,速度和启动。这是该类的所有实例都将拥有的数据。
- 接下来,我们定义了三个操作变量的方法。
在这些方法的定义中,我们遇到了一些奇特的东西:它们都有一个名为 self 的参数作为它们的第一个参数。
Python 中的 self 是什么?
老实说,如果你问我的话,这是 Python 不太优雅的语言结构之一。
还记得我们调用 car 对象上的方法吗,比如car.start()
?我们不必传递self
变量,即使start()
在类中被定义为start(self)
。
事情是这样的:
- 当我们在 Python 对象上调用一个方法时,Python 会自动填充第一个变量,按照惯例我们称之为 self。
- 第一个变量是对对象本身的引用,因此得名。
- 我们可以用这个变量来引用这个对象的其他实例变量和函数,比如
self.speed
和self.start()
。
因此,只有在 Python 类定义内部,我们才使用self
来引用作为实例一部分的变量。为了修改属于我们类的started
变量,我们使用了self.started
,而不仅仅是started
。通过使用self
,很明显我们操作的是这个实例中的一个变量,而不是在对象之外定义的、碰巧同名的其他变量。
创建多个 Python 对象
因为 Python 类只是一个蓝图,所以您可以用它来创建多个对象,就像您可以构建多辆外观相同的汽车一样。它们的行为都相似,但它们都有自己的数据,这些数据不在对象之间共享:
>>> car1 = Car()
>>> car2 = Car()
>>> id(car1)
139771129539104
>>> id(car2)
139771129539160
我们在这里创建了两个 car 对象,car1 和 car2,并使用内置方法 id()来获取它们的 id。Python 中的每个对象都有一个惟一的标识符,所以我们刚刚证明了我们从同一个类中创建了两个不同的对象。我们可以独立使用它们:
>>> car1.start()
Car started, let's ride!
>>> car1.increase_speed(10)
'Vrooom!'
>>> car1.speed
10
>>> car2.speed
0
我们刚刚启动了car1
并提高了它的速度,而car2
仍然处于暂停状态。对速度的检查证实了这些是不同状态的不同汽车!
继续学习
- 您可能也会对 Python 数据类感兴趣
- 如何从函数中返回多个值
- 关于课程的官方文件
Python 构造函数
原文:https://python.land/objects-and-classes/python-constructors
我们现在来看一个特殊的 Python 函数,它是大多数类的一部分:Python 构造函数。构造函数是在创建对象时自动调用的函数。构造函数也可以有选择地接受参数,就像常规函数一样。
目录
默认构造函数
当从一个类创建一个对象时,看起来我们在调用一个函数:
car = Car()
嗯…这不仅仅看起来像是我们在调用一个函数,我们实际上是在调用一个函数!我们不必定义这个方法,它被称为构造函数。它构造并初始化对象。每个类默认都有一个,叫做__init__
,即使我们自己没有定义。这与继承有关,你很快就会了解到。
你用过str()
函数把数字转换成字符串吗?或者是将字符串转换成数字的int()
函数?
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
>>> 'a' + str(1)
'a1'
>>> int('2') + 2
4
你在这里所做的,是通过调用类str
和int
的构造函数来创建类型str
和int
的新对象。
创建自己的 Python 构造函数
我们可以覆盖__init__
方法,通过接受参数赋予它额外的能力。让我们使用自定义构造函数重新定义Car
类:
class Car:
def __init__(self, started = False, speed = 0):
self.started = started
self.speed = speed
def start(self):
self.started = True
print("Car started, let's ride!")
def increase_speed(self, delta):
if self.started:
self.speed = self.speed + delta
print("Vrooooom!")
else:
print("You need to start the car first")
def stop(self):
self.speed = 0
我们的定制 Python 构造函数有带默认值的命名参数,所以我们可以用多种方式创建类Car
的实例:
>>> c1 = Car()
>>> c2 = Car(True)
>>> c3 = Car(True, 50)
>>> c4 = Car(started=True, speed=40)
你可能已经注意到了一个缺陷:我们现在可以创造一辆新车,它不启动,但无论如何都要设定它的速度。现在,我们就到此为止吧。
Python 继承
原文:https://python.land/objects-and-classes/python-inheritance
在编程中,尽可能多地重用代码被认为是一种好的风格。这种做法甚至有一个很好的首字母缩略词,叫做 DRY:不要重复自己。类有助于避免重复代码,因为您可以一次编写一个类,然后基于它创建许多对象。然而,当使用 Python 继承时,它们也以另一种方式帮助你。
目录
Python 中的继承
我们已经看到了遗传在起作用,但是你可能还没有意识到。还记得我告诉过你关于 Python 构造函数以及每个类都有一个构造函数(__init__
),即使你没有定义一个。这是因为每个类都继承了 Python 中最基本的类,叫做object
:
>>> dir(object)
['__class__', '__delattr__', '__dir__',
'__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__',
'__hash__', '__init__', '__init_subclass__',
'__le__', '__lt__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
当我告诉你 Python 中的一切都是对象’时,我指的是一切。这包括类,如你所见,我们也可以在类上使用dir()
;object
级。它揭示了object
有一个__init__
方法。很酷,不是吗?
Python 继承示例
类可以从其他类继承属性和函数,所以不必重复。比方说,我们希望我们的 Car 类从 Vehicle 类继承一些更通用的函数和变量。当我们这样做的时候,让我们也定义一个摩托车类。示意性地看起来是这样的:
Python 类继承
继承映射到许多现实生活中的情况。基于上面的类图,让我们看看继承的实际应用。我们将从一个通用的Vehicle
类开始:
class Vehicle:
def __init__(self, started = False, speed = 0):
self.started = started
self.speed = speed
def start(self):
self.started = True
print("Started, let's ride!")
def stop(self):
self.speed = 0
def increase_speed(self, delta):
if self.started:
self.speed = self.speed + delta
print("Vrooooom!")
else:
print("You need to start me first")
现在我们可以使用继承来重新定义我们的汽车类:
class Car(Vehicle):
trunk_open = False
def open_trunk(self):
self.trunk_open = True
def close_trunk(self):
self.trunk_open = False
我们的汽车继承了 Vehicle 类的所有方法和变量,但是增加了一个额外的变量和两个方法来操作主干。
覆盖 Python 方法
有时你想覆盖继承的__init__
函数。为了演示,我们可以创建一个摩托车类。大多数摩托车都有一个中间支架。我们将增加在初始化时输出或输入的能力:
class Motorcycle(Vehicle):
def __init__(self, center_stand_out = False):
self.center_stand_out = center_stand_out
super().__init__()
当您重写构造函数时,我们从父类继承的构造函数根本不会被调用。如果你还想要那个功能,你就要自己调用它。这是用super()
完成的:它返回一个对父类的引用,所以我们可以调用父类的构造函数。
在这种情况下,我们为中心支架添加了功能,但删除了在构造函数中设置速度和开始状态的选项。如果您愿意,您也可以添加速度和启动状态的选项,并将它们传递给Vehicle
构造函数。
覆盖其他方法
就像__init__
一样,我们也可以覆盖其他方法。例如,如果您想要实现一辆不能启动的摩托车,您可以重写 start 方法:
class Motorcycle(Vehicle):
def __init__(self, center_stand_out = False):
self.center_stand_out = center_stand_out
super().__init__()
def start(self):
print("Sorry, out of fuel!")
继续学习
这里有一些资源可以更深入地研究这个主题:
- 关于继承的官方 Python 指南。
构建您的项目:模块和包
在本节中,您将了解 Python 模块和包。模块是分组和组织代码的理想方式,而包是 Python 模块的集合。在开始学习包之前,您需要彻底理解模块。
Python 有许多内置模块,但是您也可以定义自己的模块,所以让我们来探索一下如何定义!浏览下一篇关于 Python 导入语句和模块的文章。
Python 模块:捆绑代码并从其他文件导入它
Python import 语句允许我们导入一个 Python 模块。反过来,Python 模块帮助我们组织和重用代码。在本文中,您将了解:
- 如何创建模块和组织代码
- Python import 语句允许我们导入(部分)模块
- 模块方面的一些最佳实践
- 如何创建可运行模块
在后续文章中,您将学习如何在 Python 包中捆绑模块。
目录
什么是模块?
如果你创建一个单独的 Python 文件来执行一些任务,那就叫做脚本。如果你创建一个 Python 文件来存储函数、类和其他定义,那就叫做模块。我们通过使用 Python import
语句从模块中导入来使用模块。
所以模块是一个包含 Python 代码的文件,以.py
扩展名结尾。换句话说:任何 Python 文件也是一个模块。模块的名称与文件名相同,不包括扩展名。例如,如果您创建了一个名为mymodule.py
的文件,那么您的模块的名称就是mymodule
。
为什么需要 Python 模块?
模块有很多优点:
- 它们允许我们组织代码。例如,您可以将所有的函数和与数学相关的数据存储在一个名为
math.py
的模块中。这正是 Python 在数学模块中所做的! - 我们可以更容易地重用代码。我们现在可以使用模块,而不是将相同的函数从一个项目复制到下一个项目。如果我们适当地打包模块,我们甚至可以在 Python 包索引上与他人共享它。
Python 导入
您可以使用 Python 的 import 关键字导入 Python 模块。我们通常将模块导入放在脚本的顶部,但我们不一定要这样做。例如,有时您希望动态地决定是否需要导入一个模块,尤其是当该模块占用大量内存或其他资源时。
创建模块
让我们创建一个名为mymodule.py
的简单模块。它包含的所有功能如下:
def my_function():
print('Hello world!')
我们可以在另一个脚本中导入这个模块,但是您必须确保这两个文件在同一个目录中。要导入该模块,请使用以下命令:
import mymodule
这将导入整个模块,并以名称mymodule
提供给程序的其他部分。使用import
导入模块后,我们可以使用以下命令来使用该功能:
mymodule.my_function()
要了解这一点,您可以运行并使用下面的代码片段:
https://crumb.sh/embed/EaNTcACpjwg
Python 导入并使用模块
导入 Python 模块的特定部分
我们还可以导入模块的特定部分,比如函数、变量或类。为此,请使用以下语法:
from mymodule import my_function
现在,my_funcion
函数在名称为my_function
下对程序的其余部分可用,就好像它是在文件本身内部定义的一样。所以我们可以这样使用它:
my_function()
类似地,我们可以从一个 Python 模块中导入多个元素。为此,请使用以下语法:
from mymodule import my_function, my_variable
什么时候做什么由你决定。有时候,你经常需要一个函数,以至于每次引用整个模块看起来都很混乱。其他时候,最好导入模块名,因为您需要来自同一个模块的许多不同的函数。另一个需要考虑的问题是,当你使用完整的模块名来引用一个特定的函数、变量或类时,你的代码读者会更清楚。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
通配符导入(不好的做法)
当一个模块有很多您想要导入的函数和其他元素时,用一个特殊的from module import *
语法一次性导入它们是很有诱惑力的。要从模块中导入所有内容,我们可以使用以下语法:
from mymodule import *
在大多数情况下,您应该避免使用*
语法,因为它会导致意想不到的结果,尤其是在使用您无法控制的第三方模块时。毕竟,你不知道第三方模块里面有什么,也不知道将来会有什么。通过导入所有内容,您用不必要的变量、类和函数“污染”了您的名称空间。您甚至可能会在没有注意到的情况下覆盖一些现有的定义。我的建议是:只导入特定的元素,以保持对名称空间的控制。
导入别名
有时 Python 模块名并不像你所希望的那样。也许你需要经常输入名字,比如在笔记本上。在这种情况下,您可以使用别名来缩短模块名称。例如,如果您有一个名为mymodule.py
的模块,您可以将其作为m
导入:
import mymodule as m
您将遇到的一些常见别名,尤其是在数据科学等领域,包括:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
有效的 Python 模块名称
Python 模块的名称有一些限制,因为我们必须能够从 Python 代码中导入它们,并通过常规变量使用/引用它们。因此,有效的 Python 模块名与常规变量名有很多共同之处。
模块名称:
- 是一串字母、数字
- 如果有助于可读性,可以包含下划线
- 它不能以数字开头
- 它不包含空格或其他空白字符
- 不能包含以下任何附加字符:\ / : *?"< >|–
除了这些规则之外,在命名模块时还有一些最佳实践可以遵循。值得注意的是:
- 使用小写字母作为模块名。
- 确保名称简短且具有描述性。
常见错误
一个常见的错误是为您的模块使用保留字或现有的模块名称。我自己也是这样做的,比如创建一个名为http
的模块,而 Python 已经有了一个同名的模块。
难以调试错误的另一个常见原因是将模块命名为与目录相同的名称。
如何创建可运行的 Python 模块
在模块内部,您可以检测它是用作模块还是脚本。这是很有用的,因为它允许你既可以独立运行一个模块,也可以同时从其他代码中导入它。
If __name__ == '__main__'
检查
你可能以前见过它,你可能想知道它是做什么的。为什么 Python 程序员如此频繁地在他们的脚本中包含这种检查?它与模块以及将模块用作脚本有着千丝万缕的联系。
当我们导入一个模块时,它的名字(存储在变量__name__
中)等于模块名。当一个脚本被执行时,它的名字总是被设置为字符串__main__
。因此,如果我们检查这个名字,我们会发现我们是作为一个脚本运行,还是作为一个模块包含在某个地方。
我在下面的代码片段中创建了一个简单的模块来进行这种检查。因为我没有导入这个文件,而是直接运行它,所以脚本调用了欢迎函数,我们看到一些东西打印到了屏幕上。但是,当我们导入这个模块时,脚本不会调用 greeter 函数:
https://crumb.sh/embed/4WGobwBXoWc
一个可运行的模块,检查它是被用作模块还是脚本
通过仅在“脚本模式”下运行时执行代码,我们有效地创建了既可以作为脚本又可以作为我们可以导入的模块的文件。
类对模块
你可能想知道模块和类相比怎么样。毕竟,它们都是功能性的集合。尽管有很大的不同,Python 有很好的理由同时拥有模块和类。
模块将相似的功能或相关的功能组合到一个地方。例如,一个模块可能包含类、函数和常量,我们用它们来连接特定类型的数据库。
一个类为你的问题域建模。它为一个或多个对象提供了蓝图。例如,student 类保存学生数据和处理该数据的相关函数。可以有许多学生,因此可以同时有许多学生对象。关于这方面的更多内容,请阅读关于类和对象的章节。
更多资源
python.org 页面列出了 Python 标准库,很好地概述了内置模块。
结论
您已经学习了如何将代码捆绑到一个模块中,帮助您构建项目。在这个过程中,您的项目将变得更易于维护。模块只是拼图的一部分。在下一篇关于 Python 包的文章中,您将了解模块和包如何一起工作来进一步构建您的项目。
Python 包:通过捆绑模块来构建代码
我们使用 Python 包来构建和组织我们的代码。当谈到 Python 包时,人们通常指以下之一:
- 可以用 pip 和 pipenv 等工具安装的包通常通过 Python 包索引分发。
- 代码库中的包用于构造和组织代码。
如果你正在寻找如何安装软件包,你应该阅读关于用 pip install 安装软件包的文章。另外,我还可以推荐关于虚拟环境的文章。
这篇文章是关于创建你自己的包和模块。我们将看看什么是包,它们是如何构造的,以及如何创建 Python 包。您还将发现包和模块如何协同工作来组织和构建您的代码库。
如果您对模块不熟悉,请先阅读我关于 Python 模块的文章,然后再回到这里。这两个主题密切相关。
目录
- 什么是 Python 包?
- Python 包的结构
- 一个示例 Python 包
- 在 init 中导入模块。py
- 使用 main 创建一个可运行的包。py
- 模块与软件包
什么是 Python 包?
Python 包是包含零个或多个 Python 模块的目录。Python 包可以包含子包,子包也是包含模块的目录。每个包总是包含一个名为__init__.py
的特殊文件。您将了解这个神秘文件的确切用途,以及如何使用它使您的包更容易从导入。
Python 包的结构
所以 Python 包是一个包含 Python 模块和一个__init__.py
文件的文件夹。带有两个模块的简单 Python 包的结构如下:
.
└── package_name
├── __init__.py
├── module1.py
└── module2.py
如上所述,包可以包含子包。我们可以使用子包来进一步组织我们的代码。我将在下面的一节中更详细地向您展示如何做到这一点。让我们首先来看一下带有子包的包的结构:
.
└── package_name
├── __init__.py
├── subpackage1
├── __init__.py
├── module1.py
└── subpackage2
├── __init__.py
├── module2.py
如您所见,包是分层的,就像目录一样。
什么是 init。Python 包里的 py?
__init__.py
文件是一个特殊的文件,总是在导入包时执行。用import package_name
从上面导入包时,执行__init__.py
文件。
从上面导入嵌套包时,用import package_name.subpackage1
执行package_name
和subpackage1
的__init__.py
文件。顺序如下:
- 首先执行
package_name
的__init__.py
文件, - 然后是
subpackage1
的__init__.py
文件。
我在所有的__init__.py
文件中添加了简单的打印语句来演示。我们可以在package_name
文件夹下创建一个main.py
文件,内容如下:
import package_name.subpackage1
如果我们运行这个程序,输出将是:
$ python3 main.py
Hello from package_name
Hello from subpackage1
由于我们的子包的导入,两个__init__.py
文件中的打印语句都被执行。
在 Python 包中组织您的代码
我们现在有了以下工具来正确组织我们的 Python 代码:
- 包装
- 子包
- 模块
您应该使用子包将相关的模块组合在一起。使用子包也有助于保持包和模块名称的简洁。当您发现自己在包名中使用下划线时,它们通常是一个不错的选择。
一个示例 Python 包
让我们在当前目录下创建一个名为httptools
的包。我们的目标是,有一天,这个想象中的包将包含使用 HTTP 协议可能需要的所有工具。不过,我们从简单开始,只有 HTTP GET 和 POST 请求以及一个简单的 HTTP 服务器。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
我们的包包含两个子包:client
和server
。初始包布局可能如下所示:
httptools/
__init__.py
client/
__init__.py
get.py
post.py
server/
__init__.py
run.py
一些需要注意的事项:
- 这些名称简短且具有描述性。因为
client
和server
是httptools
的子包,所以大家都很明显这些是一个 HTTP 客户端和服务器。我们不需要称他们为http_client
和http_server
。 - 我们将相似的功能分组到子包中:
- 客户端包中的客户端代码,
- 和服务器代码放在服务器包中。
- 我们已经将密切相关的代码分组到模块中。例如,我们需要做的所有 HTTP get 请求都放在
get.py
模块中。
扩展我们的 Python 包
现在假设我们想跳到异步编程模型上。虽然我们最初的服务器曾经是同步的,但现在我们也提供了异步版本。
有三种方法可以将它添加到我们的代码库中:
- 创建一个名为
httptools_async
的新包 - 在
http
包中创建名为sync
和async
的新子包 - 创建新模块,命名为
async_get
和async_post
以及 async_run。
你会选择哪个选项?
选项 1:创建新的包
选项 1 是最简单的。我们可以创建一个名为httptools_async
的新包,并将http
包的内容复制到其中。接下来,我们将所有代码改为异步。我们原始包的用户可能只需修改一行代码就可以了:将import http
改为import http_async
。由于异步编程是一种完全不同的范式,这种简单的交换可能还不够。
选项 2:创建新的子包
选项二意味着现有的库用户,即使是那些不想使用 async 的用户,也需要修改他们的代码。重新开始时,这可能是一个好的选择,但对于现有的库来说,这不是一个好的选择。
选项 3:创建新模块
还有第三种选择:创建新的模块,在模块名后面附加 async。尽管现有用户不需要更改他们的代码,但我不推荐这样做。更合理的做法是将异步模块捆绑在它们自己的包中,因为你不必在导入时一直重复使用前缀/后缀。它符合保持包名和模块名简洁的总体目标。
在 init 中导入模块。py
通过导入__init__.py
中的重要函数,我们的软件包可以得到更大的改进。__init__.py
文件通常是导入其他模块的好地方。让我们以上面的httptools.client
包为例。我们的 get.py 模块提供了一个返回响应的函数get()
。我们可以将这个函数导入到client
包的__init__.py
文件中:
from httptools.client.get import get
这是 Python 中的一种常见模式。在__init__.py
文件中导入所有需要的模块是一个好主意。如果我们使用如上所述的导入,我们包的用户可以像这样导入 get 函数:
from httptools.client import get
根据情况,也根据口味,你现在也可以这样做:
from httptools import client
client.get(...)
如果没有__init__.py
中的导入,我们包的用户将需要像我们自己一样使用 get 函数:
from http.client.get import get
这对我们来说既冗长又缺乏灵活性。如果我们决定将 get 函数转移到其他文件,这将为我们代码的其他部分或我们包的用户引入一个突破性的变化。因为我们在 init 中导入了函数。但是,我们可以灵活地移动这个函数,只需简单地更改 init.py 中的导入,对于我们包的用户来说,这是不会被注意到的。
绝对或相对进口
我们已经使用绝对导入从httptools.client.get
模块中导入了get()
函数,这意味着我们指定了指向该函数的完整树。也可以使用相对导入。相对导入的好处是可以在不知道完整路径的情况下导入想要使用的模块。在不破坏代码的情况下,包名甚至可以改变。所以相对导入使你的代码对变化更健壮。
httptools.client
的__init__.py
文件中的相对导入如下所示:
from .get import get
这也是通配符有用的情况之一。我们可以使用通配符导入httptools.client.get
模块中的所有元素:
from .get import *
这并不坏,因为我们确切地知道我们在get.py
中放了什么。另外,我们可以在get.py
中改变函数名,而不需要改变 import 语句。这也使得代码更加灵活。
使用 main 创建一个可运行的包。py
在模块一课中,我向您展示了如何创建可运行模块。我们可以通过创建一个名为__main__.py
的文件来对包做类似的事情。
如何运行包中的模块
要从包中运行模块,我们可以使用以下 Python 命令:
python -m <module name>
此命令可用于运行您的包内的特定模块,例如python -m mypackage.mymodule
,但我们也可以使用它来运行包。
创建可运行的包
要创建一个可运行的包,它需要在其根文件夹中有一个文件名__main__.py
。这个文件可以导入和引导你喜欢的任何东西。注意,我们不需要添加if __name__=='__main__'
检查:我们可以 100%确定这个模块的名字实际上是__main__
,因为我们是这样命名这个文件的。
如果您的模块名为mymodule
,您现在可以用下面的命令运行它:
python -m mymodule
模块与软件包
让我们总结一下关于 Python 包的信息以及我们在上一篇关于 Python 模块的文章中学到的知识。经常弹出的一个问题是这样的:模块和包有什么区别?
- 一个模块总是一个文件,而一个包可以包含许多模块。
- 模块用于捆绑 Python 函数、类、常量和任何其他您想重用的东西。反过来,一个包捆绑了模块。
- 模块可以独立存在,不需要成为包的一部分,而包需要模块才有用。
- 包和模块一起构成了组织代码的强大方式。
Python 数据类型
在这一节中,我们仔细看看最重要的 Python 数据类型。Python 为我们提供了几种原生数据类型来存储和处理数据。这些都是你需要很好了解的基本构件。当考虑一个问题时,解决方案的一部分通常是选择正确的数据类型。了解每种数据类型的能力将使选择正确的数据类型变得容易得多。
目录
基本和高级 Python 数据类型
我们区分基本类型和更高级的数据结构。Python 中的基本数据类型存储单个值,比如一个数字或一段文本。Python 中的基本数据类型有:
接下来,我们有更高级的 Python 数据类型。它们可以存储许多项目,如项目列表或键值对:
这些类型都有与众不同的特征。例如,范围可以快速有效地计算,元组不能修改(而列表可以),集合允许您进行数学集合计算。
Python 中的可变性
Python 数据类型可以分为两类:可变的和不可变的。或者更准确地说:可洗的和不可洗的。如果我们可以改变一个对象持有的数据,那么它就是可变的,如果我们不能改变它,那么它就是不可变的。Python 中不可变数据类型的示例有:
可变的 Python 数据类型有:
为什么以及什么时候数据类型是可变的?
我们还没有深入研究所有这些类型,但是让我们以一个列表为例。在不知道具体细节的情况下,我可以告诉你,你可以在列表中添加更多的项目,删除项目,并替换它们。对于一个列表来说,这些并不奇怪,对吗?所以一个列表是可以改变的;因此它是可变的。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
然而,整数只是一个数字,就像 2、3 和 4 一样。你不能改变一个数字;事情就是这样。我几乎能听到你现在在想什么。"但是我可以改变一个 Python 变量,即使它是一个整数!"你是对的,但是那是不同的东西。
让我们看一个例子,我们将字符串“hello”赋给一个名为mystring
的变量,然后更改它:
>>> mystring = 'hello'
>>> mystring = 'world'
我们现在做的是给一个变量重新赋值。我们没有改变字符串“hello”本身。
还有另外一种解释。一个变量指向计算机内存中的一个点。这就是我们所说的指针。在第一个实例中,mystring
指向内存中存储字符串“hello”的位置将mystring
改为“world”后,它指向内存中存储单词“world”的另一个位置。我们没有改变字符串“hello”。'
事实上,我们可以通过执行以下操作来证明这一点:
>>> mystring = 'hello'
>>> mycopy = mystring
>>> mystring = 'world'
>>> print(mycopy)
'hello'
我们创建了指向字符串“hello”的第二个变量。当我们将mystring
改为指向一个不同的字符串时,我们仍然可以引用之前的字符串,因为mycopy
也指向‘hello’字符串在内存中的位置。
例如,这不同于列表。如果变量mylist
指向内存中的一个列表结构,而我们改变了那个列表,它仍然指向同一个列表结构。我们所做的只是改变列表结构本身(它的内容)。Python 不替换列表,而是修改它。
如何检查 Python 数据类型?
Python 中有一个名为type
的内置函数,可以用来检查数据类型。让我们来看看type
在工作中的一些例子:
>>> type(3)
<class 'int'>
>>> type('hello')
<class 'str'>
>>> type([1,2,3])
<class 'list'>
如果你正在 REPL 中做实验,type
是一个有价值的函数,它可以让你更深入地了解引擎下发生了什么!
Python Integer:用示例代码解释
Python 整数是一个非小数,如 1、2、45、-1、-2 和-100。这是 Python 本身支持的三种数字类型之一,另外两种是浮点数和复数。
目录
Python 整数的最大大小
与许多其他编程语言不同,Python 3 中的整数可以有很大的值。事实上,它们是无限的,这意味着它们的大小没有限制,例如:
>>> num = 98762345098709872345000
>>> num + 1
98762345098709872345001
当然,这是有限制的,因为你的电脑没有无限的内存。然而,出于所有实际目的,您不必为此担心。
整数类型
与 Python 2 和许多其他语言不同,Python 3 只有一种类型的整数。这是 Python 渴望成为一种干净、易学的语言的一部分。我们又少了一件需要担心的事情。更多详情,请参见 PEP-0237 。
转换成整数
字符串到整数
要在 Python 中将一个字符串转换成整数,使用int()
函数:
>>> int('100')
100
整数到字符串
要在 Python 中将整数转换成字符串,使用str()
函数:
>>> str(200)
'200'
浮点到整数
要将浮点数转换成整数,使用int()
函数:
>>> int(2.3)
2
Python 随机整数
许多用例需要一个随机整数。为此,需要导入模块 random
。请注意,这提供了伪随机性,不适合加密。
让我们得到一个随机数:
>>> import random
>>> random.randint(1,10)
上面的指令返回一个从 1 到 10(含)的伪随机数,也就是说包括 1 和 10。关于随机模块的全部细节,请访问 Python 文档。
是 Python 整数吗?
要检查一个值是否是整数,我们可以使用type()
函数。对于整数,它将返回int
。这里有一个如何在if
语句中使用它的简单例子:
>>> type(2)
int
>>> if isinstance(2, int):
... print('An integer')
...
An integer
不要用if type(2) == int
。
使用isinstance()
几乎总是更好、更干净的方式,并且覆盖更多的用例,比如子类。
Python 元组:如何创建、使用和转换
Python tuple 是 Python 三种内置的序列数据类型之一,另外两种是列表和范围对象。Python 元组与更常见的 Python 列表共享许多属性:
- 它可以在一个变量中保存多个值
- 它是有序的:项目的顺序被保留
- 元组可以有重复的值
- 它是有索引的:你可以用数字来访问项目
- 元组可以具有任意长度
但是有显著的不同:
- 元组是不可变的;一旦定义,就不能更改。
- 使用可选的括号()而不是方括号[]来定义元组
- 因为元组是不可变的,所以它可以被散列,因此它可以充当字典中的键
目录
- 创建一个 Python 元组
- 使用 Python 元组的多重赋值
- 索引访问
- 追加到一个 Python 元组
- 获取元组长度
- Python 元组 vs 列表
- Python 元组 vs 集合
- 转换 Python 元组
创建一个 Python 元组
我们使用可选的括号(圆括号)从单个值创建元组,如下所示:
>>> my_numbers = (1, 2, 3)
>>> the_same = 1, 2, 3
>>> my_strings = ('Hello', 'World')
>>> my_mixed_tuple = ('Hello', 123, True)
像 Python 中的所有东西一样,元组是对象,并且有一个定义它们的类。我们还可以通过使用该类的tuple()
构造函数来创建一个元组。它允许任何 Python 可迭代的 T2 类型作为参数。在下面的例子中,我们从一个列表中创建一个元组:
>>> tuple([0,1,2])
(0, 1, 2)
>>>
现在您也知道如何将 Python 列表转换成元组了!
哪种方法最好?
Python 并不总是很容易推断出您是在使用常规括号还是在尝试创建元组。为了演示,让我们定义一个仅包含一项的元组:
>>> t = (1)
1
>>> t = (1, )
(1,)
Python 在第一次尝试时看到数字 1,它被无用的括号包围,因此 Python 将表达式分解为数字 1。然而,我们在第二次尝试中添加了一个逗号,明确地向 Python 发出信号,表明我们正在创建一个只有一个元素的元组。
只有一个条目的元组对于大多数用例来说是无用的,但是它演示了 Python 如何识别元组:因为有逗号。
如果我们可以使用tuple()
,为什么还有第二种方法呢?另一种符号更简洁,但也有它的价值,因为您可以使用它以这种简洁的方式将多个列表解包到一个元组中:
>>> l1 = [1, 2, 3]
>>> l2 = [4, 5, 6]
>>> t = (*l1, *l2)
>>> t
(1, 2, 3, 4, 5, 6)
第一个*
操作符将列表分解成单独的元素。就好像你会在那个地方把它们一个一个地打出来。如果您想知道,这个解包技巧适用于所有的可迭代类型!
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
使用 Python 元组的多重赋值
在前面的主题中,您已经看到了称为元组解包的东西。还有另一种解包元组的方法,叫做多重赋值。这是一个经常使用的东西,尤其是当从函数返回数据时,所以值得一看。
多重赋值是这样工作的:
>>> person = ('Erik', 38, True)
>>> name, age, registered = person
>>> name
'Erik'
>>> _
像使用*一样,这种类型的解包适用于 Python 中的所有可迭代类型,包括列表和字符串。
正如我在关于从 Python 函数返回多个值的 Python 技巧中解释的那样,解包元组在与返回多个值的函数结合使用时效果很好。这是一种返回多个值的简洁方法,无需求助于数据类或字典:
def get_user_by_id(userid):
# fetch user from database
# ....
return name, age
name, age = get_user_by_id(4)
索引访问
我们可以使用像[0]
和[1]
这样的索引号来访问一个元组:
>>> my_mixed_tuple = 'Hello', 123, True
>>> my_mixed_tuple[0]
'Hello'
>>> my_mixed_tuple[2]
True
>>> _
追加到一个 Python 元组
因为 tuple 是不可变的,你不能在创建 tuple 之后把数据追加到它上面。出于同样的原因,您也不能从元组中删除数据。当然,您可以从旧的元组创建一个新的元组,并将额外的项附加到它上面,方法如下:
>>> t1 = (1, 2, 3)
>>> t = (*t1, 'Extra', 'Items')
>>> t
(1, 2, 3, 'Extra', 'Items')
我们所做的是解包t1
,用解包的值和两个不同的字符串创建一个新的元组,并再次将结果赋给t
。
获取元组长度
len()
函数作用于 Python 元组就像它作用于列表和字符串等所有其他可迭代类型一样:
>>> t = 1, 2, 3
>>> len(t)
3
Python 元组 vs 列表
Python 元组和 Python 列表之间最显著的区别是,列表是可变的,而元组不是。定义元组后,不能添加或删除值。相比之下,列表允许您随意添加或删除值。这一特性可能是一个优势;你可以把它看作写保护。如果一段数据不打算改变,使用元组可以防止错误。毕竟,六个月后,您可能会忘记不应该更改数据。使用元组可以防止错误。
另一个好处是元组更快,或者至少人们是这么说的。我没有看到证据,但这是有道理的。因为它是不可变的数据类型,所以元组的内部实现可以比列表简单。毕竟,它们不需要扩大或在随机位置插入元素的方法,这通常是作为一个链表来实现的。根据我的理解,在 CPython 实现中,tuple 使用简单的类似数组的结构。
Python 元组 vs 集合
元组和 Python 集合之间最显著的区别是元组可以有重复,而集合不能。集合的全部目的是它不能包含重复。这是消除重复数据的绝佳工具。
转换 Python 元组
将元组转换为列表
Python 列表是可变的,而元组不是。如果需要,可以用下列方法之一将元组转换为列表。
最干净、可读性最强的方法是使用list()
构造函数:
>>> t = 1, 2, 3
>>> list(t)
[1, 2, 3]
更简洁但可读性较差的方法是使用解包。这种解包有时会很方便,因为它允许您将多个元组解包到一个列表中,或者添加一些额外的值:
>>> t = 1, 2, 3
>>> l = [*t]
>>> l
[1, 2, 3]
>>> l = [*t, 4, 5, 6]
>>> l
[1, 2, 3, 4, 5, 6]
将元组转换为集合
类似于列表的转换,我们可以使用set()
将元组转换为集合:
>>> t = (1, 2, 3)
>>> s = set(t)
>>> s
{1, 2, 3}
在这里,我们也可以使用解包:
>>> s = {*t}
>>> s
{1, 2, 3}
将元组转换为字符串
像 Python 中的大多数对象一样,tuple 有一个所谓的 dunder 方法,称为__str__
,它将 tuple 转换为一个字符串。当您想要打印一个元组时,您不需要显式地这样做。 Python 的打印函数会在任何不是字符串的对象上调用这个方法。在其他情况下,您可以使用str()
构造函数来获取元组的字符串表示:
>>> t = (1, 2, 3)
>>> print(t)
(1, 2, 3)
>>> str(t)
'(1, 2, 3)'
Python 列表:如何创建、排序、追加、移除等等
Python 列表是最常用的数据结构之一,还有字典。Python 列表不仅仅是一个列表,还可以作为一个堆栈,甚至是一个队列。在本文中,我将解释您可能想知道的关于 Python 列表的一切:
我已经包含了许多工作代码示例来演示。
目录
- 如何创建 Python 列表
- 访问 Python 列表元素
- 添加和删除元素
- 如何在 Python 中获取列表长度
- 统计列表中元素的出现次数
- 检查项目是否在列表中
- 查找列表中某项的索引
- 遍历列表元素
- Python 列表到字符串
- 排序 Python 列表
- 切片
- 反转 Python 列表
- 了解关于 Python 列表的更多信息
如何创建 Python 列表
让我们首先创建一个列表:
my_list = [1, 2, 3]
empty_list = []
列表包含常规的 Python 对象,用逗号分隔,并用括号括起来。列表中的元素可以有任意的数据类型,并且可以混合使用。你甚至可以创建一个列表列表。以下列表都是有效的:
my_list = [1, "Erik", { 'age': 39 }]
# This is a list with three lists inside
game_board = [[], [], []]
使用 list()函数
Python 列表和所有 Python 数据类型一样,都是对象。list 的类叫做‘list’,小写 l,如果想把另一个 Python 对象转换成 list,可以使用 list()函数,它其实就是 list 类本身的构造函数。这个函数接受一个参数:一个可迭代的对象。
所以你可以把任何可迭代的东西转换成一个列表。例如,您可以将 range 函数具体化为实际值的列表,或者将 Python 集或元组转换为列表:
>>> list(range(1, 4))
[1, 2, 3]
>>> list({1, 2, 2, 2, 3})
[1, 2, 3]
>>> list( (1, 2, 3) )
[1, 2, 3]
访问 Python 列表元素
要访问单个列表元素,您需要知道该元素的位置。由于计算机从 0 开始计数,第一个元素在位置 0,第二个元素在位置 1,依此类推。
这里有几个例子:
>>> my_list = [1, 2, 3]
>>> my_list[1]
2
>> my_list[0]
1
>> my_list[4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
如您所见,您无法访问不存在的元素。在这种情况下,Python 抛出一个 IndexError 异常,解释为“列表索引超出范围”。在我关于异常和 try 和 except 语句的文章中,我在最佳实践部分更深入地讨论了这个主题。推荐你读一读。
获取列表的最后一个元素
如果想从列表的末尾获取元素,可以提供一个负值。在这种情况下,我们从-1 而不是 0 开始计数。例如,要获取列表的最后一个元素,您可以这样做:
>>> my_list = [1, 2, 3]
>>> my_list[-1] # get the last element
3
>>> my_list[-2] # get the 2nd last element
2
访问嵌套列表元素
访问嵌套的列表元素没有太大的不同。当您访问列表元素时,将返回该列表。因此,要请求列表中的元素,您需要再次使用几个括号:
>>> my_list = [[1, 2], [3, 4], [5, 6]]
>>> my_list[0]
[1, 2]
>> my_list[0][0]
1
>>> my_list[2][1]
6
添加和删除元素
让我们看看如何添加和删除数据。有几种方法可以从列表中删除数据。你用什么,取决于你所处的情况。我将在本节中描述和演示它们。
追加到 Python 列表
列表对象有许多有用的内置方法,其中之一是 append 方法。当在列表上调用 append 时,我们将一个对象追加到列表的末尾:
>>> my_list = [1, 2]
>>> my_list.append('a')
>>> my_list
[1, 2, 'a']
>>> my_list.append(4)
>>> my_list
[1, 2, 'a', 4]
组合或合并两个列表
添加元素的另一种方式是将一个列表中的所有元素添加到另一个列表中。有两种方法可以合并列表:
- 用+运算符将它们相加。
- 使用 extend 方法将一个列表中的所有元素添加到另一个列表中。
以下是如何将两个列表相加的方法。结果是一个新的第三个列表:
>>> l1 = [1, 2]
>>> l2 = [3, 4]
>>> l3 = l1 + l2
>>> l3
[1, 2, 3, 4]
原始列表保持不变。另一种方法是使用 extend 方法用一个列表扩展另一个列表:
>>> l1 = [1, 2]
>>> l2 = [3, 4]
>>> l1.extend(l2)
>>> l1
[1, 2, 3, 4]
>>> l2
[3, 4]
当 l1 用 l2 的元素扩展时,l2 保持不变。将附加的所有值从 l2 扩展到 l1。
从列表中弹出项目
默认情况下,pop()方法移除并返回最后一项,除非您给它一个索引参数。
下面是几个示例,演示了默认行为和给定索引时的行为:
>>> my_list = [1, 2, 3, 4, 5]
>>> my_list.pop()
5
>>> my_list.pop()
4
>>> my_list.pop(0)
1
>>> my_list
[2, 3]
如果您熟悉栈的概念,那么您现在可以在一个列表上只使用 append 和 pop 方法来构建一个栈!
使用 del()删除项目
从列表中删除或移除项目有多种方式。当 pop 返回从列表中删除的项目时,del
删除它而不返回任何内容。事实上,您可以使用 del 删除任何对象,包括整个列表:
>>> my_list = [1, 2, 3, 4, 5]
>>> del(my_list[0])
>>> my_list
[2, 3, 4, 5]
>>> del(my_list[2])
>>> my_list
[2, 3, 5]
>>> del(my_list)
>>> my_list
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'my_list' is not defined
从 Python 列表中移除特定值
如果要删除特定的值,可以使用 remove 方法。例如,如果您想删除列表中第一次出现的数字 2,您可以按如下方式操作:
>>> my_list = [1, 2, 3, 2, 5]
>>> my_list.remove(2)
>>> my_list
[1, 3, 2, 5]
>>> my_list.remove(2)
>>> my_list
[1, 3, 5]
>>> my_list.remove(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
如您所见,重复调用 remove 将删除额外的 2,直到一个都不剩,在这种情况下 Python 抛出一个 ValueError 异常。
移除或清除 Python 列表中的所有项目
要从列表中删除所有项目,使用clear()
方法:
>>> my_list = [1, 2, 3]
>>> my_list.clear()
>>> my_list
[]
从列表中删除重复项
没有特殊的函数或方法来删除列表中的重复项,但是我们可以使用多种技巧来这样做。最简单的,在我看来,就是使用一个 Python 集合。集合是对象的集合,就像列表一样,但是每个元素只能包含一个。更正式的说法是:集合是不同对象的无序集合。
通过将列表转换为集合,然后再转换回列表,我们已经有效地删除了所有重复项:
>>> my_list = [1, 2, 2, 3, 5]
>>> my_set = set(my_list)
>>> my_set
{1, 2, 3, 5}
>>> list(my_set)
[1, 2, 3, 5]
在您自己的代码中,您可能希望使用这个更紧凑的版本:
>>> my_list = [1, 2, 2, 3, 5]
>>> list(set(my_list))
[1, 2, 3, 5]
由于集合与列表非常相似,您甚至不必将它们转换回列表。如果 set 提供了您需要的东西,那么就用它来避免双重转换,使您的程序更快更有效。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
替换列表中的项目
为了替换列表项,我们给给定的列表索引分配一个新值,如下所示:
>>> my_list = [1, 2, 3, 4, 5]
>>> my_list[0] = 400
>>> my_list
[400, 2, 3, 4, 5]
如何在 Python 中获取列表长度
在 Python 中,我们使用len
函数来获取对象的长度。列表也是如此:
>>> my_list = [1, 2, 3, 4, 5]
>>> len(my_list)
5
如果你熟悉其他编程语言,比如 Java,你可能会想为什么 Python 有这个功能。毕竟,它也可能是 list 的内置方法之一,就像my_list.len()
一样。这是因为,在其他语言中,这通常会导致各种方法来获取对象的长度。例如,有些人会将这个函数称为len
,有些人会将其称为 length,还有一些人甚至不会实现这个函数,而只是提供一个公共成员变量。而这也正是 Python 选择对这样一个常见操作的命名进行标准化的原因!
统计列表中元素的出现次数
不要把 count 函数和获取列表长度混为一谈,这是完全不同的。内置 counter 函数计算列表中特定值的出现次数。这里有一个例子:
>>> my_list = [1, 2, 1, 4, 1, 7]
>>> my_list.count(1)
3
>>> my_list.count(4)
1
由于数字 1 在列表中出现了三次,my_list.count(1)
返回 3。
检查项目是否在列表中
要检查项目是否在列表中,请使用以下语法:
>>> my_list = [1, 2]
>>> if 1 in my_list:
... print("It's in the list")
...
It's in the list
查找列表中某项的索引
我们可以用索引方法找到一个条目在列表中的位置。例如,在下面的列表中,4 位于位置 3(请记住,我们从零开始计数):
>>> my_list = [1, 2, 3, 4, 5]
>>> my_list.index(4)
3
index 方法有两个可选参数,start 和 stop。有了这些,我们可以继续寻找更多相同的价值观。如果我们提供一个开始值,我们不需要提供一个结束值。现在让我们在下面的列表中找到这两个 4:
my_list = [1, 2, 3, 4, 5, 4]
print(my_list.index(4))
# Output: 3
# We know there's a 4 at position 3 so
# let's continue our search from position 4
print(my_list.index(4, 4))
# Output: 5
如果你想对列表进行更高级的过滤,你应该去看看我的关于列表理解的文章。
遍历列表元素
列表是可迭代的,所以我们可以在列表的元素上使用 for 循环,就像我们可以在< iterable >语法中使用‘for<元素>来迭代任何其他元素一样:
>>> my_list = [1, 2, 3, 4, 5]
>>> for n in my_list:
... print(n)
...
1
2
3
4
5
Python 列表到字符串
在 Python 中,可以使用 str 函数将大多数对象转换为字符串:
>>> str([1, 'abc', 2.3])
"[1, 'abc', 2.3]"
如果你感兴趣,str 实际上是 Python 字符串的基类,调用str()
通过调用 str 类的构造函数构造一个新的 str 对象。这个构造函数检查提供的对象,并寻找一个名为__str__
的特殊方法(也称为 dunder 方法)。如果存在,则调用此方法。没什么更多的了。
如果您创建了自己的类和对象,您可以自己实现__str__
函数来提供对象的可打印版本。
排序 Python 列表
要对 Python 列表进行排序,我们有两个选项:
- 使用列表本身的内置排序方法。
- 使用 Python 内置的 sorted() 函数。
选项一,内置方法,提供了一个就地排序,这意味着列表本身被修改。换句话说,这个函数不返回任何东西。相反,它会修改列表本身。
选项二返回一个新列表,原始列表保持不变。你使用哪一个取决于你所处的情况。
按升序就地列表排序
让我们从最简单的用例开始:按升序排序:
>>> my_list = [10, 2, 5, 4, 2]
>>> my_list.sort()
>>> my_list
[2, 2, 4, 5, 10]
按降序就地列表排序
我们可以用一个反向参数调用 sort 方法。如果设置为 True,排序将颠倒顺序:
>>> my_list = [10, 2, 5, 4, 2]
>>> my_list.sort(reverse=True)
>>> my_list
[10, 5, 4, 2, 2]
使用排序的()
下面的例子演示了如何对列表进行升序排序,返回一个新的列表的结果:
>>> my_list = [10, 2, 5, 4, 2]
>>> sorted(my_list)
[2, 2, 4, 5, 10]
>>> my_list
[10, 2, 5, 4, 2]
从最后一条语句可以看出,原来的列表没有变化。让我们再来一次,但现在按降序排列:
>>> my_list = [10, 2, 5, 4, 2]
>>> sorted(my_list, reverse=True)
[10, 5, 4, 2, 2]
>>> my_list
[10, 2, 5, 4, 2]
不可排序列表
我们不能对所有列表进行排序,因为 Python 不能对所有类型进行相互比较。例如,我们可以对一系列数字进行排序,比如整数和浮点数,因为它们有一个确定的顺序。我们也可以对字符串列表进行排序,因为 Python 也能够比较字符串。
然而,列表可以包含任何类型的对象,我们不能比较完全不同的对象,如数字和字符串。在这种情况下,Python 抛出一个TypeError
:
>>> my_mixed_list = [1, 'a', 2, 'B']
>>> my_mixed_list.sort()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'
尽管这个错误看起来有点神秘,但只有当你知道发生了什么时,它才是合乎逻辑的。为了对列表进行排序,Python 需要对对象进行相互比较。所以在它的排序算法中,在某个时候,它检查‘a’是否< 1,因此产生了错误:'<' not supported between instances of 'str' and 'int'
。
切片
有时候你需要得到列表的一部分。Python 有一个强大的语法来做到这一点,称为切片,与其他一些编程语言相比,它使处理列表容易得多。切片适用于 Python 列表,但也适用于任何其他序列类型,如字符串、元组和范围。
切片语法如下:
my_list[start:stop:step]
几个注意事项:
start
是要包含的第一个元素位置stop
是排他的,意味着位置stop
处的元素不会被包括在内。step
是步长。稍后将详细介绍。start
、stop
、step
都是可选的。- 负值也可以使用。
为了解释切片是如何工作的,最好只看例子,并亲自尝试,这就是我们要做的:
>>> my_list = [1, 2, 3, 4, 5, 6, 7, 8]
>>> my_list[0:3] # get the first three elements of a list
[1, 2, 3]
>>> my_list[:3] # start is 0 by default
[1, 2, 3]
>>> my_list[4:] # skip the first 4 elements
[5, 6, 7, 8]
步长值
默认情况下,步长值为 1。如果你增加它,你可以跳过元素。让我们试试这个:
>>> my_list = [1, 2, 3, 4, 5, 6, 7, 8]
>>> my_list[::2] # skip one each time
[1, 3, 5, 7]
倒退
像列表索引一样,我们也可以通过切片提供负数。这里有一个小小的 ASCII 艺术展示给你如何在列表中倒计数:
my_list = [1 , 2, 3, 4, 5, 6]
-6 -5 -4 -3 -2 -1 (these are the index numbers)
请记住,为了后退,您需要设置一个负的步长:
>>> my_list = [1, 2, 3, 4, 5, 6]
>>> my_list[-1:-3:-1]
[6, 5]
反转 Python 列表
在 Python 中,实际上有三种方法可以用来反转列表:
- 就地反转,使用每个列表固有的内置反转方法
- 使用负步长的列表分片会产生一个新列表
- 用
reversed()
函数创建一个反向迭代器
在下面的代码片段中,我演示了这三个部分。以下各节将对它们进行详细说明:
https://crumb . sh/embed/qd 2vr 57 bcpv
恢复 Python 列表的三种方法
使用内置的反向方法
list.reverse()
方法执行就地反转,这意味着它对列表进行重新排序。换句话说,该方法不会以相反的顺序返回新的列表对象。下面是如何使用reverse()
:
>>> lst = [1, 2, 3, 4]
>>> lst.reverse()
>>> lst
[4, 3, 2, 1]
>>> lst.reverse()
>>> lst
[1, 2, 3, 4]
使用列表切片反转列表
虽然可以用每个列表都有的list.reverse()
方法来反转列表,但是也可以用列表切片来反转列表,使用-1 的负步长。这里的不同之处在于列表分片会产生一个新的第二个列表。它保持原始列表不变:
>>> lst = [1, 2, 3, 4]
>>> lst[::-1]
[4, 3, 2, 1]
>>> lst
[1, 2, 3, 4]
创建反向迭代器
最后,您可以使用reversed()
内置函数,它创建一个迭代器,反向返回给定 iterable(我们的列表)的所有元素。这种方法在 CPU 和内存使用方面非常便宜。它需要做的就是在 iterable 对象上向后走。它不需要移动数据,也不需要为第二个列表保留额外的内存。所以如果你需要逆向迭代一个(大)列表,这应该是你的选择。
以下是使用该功能的方法。请记住,您只能使用迭代器一次,但是创建一个新的迭代器很便宜:
>>> lst = [1, 2, 3, 4]
>>> rev = reversed(lst)
>>> rev
<list_reverseiterator object at 0x0000023DB96A25C0>
>>> for i in rev:
... print(i)
...
4
3
2
1
了解关于 Python 列表的更多信息
因为有很多关于列表理解的内容要讲,所以我为这个主题专门写了一篇文章。一个 Python list comprehension 是一个语言构造,我们用它来创建一个基于现有列表的列表,而不用创建 for-loops 。
您可能喜欢的一些其他资源:
- 官方 Python 文档关于列表。
- 如果你对内部感兴趣:列表通常在内部实现为一个链表。
- Python 也有数组,它们非常相似,来自其他编程语言的人对这个术语应该很熟悉。它们存储数据的效率更高,但只能存储一种类型的数据。
Python Set:示例代码的原因和方法
Python 集合是不同元素的集合。该集合与列表和元组有一些共同之处,但也有一些重要的区别:
- 一个 Python 集合只能包含唯一值
- 集合未排序
更正式的说法是:集合是不同对象的无序集合。在本文中,我们将仔细研究集合以及如何使用它们。我将更多地关注集合必须提供的额外功能,而不是列表和其他序列类型的基本功能。
目录
如何创建一个 Python 集合
根据具体情况,有几种方法可以创建 Python 集合。要从头开始创建一个集合并直接向其中添加一些元素,可以使用花括号:
names = { "Eric", "Ali", "John" }
# Mixed types are allowed
mixed_set = { 'a', 1, (1, 2) }
集合使用与 Python 字典相同的花括号,但是它们很容易区分,因为集合总是包含一系列元素,用逗号分隔。相反,字典包含用冒号指定的键值对。
要创建空集,您可以使用set()
功能:
my_set = set()
my_set.add(1)
my_set.add('Erik')
如您所见,您可以使用 set 对象上的add
方法向集合中添加元素。如果想一次添加多个元素,需要使用 update 方法并提供一个 iterable 对象,如 list 、 range 或 tuple :
my_set = set()
my_set.update(range(3))
my_set.update(['a', 'b'])
print(my_set)
# {0, 1, 2, 'b', 'a'}
您也可以使用set()
函数将任何可迭代对象转换成集合:
print( set([1, 2, 3]) )
# {1, 2, 3}
print( set(range(3)) )
# {0, 1, 2}
最后,您可以使用集合理解来创建集合。集合理解和列表理解完全一样,所以如果你对这个概念不熟悉,我建议你读一下链接的文章。
为了便于演示,这里举一个例子。记住字符串也是序列对象,所以它们是可迭代的。因为我们可以过滤集合理解,所以让我们也过滤标点和空格字符:
my_set = { x for x in 'Hi, my name is...' if x not in '., ' }
print(my_set)
# {'n', 'a', 'e', 'i', 's', 'y', 'H', 'm'}
有了这个例子,集合没有顺序就变得非常清楚了!
设置并列出
在我们继续之前,我想分享两个很多人搜索的常用技巧。
对列表进行重复数据删除
集合只包含唯一的元素,我们可以通过给set()
函数一个列表来创建一个集合。这些都是对列表进行重复数据删除所需的要素。重复数据删除是删除重复项的过程,将列表转换为集合是 Python 中最简单的方法:
my_list = [1, 1, 1, 2, 3, 4, 4, 4, 4, 2, 2, 2]
my_set = set(my_list)
print(my_set)
# {1, 2, 3, 4}
将集合转换为列表
要将集合转换为列表,只需用集合作为参数创建一个新列表:
A = { 1, 2, 3 }
my_list = list(A)
print(my_list)
# [1, 2, 3]
你为什么需要器械包?
人们使用器械包有很多原因。
- 最常见的方法是从序列中删除重复项(如前面演示的列表)
- 许多人用它们来执行成员测试(这是这个独特元素集中的一个元素)
但是有更多的理由使用集合。如果你不熟悉集合论,你可能想在维基百科上阅读一下。不过,我也会尽我所能解释一些基本的东西。简而言之:集合可用于执行数学集合运算,如:
- 找出两组之间的差
- 联合:组合集合,只保留唯一元素
- 交集:哪些元素在两个集合中都存在
- 寻找子集和超集
这些操作可以用文氏图可视化。文氏图显示了集合之间的逻辑关系。在你的生活中,你很有可能已经见过下面这些了:
两组的维恩图。来自维基百科的公共领域图片(来源
在接下来的示例中,我将参考这张图片,我将使用名称 A 和 b。这里的所有示例都使用两组,但重要的是要知道,它们在两组以上的情况下也同样适用。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
数学 Python 集合运算
本节将演示和解释所有的数学集合运算。别让数学部分吓到你,没那么难!
寻找 Python 集合之间的差异
让我们定义两个集合,A 和 B,并找出它们之间的区别。两套有什么区别?在查看维恩图时,我们希望找到只存在于 A 中的元素。换句话说,我们希望消除也存在于 b 中的任何重叠元素。或者,更具体地说:我们希望所有元素都在 A 中,但不在 A ∩ B 中。
我们可以通过使用减号(减法)运算符来实现:
A = { 1, 2, 3, 4, 5 }
B = { 3, 4, 5, 6, 7 }
print(A-B)
# {1, 2}
# And the reverse
print(B-A)
# {6, 7}
a 和 B 有一些重叠:数字 3、4 和 5 在两组中都有。查看文氏图时,这些数字属于标有 A ∩ B 的部分。如果我们只想要 A 中的唯一数字,我们用 A–B 从 A 中减去 B。当我们只想要 B 中的唯一数字时,我们从 B 中减去 A:B–A。
找出 Python 集合之间的对称差异
两个集合之间的对称差由集合 A 或集合 B 中的元素组成,但不是两者都有。换句话说:A 中的所有元素加上 B 中的元素,减去 A ∩ B 部分。为了找到对称差,我们可以使用^算子:
A = { 1, 2, 3, 4, 5 }
B = { 3, 4, 5, 6, 7 }
print(A^B)
# {1, 2, 6, 7}
求两个 Python 集合的交集
交集是维恩图中标有 A ∩ B 的部分。交集由两个集合中的元素组成。为了找到交集,我们可以使用&运算符:
A = { 1, 2, 3, 4, 5 }
B = { 3, 4, 5, 6, 7 }
print(A & B)
# {3, 4, 5}
子集和超集
如果 A 是 B 的子集,这意味着 A 的所有元素也存在于 B 中。然而,子集 A 可以小于集合 B,这意味着 B 中的一些元素可能不存在于 A 中。因此,如果 A 几乎完全重叠,但有一个元素不存在于 B 中,则它不是 B 的子集。我们可以用小于运算符的来检查 A 是否是 B 的子集:<。
如果 B 是 A 的超集,这意味着 B 拥有 A 的所有元素,但也可能有额外的元素。我们可以通过大于运算符:>来检查 B 是否是 A 的超集。
让我们看一些例子:
A = { 1, 2, 3 }
B = { 1, 2, 3, 4, 5 }
C = { 1, 2, 3, 10 }
# is A a subset of B
print(A < B)
# True
# is C a subset of B?
print(C < B)
# False
# No, it has a 10 that's not in B
# Is B a superset of A?
print(B > A)
# True
# B is not a superset of C since C has a 10 in it
print(B > C)
# False
print(A < A)
# False
print(A <= A)
# True
print(A >= A)
# True
真子集和超集
Python 区分了子集(在数学中我们写⊂),也称为真子集,和子集或等于(在数学中我们写⊆).
< and >操作符做前者,检查一个适当的子集。如果一个< B, it means A is a subset of B and it’s not itself (A != A). You can check this for yourself too with 【T0】 , which returns 【T1】 . If you want to check for 子集或等于,则可以使用< =。超集也是如此:使用> =。
联盟
最后,我们可以将两个 Python 集合加在一起,只保留唯一的元素。这叫做集合的并集。在数学中,联合的符号是 A ∪ B,但是在 Python 中,我们必须使用管道操作符(|
)来创建联合:
A = { 1, 2, 3 }
B = { 3, 4, 5 }
print(A|B)
# {1, 2, 3, 4, 5}
来自 A 和 B 的所有元素都出现在新创建的集合中,但是因为集合只包含唯一值,所以重叠元素 3 只出现一次。
命名集方法
几乎所有的 set 方法都有命名的等价方法,例如:
- 也可以通过调用 set 对象上的
issubset()
和issuperset()
方法来使用<= and > =方法。 - |操作符可以替换为对
union()
方法的调用 - …等等
在本文末尾的表格中,我将列出所有操作及其等价名称。
使用操作符和使用命名方法的最大区别是命名方法将任何可迭代对象作为参数。所以你可以用 A.union(L)来计算集合 A 和列表 L 之间的并集。这为我们省去了从 Python 列表到 Python 集合的转换,因此效率更高。
Python frozenset
除了常规的、可变的set
,还有frozenset
。您可以猜到这种数据类型与常规集合的不同之处:它在创建后就被直接冻结了,因此不能在其中添加或删除元素。然而,您可以混合使用set
和frozenset
类型:所有常规操作,如 union 和 intersection,也适用于set
和frozenset
的组合。
一个frozenset
的优点是它是可散列的,这意味着你可以把它作为一个字典键,或者甚至作为另一个set
中的一个元素。
所有 Python 集合操作
下表方便地列出了所有数学集合运算、所需的运算符、它们的命名方法等效项、示例代码及其作用:
名称 | 操作员示例 | 方法示例 | 它的作用 |
---|---|---|---|
联盟 | A | B | A.union(B) |
创建一个组合 A 和 B 的集合 |
交集 | 水果派 | A.intersection(B) |
用 A 和 B 之间的公共元素创建一个集合 |
差异 | A - B |
A.difference(B) |
用不在 B 中的元素创建一个集合 |
对称差 | A ^ B |
A.symmetric_difference(B) |
用 A 或 B 中的元素创建一个集合,但不能同时存在于两者中 |
是超集。 | A >= B |
A.issuperset(B) |
如果 B 的每个元素都在 A 中,则返回True |
是子集吗? | A <= B |
A.issubset(B) |
如果 A 的每个元素都在 B 中,则返回True |
是不相交的? | – | A.isdisjoint(B) |
如果 A 与 B 没有共同的元素,则返回True |
是真的超集吗? | A > B | 没有办法 | 如果 A >= B 且 A,则为 True!= B |
是真子集吗? | A < B | 没有办法 | 如果 A =< B 并且 A!= B |
Python 集合操作概述,包括示例和解释
请注意,您也可以在赋值样式中使用上述操作,例如:
- A |= B 将更新集合 A,添加集合 B 中的元素
- A |= B | C | …将更新集合 A,添加来自集合 B、C 等的元素。
以上也适用于交集(&)和差集(-)。
在下一个表格中,我列出了一些可以用来操作集合的额外操作:
名称 | 例子 | 什么是 |
---|---|---|
增加 | A.add(元素) | 将元素添加到集合 |
移动 | A.remove 元素 | 从集合中移除元素 |
抛弃 | A.discard 元素 | 如果元素存在,则将其从集合中删除(省去检查存在的麻烦) |
流行音乐 | a .波普() | 从集合中移除并返回任意元素(如果集合为空,则引发 KeyError) |
清楚的 | a .清除() | 从集合中移除所有元素 |
设定索引
如果你希望通过使用索引(比如用my_set[1]
)来访问集合元素,那你就不走运了。集合没有顺序,因此不能被索引。如果你认为你需要集合索引,你可能想错了方向。看看其他解决方案,比如使用列表或将你的集合转换成列表。
结论
我们已经了解了 Python 集合,以及它们与列表和元组等其他序列类型的不同之处。除了序列的重复数据删除,集合还可用于执行集合计算。我们回顾了所有的集合计算,并查看了示例代码,以了解它们是如何工作的。使用方便的表格,您可以快速查看所有集合操作。
虽然我相信这篇文章触及了你需要知道的一切,但你可能也想看看关于 set 的官方文档。
Python 字典:如何创建和使用,附示例
Python 字典是该语言最强大的数据类型之一。在其他编程语言和一般的计算机科学中,字典也被称为关联数组。它们允许您将一个或多个键与值相关联。如果你熟悉 JSON ,你可能会有宾至如归的感觉。字典的语法非常类似于 JSON 文档的语法。
目录
- 创建 Python 字典
- 访问和删除键值对
- 覆盖字典条目
- 使用 try…除了
- 有效的字典值
- 有效的字典键
- 创建 Python 字典的更多方法
- 检查一个关键字是否存在于 Python 字典中
- 获取 Python 字典的长度
- 字典查看对象
- 合并字典
- 比较 Python 字典
- 内置 Python 字典方法
- 结论
创建 Python 字典
让我们看看如何在 Python REPL 中创建和使用 Python 字典:
>>> phone_numbers = { 'Jack': '070-02222748',
'Pete': '010-2488634' }
>>> my_empty_dict = { }
>>> phone_numbers['Jack']
'070-02222748'
字典是用花括号创建的。在这些大括号内,我们可以添加一个或多个键值对。当添加多个键值对时,这些键值对用逗号分隔。我们示例中的第一个字典将键(如 Jack 和 Pete)与值(他们的电话号码)相关联。第二本字典是一本空的。
访问和删除键值对
既然您已经看到了如何初始化一个字典,那么让我们来看看如何在一个已经存在的字典中添加和删除条目:
>>> phone_numbers['Eric'] = '06-10101010'
>>> del(phone_numbers['Jack'])
>>> phone_numbers
{'Pete': '010-2488634', 'Eric': '06-10101010'}
默认值和 dict.get()
从字典中检索单个值的另一种方法是使用 get-方法。优势?如果找不到该键,它将返回默认值None
。您也可以指定自己的默认值。
使用 get-method,您不必用 try 包围操作…除了。当处理从 YAML 或 JSON 文件解析的配置数据时,它是理想的,在这些文件中,您的软件为未设置的配置项提供默认值。
一个例子:
>>> config = { 'host': 'example.org' }
>>> config.get('port', 80)
80
>>> config.get('schema')
>>>
最后一个 get 调用返回None
,但是最新版本的 REPL 不打印任何内容。
覆盖字典条目
要覆盖一个条目,只需给它分配一个新值。你不需要先做这件事。例如:
>>> phone_numbers = { 'Jack': '070-02222748',
'Pete': '010-2488634' }
>>> phone_numbers['Jack'] = '1234567'
使用 try…除了
如果请求的键不存在,抛出类型为KeyError
的异常:
>>> phone_numbers['lisa']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'lisa'
如果您知道数据可能会丢失,例如,当解析来自外界的输入时,请确保用try ... except
KeyError 包围您的代码。我已经在我的关于 try 的文章的最佳实践部分详细解释了这一点……除了。在那篇文章中,我还解释了请求原谅,而不是许可的概念。例如,在尝试访问某个键之前不检查它是否存在。相反,只要试一试,如果异常不存在就捕捉它。
有效的字典值
你可以把任何东西放进字典里。你不局限于数字或字符串。事实上,您可以将字典和 Python 列表放入字典中,并以非常自然的方式访问嵌套的值:
>>> a = { 'sub_dict': { 'b': True },
'mylist': [100, 200, 300] }
>>> a['sub_dict']['b']
True
>>> a['mylist'][0]
100
Python 的 JSON 解码和编码库在解析更复杂的 JSON 文档时使用了 Python 的这个特性。它创建列表、字典和其他有效数据类型的嵌套树。
有效的字典键
如果你愿意,你也可以疯狂地使用你的字典键。唯一的要求是密钥是可散列的。像列表、字典和集合这样的可变类型不起作用,并导致类似于TypeError: unhashable type: ‘dict’
的错误。
除了这个限制之外,您可以使用所有数据类型作为字典键,包括本地类型,如元组、float
和[int](https://python.land/python-data-types/python-integer)
,甚至是类名或基于类的对象。尽管对大多数人来说完全没用,我还是要演示一下:
>>> crazy_dictionary = { int: 1, float: 2, dict: 3 }
>>> crazy_dictionary[dict]
3
>>> class P:
... pass
...
>>> crazy_dictionary[P]=1
>>> p = P()
>>> crazy_dictionary[p]=2
一个更可能的用例是使用数字作为键。例如,考虑马拉松赛跑者的注册:
>>> runners = { 1000: 'Jack', 1001: 'Eric',
1002: 'Lisa' }
>>> runners[1001]
'Eric'
创建 Python 字典的更多方法
根据您的数据源,有更高级的方法来初始化字典,这可能会派上用场。
使用 dict()构造函数
dict()
函数从键值对(元组)的序列或列表构建一个字典:
>>> dict([ ('Jack', '070-02222748'),
('Pete', '010-2488634'),
('Eric', '06-10101010') ])
{'Jack': '070-02222748', 'Pete': '010-2488634', 'Eric': '06-10101010'}
词典释义
类似于 list comprehensions ,你也可以使用字典 comprehensions 来创建一个新的字典。列表只包含值,而字典包含键/值对。因此,字典理解需要定义这两者。除此之外,语法是相似的:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
请阅读我关于列表理解的文章,以获得关于理解的更详细的解释。
使用 dict.fromkeys
dict.fromkeys(keys, value)
方法根据提供给它的keys
列表创建一个新字典。所有元素的值将被设置为所提供的value
,或者默认为None
,如果您不提供值的话。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
请参见以下代码:
>>> names = ('Eric', 'Martha', 'Ellen')
>>> phone_numbers = dict.fromkeys(names, None)
>>> phone_numbers
{'Ellen': None, 'Eric': None, 'Martha': None}
将 JSON 对象解析为字典
正如在使用 JSON 一节中所解释的,您也可以像这样将 JSON 数据解码成字典:
>>> import json
>>> jsonstring = '{ "name": "erik", "age": 38,
"married": true}'
>>> json.loads(jsonstring)
{'name': 'erik', 'age': 38, 'married': True}
检查一个关键字是否存在于 Python 字典中
您可以使用关键字in
和not in
检查字典中是否存在关键字:
>>> 'Jack' in phone_numbers
True
>>> 'Jack' not in phone_numbers
False
获取 Python 字典的长度
内置 Python len()
函数返回字典中键/值对的数量:
>>> phone_numbers = { 'Jack': '070-02222748',
'Pete': '010-2488634',
'Eric': '06-10101010' }
>>> len(phone_numbers)
3
字典查看对象
一些内置的字典方法返回一个视图对象,提供一个关于字典的键和值的窗口。在我们开始使用这样的视图对象之前,您需要理解一个重要的概念:视图对象中的值随着字典内容的变化而变化。
关键字()和值()
这个例子很好地说明了这一点,在这个例子中,我们使用了其中的两个视图:键()和值()。Keys 返回字典中所有键的视图,而 values()返回其所有值的视图:
https://crumb . sh/embed/32c 9 uev 8 hsj
如果这不起作用,这里有一个非交互式版本:
phone_numbers = { 'Jack': '070-02222748',
'Pete': '010-2488634',
'Eric': '06-10101010' }
names = phone_numbers.keys()
numbers = phone_numbers.values()
phone_numbers['Linda'] = '030-987612312'
print(names)
print(numbers)
# Loop through a view object with:
for number in numbers:
print(number)
这段代码的输出是dict_keys(['Jack', 'Pete', 'Eric', 'Linda'])
。如您所见,Linda 也是列表的一部分,尽管她是在创建了names
视图对象之后添加的。
dict.items():遍历 Python 字典
字典的items()
方法返回一个 iterable view 对象,提供键和值,如下所示。您可以用一个简单的 Python for-loop 遍历这个对象:
>>> phone_numbers.items()
dict_items([('Jack', '070-02222748'),
('Pete', '010-2488634'),
('Eric', '06-10101010')])
>>> for name, phonenr in phone_numbers.items():
... print(name, ":", phonenr)
...
Jack : 070-02222748
Pete : 010-2488634
Eric : 06-10101010
或者,您可以使用keys()
和values()
方法只遍历键或值。这两个函数都返回一个可迭代的视图对象。
获得所有钥匙的更多方法
我们已经看到了dict.keys()
方法,它返回一个包含所有字典键列表的视图对象。这个对象的优点是它与字典保持同步。它非常适合循环所有的键,但是您仍然可以选择list
或sorted
方法,因为它们返回一个您也可以操作的原生列表。
还有两种简单的方法可以从字典中获取所有的键:
>>> phone_numbers = { 'Jack': '070-02222748',
'Pete': '010-2488634',
'Eric': '06-10101010' }
>>> list(phone_numbers)
['Jack', 'Pete', 'Eric']
>>> sorted(phone_numbers)
['Eric', 'Jack', 'Pete']
list()
返回按插入顺序排列的所有键,而sorted()
返回按字母顺序排列的所有键。
合并字典
如果您运行的是 Python 3.9 或更高版本,则可以对字典使用新引入的合并运算符:
merged = dict1 | dict2
如果您仍然使用 3.5 到 3.9 之间的 Python 版本,可以使用以下方法合并两个字典:
dict1 = { 'a': 1, 'b': 2 }
dict2 = { 'b': 3, 'c': 4 }
merged = { **dict1, **dict2 }
print (merged)
# {'a': 1, 'b': 3, 'c': 4}
比较 Python 字典
如果您需要比较两个字典,您可以简单地使用比较运算符,如下所示:
>>> first_dict = { 'a': 1, 'b': 2, 'c': 'a string' }
>>> second_dict = { 'a': 1, 'b': 2, 'c': 'a string' }
>>> first_dict == second_dict
True
这看起来和听起来都很琐碎,其实不然!毕竟,字典可以包含任何类型的对象!因此,Python 必须遍历所有的键和值,并逐个比较它们。
您可能想知道,具有相同键和值的字典,以另一种顺序插入,是否是相同的。让我们看看这个:
>>> first_dict = { 'a': 1, 'b': 2, 'c': 'a string' }
>>> second_dict = { 'b': 2, 'a': 1, 'c': 'a string' }
>>> first_dict == second_dict
True
它们与 Python 是一样的,尽管顺序不同。
注意:从 Python 3.7 开始,字典的顺序保证是插入顺序。换句话说,这意味着字典的顺序是由您插入条目的顺序决定的。
内置 Python 字典方法
每个字典都继承了许多方便的内置函数,如下表所示:
方法 | 什么是做 | 例子 |
---|---|---|
clear() |
删除所有键/值对(清空字典) | phone_numbers.clear() |
get(key) |
用给定的键和可选的默认值获取单个项目 | phone_numbers.get('Martha', 'Unknown person') |
items() |
从字典中返回包含键值对的视图对象 | phone_numbers.items() |
keys() |
返回一个视图对象,其中包含字典中所有键的列表 | phone_numbers.keys() |
values() |
返回一个 view_object,其中包含字典中所有值的列表 | phone_numbers.values() |
pop(key, default_value) |
返回并移除具有指定键的元素 | phone_numbers.pop('Martha') |
popitem() |
返回并移除最后插入的项目(Python 3.7 以上)或随机项目 | phone_numbers.popitem() |
setdefault(key, value) |
返回指定键的值。如果键不存在,则插入给定值 | phone_numbers.setdefault('John Doe', 1234) |
update(iterable) |
从给定的 iterable(例如字典)中添加所有对 | phone_numbers.update({"Alina": 1234, "Alice", 2345}) |
Python 字典的内置方法
结论
您已经学习了什么是 Python 字典,如何创建字典,以及如何使用它们。我们已经用示例代码查看了许多涉及 Python 字典的实际用例。如果还缺少什么,或者只是想了解更多关于字典的知识,你可以去 Python.org 的官方手册页。
语言深度潜水
在这一章中,我们将深入一些具体的 Python 语言主题。这些主题可能已经被触及。例如,我们在 Python 函数的介绍中介绍了函数。但是你将从这一章学到的高级 Python 函数概念扩展了这些知识。
此处涉及的其他主题:
- 列表压缩:一种减少代码中 for 循环数量的优雅方式
- Python docstring :用于正式记录您的 Python 代码
- 例外与尝试..Python 中处理错误的语句除外
Python Try Except:带示例代码的异常教程
Python 异常处理是识别和响应程序中错误的过程。换句话说,这是一种处理程序中可能出现的错误的方法。在本文中,您将学习如何通过使用 Python 关键字try
和except
来处理 Python 中的错误。它还将教您如何创建自定义异常,这些异常可用于定义您自己的特定错误消息。
目录
- 什么是例外?
- Python 试除
- 用 try except 捕获异常
- finally 和 else 块
- 常见的 Python 异常
- 例外最佳实践
- 创建自定义异常
- 引发(或抛出)异常
- 如何打印 Python 异常
- 继续学习
什么是例外?
异常是在程序执行过程中出现的情况。这是意想不到的事情发生的信号。Python 通过某种类型的对象来表示异常。
在 Python 中,所有内置的、非系统退出的异常都是从Exception
类派生的。异常有自己的描述性名称。例如,如果你试图将一个数除以零,你会得到一个ZeroDivisionError
异常,它也是Exception
类的子类。
对于所有异常的完整层次结构,如果您感兴趣,可以查看 Python 手册。下面是这个层次结构的一小段摘录,只是为了说明:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- Exception
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
.....
如果您对对象、类和继承的知识有点生疏,您可能想先阅读我关于对象和类的文章以及我关于继承的文章。
Python 试除
当一些意想不到的事情发生时,我们可以在错误点引发一个异常。当出现异常时,Python 会停止当前的执行流程,并开始寻找能够处理它的异常处理程序。那么什么是异常处理程序呢?这就是 try 和 except 语句发挥作用的地方。
尝试..除...之外..其他..最后
如图所示,我们可以从 try 语句开始创建一个代码块。这基本上意味着:尝试运行这段代码,但是可能会发生异常。
在我们的 try 块之后,必须跟随一个或多个 except 块。这就是奇迹发生的地方。这些 except 块可以捕获一个异常,正如我们通常所说的。事实上,许多其他编程语言都使用一个名为catch
的语句,而不是except
。每个except
块可以处理特定类型的异常。
记住:类是分等级的。因此,例外也是如此。因此,except 块必须从最具体的(如ZeroDivisionError
)到不太具体的(如ArithmeticError
)。
为了演示这一点,想象一下当我们从捕获Exception
的 except 块开始时会发生什么。第一个块将捕获基本上所有的东西,因为大多数异常都是从这个块继承的,使得除了块之外的其他块变得无用。
现在,让我们首先回到引发异常的问题。当出现异常时,能够处理异常的异常处理程序可以在附近,但也可以在不同的模块中。重要的是要认识到,Python 不会为了一个异常处理程序而随机扫描你的代码。相反,处理程序应该在调用栈的某个地方。
请暂时忘记else
和finally
。我将在本文后面详细解释它们。我们首先需要讨论调用堆栈,以真正理解异常是如何到达异常处理程序的。
调用栈
调用栈是当前正在执行的函数的有序列表。例如,您可能会调用函数 A,函数 A 调用函数 B,函数 B 调用函数 C。我们现在有一个由 A、B 和 C 组成的调用堆栈。当 C 引发异常时,Python 将在这个调用堆栈中查找异常处理程序,从结束到开始进行回溯。它可以在函数 C 中(离异常最近),在函数 B 中(稍微远一点),在函数 A 中,甚至在我们调用函数 A 的程序的顶层。
如果 Python 找到了合适的 except 块,它就执行该块中的代码。如果没有找到,Python 会自己处理异常。这意味着它将打印异常并退出程序,因为它不知道如何处理它。
希望你还在我身边!如果没有,不用担心。本页的例子有望让这一切变得更加清晰。当你读完整篇文章后,你可能想重温这一节。
用 try except 捕获异常
让我们最后写一些实际的代码!为了处理一个异常,我们需要捕捉它。正如我们刚刚了解到的,我们可以通过使用try
和except
关键字来捕捉异常。当我们在try
块中出现异常时,就会执行except
块中的代码。
简单的例子
我们先来试一个简单的例子。你应该知道,我们不能被零整除。如果我们这样做,Python 将抛出一个名为ZeroDivisionError
的异常,它是ArithmeticError
的子类:
try:
print(2/0)
except ZeroDivisionError:
print("You can't divide by zero!")
如果您在 try 块中调用了一个 Python 函数,并且该函数中发生了异常,那么代码执行流程将在异常点处停止,并执行 except 块中的代码。试着再做一次,不要尝试和排除。您将看到 Python 为我们打印了异常。您可以在下面的代码片段中做到这一点:
https://crumb.sh/embed/35QQzjrrKKY
另外,请注意,如果您没有自己处理异常,Python 会将错误输出到 stderr。在上面的片段中,这是可见的,因为输出出现在“错误”选项卡中,而不是“输出”选项卡中。
捕捉 IOError
让我们试试另一个更常见的例子。毕竟,谁会把一个数除以零呢,对吧?
与外界交互时可能会出现异常,例如处理文件或网络时。例如,如果你试图用 Python 的打开一个文件,但是那个文件不存在,你会得到一个IOError
异常。如果由于权限问题,您没有访问文件的权限,您将再次得到一个IOError
异常。让我们看看如何处理这些异常。
分配
请执行以下操作:
- 运行下面的代码,注意文件名(它不存在)。看看会发生什么。
- 将文件名改为 myfile.txt 文件,然后再次运行代码。现在发生了什么?
https://crumb.sh/embed/v8wppGhGAmi
Python try-except 赋值
或者,下面是要复制/粘贴的代码:
try:
# Open file in read-only mode
with open("not_here.txt", 'r') as f:
f.write("Hello World!")
except IOError as e:
print("An error occurred:", e)
答案
在第一种情况下没有找到文件。您应该得到以下输出:
An error occurred: [Errno 2] No such file or directory: 'not_here.txt'
在第二种情况下,创建文件后您仍然会得到一个错误。这一次是因为我们试图写入一个以只读模式打开的文件。有关这些模式的更多信息,请阅读关于使用 Python 打开、读取和写入文件的文章。错误应该是这样的:
An error occurred: not writable
虽然这是一个错误,但它不会写入操作系统的 stderr 输出。那是因为我们自己处理了异常。如果您取消了尝试..除非完全从代码中删除,然后尝试以只读模式写入文件,否则 Python 将捕获错误,强制程序终止,并显示以下消息:
Traceback (most recent call last):
File "tryexcept.py", line 3, in <module>
f.write("Hello World!")
io.UnsupportedOperation: not writable
finally 和 else 块
还记得我让你暂时忘记的另外两个街区吗?现在让我们看看这些,从finally
块开始。
try-except 中的 finally 块
无论是否发生异常,都会执行finally
块。Finally
块是有用的,例如,当你想不管发生什么都关闭一个文件或一个网络连接时。毕竟,您希望清理资源以防止内存泄漏。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
这里有一个工作中的例子,我们打开一个文件而不使用 with statement
,迫使我们自己关闭它:
try:
# Open file in write-mode
f = open("myfile.txt", 'w')
f.write("Hello World!")
except IOError as e:
print("An error occurred:", e)
finally:
print("Closing the file now")
f.close()
你也可以试试这个互动的例子:
https://crumb.sh/embed/9THbhdcbmWp
您应该会看到屏幕上显示“正在关闭文件”的消息。现在将书写模式从“w”改为“r”。你会得到一个错误,因为文件不存在。尽管有这个异常,但由于 finally 块,我们还是尝试关闭文件。这反过来也会出错:抛出一个NameError
异常,因为文件从未打开过,因此f
不存在。你可以用嵌套的try.. except NameError
来解决这个问题。你自己试试。
try-except 中的 else 块
除了except
和finally
块,您还可以添加一个 else 块。else 块仅在没有异常发生时执行。所以它不同于 finally 块,因为即使发生异常,finally 也会执行。
什么时候应该使用else
块?为什么不在try
块中添加额外的代码呢?好问题!
根据 Python 手册,使用 else 子句比在 try 子句中添加额外的代码要好。但是为什么呢?原因是它避免了意外地捕获一个不是由受 try 和 except 语句保护的代码首先引发的异常。我承认我自己不经常使用 else 块。此外,我发现它们有些令人困惑,尤其是对来自其他语言的人来说。
常见的 Python 异常
有些例外如此常见,以至于你不可避免地会遇到它们。以下是一些最常见的:
| 异常名称 | 当你遇到它的时候 | 引发异常的示例情况 |
| 句法误差 | 当 Python 语法中有错误时引发。如果没有被捕获,这个异常将导致 Python 解释器退出。 | pritn('test')
|
| KeyError | 在字典中找不到键时引发。 | d = { 'a': 1} d['b']
|
| 索引错误 | 当索引超出范围时引发。 | lst = [1, 2, 3] lst[10]
|
| 键盘中断 | 当用户点击中断键(Ctrl+C)时引发 | 按下 control+c |
在你职业生涯的某个阶段,你会遇到一些常见的例外
如果你愿意,你可以试着有意唤起这些例外。我向你保证,在你的 Python 编程生涯中,这些你会遇到无数次。知道并理解它们的含义以及它们何时出现将极大地帮助您调试代码。
例外最佳实践
现在我们已经知道了处理异常的机制,我想与您分享一些最佳实践。
除了方块,不要使用空白
我以前在博客文章“如何在 Python 中不处理异常”中写过这个问题。当你想捕捉大范围的异常时,不要使用空白块。我的意思是这样的:
try:
...
except:
print("An error occurred:")
您可能会在网上的代码示例中遇到这种情况。如果你这样做了,那就养成改进异常处理的习惯。为什么应该这样做,以及如何像上面的例子那样改进代码?
包括系统异常在内的所有异常都继承自一个名为BaseException
的类。如果一个except
子句提到了一个特定的类,那么该子句也会处理从该类派生的任何异常类。一个空的except
等价于except BaseException
,因此它将捕捉所有可能的异常。
所以虽然语法允许,但我不推荐。例如,你还会捕捉到KeyboardInterrupt
和SystemExit
异常,它们会阻止你的程序退出。相反,使用一个 try 块,其中包含您知道可以处理的显式异常列表。或者,如果你真的需要,捕捉Exception
基类来处理几乎所有的常规异常,而不是系统异常。
如果你想冒险,你可以试着捕捉所有的异常,看看会发生什么:
from time import sleep
while True:
try:
print("Try and stop me")
sleep(1)
except:
print("Don't stop me now, I'm having such a good time!")
你可能需要关闭你的终端来停止这个程序。现在改变 except 块来捕捉Exception
。您仍然可以捕获几乎所有的异常,但是程序会在出现系统异常时退出,比如KeyboardInterrupt
和SystemExit
:
from time import sleep
while True:
try:
print("Try and stop me")
sleep(1)
except Exception:
print("Something went wrong")
最好请求原谅
在 Python 中,你会经常看到这样一种模式,人们只是简单地尝试一些事情是否可行,如果不可行,就捕捉异常。换句话说,请求原谅比请求允许更好。这与其他语言不同,在其他语言中,你更倾向于请求许可。例如,在 Java 中,异常会减慢你的程序,你通过检查对象而不是简单地尝试来“请求许可”。
更具体地说:在 Python 中,我们通常只是试图访问字典中的键。如果键不存在,我们将得到一个异常并处理它。假设我们刚刚将一些外部提供的 JSON 转换成了一个字典,现在开始使用它:
import json
user_json = '{"name": "John", "age": 39}'
user = json.loads(user_json)
try:
print(user['name'])
print(user['age'])
print(user['address'])
...
except KeyError as e:
print("There are missing fields in the user object: ", e)
# Properly handle the error
...
这将打印错误:
There are missing fields in the user object: 'address'
我们本来可以添加三个检查(if 'name' in user
、if 'age' in user
等)。)以确保所有字段都在那里。但这不是一个好的做法。它可能会引入大量代码来检查键是否存在。相反,我们在 except 块中请求原谅一次,这样更干净,可读性更好。如果您担心性能:在 Python 中,异常不会占用太多 CPU 周期。大量的比较实际上比捕捉一个异常要慢(如果它发生的话!).
创建自定义异常
正如我们之前所学的,所有内置的、非系统退出的异常都是从Exception
类派生的。所有用户定义的异常也应该从此类派生。所以如果我们想创建自己的异常,我们需要创建一个Exception
类的子类。
例如,如果您想要创建一个异常来指示没有找到用户,那么您可以创建一个UserNotFoundError
异常。最基本的形式是这样的:
class UserNotFoundError(Exception):
pass
它继承了Exception
的所有属性和方法,但是我们给了它一个新的名字以区别于Exception
类。这样,我们就能够用一个 except 块明确地捕捉它。
这个异常的名字清楚地告诉了我们遇到的问题的类型,所以作为一个额外的好处,它也可以作为一种代码文档。就像命名良好的变量和函数一样,在回读代码时,命名良好的异常会带来很大的不同。
我们将在下面的例子中使用这个类。
引发(或抛出)异常
我们知道一些内置异常以及如何创建自定义异常。我们还知道如何用 try 和 except 捕捉异常。剩下的就是所谓的引发或抛出异常。您可以使用 raise 关键字自己引发异常。
在下面的例子中,我们使用您之前定义的UserNotFoundError
。我们调用函数fetch_user
,它从一个虚拟的数据库中获取一些用户数据。如果找不到用户,该数据库将返回 None。我们决定不返回 None,这将迫使调用者每次都检查 None。相反,我们使用我们的习惯UserNotFoundError
。
class UserNotFoundError(Exception):
pass
def fetch_user(user_id):
# Here you would fetch from some kind of db, e.g.:
# user = db.get_user(user_id)
# To make this example runnable, let's set it to None
user = None
if user == None:
raise UserNotFoundError(f'User {user_id} not in database')
else:
return user
users = [123, 456, 789]
for user_id in users:
try:
fetch_user(user_id)
except UserNotFoundError as e:
print("There was an error: ", e)
分配
这有个小任务。我们可以使用一个常规的异常对象来代替。这样,我们不需要定义一个自定义的。为什么这是个坏主意?
回答
事实上,你可以引发一个常规的异常,比如用raise Exception('User not found')
。但是如果你这样做了,你需要捕捉所有类型异常。正如我们所知,有很多这样的例子。您可能会无意中捕捉到一些您无法处理的其他异常。例如,数据库客户端可能抛出一个DatabaseAuthenticationError
,它也是Exception
的子类。
如何打印 Python 异常
只要捕捉得当,就可以直接打印异常。你可能已经看过上面的例子了。为了清楚起见,下面是一个如何捕捉和打印异常的示例:
try:
...
except Exception as e:
print("There was an error: ", e)
如果您想打印调用堆栈,就像 Python 在自己没有捕捉到异常时所做的那样,您可以导入回溯模块:
import traceback
try:
...
except Exception:
traceback.print_exc()
继续学习
这里有更多的资源可以加深你的知识:
- 我的博客文章'如何在 Python 中不处理异常
- Python 函数介绍
- 对象和类,以及 Python 继承
- 关于例外情况的官方文件。
- 关于错误的官方文件。
Python 函数:高级概念
您知道如何强制关键字参数、创建函数装饰器、创建匿名函数或者将数组或字典解包到函数的参数中吗?在这篇 Python 函数高级概念的文章中,我们将讨论一些围绕函数的更高级的概念。我将这些放在一篇单独的文章中,因为我不想让初学者对这些概念感到困惑。
你可能想先阅读我们对 Python 函数的介绍。
目录
强制关键字参数
关键字参数有许多优点:
- 你没有被强迫按照特定的顺序来提供你的论点——名字很重要,而不是立场。
- 关键字参数提供了清晰度。不用查函数本身,你通常可以通过看名字猜出参数的用途。
那很好,但是你可能已经知道这些事情了。您可能不知道的是,您还可以强制关键字参数。在 PEP 3202 中描述了细节,但是归结起来就是在想要强制作为关键字参数的参数前使用星号。或者,如下所示,在所有参数之前,强制所有参数都是关键字参数:
>>> def f(*, a, b):
... print(a, b)
...
>>> f(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 0 positional
arguments but 2 were given
>>> f(a=1, b=2)
1 2
>>>
当您开发某种类型的库时,这个高级函数技巧可能会派上用场。也许你还不想坚持某个论点的顺序。在这种情况下,这是强制库的用户使用命名参数的方法,使顺序变得无关紧要。
使用*和**作为函数参数
有些函数需要很长的参数列表。虽然这应该完全避免,例如,通过使用数据类,但这并不总是由你决定。在这种情况下,第二个最好的选择是创建一个包含所有命名参数的字典,并将其传递给函数。这通常会使您的代码更具可读性。您可以通过使用前缀**
来解包字典,以便与命名的关键字一起使用:
>>> def f(a, b):
... print(a, b)
...
>>> args = { "a": 1, "b": 2 }
>>> f(**args)
1 2
类似地,我们可以使用单个*
来解包一个列表,并将其内容作为位置参数提供给一个函数:
>>> def f(a, b, c):
... print(a, b, c)
...
>>> l = [1, 2, 3]
>>> f(*l)
1 2 3
装饰您的功能
装饰器是一个函数的包装器,它以某种方式修改函数的行为。decorators 有许多用例,您可能以前在使用 Flask 这样的框架时使用过。
让我们创建自己的装饰器;它比你想象的要简单,也许有一天会派上用场:
def print_argument(func):
def wrapper(the_number):
print("Argument for", func.__name__, "is", the_number)
return func(the_number)
return wrapper
@print_argument
def add_one(x):
return x + 1
print(add_one(1))
在print_argument
中,我们定义了一个包装函数。该函数打印被调用函数的参数和名称。接下来,它执行实际的函数并返回结果,就像函数被定期调用一样。使用@print_argument
,我们将装饰器应用于一个函数。也许没有必要说:这个装饰器也可以在其他函数中重用。
我们的小脚本的输出将是:
Argument for add_one is 1
2
匿名函数
有时候,命名一个函数是不值得的。一个例子是当你确定函数只会被使用一次。对于这种情况,Python 为我们提供了匿名函数,也称为 lambda 函数。
lambda 函数可以赋给一个变量,从而创建了一种简洁的函数定义方式:
>>> add_one = lambda x: x + 1
>>> add_one(3)
4
当你需要使用一个函数作为参数时,这就变得更有趣了。在这种情况下,该函数通常只使用一次。如您所知,map
将一个函数应用于一个可迭代对象的所有元素。我们可以在调用 map 时使用 lambda:
>>> numbers = [1, 2, 3, 4]
>>> times_two = map(lambda x: x * 2, numbers)
>>> list(times_two)
[2, 4, 6, 8]
>>>
事实上,这是一种你会经常看到的模式。当你需要对一个 iterable 对象的每个元素应用一个相对简单的操作时,结合 lambda 函数使用map()
既简洁又高效。
Python 列表理解:示例教程
数学中有一个概念叫做集合构造符号,也叫集合理解。受这一原则的启发,Python 提供了列表理解。事实上,Python 列表理解是该语言的定义特性之一。它允许我们创建简洁、可读的代码,胜过那些丑陋的替代方法,比如 for 循环或使用map()
。
我们先来看看最广为人知的类型:列表理解。一旦我们很好地掌握了它们是如何工作的,你还将学习集合理解和词典理解。
目录
什么是列表理解?
Python 列表理解是一种语言结构。它用于基于现有列表创建一个 Python 列表。听起来有点模糊,但在几个例子之后,那‘啊哈!’相信我,那一刻会到来的。
列表理解的基本语法是:
[ <expression> for item in list if <conditional> ]
“如果”部分是可选的,您很快就会发现。然而,我们确实需要一个列表来开始。或者,更具体地说,任何可以被迭代的东西。我们将使用 Python 的range()
函数,它是一种特殊类型的迭代器,称为生成器:它在每次迭代中生成一个新数字。
列表理解的例子
理论到此为止,让我们看看最基本的例子,我鼓励你启动一个 Python REPL 来亲自尝试一下:
>>> [x for x in range(1, 5)]
[1, 2, 3, 4]
一些观察结果:
expression
部分就是x
- 我们使用 range()函数代替列表。我们也可以使用
[1, 2, 3, 4]
,但是使用 range()更有效,对于更长的范围需要更少的输入。
结果是从 range() 获得的元素列表。不是很有用,但是我们确实创建了我们的第一个 Python 列表理解。我们也可以使用:
>>> list(range(1,5))
[1, 2, 3, 4]
因此,让我们加入 if 语句,使它更有用:
>>> [x for x in range(1,10) if x % 2 == 0]
[2, 4, 6, 8]
if 部分的作用类似于一个过滤器。如果【T2 if 之后的条件解析为真,则包含该项目。如果它解析为 False,它将被忽略。这样,我们可以使用列表理解只得到偶数。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
到目前为止,我们的expression
(x
)真的很简单。为了让这一点非常清楚,expression
可以是任何有效的 Python 代码,并被认为是一个表达式。示例:
>>> [x + 4 for x in [10, 20]]
[14, 24]
这个表达式给 x 加四,还是挺简单的。但是我们也可以做一些更复杂的事情,比如用x
作为参数调用函数:
def some_function(a):
return (a + 5) / 2
m = [some_function(x) for x in range(8)]
print(m)
# [2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0]
更高级的例子
你掌握了基础;恭喜你。让我们继续看一些更高级的例子。
嵌套列表理解
如果expression
可以是任何有效的 Python 表达式,也可以是另一个列表理解。当您想要创建矩阵时,这很有用:
>>> m = [[j for j in range(3)] for i in range(4)]
>>> m
[[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]
或者,如果您想要展平前一个矩阵:
>>> [value for sublist in m for value in sublist]
[0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]
相同,但多了一些空格,使这一点更清楚:
>>> [value
for sublist in m
for value in sublist]
[0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]
第一部分循环遍历矩阵m
。第二部分遍历每个向量中的元素。
列表理解的替代方案
Python 语言可以不需要理解;它看起来就没那么漂亮了。使用像map()
和reduce()
这样的函数式编程函数可以做列表理解所能做的一切。
另一种选择是使用 for 循环。如果你来自 C 风格的语言,比如 Java,你会倾向于使用 for 循环。尽管这不是世界末日,但是您应该知道列表理解更具性能,并且被认为是更好的编码风格。
其他理解
如果有列表理解,为什么不创建字典理解呢?或者集合理解呢?正如你所料,两者都存在。
集合理解
理解一个 Python 集合的语法没有太大的不同。我们只是用花括号代替方括号:
{ <expression> for item in set if <conditional> }
例如:
>>> {s for s in range(1,5) if s % 2}
{1, 3}
词典释义
字典需要一个键和值。否则,又是同样的把戏:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
唯一的区别是我们在expression
部分定义了键和值。
Python 迭代器:示例代码及其工作原理
要理解什么是 Python 迭代器,您需要知道两个术语:迭代器和可迭代的:
Iterator
An object that can be iterated, meaning we can keep asking it for a new element until there are no elements left. Elements are requested using a method called __next__
.
Iterable
An object that implements another special method, called iter. This function returns an iterator.
多亏了迭代器,Python 有了非常优雅的 for 循环。迭代器也使理解成为可能。尽管理解所有的内部工作需要一些工作,但它们在实践中非常容易使用!
目录
Python 迭代器如何工作
如上所述,Python 迭代器对象实现了一个函数,该函数需要携带确切的名称__next__
。这个特殊函数一直返回元素,直到它用完了要返回的元素,在这种情况下,类型为StopIteration
的异常被引发。要获得一个迭代器对象,我们需要首先在一个可迭代对象上调用__iter__
方法。
我们可以使用内置的 Python range 函数看到这一切,这是一个内置的 Python iterable。让我们做一个小实验:
>>> my_iterable = range(1, 3)
>>> my_iterator = my_iterable.__iter__()
>>> my_iterator.__next__()
1
>>> my_iterator.__next__()
2
>>> my_iterator.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
如你所见:
- range 返回一个 iterable 对象,因为它有
__iter__
方法。 - 我们调用函数并将它返回的迭代器赋给
my_iterator
。 - 接下来,我们开始重复调用
__next__
方法,直到到达范围的末尾并引发StopIteration
异常。
当然,这不是你在实践中使用迭代器的方式。我只是在演示它们内部是如何工作的。如果您需要手动获取迭代器,请使用iter()
函数。而如果需要手动调用__next__
方法,可以使用 Python 的next()
函数。
因为只有下一个函数,你只能用迭代器前进。没有办法重置迭代器(除了创建一个新的迭代器)或获取以前的元素。
为什么迭代器和可迭代对象是分开的对象?
迭代器和可迭代对象可以是独立的对象,但不是必须的。没有什么能阻止我们回到这里。如果愿意,可以创建一个既是迭代器又是可迭代的对象。你只需要同时实现__iter__
和__next__
。
那么,为什么构建这种语言的聪明人决定拆分这些概念呢?这与保持状态有关。迭代器需要维护位置信息,例如指向内部数据对象(如列表)的指针。换句话说:它必须跟踪下一个要返回的元素。
如果 iterable 本身保持这种状态,您一次只能在一个循环中使用它。否则,其他循环会干扰第一个循环的状态。通过返回一个新的迭代器对象和它自己的状态,我们没有这个问题。这很方便,特别是当你使用并发性的时候。
内置 Python 迭代器
一旦你开始寻找,你会发现许多 Python 类型是可迭代的。以下是 Python 固有的一些可迭代类型:
还有一些被称为生成器的特殊类型的迭代。最突出的生成器示例是 range()函数,它返回指定范围内的项目。这个函数生成这些数字,而不需要在一个实际的列表中具体化它们。显而易见的优点是,它可以节省大量内存,因为它需要做的只是记录上一次迭代的次数,以计算下一项。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
如何使用 Python 迭代器
现在我们了解了迭代器的工作原理,让我们看看如何使用 Python 迭代器。你会很快发现它的感觉和外观都很自然,一点也不难!
for 循环中的迭代器
与其他编程语言不同,循环 要求可迭代。下面是我们迭代一个列表和一个字符串的两个例子:
>>> mystring = "ABC"
>>> for letter in mystring:
... print(letter)
...
A
B
C
>>> mylist = ['A', 'B', 'C']
>>> for letter in mylist:
... print(letter)
...
A
B
C
如您所见,就可迭代性而言,Python 字符串的行为与 Python 列表相同。
理解中的迭代器
就像 for 循环一样,理解也需要一个可迭代的对象:
>>> [x for x in 'ABC']
['A', 'B', 'C']
>>> [x for x in [1, 2, 3,4] if x > 2]
[3, 4]
>>>
迭代 Python 字典键
Python 字典是可迭代的,因此我们可以循环遍历字典的所有键。字典迭代器只返回键,不返回值。这里有一个例子:
>>> d = {'name': 'Alice', 'age': 23, 'country': 'NL' }
>>> for k in d:
... print(k)
...
name
age
country
有时候看到有人用这个来代替:for k in d.keys()
。虽然结果一样,但明显少了几分优雅。
迭代字典值
要迭代 Python 字典的值,可以使用 values()方法:
>>> for k in d.values():
... print(k)
...
Alice
23
NL
迭代字典键和值
如果您想从字典中获得键和值,请使用items()
方法。您可以将items()
与 for 循环或 Python 列表理解一起使用:
>>> for k,v in d.items():
... print(k, v)
...
name Alice
age 23
country NL
>>>
>>> # With a list comprehension and f-string
>>> [f'{k}: {v}' for k, v in d.items()]
['name: Alice', 'age: 23', 'country: NL']
将 Python 迭代器转换为列表、元组、字典或集合
可以使用list()
函数将迭代器具体化为一个列表。同样,您可以使用set()
函数将迭代器具体化为一个集合,或者使用tuple()
函数将其具体化为一个元组:
>>> list(range(1, 4))
[1, 2, 3]
>>> set(range(1, 4))
{1, 2, 3}
>>> tuple(range(1, 4))
(1, 2, 3)
如果您有一个返回(键,值)元组的迭代器,您可以用dict()
将其具体化。
用 Python 逐行读取文件
多亏了迭代器,用 Python 逐行读取文件非常容易:
with open('cities.txt') as cities:
for line in cities:
proccess_city(line)
open()函数返回一个 iterable 对象,可以在 for 循环中使用。如果你喜欢,可以阅读我的综合文章,其中有关于用 Python 处理文件的所有细节。
创建自己的 Python 迭代器
创建自己的迭代器没有什么魔力。我将用一个返回偶数的简单迭代器类来演示。
正如我们所了解的,我们需要实现 iter 和 next。为了简单起见,我们将在一个单独的类中做这件事。我们用一个 iter 方法来完成这个任务,该方法只返回self
。我们可以把它变成一个无限的迭代器。但是为了便于演示,我们将在通过数字 8 时引发一个StopIteration
异常。
请记住,如果您以这种方式构建迭代器,您就不能在并发环境中使用它。在这种情况下,您应该在每次调用__iter__
时返回一个新对象。
如果你需要复习,请先阅读我们关于类和对象的教程。
class EvenNumbers:
last = 0
def __iter__(self):
return self
def __next__(self):
self.last += 2
if self.last > 8:
raise StopIteration
return self.last
even_numbers = EvenNumbers()
for num in even_numbers:
print(num)
如果运行这个程序,输出将是:
2
4
6
8
如果您愿意,可以交互式地玩这个例子:
https://crumb . sh/embed/vywqnj 476 au
Python range()函数:示例操作指南
Python range()函数可用于创建数字序列。range()函数可以被迭代,并且在与 for 循环结合时是理想的。本文将仔细研究 Python 范围函数:
- 这是什么?
- 如何使用 range()
- 如何用 range()函数创建 for 循环
我还将向您展示如何在 for 循环中使用范围来创建您想要的任何类型的循环,包括:
- 数量范围不断增加的常规循环
- 向后循环(例如从 10 到 0)
- 跳过值的循环
目录
什么是 Python range 函数?
Python 的range
作为内置函数,通常用于在 for-loops 中循环特定次数。像 Python 中的许多东西一样,它实际上是一个 Python 类型(或类),但当在循环中使用它时,我们可以将其视为一个返回 iterable 对象的内置函数。
Range 根据我们的输入为我们提供了一个经过计算的数字序列。有三种方法可以调用 range 函数:
# With one integer argument it
# counts from 0 to stop
range(stop)
# With two integer arguments it
# counts from start to stop
range(start, stop)
# With three integer arguments it
# counts from start to stop,
# with a defined step size
range(start, stop, step)
参数 start、stop 和 step 总是整数。当我们调用 range 时,它返回一个类的对象range
。这个 range 对象可以反过来提供一个 Python 迭代器,这意味着我们可以循环(迭代)它的值。范围迭代器在每次调用时都会计算一个新值。该值基于开始、停止和步长值。
如果这听起来很难:的确很难。如果您不完全理解迭代器和可迭代性,此时您有两个选择:
- 继续阅读,看看如何使用范围。我不会怪你。
- 首先阅读迭代器和可迭代对象,然后回到这里。
如果你想学习 Python,我建议你帮自己一个忙,选择第二个选项。
如何使用 Python 范围
使用范围有三种方式。我们将从最简单的用例开始,详细研究这三个方面。为了让您彻底理解 Python range 函数,稍后我将向您展示 range 在内部是如何工作的;其实挺简单的!然而,最好先看看如何使用范围的例子,这样你就能更好地了解它们是什么以及它们能做什么。
只有停止值的范围
如果我们用一个参数调用range
,这个参数就是停止值。在这种情况下,range
将从 0 开始计数,直到达到停止值。
例如,如果我们调用range(5)
,当我们迭代它时,创建的 range 对象将返回值 0、1、2、3 和 4。所以,重要注意: range 一到达止损值就停止,不会返回止损值本身。
如果这听起来令人困惑,一个简单的例子将有望澄清它。让我们在 for 循环中使用range
调用:
https://crumb . sh/embed/3ksrw 4 kt ky
为什么 range 不包括结束值?
你可能想知道为什么range
不包括结束值。这一切都归结到一个重要的原则,称为零基索引。计算机从零开始计数,而人类从 1 开始计数。当计算机需要给 3 个元素编号时,它从元素 0 开始,然后是 1,然后是 2。索引也是一样。当访问 Python 列表中的元素时,第一个元素驻留在位置 0。我们可以这样访问它:my_list[0]
。
由于这种从零开始的索引,如果 Python 范围也从零开始计数就容易多了。例如,如果你想使用一个范围来遍历一些东西,你可能会想从元素 0 开始。
带起始值和终止值的范围
我们并不总是希望从零开始计数,这就是为什么我们也可以用起始值和终止值来调用 range。对 range(2,5)的调用将返回值 2、3 和 4。这是另一个例子:
https://crumb.sh/embed/ukfdzkeqogo
步长为正值的范围
最后,range
()采用一个可选参数,称为步长。步长定义了计算出的数字之间的步长。最好只说明这一点:
for i in range(0, 6, 2):
print(i, end=" ")
# Output will be: 0 2 4
每一步跳过一个数字,因为步长是 2。让我们增加步长:
for i in range(0, 101, 10):
print(i, end=" ")
# Output will be:
# 0 10 20 30 40 50 60 70 80 90 100
因为我们将stop
设置为 101,所以值 100 也包含在这个范围内。
负步长范围
范围也可以倒计数。如果我们将步长设置为负数,范围将向后计数。如果我们想倒计数,我们需要确保start
的值比end
的值大。这里有一个例子:
for i in range(5, 0, -1):
print(i, end=" ")
# Output will be:
# 5 4 3 2 1
我们从 5 开始倒数到 0。因为范围不包括结束值本身,所以不包括 0。如果要包含 0,需要将end
设置为-1。
Python 范围如何工作
现在,您已经看到了 range 的作用,我将告诉您 range 如何在内部工作,以便更好地理解它们。实际上,它没有多少魔力!
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
范围是一个可迭代的对象,这个对象可以返回一个迭代器来跟踪它的当前状态。假设我们用调用range(3)
创建一个范围。这将返回一个具有我们所请求的设置的 range 类型的对象。在内部,Python 范围将设置以下内部变量:
- 起始值为 0,
- 结束值为 3,
- 并将步长值设置为默认值 1。
当我们开始迭代一个 range 对象时,range 对象将返回一个迭代器。这个迭代器使用上面定义的值,但是有一个额外的值:一个计数器变量i
,默认情况下初始化为 0,并在每次迭代中增加。
每次调用迭代器来获取下一个数字时,range 都会查看其内部状态,并计算要返回的下一个数字。下一个值是这样计算的:
next = start + step*i
。
默认情况下,范围的步长为 1:
- 所以在第一次调用时,进行的计算是这样的:
0 + 1*0 == 0
。Range 返回 0,计数器i
加 1,使i == 1
- 在第二次调用时,它使用其当前状态进行相同的计算:
0 + 1*1 = 1
。它再次将计数器i
增加到 2。 - 第三次调用返回 2,因为
0 + 1*2 == 2
。 - 我们现在已经到达了范围的尽头。正如我在关于 Python 迭代器的文章中所解释的,对迭代器的任何后续调用都将返回一个
StopIteration
异常,表明不再有条目。
如果用range(0, 5, 2)
给你的范围一个步长 2,你可以重复上面的计算,看到返回值现在是:0 和 2。
用代码演示
我们可以用一些代码来演示这个过程。在下面的例子中,我们首先创建一个 range 对象。接下来,我们使用内置的iter()
函数从 range 对象手动请求一个迭代器。我们开始使用另一个内置函数从它那里请求下一个值:next()
。在 3 次调用之后,您看到 iterable 被耗尽,并开始引发StopIteration
异常:
my_range = range(0, 3)
# Obtain the iterable. In a loop, this manual
# step is not needed
my_iter = iter(my_range)
# Request values from the iterable
print(next(my_iter))
# 0
print(next(my_iter))
# 1
print(next(my_iter))
# 2
print(next(my_iter))
# throws a StopIteration exception
如果您愿意,您也可以自己运行这段代码:
https://crumb . sh/embed/C4 dmggj 56 qev
Python 范围:Iterable 还是 iterator?
range 对象是可迭代的,这意味着它是一个可以返回迭代器的对象。正如你在上面的例子中看到的,我们可以用 iter()函数手动获得这个迭代器。在 for 循环中使用时,循环会自动请求迭代器,一切都会得到处理。
range 对象(iterable)将在每次调用时返回一个新的迭代器。因此,尽管这不是我们通常使用范围的方式,但我们实际上可以通过向 range 对象请求新的迭代器来继续使用它。如果你想详细理解这一点,请阅读 iterables 和 iterators 。
Python 范围 vs 列表
与列表相比,范围提供了一个很大的优势:它们需要一个恒定的、可预测的内存量,因为它们是动态计算的。一个范围需要跟踪的只是开始、停止、结束和迭代计数器。因此范围(1000)需要与范围(1)相同的内存量。
相比之下:值为 0 到 999 的列表比只有值 1 的列表占用的内存多一千倍。
在 Python 2 中,情况并非如此。常规范围将具体化为一个实际的数字列表。在某个时候,xrange 被引入。在 Python 3 中,xrange 将成为范围的缺省值,旧的物化范围被丢弃了。此外,还增加了一些额外的特性,比如切片和比较范围的能力。
名称 xrange 在 Python 3 中不存在。如果您遇到它,您可能会看到 Python 2 代码。我写了一篇关于这个主题的文章,如果你想把它转换成 Python 3 代码的话。
实践中的 Python 范围
下面是一些常见的和不太常见的操作,除了在循环中使用它们之外,在实践中还可能用到。
将范围转换为列表
我们了解到范围是计算出来的,而列表是物化的。有时你想从一个范围中创建一个列表。你可以用 for 循环来实现。或者,更 Python 化更快:通过使用 Python 列表理解。然而,最好的方法是使用list()
函数,它可以将任何可迭代对象转换成列表,包括范围:
my_list = list(range(0, 3))
print(my_list)
# [0, 1, 2]
成员资格测试
范围的行为类似于常规集合,因此我们也可以测试它们是否属于某个值。例如,要测试某个数字是否属于某个范围,您可以这样做:
my_number = 10
print(my_number in range(0, 11))
# True
范围切片
虽然我不确定你是否需要它(我从未在实践中使用过),但这是一个非常酷的特性。就像您可以分割列表和其他具有序列类型的对象一样,您也可以分割 range 对象。这样做时,您可能会期望范围被具体化为一个列表。事实并非如此。相反,会计算并返回一个新的 range 对象,正如您在以下关于 Python REPL 的示例中所看到的:
>>> range(0, 5)[2:4]
range(2, 4)
>>> range(0, 5)[4:2:-1]
range(4, 2, -1)
>>> range(0, 10)[2:8:2]
range(2, 8, 2)
比较范围
最后,范围也可以比较。比较范围时,它们作为序列进行比较。您可能希望 Python 只检查它们是否是同一个对象。在 Python 3.3 问世之前,情况一直如此。此后,产生相同序列的两个不同范围在 Python 中将被视为相等。
以下是 REPL 的一些例子:
>>> range(0, 2) == range(0, 3)
False
>>> range(0) == range(4, 2)
True
在最后一个例子中,开始为 4,结束为 2 的范围将导致长度为零的范围,因此它等于 a range(0)
。
结论
您已经了解了 Python 的范围以及如何在 for 循环中使用它。不仅如此;您还了解了产品系列的内部运作方式。除了这些基础知识之外,您还学习了如何将一个范围转换为一个列表,范围可以进行比较,测试成员资格,我们甚至可以对范围进行切片。
如果您想了解更多关于产品系列的信息,您可能会对以下资源感兴趣:
- 我关于 Python 迭代器和可迭代性的文章
- 关于靶场的官方文件
- Trey Hunner 的这篇文章详细解释了为什么 range 不是迭代器(但它是可迭代的)*
Python Docstring:记录您的代码
python docstring 允许您更正式地记录代码。我们首先来看看 docstring 到底是什么。接下来,我们将创建自己的文档字符串。最后,我将向您展示如何读取 docstrings。
目录
什么是 Python docstring?
让我们从定义什么是 docstring 开始。这直接取自 PEP-0257 ,其中介绍了文档字符串:
Docstring
A docstring is a string that occurs as the first statement in a module, function, class, or method definition. Such a docstring becomes the __doc__
special attribute of that object.
在 Python 注释和文档字符串之间有着明显的区别。Python 解释器完全忽略注释,而文档字符串是解释器看到的实际的字符串。因为我们没有指定字符串,所以在运行代码时解释器认为它是无用的,实际上被忽略了。因此,简而言之,docstring 的效果是一样的:它是记录代码的一段文本。
然而,注释和文档字符串之间仍然有本质的区别,因为 Python 实际上可以在请求时找到并显示文档字符串,因为它被赋予了__doc__
特殊属性。您很快就会了解到,它可以作为一种正式记录代码的方式。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
如何创建 Python 文档字符串
如上所述,我们可以简单地插入一个字符串作为任何模块、函数、类或方法的第一条语句,它就成为 docstring。下面是一个如何使用文档字符串记录一个 Python 类及其函数的例子:
class MyDocumentedClass:
"""This class is well documented but doesn't do anything special."""
def do_nothing(self):
"""This method doesn't do anything but feel free to call it anyway."""
pass
如你所见,我用三重引号创建了这些字符串。这是有原因的:作为一个 docstring,它们更容易被识别,并且以后(如果需要的话)更容易将它们扩展成多行字符串。
如何读取文档字符串
docstring 在内部被分配给__doc__
特殊属性。但是,通常情况下,您不会以这种方式访问它。相反,我们在 Python REPL 中调用help(MyDocumentedClass)
来查看这些文档字符串的结果:
Help on class MyDocumentedClass in module __main__:
class MyDocumentedClass(builtins.object)
| This class is well documented but doesn't do anything special
|
| Methods defined here:
|
| do_nothing(self)
| This method doesn't do anything but feel free to call it anyway
下面是同一个班级的一个互动例子,你可以自己尝试一下:
https://crumb.sh/embed/3NduXvFKHzK
将 docstrings 应用于我们的类,并使用帮助功能打印它们
您可以使用 Python 中任何对象的帮助。我鼓励你试试这个。例如,输入help(print)
从 print 函数获取 docstring,或者输入help(str)
查看 string 类及其所有有用的方法。你也可以要求一个类中的方法的文档,例如,使用help(str.title)
你可以学习字符串对象上的title()
方法做什么。
继续学习
Python pass(什么都不做):何时以及如何使用
Python 有一个特殊的关键字叫做pass
。Python pass
关键字告诉 Python 什么都不做。换句话说:只需传递这一行代码并继续。如果你习惯于用类 C 语言编程,你可能从来不需要这样的语句。
- 那么为什么这个关键字在 Python 中存在而在其他语言中不存在呢?
- 你会在哪里以及如何使用它?
本文回答了这些问题,并向您展示了需要 Python 的pass
关键字的几种情况。我们还将看看一个有趣的替代方案,你可能会喜欢用它来代替!
目录
什么是 pass 关键字
Python 的pass
是关键词,就像if
、else
、return
一样,其他都是关键词。pass
什么也不做。它是一个告诉 Python 继续运行程序的关键字,忽略这一行代码。pass
通常用作未来代码的占位符。
注意,因为pass
本身形成了一个有效的语句,所以 pass 也可以被称为一个语句。因此,你会看到人们使用 pass 语句和 pass 关键字,这两个词都是正确的。
Python 为什么需要 pass 关键字?
Python 依赖于缩进。Python 只能检测缩进了等量空格(通常是四个空格字符)的代码块。Python 语法是这样定义的,在某些地方需要代码。这样做是因为不在这些地方添加代码没有太大意义,并且对可读性没有帮助。
让我们来看看你必须有代码的四个地方。可能还有更多(请让我知道),但这些是最突出的。如果您不在这些地方输入代码,Python 将退出程序,并出现一个IndentationError
异常:
为了便于演示,让我们尝试其中的一些:
def myfunction():
# This function has no body, which is not allowed
myfunction()
Python 将无法运行此代码,并出现以下错误:
File "myfile.py", line 4
myfunction()
^
IndentationError: expected an indented block after function definition on line 1
类似的投诉会导致其他情况:
if True:
# No code, not allowed!
else:
# Here too, code is required
try:
# let's try doing nothing (not allowed)
except Exception:
# Again: this can't be empty!
class MyClass:
# A completely empty class is not allowed
如你所见,注释不被认为是代码,所以注释不能用来填补空白!这就是 pass 语句发挥作用的地方。在上述所有情况下,pass 语句可用于插入不执行任何操作但仍然满足某些代码就位要求的代码。让我们填补空白:
def myfunction():
pass
myfunction()
if True:
pass
else:
pass
try:
pass
except Exception:
pass
class MyClass:
pass
这是所有有效但完全无用的代码。
何时使用 Python 的 pass 关键字
那么什么时候应该用pass
?原因有几个,我觉得这几个是最常见的。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
作为占位符
如前所述,pass
在开发过程中很有用。我们可以用它来创建存根函数和类,这些函数和类将在以后实现。这样我们可以创建不抛出错误的可运行代码,即使不是所有的函数、类、错误处理和 if-else 情况都实现了。
忽略异常
忽略异常是很常见的,尤其是当它们是KeyError
s 的时候。这是一种你会经常看到的模式:
try:
email = customer['email']
send_notification(email, ...)
except KeyError:
# Not the end of the world, let's continue
pass
我们发现了错误,但是不想做任何事情。然而,Python 需要在except
块中的代码,所以我们使用pass
。
调试时创建断点
pass
的另一个有趣用途是在代码中创建断点。有时你想在一些代码后创建一个断点。如果代码在一个函数的末尾,你可能想在末尾添加一个额外的pass
并在那里设置你的断点。要了解更多关于断点的知识,请务必阅读我的文章“T2”在 VSCode 中调试 Python 代码,以及“T4”关于使用 Python 调试器的文章。
创建没有额外功能的子类
如果您想创建一个子类而不添加功能,您可以使用pass
来实现。如果您想创建一个自定义命名的异常,这将非常方便,例如:
class MyCustomException(Exception):
pass
何时不使用通行证
在我看来,在生产代码中应该非常谨慎地使用pass
关键字。如果有,应该是偶然的。如前所述,使用pass
忽略一个异常显然是可以的。但是你应该使用pass
来发布充满存根的产品代码吗?我不确定!
如果您发现自己经常使用 pass 关键字,例如在if
… else
构造中,您可能想要尝试重写您的代码。让我们看一个例子:
if customer_exists:
pass
else:
redirect_to_signup_page()
在上面的例子中,我们检查客户是否存在。如果是的话,我们都准备好了,我们使用pass
继续。如果没有,我们重定向到一个注册页面。不过,像这样的代码可以很容易地重写,从而减少代码行,降低复杂性。通常,您需要做的只是颠倒逻辑:
if not customer_exists:
redirect_to_signup_page()
我们为自己节省了两行代码,使代码更具可读性。
使用通行证的替代方案
我写过一篇关于 Python 中一种特殊的对象的博客文章,这种对象叫做省略号。在那篇文章中,我提到过我们可以用省略号代替pass
。省略号对象不做任何事情,因此可以像使用pass
一样使用它。这里有一个例子:
def create_account(username, password):
# TODO: implement me
...
我没有使用pass
,而是使用了构成省略号对象的三个点...
。看起来很酷,如果你问我,它几乎乞求被一些实际的代码取代!
然而,这还不止于此。你可以把任何没有副作用的语句放在你本来应该放pass
的地方,这里有一些可能性:
- 一个数字,比如
0
- 一个布尔值(
True
或False
) - 一个字符串:“尚未实现”
尽管有这些选择,pass
在这些情况下是事实上的标准,并且清楚地向其他人传达这是有意留空的,或者需要实现。所以当有疑问时,使用pass
,尤其是当你在团队中工作的时候。
Python 传球 vs 继续
很多人问我pass
和continue
有什么区别。他们可能看起来一样,但他们远不是一样的。而pass
什么都不做,continue
基本上停止当前的代码流,直接开始下一次循环迭代。因此,continue
只能在循环内部工作,不能替代pass
。
下面是如何在一个 for 循环中使用continue
:
# Send voucher to all inactive customers
for customer in customers:
if customer.is_active():
# Skip this customer
continue
# do some stuff to send voucher
...
结论
我们学习了什么是 Python 的 pass 关键字,为什么 Python 需要pass
关键字,以及在什么情况下应该和不应该使用它。我们甚至看到了一个非常酷的替代方案:省略号。最后,我解释了pass
和continue
的区别。
与操作系统的交互
Python 因许多事情而闻名,其中之一就是它作为脚本语言的功能。它是一种很好的语言,可以将事物粘合在一起,并与底层操作系统进行交互。您将学习如何使用 Python 与底层操作系统进行交互:
- 用 Python 打开、读写文件
- 用子程序模块运行外部命令
- Python 网络:创建客户端和服务器
这一节是新的;稍后将添加更多内容。
Python 读写文件:示例
文件是使用计算机的重要组成部分,因此使用 Python 读写文件是您需要掌握的基本技能。在本文中,我将向您展示如何完成您的目标,例如:
- 如何在 Python 中打开文件
- 用 Python 读取文件(一次或逐行)
- 用 Python 写文件
- 复制、移动、重命名和删除文件
- 检查文件或目录是否存在
当处理文件时,你需要了解文件模式和权限。如果你需要的话,我也包括了对这个主题的解释。
目录
- 用 Python 打开一个文件
- Python 写文件
- Python 追加到文件
- Python 读取文件到列表
- 常见的 Python 文件操作
- 处理 Python 文件异常
- 关于 Unix 文件权限
- 继续学习
用 Python 打开一个文件
在 Python 中,我们用open()
函数打开一个文件。这是 Python 内置函数的一部分,你不需要导入任何东西来使用open()
。open()函数至少需要一个参数:文件名。如果文件成功打开,它将返回一个 file 对象,您可以使用该对象读取和写入该文件。
当你用 Python 打开一个文件时,你正在使用系统资源,一旦你完成了,你需要释放这些资源。如果不这样做,就会造成所谓的资源泄漏。如果您经常这样做,您可能会遇到问题,因为普通操作系统用户可以打开的文件数量是有限的。
不过这不一定是个问题。当您的程序退出时,所有资源都会自动释放。Python 开发者的想法很好,也很体贴,但是很多软件都是无限期运行的。有了这样的软件,你需要更加小心,关闭你不再需要的资源。这样,您可以确保其他软件可以安全地访问该文件。此外,当您打开许多文件时,每个打开的文件都会占用内存和其他资源。由于这些原因,当你完成一个文件时,关闭它是一个好习惯。
关闭文件曾经是一项手动任务,很容易被忘记。幸运的是,Python 有‘with’语句来帮助我们。我们将很快讨论with
声明。我首先要向你展示“老式的做事方法”。
打开文件的老式方法
为了说明 with 语句如此有用的原因,让我们首先以传统的手动方式打开和关闭一个文件:
https://crumb . sh/embed/aetlu 9 ifs
打开文件的老式方法。您应该更喜欢使用 with 语句。
如果你想冒险,试着去掉f.close()
。它应该是一样的;Python 不会抱怨,会在退出时关闭文件资源。
如果上面的交互式示例由于某种原因无法运行,下面是相同代码的文本版本:
f = open('text.txt')
print(f.read())
f.close()
Python 一次读取文件内容
在上面的例子中,您可以看到我们如何从文件中读取所有内容,以便打印它。如果您想一次将所有文件内容读入一个字符串,请对 file 对象使用read()
方法,不带参数。这对于小文件来说没有问题,但是要意识到你是一次将所有的内容加载到内存中。这可能是大文件的一个问题,但是我们很快就会解决这个问题。
在 Python 中使用 with open()
在前面的例子中有一个潜在的问题。如果在 Python 读取文件时发生了异常,那么f.close()
调用将永远不会到达。这是一个微不足道的例子,但是你可以想象,对于更复杂的软件,其中的异常可能会被捕获,以便软件可以继续运行,它可能会很快出错。
另一个可能出现的问题是,您只是忘记编写 close()调用。它发生了,我们都是人类。因此,对于现代 Python,建议尽可能使用 with 语句。通过使用 Python 的with open()
,打开的文件资源将只在缩进的代码块中可用:
https://crumb.sh/embed/39hFGitfZhd
如果上面的交互式示例由于某种原因无法运行,下面是相同代码的文本版本:
with open('text.txt') as f:
text = f.read()
print(text)
在本例中,with 语句确保文件关闭,即使发生异常。这是因为一旦我们跳出这个 with 语句的范围,Python 就会自动关闭资源。由于这种自动关闭,您不能在 with 语句之外使用文件(在我们的例子中称为f
)。如果你想再次使用它,你必须再次打开它。
文件模式
到目前为止,我们使用的open()
函数只有一个参数:文件名。这是一个强制参数,但是 open()也有一些可选的额外参数,比如文件 模式 。该模式默认为“rt”,代表' r ead t ext '。你打开文件是为了读,这意味着你不能写,你希望它是一个文本文件。
下表列出了您可以通过的其他模式:
| 性格;角色;字母 | 意义 |
| r | 打开文件进行读取(默认) |
| w | 打开文件进行写入,首先截断文件 |
| x | 创建一个新文件并打开它进行写入 |
| 答 | 打开以供写入,如果文件已经存在,则追加到文件末尾 |
| t | 文本模式(默认),可与 rwxa 结合使用 |
| b | 二进制模式(与文本模式相反),可以与 rwxa 结合使用 |
| + | 打开磁盘文件进行更新(读写) |
可用于 open()的模式参数的文件模式
我不会在这里详细介绍所有的模式,而是在下面的适当部分解释和演示其中的大部分。
Python 写文件
既然您已经了解了文件模式和如何打开文件,我们也可以使用 Python 来写入文件。我们可以分三步走:
- 首先,我们需要确定文件模式。如果你看上面的表格,我们需要用 w 和 t。因为“t”是默认值,所以我们可以忽略它。
- 接下来,我们需要打开文件进行写入
- 最后,我们在文件对象上调用 write()方法。
在这种情况下,Write 需要一个参数:一个 Python 字符串。如果您有另一种类型的数据,如数字,您需要手动将其转换为字符串。毕竟,文本文件没有数据类型的概念:它们只包含文本。
在下面的例子中,我们使用 str()函数将整数转换为字符串,并将它们写入文件:
https://crumb . sh/embed/3 nwtvkb 2 r xk
用 Python 写一个文件,使用写模式
如果上面的交互示例不起作用,下面是相同的代码:
with open('test.txt', 'w') as f:
for i in range(1, 5):
f.write(str(i))
with open('test.txt', 'r') as f:
print(f.read())
现在,您的文件系统中有一个文件(与您的脚本在同一个目录中),其内容如下:
1234
换行符
你期望每个数字都在一个新的行上吗?
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
写文件时,需要显式指定换行符。你可以在上面的交互例子中尝试一下。修改代码,使其看起来像这样:
with open('test.txt', 'w') as f:
for i in range(1, 5):
f.write(f'Number {i}\n')
with open('test.txt', 'r') as f:
print(f.read())
请注意,我在这里使用了 f 弦。我个人很喜欢它们,但是你也可以用类似str(i) + '\n'
的东西。
Python 追加到文件
现在我们有了一个测试文件,我们还可以添加一些额外的数据。默认情况下,当我们打开一个文件进行写入时,我们会覆盖任何已经存在的内容。因此,我们再次首先查看上面的文件模式表,我们可以使用哪种模式将数据追加到文件中。我们需要模式“a ”,用于追加。
在下面的例子中,我们:
- 使用 Python 的
with open()
写入一个文件,就像前面的例子一样。 - 之后,我们再次打开文件,这次使用 append 标志,并添加一些额外的行。
- 回读文件内容并打印到屏幕上,以便我们可以检查结果
给你:
https://crumb . sh/embed/4xb 5 eteh 6 FP
用 Python 向文件追加文本
如果上面的交互示例不起作用,下面是相同的代码:
# First create a file, with a couple of lines
with open('test.txt', 'w') as f:
for i in range(1, 5):
f.write(f'Number {i}\n')
# Now add some extra lines using append mode
with open('test.txt', 'a') as f:
for i in range(5, 8):
f.write(f'Append number {i}\n')
with open('test.txt') as f:
print(f.read())
Python 读取文件到列表
对于小文件,将所有行一次读入一个列表会很方便。有两种方法可以做到这一点:
with open('test.txt') as f:
lines = list(f)
# lines = ['1\n', '2\n', '3\n', '4\n', '5\n', '6\n', '7\n']
相当于:
with open('test.txt') as f:
lines = f.readlines()
# lines = ['1\n', '2\n', '3\n', '4\n', '5\n', '6\n', '7\n']
逐行读取文件
你并不总是有一次阅读文件的奢侈。例如,当您需要处理 140 GB 大小的日志文件时,您不能将其全部读入内存并侥幸逃脱。你的程序可能会崩溃,或者至少变得非常慢。
在这种情况下,逐行解析文件要高效得多。为了逐行读取文件,我们可以将打开的文件视为一个迭代器。打开文件后,我们可以使用一个 Python for-loop 来遍历这些行:
with open('myfile.txt', 'r') as f:
for line in f:
print(line)
常见的 Python 文件操作
Python 有内置模块来执行常见的文件操作,比如删除文件、创建目录、移动文件等等。这些函数中的大部分都可以在os
模块中获得,所以你需要先导入它。在这一节中,我们将介绍一些您可能需要执行的操作。
使用 Python 检查文件是否存在
要检查文件是否存在,我们可以使用isfile
函数:
import os
if os.path.isfile('myfile.txt'):
print("It's a file")
如果文件存在,这将返回True
,如果不存在,则返回False
。
使用 Python 检查目录是否存在
类似地,要检查目录是否存在,我们可以使用isdir
函数:
import os
if os.path.isdir('mydir'):
print("It's a directory")
如果目录存在,这将返回True
。否则将返回False
。
创建目录
要创建目录,使用os
模块中的mkdir
函数:
import os
os.mkdir('mydir')
删除文件
要删除一个文件,我们可以使用下面的命令:
import os
os.remove('myfile.txt')
这将删除文件。如果文件不存在,它将引发一个异常。
重命名(或移动)文件
要重命名或移动文件,我们可以使用以下命令:
import os
os.rename('myfile.txt', 'myfile_renamed.txt')
只要文件在同一个文件系统中,重命名操作就会起作用。如果您想将一个文件从一个文件系统移动到另一个文件系统,您需要来自shutil
的更高级的move
函数,如下所述。
用 shutil 移动文件
为了以类似终端外壳上的mv
命令的方式移动文件,我们可以使用shutil
模块。与os.rename
的一个重要区别是,这个函数也可以在文件系统之间移动文件。重命名只能重命名/移动同一文件系统中的文件:
import shutil
shutil.move('/mnt/filesystem1/myfile.txt', '/mnt/filesystem2/mydir')
# Move to a directory, keeping the name intact
shutil.move('/home/erik/myfile.txt', '/home/erik/backups/')
用 Python 复制文件
shutil
模块是一个更高级的模块,它提供了许多可用于复制单个文件或复制和删除整个文件树的功能:
import shutil
# Copy a single file
shutil.copy('/home/erik/myfile.txt', '/home/erik/myfile_copy.txt')
# Copy entire tree of files
shutil.copytree('mydir', 'mydir_copy')
# Remove a tree of files
shutil.rmtree('mydir')
处理 Python 文件异常
如果出现错误,shutil
和os
模块将引发异常。您可以使用try
和except
模块来处理异常。您需要处理的异常几乎总是属于OSError
类型。然而,有时您可能还需要处理其他异常,比如SameFileError
。
我写了一个详细的教程,关于如何处理异常和 try-catch 块,你可能想读一下。
关于 Unix 文件权限
在处理文件时,您不可避免地会遇到文件权限问题。这是关于 Unix 文件权限的简短入门。你迟早会遇到它们,尤其是在云中工作时,因为大多数云服务器都运行 Linux。
文件权限
权限是按文件指定的。当您列出目录时,您将能够看到其权限。例如,在 Linux 下,我们可以用 ls -l
命令这样做:
$ ls -l
total 0
-rwxr-xr-x 1 erik erik 0 Jun 25 10:33 build_script.sh
-rw-r--r-- 1 erik erik 0 Jun 25 10:33 myapp.py
在输出中,您可以看到我出于演示目的创建的两个虚拟文件。第一个是脚本文件,第二个是名为 myapp.py 的 Python 文件。在这一行的开头,您会看到像 r、w 和 x 这样的神秘字母,它们定义了文件权限。文件可以具有以下权限:
| 信 | 许可 |
| r | 该文件可以被读取 |
| w | 文件是可写的 |
| x | 该文件是可执行的 |
| – | 未设置,未授予权限 |
可能的文件权限
用户和组
然而,仅仅知道权限是不够的。正如您在上面的示例清单中看到的,对于每个文件, r 重复了三次。如果你想知道为什么:计算机系统通常有多个用户和组。
每个文件都有三种访问类型的权限:
- 用户:文件的所有者。创建文件的人自动成为所有者
- 组:由 0 个或更多用户组成的组
- 其他所有人;也称为“世界”
权限也按此顺序列出。因此,对于每种访问类型,我们有三个权限字段,结果是一个九个字符长的字符串。下面的 ASCII 艺术应该可以澄清一些事情:
User Group World
0 123 456 789
- rwx r-x r-x
Positions 1, 2, 3 define permissions for the user
Positions 4, 5, 6 define permissions for the group that the user is in
Positions 7, 8, 9 define permissions for world
Position 0 is the file type (see below)
文件类型
最后,有几种文件类型。最广为人知的是常规文件和目录,但还有更多类型。这是完整的列表:
- –:常规文件
- d :目录
- c :字符设备文件
- b :块设备文件
- s :本地套接字文件
- p :命名管道
- l :符号链接
为了演示,您可以检查文件/dev/random
:
ls -l /dev/random
crw-rw-rw- 1 root root 1, 8 jun 22 08:44 /dev/random
这是一个字符设备,当读取时提供一个随机数据流。如您所见,它也是可写的。在这种情况下,写入/dev/random
将更新熵池。
还有更多的东西需要学习,您可能也想深入了解我们关于使用 Unix shell 的章节。所以,如果你想或需要,就去探索吧,但是这里的快速介绍应该可以让你开始。
继续学习
使用这些资源扩展您的知识和理解:
- 如何在 Python 中加载、读取和写入 YAML
- 在 Python 中使用JSON
- 关于使用文件的官方 Python 文档
- 官方 shutil 文档
- 维基百科关于文件权限的页面
- 我们关于使用 unix shell 的章节
Python 子流程:运行外部命令
尽管 PyPI 上有许多库,但有时您需要从 Python 代码中运行一个外部命令。内置的 Python 子流程模块使这变得相对容易。在本文中,您将学习一些关于流程和子流程的基础知识。
我们将使用 Python 子流程模块安全地执行外部命令,捕获输出,并有选择地向它们提供来自标准 in 的输入。如果你熟悉流程和子流程的理论,你可以安全地跳过第一部分。
目录
流程和子流程
在计算机上执行的程序也称为进程。但是到底什么是过程呢?让我们更正式地定义它:
Process
A process is the instance of a computer program that is being executed by one or more threads.
一个进程可以有多个 Python 线程,这叫做多线程。反过来,一台计算机可以同时运行多个进程。这些进程可以是不同的程序,但也可以是同一程序的多个实例。我们关于 Python 并发性的文章详细解释了这一点。以下图片也来自那篇文章:
一台计算机可以同时运行多个进程,一个进程可以有多个线程
如果您想要运行外部命令,这意味着您需要从您的 Python 进程创建一个新的进程。这种流程通常称为子流程或子流程。从视觉上看,这是一个流程产生两个子流程时发生的情况:
产生两个子流程的父流程
内部发生的事情(在操作系统内核内部)被称为分叉。流程会自行分叉,这意味着会创建并启动一个新的流程副本。如果您希望并行化代码并利用机器上的多个 CPU,这可能会很有用。这就是我们所说的多重处理。
不过,我们可以利用同样的技术来开始另一个过程。首先,该流程自行分叉,创建一个副本。反过来,这个副本会用另一个进程替换自己:您希望执行的进程。
subprocess.run 函数
我们可以采用底层方式,使用 Python 子流程模块自己完成大部分工作,但幸运的是,Python 还提供了一个包装器,可以处理所有的细节,而且也很安全。多亏了包装器,运行一个外部命令可以归结为调用一个函数。这个包装器是来自subprocess
包的函数run()
,这就是我们将在本文中使用的。
我认为让您知道内部发生了什么是很好的,但是如果您感到困惑,请放心,您不需要这些知识来做您想做的事情:使用 Python 子流程模块运行外部命令。
用 subprocess.run 创建一个 Python 子流程
理论已经讲得够多了,是时候动手编写一些代码来执行外部命令了。
首先,你需要导入子流程库。由于它是 Python 3 的一部分,所以不需要单独安装。从这个库中,我们将使用 run 命令。该命令是在 Python 3.5 中添加的。确保您至少拥有那个 Python 版本,但最好是运行最新版本。如果你需要帮助,请查看我们详细的 Python 安装说明。
让我们从一个简单的 ls 调用开始,列出当前的目录和文件:
>>> import subprocess
>>> subprocess.run(['ls', '-al'])
(a list of your directories will be printed)
事实上,我们可以从 Python 代码中调用 Python,即二进制代码。接下来,让我们请求系统上默认 python3 安装的版本:
>>> import subprocess
>>> result = subprocess.run(['python3', '--version'])
Python 3.8.5
>>> result
CompletedProcess(args=['python3', '--version'], returncode=0)
逐行解释:
- 我们导入子流程库
- 用一个参数运行一个子进程,在本例中是 python3 二进制文件:
--version
- 检查结果变量,其类型为
[CompletedProcess](https://docs.python.org/3/library/subprocess.html#subprocess.CompletedProcess)
该进程返回代码 0,表示它已成功执行。任何其他返回代码都意味着存在某种错误。这取决于您调用的进程,不同的返回代码意味着什么。
正如您在输出中看到的,Python 二进制文件在标准输出(通常是您的终端)上打印了它的版本号。您的结果可能会有所不同,因为您的 Python 版本可能会有所不同。也许,您甚至会得到类似这样的错误:FileNotFoundError: [Errno 2] No such file or directory: 'python3'
。在这种情况下,确保 Python 二进制文件在您的系统上也叫做 python3,并且它在 PATH 中。
捕获 Python 子流程的输出
如果您运行一个外部命令,您可能希望捕获该命令的输出。我们可以通过 capture_output=True 选项实现这一点:
>>> import subprocess
>>> result = subprocess.run(['python3', '--version'], capture_output=True, encoding='UTF-8')
>>> result
CompletedProcess(args=['python3', '--version'], returncode=0, stdout='Python 3.8.5\n', stderr='')
如你所见,Python 这次没有把它的版本打印到我们的终端上。subprocess.run 命令重定向了标准输出和标准错误流,因此它可以捕获它们并为我们存储结果。在检查结果变量之后,我们看到 Python 版本是从标准输出中捕获的。因为没有错误,所以 stderr 为空。
我还添加了选项编码='UTF-8 '。如果没有,subprocess.run
假设输出是一个字节流,因为它没有这个信息。如果你想的话,试试看。因此,stdout
和stderr
将是字节数组。因此,如果您知道输出将是 ASCII 文本或 UTF-8 文本,您最好指定它,以便 run 函数也相应地编码捕获的输出。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
或者,您也可以使用 text=True 选项,而不指定编码。Python 将输出捕捉为文本。如果您知道编码,我建议您明确指定编码。
标准输入的进给数据
如果外部命令需要标准输入上的数据,我们也可以使用 Python 的subprocess.run
函数的input
选项轻松实现。请注意,我不会在这里讨论流数据。我们将建立在前面的例子上:
>>> import subprocess
>>> code = """
... for i in range(1, 3):
... print(f"Hello world {i}")
... """
>>> result = subprocess.run(['python3'], input=code, capture_output=True, encoding='UTF-8')
>>> print(result.stdout)
>>> print(result.stdout)
Hello world 1
Hello world 2
我们刚刚使用 Python 执行了一些带有 python3 二进制代码的 Python 代码。完全没用,但是(希望)很有教育意义!
代码变量是一个多行 Python 字符串,我们使用input
选项将其作为输入分配给subprocess.run
命令。
运行 shell 命令
如果您希望在类似 Unix 的系统上执行 shell 命令,我指的是您通常在类似 Bash 的 shell 中输入的任何命令,您需要认识到这些命令通常不是执行的外部二进制文件。例如,像for
和while
循环这样的表达式,或者管道和其他操作符,都是由 shell 自己解释的。
Python 通常有内置库形式的替代方案,这是您应该更喜欢的。但是如果您需要执行一个 shell 命令,不管出于什么原因,当您使用shell=True
选项时,subprocess.run
会很乐意这样做。它允许您输入命令,就像在 Bash 兼容的 shell 中输入命令一样:
>>> import subprocess
>>> result = subprocess.run(['ls -al | head -n 1'], shell=True)
total 396
>>> result
CompletedProcess(args=['ls -al | head -n 1'], returncode=0)
但是有一个警告:使用这种方法容易受到命令注入攻击(参见:警告)。
注意事项
运行外部命令并非没有风险。请仔细阅读这一部分。
os.system 与子进程. run
您可能会看到使用os.system()
来执行命令的代码示例。然而,subprocess
模块更强大,官方 Python 文档推荐使用它而不是os.system()
。os.system
的另一个问题是它更倾向于命令注入。
命令注入
一种常见的攻击或利用是注入额外的命令来获得对计算机系统的控制。例如,如果您要求用户输入,并在调用os.system()
或subprocess.run(...., shell=True)
时使用该输入,那么您就面临着命令注入攻击的风险。
为了演示,下面的代码允许我们运行任何 shell 命令:
import subprocess
thedir = input()
result = subprocess.run([f'ls -al {thedir}'], shell=True)
因为我们直接使用用户输入,所以用户可以简单地通过添加分号来运行任何命令。例如,以下输入将列出/目录并回显文本。自己尝试一下:
/; echo "command injection worked!";
解决方案是而不是尝试清理用户输入。你可能会开始寻找分号,并拒绝输入你找到一个。不要;在这种情况下,黑客至少可以想出 5 种附加命令的方法。这是一场艰苦的战斗。
更好的解决方案是不使用shell=True
,像我们在前面的例子中所做的那样,在一个列表中输入命令。这样的输入在这种情况下会失败,因为子进程模块将确保输入是您正在执行的程序的参数,而不是新命令。
使用相同的输入,但使用shell=False
,您将得到以下结果:
import subprocess
thedir = input()
>>> result = subprocess.run([f'ls -al {thedir}'], shell=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/subprocess.py", line 489, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/lib/python3.8/subprocess.py", line 854, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/lib/python3.8/subprocess.py", line 1702, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'ls -al /; echo "command injection worked!";'
该命令被视为ls
的一个参数,这反过来告诉我们它找不到那个文件或目录。
用户输入总是危险的
事实上,使用用户输入总是危险的,不仅仅是因为命令注入。例如,假设您允许用户输入文件名。之后,我们读取文件并显示给用户。虽然这看起来无害,但是用户可以输入如下内容:../../../../configuration/settings.yaml
其中settings.yaml
可能包含您的数据库密码…哎呀!您总是需要适当地清理和检查用户输入。如何正确地做到这一点,超出了本文的范围。
继续学习
以下相关资源将帮助您更深入地了解这一主题:
- 官方文档包含了关于子进程库的所有细节
- 我们关于 Python 并发的文章解释了更多关于进程和线程的内容
- 我们关于使用 Unix shell 的章节可能会派上用场
- 学习一些基本的 Unix 命令
虚拟环境和包管理
到目前为止,我们停留在核心 Python 生态系统中。我们要么创建自己的代码,要么使用标准 Python 发行版中发布的模块。然而,有一个巨大的世界,人们使用 Python 做任何你能想到的事情。这些人中的许多人以包的形式与世界分享他们的代码。让我们称这样的包为第三方包。
教程的这一部分向您介绍第三方 Python 包、虚拟环境、pip 包管理器,以及结合了这两者的更好的替代方案,如 Pipenv 和 poem。
目录
- 什么是第三方包?
- 什么是虚拟环境?
什么是第三方包?
如果你遵循了这个站点上的教程,你已经学习了创建你自己的 Python 包。这是一种在目录结构中组织代码的简洁方式。Python 还允许我们创建可共享的包,这些包甚至可以通过位于 PyPI.orgT3 的 Python 包索引(免费)分发给全世界。
在撰写本文时,该网站上有将近 400,000 个包,我们可以用一个简单的命令从 Python 包索引中安装任何我们喜欢的东西: pip install <包名> 。
什么是虚拟环境?
一个 Python venv(虚拟环境的缩写)允许你将 Python 包保存在一个与系统其余部分隔离的位置。这与在系统范围内安装它们的另一种选择形成了对比。虚拟环境具有重要的优势,我们将首先介绍这些优势。在下一篇文章中,我将向您展示如何使用虚拟环境。
之后,您需要学习如何在虚拟环境内部或者系统范围内安装软件包。我们用 pip install 命令来完成这项工作。
最后,虚拟环境和 pip 附带了一个默认的 Python 安装。然而,现在有更好的工具。我们将看看 Pipenv ,它将包管理和虚拟环境结合到一个工具中,为我们做一些额外的事情。
Python venv:如何创建、激活、停用和删除
Python 虚拟环境允许您在与系统其余部分隔离的位置安装 Python 包,而不是在系统范围内安装它们。我们来看看如何使用 Python venv,Python 虚拟环境的简称,也缩写为 virtualenv 。
在本文中,您将了解到:
- 使用虚拟环境的优势
- 如何创建 venv
- 如何激活和停用它
- 删除或移除 venv 的不同方法
- venv 如何在内部工作
如果你想彻底了解虚拟环境并获得一些实际操作,请看看我的 Python 基础 II 课程。它深入探讨了模块、包、虚拟环境和包管理器。
目录
- 为什么您需要虚拟环境
- 虚拟环境与其他选择
- 如何创建 Python venv
- Python venv 激活
- Python venv 如何工作
- 关闭 Python venv
- 删除一个 Python venv
- 结论
- 继续学习
为什么您需要虚拟环境
虚拟环境是一个好主意的原因有很多,这也是我在我们继续开始安装第三方软件包之前向您介绍虚拟环境的原因。让我们一个一个地检查它们。
防止版本冲突
你可以说如果你在系统范围内安装第三方软件包,你会非常高效。毕竟,您只需要安装一次,就可以从多个 Python 项目中使用这个包,从而节省您宝贵的时间和磁盘空间。然而,这种方法有一个问题,可能会在几周或几个月后显现出来。
假设您的项目Project A
是针对library X
的特定版本编写的。将来,您可能需要升级库 X。比方说,您需要您启动的另一个项目(称为项目 B)的最新版本。您将库 X 升级到最新版本,项目 B 开始正常工作。太好了!但是一旦你这么做了,你的Project A
代码就会崩溃。毕竟,在主要版本升级时,API 可能会发生重大变化。
虚拟环境通过将您的项目与其他项目和系统范围的包隔离开来解决了这个问题。您在这个虚拟环境中安装软件包,专门用于您正在处理的项目。
易于复制和安装
虚拟环境使得定义和安装特定于您的项目的包变得容易。使用一个 requirements.txt 文件,您可以为所需的包定义精确的版本号,以确保您的项目将始终使用一个经过代码测试的版本。这也有助于你的软件的其他用户,因为虚拟环境可以帮助其他人重现你的软件构建时的真实环境。
在任何地方都有效,即使不是 root
如果你在一个共享的主机上工作,比如大学或虚拟主机提供商,你将不能安装系统范围的软件包,因为你没有管理员权限这样做。在这些地方,虚拟环境允许您在项目中本地安装任何您想要的东西。
虚拟环境与其他选择
还有其他选项来隔离您的项目:
- 在最极端的情况下,你可以买第二台电脑,在那里运行你的代码。问题已解决!虽然有点贵!
- 虚拟机是一个便宜得多的选择,但仍然需要安装完整的操作系统——对于大多数用例来说,这也是一种浪费。
- 接下来是集装箱化,比如 Docker 和 Kubernetes。这些可以是非常强大的,是一个很好的选择。
尽管如此,在很多情况下,我们只是创建小项目或一次性脚本。或者,您可能只是不想将您的应用程序容器化。毕竟,这是你需要学习和理解的另一件事。无论原因是什么,虚拟环境都是隔离项目依赖性的好方法。
如何创建 Python venv
有几种方法可以创建 Python 虚拟环境,具体取决于您运行的 Python 版本。
在你继续阅读之前,我想给你介绍另外两个工具, Python 诗歌和 Pipenv 。这两个工具结合了您将要学习的工具的功能:virtualenv 和 pip。除此之外,它们还增加了几个额外的功能,最显著的是它们能够进行适当的依赖性解析。
为了更好地理解虚拟环境,我建议您先从本文开始学习基础知识。我只想确保您知道有更好的方法来管理您的包、依赖项和虚拟环境。
Python 3.4 及以上版本
如果您运行的是 Python 3.4+版本,则可以使用 Python 中内置的 venv 模块:
$ python -m venv [directory]
这个命令在指定的目录中创建一个 venv,并将 pip 复制到其中。如果你不确定该如何称呼这个目录:venv
是一个常见的选项;它不会让任何人猜测它是什么。
在本文的后面,我们将仔细查看刚刚创建的目录。但是我们先来看看如何激活这个虚拟环境。
所有其他 Python 版本
适用于任何 Python 版本的替代方法是使用 virtualenv 包。您可能需要先用 pip install 安装它:
$ pip install virtualenv
安装后,您可以创建一个虚拟环境,包括:
$ virtualenv [directory]
Python venv 激活
如何激活虚拟环境取决于您使用的操作系统。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
Windows venv 激活
要在 Windows 上激活 venv,您需要运行由 venv 安装的脚本。如果您在名为myenv
的目录中创建了 venv,那么命令应该是:
# In cmd.exe
venv\Scripts\activate.bat
# In PowerShell
venv\Scripts\Activate.ps1
Linux 和 MacOS venv 激活
在 Linux 和 MacOS 上,我们用 source 命令激活我们的虚拟环境。如果您在myvenv
目录中创建了 venv,那么命令应该是:
$ source myvenv/bin/activate
就是这样!我们准备好摇滚了!你现在可以用 pip 安装包了,但是我建议你先继续阅读以更好地理解 venv。
我的课程 Python 基础 II 广泛涵盖了:
- 创建你自己的模块和包,
- 使用虚拟环境
- 利用 Python 包管理器比如 poem 和 Pipenv,让程序员的生活变得更加轻松。
提高您作为 Python 程序员的工作效率,今天就加入我的课程吧!
Buy now for $29 (from $49)****
Python venv 如何工作
当你激活一个虚拟环境时,你的PATH
变量被改变。在 Linux 和 MacOS 上,用echo $PATH
打印路径就可以自己看了。在 Windows 上,使用echo %PATH%
(在 cmd.exe)或$Env:Path
(在 PowerShell)。在我的情况下,在 Windows 上,它看起来像这样:
C:\Users\erik\Dev\venv\Scripts;C:\Program Files\PowerShell\7;C:\Program Files\AdoptOpen....
这是一个很大的列表,我只展示了它的开始。正如您所看到的,我的 venv 的脚本目录放在所有其他东西的前面,有效地覆盖了所有系统范围的 Python 软件。
那么这个路径变量是做什么的呢?
当您输入一个在当前工作目录中找不到的命令时,您的操作系统会开始查找 PATH 变量中的所有路径。Python 也是一样。当您导入一个库时,Python 开始在您的路径中寻找库的位置。这就是我们的 venv 魔术发生的地方:如果你的 venv 在所有其他路径的前面,操作系统将在查看系统范围的目录之前首先查看它,比如/usr/bin
。因此,在我们的 venv 中安装的任何东西都会首先被发现,这就是我们如何覆盖系统范围的包和工具。
venv 里面有什么?
如果您查看 venv 的目录,您会在 Windows 上看到类似这样的内容:
.
├── Include
├── Lib
│ └── site-packages
├── pyvenv.cfg
└── Scripts
├── activate
├── activate.bat
├── Activate.ps1
├── deactivate.bat
├── pip3.10.exe
├── pip3.exe
├── pip.exe
├── python.exe
└── pythonw.exe
在 Linux 和 MacOS 上:
虚拟目录树
你可以看到:
- Python 命令以
python
和python3
的形式提供(在 Linux 和 MacOS 上),并且通过创建到它的符号链接,版本被固定到您创建 venv 时使用的版本。 - 在 Windows 上,Python 二进制文件被复制到脚本目录。
- 您安装的所有软件包都位于站点软件包目录中。
- 我们有针对多种 shell 类型(bash、csh、fish、PowerShell)的激活脚本
- Pip 可以以 pip 和 pip3 的名字获得,更具体地说是以
pip3.7
的名字获得,因为在写这篇文章的时候我已经安装了 Python 3.7。
关闭 Python venv
一旦你完成了你的项目,关闭它的 venv 是一个好习惯。通过停用,您基本上离开了虚拟环境。如果不停用 venv,所有其他执行的 Python 代码,即使在项目目录之外,也将在 venv 内部运行。
幸运的是,停用虚拟环境再简单不过了。只要输入这个:deactivate
。它在所有操作系统上都是一样的。
删除一个 Python venv
您可以完全删除一个虚拟环境,但是如何删除取决于您用来创建该虚拟环境的内容。让我们看看最常见的选项。
删除用 Virtualenv 或 python -m venv 创建的 venv
正如本文所演示的,如果您使用virtualenv
或python -m venv
来创建您的虚拟环境,那么没有特殊的命令来删除虚拟环境。在创建 virtualenv 时,您给了它一个目录来创建这个环境。
如果您想要删除此虚拟目录,请先停用,然后删除该目录及其所有内容。在类似 Unix 的系统上和 Windows Powershell 中,您应该这样做:
$ deactivate
# If your virtual environment is in a directory called 'venv':
$ rm -r venv
用 Pipenv 删除 venv
如果你使用 Pipenv 来创建 venv,那就简单多了。您可以使用以下命令删除当前的 venv:
pipenv --rm
确保您在项目目录中。换句话说,Pipenv
和Pipenv.lock
文件所在的目录。这样,pipenv 知道它必须删除哪个虚拟环境。
如果这不起作用,你可以变得更糟,手动移除静脉。首先,使用以下命令询问 pipenv 实际的 virtualenv 位置:
$ pipenv --env
/home/username/.local/share/virtualenvs/yourproject-IogVUtsM
它将输出虚拟环境及其所有文件的路径,看起来类似于上面的示例。下一步是删除整个目录,这样就完成了。
删除一个有诗的 venv
如果您使用诗歌创建了 virtualenv,您可以使用以下命令列出可用的 venvs:
poetry env list
您将得到如下列表:
test-O3eWbxRl-py2.7
test-O3eWbxRl-py3.6
test-O3eWbxRl-py3.7 (Activated)
您可以使用poetry env remove
命令删除您想要的环境。您需要从上面的输出中指定确切的名称,例如:
poetry env remove test-O3eWbxRl-py3.7
结论
您学习了如何创建、激活、停用和删除虚拟环境。我们也透过窗帘来了解 venv 的工作原理和方式。现在你知道了如何创建 venv,你需要学习如何在它里面安装软件包。在那之后,我强烈建议你学习一下 Pipenv 或者诗歌。这些工具将虚拟环境的管理与适当的包和依赖关系管理相结合。
继续学习
- 接下来:如何在你的 venv 中安装带有 pip 的软件包
- Pipenv 是管理您的 venv 和包的更好方式。
- 学习最常见的 Linux 命令(如 cd、mkdir、pwd 等)
- 官方 venv 文档:如果你想知道所有的细节和命令行选项
Pip 安装:安装和删除 Python 包
原文:https://python . land/virtual-environments/installing-packages-with-pip
Pip install 是使用 Pip 包管理器安装 Python 包的命令。如果你想知道 Pip 代表什么,Pip 这个名字是“Pip Installs Packages”的递归缩写。使用 pip 安装 Python 包有两种方法:
- 手动安装
- 使用一个定义所需包及其版本号的
requirements.txt
文件。
但是在我们开始之前,让我们确保pip
本身已经安装好了!
目录
- Python:安装 Pip
- Pip 安装 Python 包
- Pip 安装需求. txt 文件
- 带有 pip install -i 的自定义存储库
- 使用 pip install -e 进行可编辑安装
- Pip 卸载
- 更多画中画命令
- Python 包索引
- 继续学习
Python:安装 Pip
首先:我们需要安装 pip 本身。好消息是 Pip 可能已经存在于您的系统中。大多数 Python 安装程序也会安装 Pip。如果您使用的是从python.org下载的 Python 2 >=2.7.9 或 Python 3 >=3.4,则已经安装了 Python 的 pip。如果你在一个虚拟环境中工作,pip 也会为你安装。
因此,在您尝试安装 Pip 之前,请确保您的系统上还没有安装它。打开终端(Linux/MacOS)或 Windows shell,键入以下命令:
pip help
如果pip
命令出错,请尝试pip3
命令。Python 2 和 3 可以在某些系统上并排安装。在这些系统上,pip 通常以pip3
的名字安装:
pip3 help
如果这也不起作用,您可以尝试大多数现代 Python 安装中内置的 pip 模块:
python3 -m pip help
如果这也失败了,您需要自己安装它,所以让我们来看看如何手动安装它。你应该只在万不得已时才这么做,我强烈建议你先检查一下你的 Python 安装是否顺利。
在 Windows 和 Mac 上安装 Pip
在 Windows 和 Mac 上,可以下载一个 Python 脚本来安装 pip,名为 get-pip.py 。从命令提示符或终端窗口下载文件并使用 Python 运行它:
python3 get-pip.py
确保您位于下载脚本的目录中。
在 Linux 上安装 Pip(Ubuntu,Debian,Redhat)
您可以在 Debian、Ubuntu、Linux Mint 和其他 Debian 衍生工具上使用 apt 包管理器安装 pip。这是最值得推荐的方法,可以确保您的系统保持一致的状态。
$ sudo apt install python3-pip
如果您的系统使用 yum 软件包管理器,您可以尝试以下方法:
$ sudo yum install python-pip
Pip 是 EPEL(Enterprise Linux 的额外包)的一部分,所以您可能需要启用第一个。
如果这些方法失败了,您还可以下载一个 Python 脚本,它将使用以下命令为您安装 pip:
$ curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
....
$ python3 get-pip.py
Pip 安装 Python 包
我必须强调这一点:最好是在虚拟环境中安装软件包。好消息是:默认情况下,pip 存在于您的虚拟环境中。因为我们 venv 中的所有东西都是本地安装的,所以你不需要成为拥有sudo
或su
的超级用户,也不会有软件包版本冲突的风险。
话虽如此,您也可以在 Python venv 之外安装软件包。这仅推荐用于您可能在许多脚本中需要的更通用的包,或者如果该包作为更通用的工具使用。一些符合这种描述的示例库将是 Numpy 和 Pandas,一个 REPL 的替代品,如 ipython ,或者完整的环境,如 Jupyter Notebook 。
本地安装(无根用户或超级用户)
幸运的是,现在大多数操作系统上的 Python 安装以这样一种方式配置您的系统,即您不必成为管理员(或 Unix 系统上的 root)就可以在 venv 之外安装软件包。
然而,你并不总是拥有超级用户权限来安装系统范围内的软件包,例如,当在工作场所或学校的共享或锁定系统上工作时。
在这种情况下,您可以使用--user
选项将包安装到您平台的 Python 用户安装目录中。这通常意味着软件包安装在以下位置:
- 在类 Unix 系统上。
%APPDATA%\local\programs\python
在 Windows 上
事实上,在虚拟环境之外运行时,在本地安装目录中安装包通常是默认的。因此,让我们首先尝试升级我们的帐户范围的 pip 安装。确保您当前不在虚拟环境中,并输入:
pip install --upgrade pip
根据您使用的 Python 安装程序和您所在的系统,pip 可能会尝试在系统范围内安装软件包。因此,如果您不使用 sudo 之类的东西或者没有成为管理员,您可能会遇到权限错误。如果您得到许可错误,这意味着 pip 试图安装它的系统范围。在这种情况下,您可以添加--user
选项,强制将其安装在您的系统帐户中:
pip install --user --upgrade pip
这些命令要求pip
在你的用户帐户中安装pip
,如果已经安装了就更新它。在我的系统上,这给了我一个信息,要求已经被满足,意味着 pip 已经是最新的了。
现在,作为一个练习,您可以尝试安装 ipython 。这是标准 Python REPL 的一个很好的替代品。
在 venv 中安装 Python 包
现在,让我们尝试在虚拟环境中安装一个包。对于这个例子,我选择了simplejson
。首先,激活您的虚拟环境,然后键入:
pip install simplejson
您刚刚在虚拟环境中安装了一个包,因此只有在您激活此 venv 时才能访问它。
在系统范围内安装 Python 包
我先重申一下,我不建议这样。如果您没有阅读该主题的其余部分就跳到这里,请先阅读上面关于在您的用户帐户或虚拟环境中安装软件包的部分。
在 Linux 上,您可以以 root 用户的身份在系统范围内安装软件包。我强烈建议你不要这样做,但下面是如何用sudo
来做:
sudo pip install --upgrade pip
Pip 安装需求. txt 文件
在虚拟环境中,安装特定版本的包是一个好习惯。它确保您从一开始就获得使用虚拟环境的全部好处。毕竟,我们这样做是为了通过确定特定的依赖版本来确保我们的软件总是按预期工作。
一个requirements.txt
文件包含一个简单的依赖列表,每行一个。最简单的形式是这样的:
simplejson
chardet
但是我们真正想要的是固定版本。这也不难:
chardet==3.0.4
simplejson==3.17.0
您还可以通过使用> =和< =或者甚至是它们的组合来稍微放宽这些约束:
chardet>=3.0.0,<=3.1.0
simplejson>=3.17.0
您可以使用这些说明符中的任何一个:==
、>
、>=
、<
、<=
选择什么版本范围
你怎么知道用什么范围?这是大多数教程和课程似乎都在回避的话题。不幸的是,对此没有硬性规定。你必须阅读发行说明以及相关软件包中的内容。
例如,让我们假设您正在使用两个包,A 和 b。也许您知道某个特性在包 A 版本 3.1.0 中可用,所以您至少需要那个版本。但是从新闻中,你了解到作者正计划在 4.0.0 版本中对包 A 的 API 进行大修。另外,B 包有点陈旧,维护不好。它多年来一直停留在 0.6.2 版本,需要 3.1.6 或更低版本的包 A(而 A 目前是 3.5.0)。您可以定义以下约束,以确保一切都很好地协同工作:
A>=3.1.0,<=3.1.6
B==0.6.2
你看,有时需要一些杂耍来正确定义版本约束。这就是为什么许多开发人员使用 Pip 的替代品,如poem或 Pipenv 。这些工具提供了高级的依赖关系管理,如果可能的话,会自动解决所有的依赖关系。在本课程中,我们将进一步深入了解包管理器。现在,我们要确保你了解基本知识。这样,包管理器看起来就不那么神奇了,而且你会更好地理解它们为你做了什么。这可不是什么有趣的事情
Pip 冻结
使用 pip 的冻结选项创建您的需求文件可以让您的生活稍微轻松一些。首先,编写您的软件并安装您需要的所有需求。完成后,一切看起来都很好,使用下面的命令:
$ pip freeze > requirements.txt
Pip 用所有当前安装的依赖项创建了一个requirements.txt
文件,包括版本号。整洁!您可以选择稍微放宽版本,例如,允许对软件包进行较小的更新(错误修复、安全修复)。但是请记住,这也是一种风险:由于其中一个需求改变了它的 API 的行为或其他原因,项目总是有可能不按预期的那样运行。
从 requirements.txt 文件安装 Pip
最后,要安装该文件中列出的所有依赖项,请使用:
$ pip install -r requirements.txt
这是您在阅读 Python 项目的安装说明时经常会看到的说明,现在您已经确切地知道它是做什么的以及它是如何工作的了!
带有 pip install -i 的自定义存储库
默认的 PyPI 存储库位于 https://pypi.org/simple.,但是您也可以使用一个替代的存储库。例如,如果您的公司仅允许来自内部镜像的已批准软件包的子集。或者,您的公司可能有一个自带的私人镜子。该存储库可以位于 HTTP(s) URL 或文件系统位置。
要指定自定义存储库,请使用**-i**
或**--index-url**
选项,如下所示:
pip install -i https://your-custom-repo/simple <package name>
or
pip install -i /path/to/your/custom-repo/simple <package name>
URL 必须指向符合 PEP 503(简单存储库 API)的存储库或以相同格式布局的本地目录。
使用 pip install -e 进行可编辑安装
Pip 可以选择进行可编辑的安装,这意味着您可以从本地源安装软件包。但是 pip 并没有将包文件复制到系统中的某个位置,而是创建了到安装它的代码库的符号链接。通过这种方式,您可以在项目上工作并进行更改,并且能够直接测试这些更改,而无需不断地重新安装新版本的包。
您可能需要此选项的原因有很多:
- 当您创建自己的包时
- 如果你想修理别人的包裹
- 如果你想安装一个软件包的所谓的最新版本,就从它的源代码库中安装
可编辑安装选项通过-e
选项或--editable
选项调用。让我们看看 pip 对这个选项有什么看法:
pip install --help
..
-e, --editable Install a project in editable mode (i.e. setuptools "develop mode") from a local project path or a VCS url.
..
通常情况下,您将使用 git 这样的版本控制系统进行项目工作。当在存储库的根目录中时,您将安装带有以下内容的项目:
pip install -e .
如果您不在项目根目录中,您可以简单地提供包源代码的路径,而不是使用点号。
Pip 卸载
要使用 pip 卸载软件包,我们可以使用' uninstall '子命令,例如卸载simplejson
:
pip uninstall simplejson
按照使用需求文件的 pip install 命令,您也可以使用这样的文件来卸载软件包:
pip uninstall -r requirements.txt
更多画中画命令
我们还可以使用 pip 来获得关于一个包或当前安装的包的更多信息。Pip 还提供搜索功能。让我们研究一下这些附加命令。
Pip 列表:列出已安装的软件包
要使用 pip 列出所有当前安装的软件包,请使用以下命令:
$ pip list
Package Version
----------------------- ---------------------
async-generator 1.10
attrs 20.3.0
autopep8 1.5.7
...
它将显示软件包名称和版本。当您在虚拟环境中运行该命令时,只会显示 venv 的包。如果您在 venv 之外运行它,将列出所有帐户范围或系统范围的软件包。
Pip 显示:获取软件包详细信息
现在您已经知道安装了哪些软件包,您可能需要更详细地检查其中一个。为此,我们有“显示”子命令:
$ pip show attrs
Name: attrs
Version: 20.3.0
Summary: Classes Without Boilerplate
Home-page: https://www.attrs.org/
Author: Hynek Schlawack
Author-email: hs@ox.cx
License: MIT
Location: /usr/lib/python3/dist-packages
Requires:
Required-by:
这是一种快速查找包是什么和做什么的方法,以及谁创作了它。
Pip 搜索:查找包
我们过去可以使用 pip 找到匹配某个字符串的所有包。虽然很方便,但这给 PyPI 的服务器带来了很大的负载。不幸的是,他们已经决定禁用功能。幸运的是,我们仍然有像 DuckDuckGo、Google 和 Bing 这样的搜索引擎来查找资料!
Python 包索引
我们还没有广泛讨论 Python 包索引,它位于pypi.org。该网站为我们提供了一些信息:
- 您可以创建一个帐户,只有当您想要发布自己的包时才需要它
- 它提供了一个搜索功能(更多关于这一点)
- 它向我们展示了使用统计数据,如软件包和用户的总数
- 我们可以按类别浏览
老实说,Python 包索引有所欠缺,我认为没有多少人使用这个网站。
PyPI 搜索功能
网站的功能没有提供太多价值的一个例子是搜索功能。如果你确切地知道你在寻找什么,那很好。让我们试着搜索一下非常受欢迎的请求包。它将首先显示,我们可以点击此处的信息页面,在这里我们可以看到以下内容:
- 包的描述
- 作者的姓名和电子邮件地址
- 项目主页、文档和源代码的链接
- 历史版本列表
所有这些信息都是由软件包作者提供的,所以请记住这一点。
现在假设我们不记得名称请求,而是搜索单词 HTTP:
PyPI.org 研究
如果你愿意的话,你可以试着自己看看:requests 包在哪里都找不到。因此,如果您需要发现一个库来做一件特定的事情,比如做 HTTP 请求,我强烈建议您使用 Google。
简单的 Google 搜索会发现 Python 有一个内置的 HTTP 库,这已经足够好了。然而,第一个结果页面也多次提到请求,这给了我们一个强烈的信号,表明这个包也被很多人使用。
PyPI 类别
如果你决定浏览类别,你也会发现它们没什么帮助。幸运的是,Python 社区很强大,许多站点(比如 Python Land!)填补空白,并就使用哪些软件包提供好的建议。
所以简而言之:PyPI 站点对于发现来说不是很有用,但是它在给你提供包信息和到文档和源代码库之类的链接方面已经足够了。
继续学习
- 如果你想要一个功能更丰富的替代品,你应该检查一下 Pipenv 。
- 如果你还没有看我的虚拟环境教程的话,看看吧。
- 如果您想了解更多关于 pip 及其所有选项的信息,请访问官方 PyPI 文档。
- 阅读 pip 的配置文件
- 可编辑安装(也称为开发模式)的官方 Python.org 部分
Python 诗歌:使包和 venv 管理变得容易
poem 是 Python 的一个包管理器。这是一个有多种用途的工具。它:
- 方便地管理您的虚拟环境(无需手动创建)
- 是安装 Python 包的一个很好的工具(你不再需要 pip install
- 帮助您管理依赖关系
- 可用于构建可与全世界共享的 Python 包
本文仔细研究了 Python 诗歌。我们将首先正确地安装它,然后我将向您展示如何用诗歌创建项目,并管理您的虚拟环境和项目依赖关系。
目录
- 为什么是诗歌?
- 安装 Python 诗歌
- 用 Python 诗歌开始一个项目
- 用 Python 诗安装和移除包
- 使用项目的虚拟环境
- 更新您的依赖关系
- 构建和发布包
- 诗是互通的
- 诗歌命令备忘单
- 诗歌 vs Pipenv
- 了解更多信息
- 结论
为什么是诗歌?
与使用 pip 或其他包管理器相比,诗歌有几个优点。首先,它是在虚拟环境中安装 Python 包的直观工具。此外,它还可以方便地管理您的虚拟环境。诗歌还管理你的依赖关系:它会试图找到一个很好地协同工作的依赖关系的组合,并将这个组合存储在一个所谓的锁文件中。
一旦你想把你的软件作为一个 Python 包来分发,poems 也极大地简化了 Python 包的构建和上传!
安装 Python 诗歌
诗本身推荐你用他们的安装程序。这个安装程序将通过出售将诗歌与你系统的其他部分隔离开来,这样它就不会与你可能通过pip
安装的其他依赖项冲突。
使用安装程序
诗歌最近更新了它的安装程序。在本教程中,我将向您展示使用它的最新方法。您会经常找到引用另一个 URL 的其他教程。我建议不要用那个。要使用他们最新最好的安装程序,您可以在 Windows 上使用以下命令:
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
在 Mac 或 Linux 系统上:
curl -sSL https://install.python-poetry.org | python3 -
使用画中画
像大多数 Python 包一样,如果你愿意,你也可以用pip
安装 Python 诗歌。但是,这不是推荐的方法:
pip install --user poetry
如果你使用 pip,我建议你把它安装在你的用户帐号里。如今,这可能已经是 Python 安装的默认行为了。然而,我们可以通过如上所示显式使用--user
标志来确保这一点。
测试你的诗歌装置是否有效
最后,使用以下命令测试安装是否工作正常:
poetry --version
如果您看到类似Poetry 0.13.0
的内容,这意味着安装成功。
让 Python 诗歌保持最新
如果您使用了诗歌安装程序脚本,您可以使用以下命令更新诗歌:
poetry self update
如果您决定使用 pip,您将需要使用 pip 来更新诗歌:
pip install --upgrade poetry
用 Python 诗歌开始一个项目
当你用诗歌开始一个项目时,会发生相当多的事情。让我们首先创建一个新项目:
poetry new demo
这个命令在当前目录中创建了一个名为demo
的新目录。当我们检查目录时,我们会看到几个文件:
demo
├── demo
│ └── __init__.py
├── pyproject.toml
├── README.rst
└── tests
├── __init__.py
└── test_demo.py
demo
目录是您的项目所在的位置。在其中,创建了另一个名为demo
的目录。这是您的主包,包含一个__init__.py
文件,方便您使用。你可以自由地重命名或删除这个包,它只是为了你的方便。
该命令还创建了一个pyproject.toml
文件,包含项目的元数据。我们很快会仔细看看那个文件。然后是一个README.rst
文件,它应该包含项目的简短描述。它类似于更常见的 Markdown README.md 文件。我个人总是立刻将这个文件重命名为 README.md。
最后,我们看到一个tests
目录,一个包含单元测试的包,如果有的话。
您可以随意删除这些文件和目录中的任何一个,除了pyproject.toml
文件。例如,当您不想使用项目中的包和模块时,您可以删除demo directory. When you don't want to work with tests, you can remove the tests
目录。基本结构只是作为一个建议,并吸引人们遵循一些最佳实践。
py project . toml 文件
现在让我们仔细看看pyproject.toml
文件。它是一个 TOML 文件,包含项目的元数据。TOML 是一种简单的格式,易于读写。如果您曾经使用过,这种格式看起来会很熟悉。ini 文件,但是 TOML 有一个官方的规范,表达能力更强。我们的pyproject.toml
文件看起来像这样:
[tool.poetry]
name = "demo"
version = "0.1.0"
description = ""
authors = ["Your name <your@e-mail.address>"]
[tool.poetry.dependencies]
python = "^3.10"
[tool.poetry.dev-dependencies]
pytest = "^5.2"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
如您所见,该文件分为几个部分:
tool.poetry
部分包含项目的元数据,如名称、版本、描述和作者。tool.poetry.dependencies
包含项目的依赖关系。这些依赖项是在此项目中运行软件所必需的。- dependencies 包含从事此项目的开发人员所需的依赖项。最终产品不需要这些依赖项,只需要开发和测试应用程序。
- 最后,第四部分是
build-system
部分,包含构建系统的设置。我们暂时忽略这一点。
用 Python 诗安装和移除包
要添加和安装包(您的项目依赖项),您可以编辑pyproject.toml
文件,或者使用poetry add <package>
命令。我强烈建议你使用poetry add
命令,因为它不仅仅是编辑文件。它立即:
- 寻找不与其他依赖项冲突的合适版本
- 在附带的虚拟环境中安装软件包
- 创建或更新名为
poetry.lock
的锁文件
让我们来看看实际情况。我们将安装最新版本的流行 Python 请求包:
poetry add requests
输出应该如下所示:
Creating virtualenv demo-IUjJzrPZ-py3.10 in C:\Users\erik\AppData\Local\pypoetry\Cache\virtualenvs
Using version ^2.28.0 for requests
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 15 installs, 0 updates, 0 removals
• Installing pyparsing (3.0.9)
• Installing atomicwrites (1.4.0)
• Installing attrs (21.4.0)
• Installing certifi (2022.6.15)
• Installing charset-normalizer (2.0.12)
• Installing colorama (0.4.5)
• Installing idna (3.3)
• Installing packaging (21.3)
• Installing more-itertools (8.13.0)
• Installing pluggy (0.13.1)
• Installing py (1.11.0)
• Installing urllib3 (1.26.9)
• Installing wcwidth (0.2.5)
• Installing pytest (5.4.3)
• Installing requests (2.28.0)
发生了什么事?
首先:这个命令触发了虚拟环境的创建,因为这是我们第一次实际使用这个项目。虚拟环境不是在项目目录中创建的,而是在用户帐户的目录中创建的。在我的例子中,这个目录是:C:\Users\erik\AppData\Local\pypoetry\Cache\virtualenvs
。
将虚拟环境从项目中分离出来,可以更容易地将虚拟环境从版本控制中排除。如果您希望将虚拟环境放在项目目录中,您可以使用以下命令设置virtualenvs.in-project=true
选项:
poetry config virtualenvs.in-project true
此设置将应用于所有将来的项目。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
接下来,安装一堆包,以及我们请求的包。这些都是requests
包的依赖项。我们可以检查pyproject.toml
文件,看看这个包是否也添加到了那里:
[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.28.0"
它只列出了我们请求的包,而不是它所有的依赖项。毕竟,这些依赖项将在安装时由包管理器整理出来,并根据您指定的所有需求选择最佳版本。但是,依赖项将存储在锁文件中。在输出中,我们可以看到一条消息,正在写锁文件。我们来看看这个锁文件是干什么用的。
锁定包
安装完requests
后,还创建了一个新文件,名为poetry.lock
。该文件有点太大,无法包含在此页面中,但是如果您亲自查看,您会看到许多类似以下的条目:
[[package]]
name = "idna"
version = "3.3"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.5"
这些都是requests
包的需求包,包括requests
本身。将这些包的版本锁定在一个poetry.lock
文件中可以确保在重新创建虚拟环境时这些包的版本总是相同的。
这是一个强大的特性,因为它确保了我们的项目构建是确定的。这对于 CI/CD 来说很重要,对于开发人员拥有一个易于创建和重新创建的一致环境来说也很有帮助。此外,它还可以用作缓存。当重新安装有锁文件的依赖项时,poem 不需要解析所有的依赖项,因为它们已经存储在锁文件中了。
添加开发人员依赖项
如前所述,还有一个名为tool.poetry.dev-dependencies
的部分列出了构建和测试项目所需的所有依赖项。使用和运行项目(作为普通用户)不需要这些依赖项;因此,它们也不会自动安装。
要添加一个 dev 依赖项,我们可以在 add 命令中添加--dev
选项,如下所示:
poetry add --dev <package name>
移除包
要从项目中移除包,请使用以下命令:
poetry remove <package name>
这将删除该包及其所有依赖项,除非那些依赖项也是您的pyproject.toml
文件中列出的另一个包所需要的。类似于添加开发依赖项,我们也可以使用额外的--dev
选项删除它们:
poetry remove --dev <package name>
安装现有 Python 诗歌项目的依赖项
当您有一个基于诗歌的现有项目时,您可以使用以下命令一次安装pyproject.toml
中列出的所有依赖项:
poetry install
如果有一个poetry.lock
文件存在,这将从那个锁文件安装所有的包。如果没有,poems 将解析依赖项,安装软件包,并生成一个新的锁文件。
使用项目的虚拟环境
诗歌也为我们创造了一个虚拟的环境。我们现在将学习如何使用这个虚拟环境。
运行您的脚本
您可以使用poetry run
命令在项目的虚拟环境中运行脚本。例如,如果您创建了一个名为main.py
的文件,您可以使用以下命令运行它:
poetry run python main.py
同样,您也可以运行安装在虚拟环境中的命令。例如,要运行作为开发者依赖项安装的pytest
,可以使用poetry run pytest
。
激活虚拟环境
如果你想交互地使用虚拟环境,你可以使用poetry shell
命令。这个命令将在新启动的 shell 中激活虚拟环境。一旦在那里,你不需要使用诗歌运行来运行你的脚本。您可以像使用任何其他虚拟环境一样使用虚拟环境。
更新您的依赖关系
更新软件包有两种情况:
- 根据
pyproject.toml
定义的版本约束更新包 - 将软件包更新到其最新(或特定)版本
因为包版本被锁定在poetry.lock
文件中,它们不会自动更新。这是一件好事:它确保您的项目按照预期继续工作,即使您试图在一年后重新构建它。然而,我们都知道让你的软件和依赖项保持最新是多么重要,所以我们确实需要一种方法来更新它们。
要更新依赖关系,可以使用poetry update
命令。该命令更新虚拟环境中的依赖项,然后更新poetry.lock
文件。尽管如此,它仍将遵守pyproject.toml
文件的约束。例如,如果您定义了一个希望保持在 3.0.0 以上和 4.0.0 以下版本的依赖项,poems 将尝试将其更新到仍低于4.0.0
的最新3.x.x
版本。
我们也可以用poetry update package_name
更新一个特定的包。
注意,使用poetry update
相当于删除poetry.lock
文件并再次运行poetry install
。
将软件包更新到最新版本
如果想更严谨,想尝试最新版本,需要使用poetry add
命令。以下命令演示了如何将requests
包更新到其最新版本或特定版本:
# Install the latest version
poetry add requests@latest
# Install a specific version
poetry add requests@2.28.0
构建和发布包
诗歌使构建包变得容易。我只会对此做一个简单的介绍。关于完整的说明,我建议你阅读官方文档。
本质上,要构建一个包,可以使用poetry build
命令。这个命令将在一个新创建的名为dist
的目录中创建两个文件。第一个文件是一个车轮文件(。whl),一个编译后的包,第二个是包含包源代码的 tar.gz 文件。
准备好这两个文件后,您可以将它们发布到存储库中。默认情况下,这将是公共的 PyPI 存储库。不过,您也可以发布到私有存储库中。无论您在哪里发布,您可能都需要首先为存储库设置凭证,以便 poems 可以访问它。
对于 PyPI,建议创建一个访问令牌,而不是更老式的用户名和密码。一旦创建了这样的访问令牌,就可以使用以下内容对其进行配置:
poetry config pypi-token.pypi <token>
现在,您已经准备好发布您的包了:
poetry publish
诗是互通的
诗歌有几个特点,使它可以很好地与现有的项目。它还有一个导出功能,可以将您的项目转换回老式的基于 pip 的项目。
将现有项目转换为诗歌
要在预先存在的项目中使用诗歌,您可以使用poetry init
命令。该命令将在当前目录下创建一个pyproject.toml
文件:
cd my-project
poetry init
这将启动一个交互式向导,帮助您为现有项目创建一个pyproject.toml
文件!
导出到常规 requirements.txt 文件
您可以将使用诗歌创建的依赖项列表导出到一个requirements.txt
文件中。例如,当你需要和不使用诗歌的人一起工作时,这就派上了用场。我更多地使用它是在创建 Docker 图像的时候。作为构建步骤的一部分,我将我的依赖项导出到一个requirements.txt
文件中,因此我不需要在Dockerfile
中安装和使用诗歌。
要将您的依赖项导出到一个requirements.txt
文件,使用以下命令:
poetry export -f requirements.txt > requirements.txt
诗歌命令备忘单
下表列出了最常用的诗歌命令,包括它们的简短描述。
| poetry --version
| 显示您的诗歌安装版本 |
| poetry new <name>
| 创建新的诗歌项目 |
| poetry init
| 启动一个向导,帮助您将现有项目转换为诗歌项目 |
| poetry add <package>
| 将包添加到pyproject.toml
,解决依赖性,并将包安装在 venv 中 |
| poetry remove <package>
| 从项目中移除包(包括其依赖项) |
| poetry show
| 列出已安装的软件包 |
| poetry export -f <filename>
| 导出依赖项列表(目前只导出为 requirements.txt 格式) |
| poetry install
| 安装当前诗歌项目的所有依赖项。使用poetry.lock
(如果有)。 |
| poetry run <command>
| 在项目的虚拟环境中运行命令 |
| poetry shell
| 启动一个新的 shell,激活项目的虚拟环境 |
诗歌命令
诗歌 vs Pipenv
尽管我也写过一个替代方案,叫做 Pipenv ,我还是强烈推荐诗歌。即使诗歌和 Pipenv 有很多重合。我觉得大多数人都会同意诗歌是更好的选择。Pipenv 铺平了道路,但在过去几年中,它的发展停滞了一点。与此同时,诗歌获得了大量的牵引力和用户。
诗歌相对于 Pipenv 的几个优势是:
- 它使用现在标准化的
pyproject.toml
- 许多人说它在解决依赖性方面更快
- 诗歌还可以帮助你构建你的项目,并将其发布在 PyPI 或私有存储库上。
- 这很主观,但我更喜欢(彩色的)命令行界面
了解更多信息
如果您想进一步了解这款出色的工具,可以尝试以下资源:
- 这里的诗歌介绍有详细的安装说明,包括在命令行上启用自动完成的说明。
- 如果你想学习如何准确定义版本范围,诗歌网站有一个关于依赖规范的扩展部分。
- 您可以在此页面上了解更多关于创建和发布您自己的包的信息。
结论
你学到了诗歌所能提供的。到目前为止,您可能已经看到了像 poem 这样的工具是如何使您的生活比使用 pip install 和手动创建虚拟环境容易得多。我们已经了解了诗歌项目是如何构建的,pyproject.toml
和poetry.lock
文件是做什么用的,以及如何安装和更新项目的依赖项。我们还快速浏览了如何创建自己的包,以及如何通过 PyPI 与他人共享。
Python Pipenv:另一个包管理器
Pipenv 是做类似事情的 Python 诗歌的替代品。像诗歌一样,许多人更喜欢 Pipenv 而不是分别安装pip和 virtualenv 。由你决定你更喜欢哪种方法。也许你开始在一个团队或开源项目中工作,在那里 pip + virtualenv 是标准;没关系。也许,看完这个,你想说服他们改用 pipenv!然而,如果你还没有这样做,我建议你先看看诗歌。如果你问我,这是更好的选择!
目录
Pipenv vs virtualenv
与单独使用pip
和virtualenv
相比,使用 pipenv 作为 Python 包管理器有几个优点。这些是主要的:
- 你不再需要单独使用 pip 和 virtualenv。取而代之的是,你有一个工具可以做所有的事情甚至更多!
- pipenv 将您的顶层依赖项与最后测试的组合(例如,pip 冻结输出)分开。对于开发人员来说,这使得依赖关系管理更加用户友好。
- pipenv 鼓励使用最新版本的依赖项来最小化安全风险。它甚至可以扫描你的依赖已知的漏洞。
- pipenv 通过
pipenv graph
让您深入了解依赖图。 - pipenv 散列所有依赖项。它将检测在您最初将包作为依赖项包含进来之后被篡改的包。
pipenv
也可以处理requirements.txt
文件。如果有,它会自动检测并转换成一个Pipfile
。
也许这些要点会让你感到困惑。别担心,他们都会解释的!
管道安装
我们可以简单地用pip
来安装pipenv
:
$ pip install --user pipenv
--user
选项只为本地用户安装 pipenv。如果您使用它,您将确保不会干扰系统范围的包。老实说,我只是全球安装,没有任何问题。如果你得到一个错误,没有找到画中画,尝试pip3
或python -m pip
。如果你还有问题,你可能还想查看一下 Python 安装说明。
如果你在 MacOS 或者 Linux 上,我也可以推荐家酿。去家酿网站安装。之后,事情就简单了:
$ brew install pipenv
如何使用 Python pipenv
现在您已经安装了 pipenv,让我们来试试吧!
首先,创建一个目录。我把我的叫做myenv
。现在,将 cd 放入目录,并安装您的第一个 pipenv 依赖项:
$ pipenv install requests
接下来发生的是:
- pipenv 检测到还没有虚拟环境,所以它创建了一个。
- 它安装请求。
- 它创建了两个文件:
Pipfile
和Pipfile.lock
。
下面的截图显示了 pipenv 在我的系统上的运行。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
Pipenv 在作者的系统上运行
完成后,您可以选择通过以下方式进入虚拟环境:
$ pipenv shell
这是可选的,因为您可以像这样在虚拟环境中直接启动任何命令:
$ pipenv run <your command>
例如,要在虚拟环境中启动 Python 脚本:
$ pipenv run python3 myscript.py
Pipenv 是如何工作的?
我不喜欢黑盒。我想知道发生了什么事。所以我们先来考察一下Pipfile
。它应该是这样的:
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
requests = "*"
[requires]
python_version = "3.6"
如您所见,Pipfile 包含了我们的顶级需求,但没有指定版本号,因为我们没有指定版本号。现在看一下 Pipfile.lock。这是一个很大的文件,所以我在这里只展示一个片段来说明:
...
"requests": {
"hashes": [
"sha256:43999036bfa82904....",
"sha256:b3f43d496c6da..."
],
"index": "pypi",
"version": "==2.23.0"
},
...
列出了它的所有依赖项,包括版本号。基本上,这是 pip freeze 输出的高级版本。这些是我们合作过的版本。保证起作用的是一揽子计划的组合。
但是虚拟环境在哪里呢?如果你仔细看@fig:pipenv_in_action 中的截图,你会看到 virtualenv 是在~/.local/share/virtualenvs/myenv-mAvwj65b/
中创建的。我们也可以用pipenv --venv
问 pipenv。
因为我们已经熟悉了虚拟环境的结构,所以您会注意到这一个并没有什么不同。它只是位于其他地方,在你的项目结构之外。这样,您就不需要将它从您的版本控制系统中排除。
如果您在另一个位置创建另一个同名的虚拟环境,它将不会被重用。我认为,目录名中的散列是基于到 virtualenv 的路径的。Pipenv 使用它来查找正确的名称,尽管名称重复。
附加 Pipenv 特性
让我们看看 Pipenv 区别于普通 pip 和 virtualenvs 的更多特性。
分离开发包
使用pip
,您只能通过使用第二个需求文件(例如requirements-dev.txt
)将开发需求与生产需求分开。使用pipenv
,您可以简单地使用--dev
选项:
$ pipenv install pytest --dev
开始为您的项目工作的另一个开发人员可以安装所有需求,包括开发人员需求,使用:
$ pipenv install --dev
依赖性管理
尝试安装核心依赖项所需的所有子依赖项。不过,冲突可能会出现。如果包 A 至少需要包 B 的 1.10 版本,而包 C 需要 1.9 版本,那么您就有问题了。pipenv
不能为你解决,但会就此警告你,默认拒绝继续。它还提供了对您与pipenv graph
命令的依赖关系的深入了解:
$ pipenv graph
pytest==5.3.5
- attrs [required: >=17.4.0, installed: 19.3.0]
- importlib-metadata [required: >=0.12, installed: 1.5.0]
- zipp [required: >=0.5, installed: 3.1.0]
- more-itertools [required: >=4.0.0, installed: 8.2.0]
- packaging [required: Any, installed: 20.3]
- pyparsing [required: >=2.0.2, installed: 2.4.6]
- six [required: Any, installed: 1.14.0]
- pluggy [required: >=0.12,<1.0, installed: 0.13.1]
- importlib-metadata [required: >=0.12, installed: 1.5.0]
- zipp [required: >=0.5, installed: 3.1.0]
- py [required: >=1.5.0, installed: 1.8.1]
- wcwidth [required: Any, installed: 0.1.8]
requests==2.23.0
- certifi [required: >=2017.4.17, installed: 2019.11.28]
- chardet [required: >=3.0.2,<4, installed: 3.0.4]
- idna [required: >=2.5,<3, installed: 2.9]
- urllib3 [required: >=1.21.1,<1.26,!=1.25.1,!=1.25.0, installed: 1.25.8]
安全漏洞的检测
pipenv
可以扫描您的包中的安全漏洞。它通过使用safety
包来实现。输入:
$ pipenv check
并且pipenv
会扫描你的依赖图,寻找有已知安全漏洞的包!
Python 并发:分而治之
并发是指同时处理多件事情。在 Python 中,可以通过几种方式实现并发性:
- 用 线程化 ,通过让多个线程轮流。
- 有了 多重处理,我们正在使用多个进程。这样,我们就可以真正使用多个处理器内核一次完成多项任务。这就是所谓的并行。
- 使用异步 IO ,启动一个任务并继续做其他事情,而不是等待网络或磁盘的回答。
- 通过使用分布式计算,这基本上意味着同时使用多台计算机
目录
- Python 并发基础知识
- I/O 限制与 CPU 限制问题
- 我应该研究 Python 的并发性吗?
Python 并发基础知识
尽管 Python 并发有时会很困难,但这也是值得的,是一项很好的技能。我们先从一些基础知识开始。接下来,我们会问自己这样一个问题:我真的需要(或想要)并发性吗,或者替代方案对我来说是一个足够好的解决方案吗?
线程和进程的区别
所以我们已经知道了在 Python 中有多种方法来创建并发性。第一种轻量级的方法是使用线程。
一个 Python 线程是一个独立的执行序列,但是它与属于你的程序的所有其他线程共享内存。默认情况下,Python 程序有一个主线程。您可以创建更多的它们,并让 Python 在它们之间切换。这种转换发生得如此之快,以至于看起来它们是同时并排运行的。
单线程与多线程
在 Python 中创建并发的第二种方式是使用多个进程。流程也是一个独立的执行序列。但是,与线程不同,进程有自己的内存空间,不与其他进程共享。一个进程可以克隆自己,创建两个或更多实例。下图说明了这一点:
多重处理
在此图中,父进程创建了自身的两个克隆,从而产生了两个子进程。所以我们现在总共有三个过程,同时工作。
创建并发程序的最后一种方法是使用异步 IO,简称 asyncio。异步 IO 不是线程化的,也不是多处理的。事实上,它是一个单线程、单进程的范例。在这一章中,我暂时不会讨论异步 IO,因为它对 Python 来说有点新,而且我感觉它还没有完全出现。我确实打算在将来扩展这一章,将 asyncio 也包括在内。
I/O 限制与 CPU 限制问题
大多数软件受 I/O 限制,不受 CPU 限制。如果您对这些术语不熟悉:
I/O bound software
Software that is mostly waiting for input/output operations to finish, usually when fetching data from the network or from storage media
CPU bound software
Software that uses all CPU power to produce the needed results. It maxes out the CPU.
让我们再来看看在 Python 中实现并发的不同方法,但是现在是从 I/O 和 CPU 的角度。
Python 线程并发
在等待来自网络或磁盘的回答时,您可以使用多线程保持其他部分运行。线程是一个独立的执行序列。默认情况下,Python 程序有一个主线程。但是您可以创建更多的它们,并让 Python 在它们之间切换。这种转换发生得如此之快,以至于看起来它们是同时并排运行的。
然而与其他语言不同的是, Python 线程不会同时运行;他们轮流代替。这是因为 Python 中有一种机制叫做 全局解释器锁(GIL) 。简而言之,GIL 确保总有一个线程在运行,所以线程不会互相干扰。下一页将详细解释这一点以及线程库。
结论是线程对于 I/O 绑定的软件有很大的不同,但是对于 CPU 绑定的软件没有用。这是为什么呢?很简单。当一个线程在等待网络回复时,其他线程可以继续运行。如果您发出大量网络请求,线程会带来巨大的不同。如果您的线程正在进行繁重的计算,它们只是在等待轮到自己继续。线程化只会带来更多的开销:不断地在线程间切换会涉及到一些管理。
使用 asyncio 的 Python 并发
Asyncio 是 Python 中相对较新的核心库。它解决了与线程相同的问题:它加快了 I/O 绑定软件的速度,但方式不同。我马上承认,直到最近,我还不是 Python 中 asyncio 的粉丝。这似乎相当复杂,尤其是对初学者来说。我遇到的另一个问题是,asyncio 库在过去的几年里发展了很多。网上的教程和示例代码经常是过时的。
不过,这并不意味着它毫无用处。这是人们在许多高性能应用程序中使用的强大范例,所以我计划在不久的将来在本教程中添加一篇 asyncio 文章。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
多重处理的 Python 并发
如果你的软件是 CPU 受限的,你可以经常重写你的代码,这样你就可以同时使用更多的处理器。这样,您可以线性扩展执行速度。这种类型的并发就是我们所说的并行,尽管有 GIL,你也可以在 Python 中使用它。
不是所有的算法都可以并行运行。例如,并行化递归算法是不可能的。但是几乎总有一种替代算法可以并行工作。
使用更多处理器有两种方式:
- 在同一台机器上使用多个处理器和/或内核。在 Python 中,我们可以通过多重处理库做到这一点。
- 使用计算机网络来使用分布在多台机器上的许多处理器。我们称之为分布式计算。
Python 的多处理库,不同于 Python 线程库,绕过了 Python 全局解释器锁。这是通过实际生成多个 Python 实例来实现的。现在,多个 Python 进程同时运行多个代码副本,而不是线程在单个 Python 进程中轮流运行。
就用途而言,多处理库与线程库非常相似。可能出现的一个问题是:为什么要考虑线程?你可以猜到答案。线程更“轻”:它需要更少的内存,因为它只需要一个运行的 Python 解释器。产生新进程也有开销。因此,如果您的代码是 I/O 绑定的,线程可能就足够好了,而且更加高效和轻量级。
我应该研究 Python 的并发性吗?
在您考虑 Python 中的并发性(这可能相当棘手)之前,请务必先好好看看您的代码和算法。许多速度和性能问题可以通过实现更好的算法或添加缓存来解决。整本书都是关于这个主题的,但是有一些通用的指导方针可以遵循:
- 衡量,不要猜测。衡量代码的哪些部分运行时间最长。先关注那些部分。
- 实现缓存。如果您从磁盘、网络和数据库中执行多次重复查找,这将是一个很大的优化。
- 重用对象而不是在每次迭代中创建一个新的。Python 必须清理你创建的每个对象来释放内存。这就是我们所说的垃圾收集。对许多未使用的对象进行垃圾收集会大大降低软件的速度。
- 如果可能的话,减少代码中的迭代次数,减少迭代中的操作次数。
- 避免(深度)递归。Python 解释器需要大量的内存和内务处理。用生成器和迭代之类的东西来代替。
- 减少内存使用。一般来说,尽量减少内存的使用。例如:逐行解析一个巨大的文件,而不是先把它加载到内存中。
- 不做。你真的需要做那个手术吗?以后还能做吗?或者可以做一次,它的结果可以存储而不是反复计算吗?
- 使用 PyPy 或 Cython 。您还可以考虑替代的 Python 实现。有一些速度很快的 Python 变种。更多信息见下文。
PyPy
您可能正在使用 Python 的参考实现 CPython。大多数人都这样。它被称为 CPython,因为它是用 c 编写的。如果你确定你的代码是 CPU 受限的,这意味着它正在进行大量的计算,你应该看看 PyPy ,CPython 的一个替代方案。这是一个潜在的快速解决方案,不需要你修改任何一行代码。
PyPy 声称,平均来说,它比 CPython 快 4.4 倍。它是通过使用一种叫做的技术来实现的。Java 和。NET 框架是 JIT 编译的其他值得注意的例子。相比之下,CPython 使用解释来执行你的代码。虽然这提供了很大的灵活性,但它也非常慢。
使用 JIT,您的代码在运行程序时被编译。它结合了提前编译(C 和 C++等语言使用)的速度优势和解释的灵活性。另一个好处是,JIT 编译器可以在运行时不断优化您的代码。您的代码运行的时间越长,它就变得越优化。
PyPy 在过去的几年里取得了长足的进步,通常可以作为 Python 2 和 3 的替代产品。它也可以与像 Pipenv 这样的工具完美配合。试试看!
Cython
Cython 提供了类似 C 的性能,其代码大部分是用 Python 编写的,但可以将部分 Python 代码编译成 C 代码。这样,您可以将算法的关键部分转换成 C 语言,这通常会提供巨大的性能提升。
它是 Python 语言的超集,这意味着它为 Python 语法增加了额外的内容。不是 PyPY 那样的顺带替换。它需要适应您的代码,并了解 Cython 添加到语言中的额外内容。
使用 Cython,也可以利用 C++语言,因为 C++标准库的一部分可以直接从 Cython 代码中导入。Cython 在 Python 的科学用户中特别受欢迎。几个值得注意的例子:
- SageMath 计算机代数系统在性能和与其他库的接口方面都依赖于 Cython
- SciPy、pandas 和 scikit-learn 库的重要部分都是用 Cython 编写的
- XML 工具包 lxml 主要是用 Cython 编写的
Python GIL(全局解释器锁)
Python 有一个特性,这使得并发编程更加困难。它被称为 Python GIL ,是全局解释器锁的缩写。GIL 确保任何时候都只有一个线程在运行。因为一次只能运行一个线程,所以不可能使用多个带线程的处理器。但是不要担心,有一种方法可以解决这个问题,使用多处理库。
目录
- 线程安全
- 竞赛条件的演示
- 我们能摆脱蟒蛇 GIL 吗?
- 2021 年 Python GIL 的最新进展
线程安全
如前所述, Python 线程共享相同的内存。由于多个线程同时运行,我们不知道线程访问共享数据的顺序。因此,访问共享数据的结果取决于调度算法。这个算法决定哪个线程何时运行。线程“竞相”访问/更改数据。
Thread safety
Thread-safe code only manipulates shared data in such a way, that it does not interfere with other threads.
发明 GIL 是因为 CPython 的内存管理不是线程安全的。由于一次只运行一个线程,CPython 可以放心,永远不会出现竞争情况。
竞赛条件的演示
举个例子,让我们创建一个名为a
的共享 Python 变量,值为 2:
a = 2
现在假设我们有两个线程,线程 1 和线程 2。它们执行以下操作:
- 线程 _ 一:
a = a + 2
- 线程 _ 二:
a = a * 3
如果 thread_one 能够首先访问a
,然后 thread_two 能够访问,那么结果将是:
- a = 2 + 2,
a
现在是 4。 - a = 4 * 3,
a
现在是 12。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
但是,如果碰巧 thread_two 先运行,然后 thread_one 运行,我们会得到不同的输出:
- a = 2 * 3,
a
现在是 6 a
a = 6+2,现在是 8
因此,执行顺序显然对输出有影响。然而,还有一个更糟糕的结果!如果两个线程同时读取变量a
,做它们自己的事,然后赋新值,会怎么样?他们都会看到 a = 2。取决于谁先写出它的结果,a 最终会是 4 或 6。不是我们所期望的!这就是我们所说的竞争条件。
Race condition
The condition of a system where the system’s behavior is dependent on the sequence or timing of other, uncontrollable events.
竞争条件很难发现,尤其是对于不熟悉这些问题的软件工程师来说。此外,它们往往随机发生,导致不稳定和不可预测的行为。众所周知,这些错误很难发现和调试。这正是 Python 有 GIL 的原因——让大多数 Python 用户的生活更轻松。
我们能摆脱蟒蛇 GIL 吗?
如果 GIL 在并发性方面阻碍了我们,我们难道不应该摆脱它或者关闭它吗?没那么容易。其他特性、库和包已经开始依赖 GIL,所以必须有东西来取代它,否则整个生态系统将会崩溃。这是一个很难解决的问题。如果你感兴趣,你可以在 Python wiki 上阅读更多相关内容。
2021 年 Python GIL 的最新进展
最近,有人通过提供一个去除了 GIL 的很有前途的概念验证 CPython 版本重新开始了讨论。这个概念验证的源代码可以在 Github 这里找到。作者附上了一份综合文件,解释了该操作的细节和特点。对于那些想更多了解这个主题的人来说,这是一本有趣的读物。
设定基线
原文:https://python.land/python-concurrency/setting-the-baseline
在所有关于 Python 并发和 Python GIL 的理论之后,我们现在准备好一些示例代码和实验。我们开始工作吧!
目录
我们的测试函数
让我们首先定义一个函数,我们可以用它来测试我们不同的选项。以下所有示例都使用相同的函数,称为 heavy :
def heavy(n, myid):
for x in range(1, n):
for y in range(1, n):
x**y
print(myid, "is done")
heavy 函数是一个嵌套的 Python for 循环,它执行乘法运算。这是一个受 CPU 限制的函数。如果您在运行时观察您的系统,您将看到 CPU 使用率接近 100%(对于一个内核)。你可以用你想要的任何东西来代替它,但是要注意竞争条件——不要使用共享对象或变量。
我们将以不同的方式运行这个函数,并探索常规单线程 Python 程序、多线程和多重处理之间的差异。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
基线:单线程执行
每个 Python 程序至少有一个线程:主线程。下面你会发现单线程版本,这是我们的速度基准。它按顺序运行我们的繁重函数 80 次:
import time
# A CPU heavy calculation, just
# as an example. This can be
# anything you like
def heavy(n, myid):
for x in range(1, n):
for y in range(1, n):
x**y
print(myid, "is done")
def sequential(n):
for i in range(n):
heavy(500, i)
if __name__ == "__main__":
start = time.time()
sequential(80)
end = time.time()
print("Took: ", end - start)
在我的系统上,这大约需要 46 秒才能完成。
请注意,if __name__ == "__main__":
部分是在 Windows 计算机上工作所必需的,但最好总是使用它。
在接下来的文章中,我们将探索一个线程版本和一个多处理版本,并了解这两种编写并发代码方式的区别。
Python 线程
既然我们已经设置了基线,我们可以尝试通过使用 Python 线程库来提高代码的速度。您很快就会发现,当您的软件受 CPU 限制时,线程就没有多大用处了。这就是为什么我还将展示 IO 绑定软件如何从线程化中获得巨大好处。如果你需要一个关于 IO 绑定和 CPU 绑定之间的区别的提示,请首先阅读我关于 Python 并发性的文章。
目录
一个 CPU 绑定的 Python 线程示例
在下面的例子中,我们使用多线程来运行我们的重 函数,再次运行 80 次。每次调用都有自己的线程:
import threading
import time
# A CPU heavy calculation, just
# as an example. This can be
# anything you like
def heavy(n, myid):
for x in range(1, n):
for y in range(1, n):
x**y
print(myid, "is done")
def threaded(n):
threads = []
for i in range(n):
t = threading.Thread(target=heavy, args=(500,i,))
threads.append(t)
t.start()
for t in threads:
t.join()
if __name__ == "__main__":
start = time.time()
threaded(80)
end = time.time()
print("Took: ", end - start)
这个线程版本在我的系统上运行大约需要 47 秒。如果繁重的函数有很多阻塞 IO,比如网络调用或文件系统操作,这个版本将是一个很大的优化。原因是而不是CPU 绑定函数的优化是 Python GIL !
如果 Python 没有 GIL,速度会快得多。然而,尽管有 80 个线程,它的运行速度和基线差不多。实际上,基线甚至更快,因为它没有线程创建和线程间切换的所有开销。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
一个 IO 绑定的 Python 线程示例
这是工作中的 GIL。每个线程轮流运行,而不是同时运行。为了证明这一点,如果 heavy 是一个 I/O 绑定的函数,这将会给我们带来巨大的速度提升,我们将创建一个小测试!我们可以使用time.sleep()
来模拟 I/O 绑定软件。睡眠与阻塞 IO 有着相同的效果:它允许 CPU 做其他事情,并在特定时间过后返回。
我在下一段代码中修改了 heavy 函数:
import threading
import time
# An I/O intensive calculation.
# We simulate it with sleep.
def heavy(n, myid):
time.sleep(2)
print(myid, "is done")
def threaded(n):
threads = []
for i in range(n):
t = threading.Thread(target=heavy, args=(500,i,))
threads.append(t)
t.start()
for t in threads:
t.join()
if __name__ == "__main__":
start = time.time()
threaded(80)
end = time.time()
print("Took: ", end - start)
即使我们有 80 个 Python 线程都休眠了两秒钟,这段代码仍然在两秒多一点的时间内完成。当休眠时,Python 线程库可以调度其他线程运行。太棒了。
继续学习
如果你想了解更多关于 Python 线程的知识,请务必阅读官方文档。您可能很好奇我们是否以及如何优化 CPU 受限的代码。这正是我们将在下一篇关于 Python 多处理的文章中发现的。
Python 多重处理
原文:https://python . land/python-concurrency/python-multi processing
有了 Python 多重处理库,我们可以编写真正的并行软件。当我们使用 Python 线程时,我们没有利用多个 CPU 或 CPU 内核。然而,有了这个库,我们将。首先,我们需要导入模块multiprocessing
而不是threading
。
目录
基于多重处理的示例
import time
import multiprocessing
# A CPU heavy calculation, just
# as an example. This can be
# anything you like
def heavy(n, myid):
for x in range(1, n):
for y in range(1, n):
x**y
print(myid, "is done")
def multiproc(n):
processes = []
for i in range(n):
p = multiprocessing.Process(target=heavy, args=(500,i,))
processes.append(p)
p.start()
for p in processes:
p.join()
if __name__ == "__main__":
start = time.time()
multiproc(80)
end = time.time()
print("Took: ", end - start)
这段代码运行时间大概 23 秒,是线程版的一半!
如您所见,这在代码方面与线程版本几乎相同。线程模块和多处理模块被有意设计成非常等效。但是这一次 80 次调用的 heavy 完成的速度大约快了一倍。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
我的测试系统(一台小型台式电脑)只有两个 CPU 内核,所以这解释了为什么它是两倍。如果我在我全新的笔记本电脑上运行这段代码,有 4 个更快的 CPU 内核,速度会快 4 倍以上。这完美地展示了在 CPU 受限代码的情况下,多处理为我们提供的线性速度提升。
Python 多处理池
我们可以通过使用multiprocessing.Pool(p)
让多处理版本更优雅、更快。这个 Python 多重处理助手创建了一个大小为p
的进程池。如果您不为p
提供一个值,它将默认为您系统中 CPU 内核的数量,这在大多数时候实际上是一个明智的选择。
通过使用Pool.map()
方法,我们可以向池提交工作。这项工作以简单函数调用的形式进行:
import time
import multiprocessing
# A CPU heavy calculation, just
# as an example. This can be
# anything you like
def heavy(n, myid):
for x in range(1, n):
for y in range(1, n):
x**y
print(myid, "is done")
def doit(n):
heavy(500, n)
def pooled(n):
# By default, our pool will have
# numproc slots
with multiprocessing.Pool() as pool:
pool.map(doit, range(n))
if __name__ == "__main__":
start = time.time()
pooled(80)
end = time.time()
print("Took: ", end - start)
这个版本的运行时与非池化版本大致相同,但是它必须创建更少的进程,因此效率更高。毕竟,我们没有创建 80 个流程,而是创建了 4 个,并且每次都重用它们。
继续学习
如果您想了解更多关于 Python 多处理和使用多处理池的知识,您可以前往官方文档。如果您经常使用命令行,您可能也会对我关于 Bash 多处理的文章感兴趣!
用 Python 处理数据
Python 是进行数据处理的理想语言,这也是它被广泛用于数据科学的原因之一。Python 支持 CSV、JSON 和 XML 等现成的数据格式。然而,PyPI 上也有许多外部库来帮助您处理数据,比如 PyYAML。
Python 数据处理主题
在这一节中,我们将深入处理各种数据。目前,我只涉及了 JSON 和 YAML 的主题,但是还有更多的主题正在酝酿中:
- 处理纯文本文件
- 使用 JSON
- 用 Python 读写 YAML
- 处理 CSV 数据
- 用 Python 读取 Excel 数据
- 处理 PDF 文件
Python 中的 JSON:如何读、写和解析
JSON 是 JavaScript 对象符号的缩写,是一个开放标准。尽管它的名字并不意味着如此,但它是一种独立于语言的数据格式。通过 Python 的 JSON 库,我们可以读取、写入和解析 JSON,以便使用这种通用的数据格式存储和交换数据。这是一种流行的数据格式,因为它对人类来说也很容易读写,尽管不像 YAML 那么容易!
在 Python 中使用 JSON 非常简单!Python 有两种数据类型,它们共同构成了在 Python 中使用 JSON 的完美工具:字典和列表。
目录
- 导入 JSON 库
- 如何在 Python 中解析 JSON
- 用 json.dumps 编码 JSON】
- 在命令行漂亮打印 JSON
- 如何在 python 中读取 JSON 文件
- 如何用 Python 把 JSON 写到文件里
- 常见问题解答
- 继续学习
导入 JSON 库
Python 附带了一个强大而优雅的 JSON 库来帮助你解码和编码 JSON。您可以通过以下方式导入模块:
import json
这个库是 Python 的一部分,所以你不需要用 Pip 包管理器安装它。
如何在 Python 中解析 JSON
解析一串 JSON 数据,也称为解码 JSON,就像使用json.loads(…)
一样简单。Loads 是 load string 的缩写。
它转换:
这里有一个json.loads
的例子:
https://crumb . sh/embed/xh 7 quabjqji
使用json.loads
解析 JSON 字符串
如果上面的交互式示例不起作用(它仍处于测试阶段),这里有一个更静态的示例:
>>> import json
>>> jsonstring = '{"name": "erik", "age": 38, "married": true}'
>>> person = json.loads(jsonstring)
>>> print(person['name'], 'is', person['age'], 'years old')
erik is 38 years old
>>> print(person)
{'name': 'erik', 'age': 38, 'married': True}
输出可能看起来像一个字符串,但它实际上是一个字典,你可以在你的代码中使用,就像我们关于 Python 字典的页面上解释的那样。您可以自己检查:
>>> type(person)
<class 'dict'>
用 json.dumps 编码 JSON】
用 Python 的json.dumps
对 JSON 数据进行编码就像解码一样简单。使用json.dumps
(“转储到字符串”的缩写)将由字典、列表和其他本地类型组成的 Python 对象转换成一个字符串:
https://crumb.sh/embed/JdYSfwepFSH
将字典编码成 JSON 字符串
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
下面是同样的例子,以防上面的交互式例子在你的浏览器中不起作用:
import json
person = {'name': 'erik', 'age': 38, 'married': True}
json_string = json.dumps(person)
print(json_string)
# {"name": "erik", "age": 38, "married": true}
# To make sure, let's print the type too
print(type(json_string))
# <class 'str'>
这是同一个文档,转换回字符串!如果想让 JSON 文档更易于阅读,可以使用 indent 选项。它将使用空格字符很好地格式化 JSON:
>>> person = {'name': 'erik', 'age': 38, 'married': True}
>>> print(json.dumps(person, indent=2))
{
"name": "erik",
"age": 38,
"married": true
}
在命令行漂亮打印 JSON
Python 的 JSON 模块也可以从命令行使用。它将验证和美化您的 JSON:
$ echo "{ \"name\": \"Monty\", \"age\": 45 }" | \
python3 -m json.tool
{
"name": "Monty",
"age": 45
}
不过,你也可能对使用 jq 工具感兴趣!
如何在 python 中读取 JSON 文件
除了json.loads
,还有一个函数叫做json.load
(不带 s)。它将从一个文件中加载数据,但是你必须自己打开文件。如果您想将 JSON 文件的内容读入 Python 并解析它,请使用以下示例:
with open('data.json') as json_file:
data = json.load(json_file)
...
如何用 Python 把 JSON 写到文件里
json.dump
函数用于将数据写入 JSON 文件。你需要先用写模式打开文件:
data = {'name': 'Eric', 'age': 38 }
with open('data.json', 'w') as json_file:
json.dump(data, json_file)
常见问题解答
How do I convert a list (array) to JSON in Python?
简单地使用上面描述的方法。json.dump
和json.dumps
函数接受字典和列表
How do I convert a dict or dictionary to JSON in Python?
类似于数组,所以在字典上使用json.dump
或json.dumps
。
How can I sort the JSON output in Python?
dump 和 dumps 函数都接受一个叫做 sort_keys 的选项,例如:json.dumps(data, sort_keys=True)
。
Does the JSON library output Unicode data?
默认情况下:否。库输出 ASCII 并将转换不属于 ASCII 的字符。如果希望输出 Unicode,请将 ensure _ ascii 设置为 False。示例:json.dumps(data, ensure_ascii=False)
继续学习
- 如果你正在寻找一种易于人类编写的格式(例如:配置文件),请阅读我们关于用 Python 读写 YAML 的文章。
- JMESPath 是一种用于 JSON 的查询语言。Python 中的 JMESPath可以让你轻松地从 JSON 文档或字典中获取你需要的数据。
- 如果您需要在命令行上解析 JSON,请在一个名为 jq 的工具上尝试我们的文章!
- 复习一下使用 Python 打开、写入和读取文件的。
JMS path Python:JSON 查询语言
原文:https://python . land/data-processing/working-with-JSON/jmespath
JMESPath 是一种 JSON 查询语言。Python 中的 JMESPath 允许您轻松地从 JSON 文档或字典中获取所需的数据。这个库可用于 Python,也可用于许多其他编程语言,这意味着如果您掌握了 JMESPath 查询语言,您可以在许多地方使用它。
目录
JMS path 解决的问题
正如我们在上一页看到的,使用 Python 自己的 JSON 库,很容易从 Python 字典中获得嵌套值。例如:doc["person"]["age"]
将在如下所示的文档中获取年龄的嵌套值:
{
"persons": {
"name": "erik",
"age": "38"
}
}
但是,如果您想从一个人员数组中提取所有的年龄字段,该怎么办呢?
{
"persons": [
{ "name": "erik", "age": 38 },
{ "name": "john", "age": 45 },
{ "name": "rob", "age": 14 }
]
}
我们可以编写一个 Python for-loop 并遍历所有人。很简单。但是循环很慢,并且会给代码带来复杂性。这就是JMS path的用武之地!
为 Python 安装 JMESPath】
JMESPath 不是 Python 标准库的一部分,这意味着您需要用 pip 或 pipenv 来安装它。与大多数 Python 包一样,JMESPath 包托管在 PyPI 上。它的名字并不奇怪,就是 jmespath。
例如,在虚拟环境中使用 pip 时,可以安装 pip ,导入模块,如下:
$ pip3 install jmespath
$ python3
Python 3.8.2 (default, Jul 16 2020, 14:00:26)
>>> import jmespath
>>> j = { "people": [{ "name": "erik", "age": 38 }] }
>>> jmespath.search("people[*].age", j)
[38]
>>>
JMESPath Python 示例
让我们从一些简单的用例开始。我们将从数组中获取第一个人,然后获取第一个人的年龄:
>>> jmespath.search('persons[0]', persons)
{'name': 'erik', 'age': 38}
>>> jmespath.search('persons[0].age', persons)
38
在上面的问题陈述中,我们希望从 JSON 文档中的人员数组中提取所有年龄字段。这个 JMESPath 表达式将完成这项工作:
>>> import jmespath
>>> persons = {
... "persons": [
... { "name": "erik", "age": 38 },
... { "name": "john", "age": 45 },
... { "name": "rob", "age": 14 }
... ]
... }
>>> jmespath.search('persons[*].age', persons)
[38, 45, 14]
假设您想过滤列表,只获取名为“erik”的人的年龄。您可以使用过滤器来完成此操作:
>>> jmespath.search("persons[?name=='erik'].age", persons)
[38]
注意,我们现在使用双引号,因为我们需要在过滤器表达式中引用名称。
继续学习
你现在可以开始实验了!如果您想了解更多信息,请尝试以下链接:
Python YAML:如何加载、读取和编写 YAML
YAML 是“YAML 不是标记语言”的递归首字母缩写,是一种人类可读的数据序列化语言。它通常用于配置文件,但也可用于数据交换。最常用的 python YAML 解析器是 PyYAML,这是一个允许你加载、解析和编写 YAML 的库,很像 Python 的 JSON 库帮助你使用 JSON。
这篇文章教你如何用 PyYAML 加载、读取和写入 YAML 文件。此外,您还将了解如何在您的系统上安装它,以及 YAML 与 JSON 等替代产品的比较。
目录
- 什么是 YAML?
- 为什么将 YAML 与 Python 结合使用?
- 安装和导入 PyYAML
- 用 Python 读取并解析 YAML 文件
- 用 Python 解析 YAML 字符串
- 解析包含多个 YAML 文档的文件
- 将 YAML 写入(或转储)到文件中
- 使用 Python 将 YAML 转换成 JSON】
- 将 JSON 转换成 YAML
- PyYAML safe _ load()vs load()
- 追加资源
什么是 YAML?
YAML 标志
据官网(1)YAML 是一种对所有编程语言都友好的数据序列化语言。 YAML 最常用于配置文件,但也用于数据交换。
YAML 对于人类来说很容易写和读,即使对于非程序员来说也是如此。同时,解析 YAML 也很容易,尤其是使用 Python 和 PyYAML 库!它的人性化和可读性是 YAML 相对于其他格式的最大优势,比如 JSON 和 XML。
这些是 YAML 最突出的特征:
- 您可以在 YAML 文件中使用注释
- 您可以使用
---
分隔符在一个 YAML 文件中存储多个文档。Kubernetes 定义中经常使用的一个特性。 - 对人类来说很容易阅读
- 很容易为计算机解析
1)有趣的事实是,YAML 官方网站是以有效的 YAML 文件的形式编写的。
为什么将 YAML 与 Python 结合使用?
如果你问我,YAML 是完美的配置文件。这正是我和许多其他开发人员使用它最多的方式。其他人似乎同意,因为许多大型项目,如 Docker 和 Kubernetes,使用 YAML 来定义部署。它的语法比常用的替代方法更丰富。ini 文件,但是看起来还是不错的,并且易于编写和解析。
不过,将 YAML 与 Python 结合使用也有一些缺点:
- YAML 不是标准 Python 库的一部分,而 XML 和 JSON 是
- 它对缩进的依赖有时令人沮丧(不过,Python 开发人员已经习惯了,对吧?)
- 对于简单的用例,比如简单对象的数据交换,它可能有点太通用了。
如果您正在寻找一种良好的数据交换和存储格式,我推荐 JSON、XML 或其他更有效的格式,如协议缓冲区和 Avro。
安装和导入 PyYAML
有多个 Python 包可以解析 YAML 数据。然而,PyYAML 是解析 YAML 最流行也是最完整的实现。PyYAML 不是标准 Python 库的一部分,这意味着您需要用 Pip 来安装它。使用以下命令安装 PyYAML,最好是在虚拟环境中:
$ pip install pyyaml
在某些系统上,您需要使用 pip3:
$ pip3 install pyyaml
要在您的脚本中使用 PyYAML,导入模块,如下所示。请注意,您没有导入“pyyaml”,而只是导入了“yaml”:
import yaml
用 Python 读取并解析 YAML 文件
一旦导入了 YAML 解析器,我们就可以加载一个 YAML 文件并解析它。YAML 文件通常带有扩展名.yaml
或.yml
。让我们以下面的 YAML 文件为例,名为config.yaml
:
rest:
url: "https://example.org/primenumbers/v1"
port: 8443
prime_numbers: [2, 3, 5, 7, 11, 13, 17, 19]
加载、解析和使用这个配置文件非常类似于用 Python JSON 库加载 JSON。首先,我们打开文件。接下来,我们用yaml.safe_load()
函数解析它。请注意,我对输出做了一点修改,以便于您阅读:
>>> import yaml
>>> with open('config.yml', 'r') as file
... prime_service = yaml.safe_load(file)
>>> prime_service
{'rest':
{ 'url': 'https://example.org/primenumbers/v1',
'port': 8443
},
'prime_numbers': [2, 3, 5, 7, 11, 13, 17, 19]}
>>> prime_service['rest']['url']
https://example.org/primenumbers/v1
YAML 解析器返回最符合数据的常规 Python 对象。在这种情况下,它是一个 Python 字典。这意味着可以使用所有常规的字典特性,比如使用带有默认值的get()
。
这是同一个例子,但是是交互式的,所以你可以自己尝试一下:
https://crumb . sh/embed/xbk 87 vueyxv
用 Python 解析 YAML 字符串
您可以使用yaml.safe_load()
来解析各种有效的 YAML 字符串。这里有一个例子,它将一个简单的条目列表解析成一个 Python 列表:
>>> import yaml
>>>
>>> names_yaml = """
... - 'eric'
... - 'justin'
... - 'mary-kate'
... """
>>>
>>> names = yaml.safe_load(names_yaml)
>>> names
['eric', 'justin', 'mary-kate']
解析包含多个 YAML 文档的文件
YAML 允许你在一个文件中定义多个文档,用三个破折号(---
)分隔它们。PyYAML 也会很乐意解析这样的文件,并返回一个文档列表。您可以通过使用yaml.safe_load_all()
功能来实现。该函数返回一个生成器,该生成器将依次返回所有文档。
注意,只要从 YAML 读取文档,就需要打开文件,所以必须在with
子句内进行处理。下面是一个演示此功能的交互式示例:
https://crumb.sh/embed/3FkJXZuuK4H
将 YAML 写入(或转储)到文件中
虽然大多数人只会把 YAML 作为配置文件来读,但是写 YAML 也很方便。示例用例可能是:
- 使用用户的当前设置创建初始配置文件
- 将程序状态保存在一个易于阅读的文件中(而不是使用 Pickle 之类的东西)
在以下示例中,我们将:
- 像我们之前做的那样创建一个带有姓名的列表
- 用
yaml.dump
将名称保存到 YAML 格式的文件中 - 阅读并打印文件,作为一切按预期工作的证明
给你:
https://crumb . sh/embed/maabpr 3 qx
下面是与非交互式示例相同的代码:
import yaml
names_yaml = """
- 'eric'
- 'justin'
- 'mary-kate'
"""
names = yaml.safe_load(names_yaml)
with open('names.yaml', 'w') as file:
yaml.dump(names, file)
print(open('names.yaml').read())
- eric
- justin
- mary-kate
使用 Python 将 YAML 转换成 JSON】
如果您需要将 YAML 转换成 JSON,您可以像上面那样简单地解析 YAML。在下一步中,您可以使用 JSON 模块将对象转换成 JSON。
在本例中,我们打开一个基于 YAML 的配置文件,用 PyYAML 解析它,然后用 JSON 模块将其写入一个 JSON 文件:
https://crumb.sh/embed/rAUgstafYtA
下面是与非交互式示例相同的代码:
import yaml
import json
with open('config.yml', 'r') as file:
configuration = yaml.safe_load(file)
with open('config.json', 'w') as json_file:
json.dump(configuration, json_file)
将 JSON 转换成 YAML
为了完整起见,让我们反过来做:将 JSON 转换成 YAML:
https://crumb.sh/embed/CjAnBJVynyU
如何用 Python 将 JSON 转换成 YAML
PyYAML safe _ load()vs load()
你会遇到很多 PyYAML 用法的例子,其中使用了load()
而不是safe_load()
。我故意到现在才告诉你load()
功能。由于大多数人都有工作要做,并且倾向于快速复制粘贴一些示例代码,我希望他们使用最安全的方法用 Python 解析 YAML。
然而,如果你对这两者之间的区别感到好奇,这里有一个简短的总结:load()
是一个非常强大的函数,就像泡菜,如果你知道那个函数的话。这两种方法都非常不安全,因为它们允许攻击者执行任意代码。PyYAML 的 load 函数允许您序列化和反序列化完整的 Python 对象,甚至执行 Python 代码,包括调用os.system
库,该库可以在您的系统上执行任何命令。
在最近的 PyYAML 版本中,load()
函数被弃用,当您以不安全的方式使用它时,它会发出一个很大的警告。
如果你像我们 99%的人一样解析常规的 YAML 文件,你应该总是使用safe_load()
,因为它只包含了 load 函数的一个子集。所有可怕的、任意代码执行类型的东西都被剔除了。
追加资源
以下是您可能想继续使用的一些资源:
- 【PyYAML 官方文档
- 如何用 Python 打开、读取和写入文件
- 在 Python 中使用 JSON
- 命令行上的 JSON
从 Python 2 迁移到 3
目录
- 为什么要迁移?
- 可以同时拥有 python 2 和 3 吗?
为什么要迁移?
因为已经不支持 Python 2 了!对 Python 2 的支持应该在 2020 年初就停止了。上一个主要的 2.7.x 版本是在 2020 年 4 月。Python 2 的所有开发都已停止。这意味着不会有安全更新。
许多包维护者已经迁移到 Python 3。一些仍然支持 Python 2,而另一些已经放弃了支持。从这里开始,大部分包会逐渐停止支持。Python 3.0 于 2008 年 12 月 3 日发布。所以是的,我们都有足够的时间迁移。如果你还没有,你现在应该把它作为头等大事。最多,你应该在 2020 年底之前运行 Python 3。否则,您将面临漏洞、无法运行的软件等风险。
除了安全性和支持,Python 3 还有很多好处;这种语言已经变得更好了,这可以在我们关于 Python 3 的优势的页面上看到。
可以同时拥有 python 2 和 3 吗?
如果您真的需要为遗留代码库运行 Python 2,您可以轻松地将它与 Python 3 一起运行。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
Windows 操作系统
要在 Windows 上同时运行 Python 2 和 3,你需要安装两个版本的 Python 。之后,我推荐使用 Python 启动器。这个启动器可以用py
命令调用。它将检查您的脚本中所谓的指定 Python 2 或 Python 3 的 shebang。
或者,您可以明确地选择一个版本:使用py -3 yourscript.py
用 Python 3 启动您的脚本,或者使用py -2 yourscript.py
用 Python 2 启动它。
Linux 操作系统
如果你想在 Linux 上同时安装 Python 2 和 Python 3,你需要同时安装两者。Python 2 通常可以用python
命令启动,而 Python 3 则用python3
命令启动。
在命令行上检查 Python 版本
原文:https://python . land/migrating-from-python-2-to-3/how-to-check-your-python-version
不确定您运行的是哪个 Python 版本?在这篇简短的操作指南中,您将学习如何检查您的 Python 版本。你不想无意中运行一个旧版本,即使你刚刚安装了最新版本!是的,会发生这种情况。检查 Python 版本很容易,我们就这么做吧。
检查 Python 版本
这个简单的命令适用于所有操作系统,包括 Windows、Linux 和 MacOS。假设您已经安装了Python,并且终端打开了,那么您可以使用下面的命令检查您当前的 Python 版本:
$ python --version
Python 2.7.16
希望你的版本是 3.7.x 或者更高。但是如果上面写的是 2.7.x(或者更低),现在还不用担心。
Python 长期以来有两个版本:版本 2 和版本 3。正如我们在关于 Python 的历史的文章中所解释的,它们是并存的。
在许多系统上,安装了两个版本的 Python。当某些软件需要版本 2,而操作系统或用户也需要版本 3 时,就会出现这种情况。在这种情况下,通常,当您输入python
时 Python 2 运行,当您输入python3
时 Python 3 运行。
让我们再次检查 Python 版本,但现在尝试使用 python3:
$ python3 --version
Python 3.7.6
如果您的输出看起来相似,那么恭喜您。您已经安装了 Python 3!如果没有,请按照中的步骤为您的操作系统安装 Python 3 。
如何迁移到 Python 3
原文:https://python . land/migrating-from-python-2-to-3/how-to-migrate-to-python-3
一旦安装了 Python 3 并开始工作,就该迁移到 Python 3 了。大多数情况下,升级代码并不难,可以遵循多种策略。
你需要做的是:
- 升级您自己的代码
- 升级到依赖项的较新版本
幸运的是,有一些工具可以帮助我们!
目录
2to3
2to3
是一个 Python 程序,它读取 Python 2 源代码,并应用一系列修复程序将其转换成有效的 Python 3 代码。标准库包含一组丰富的修复程序,可以处理几乎所有的代码。
Python 3 中一个显著的变化是print
现在是一个叫做print()
的函数。例如,这段 Python 2 代码:
def greet(name):
print "Hello, {0}!".format(name)
print "What's your name?"
name = raw_input()
greet(name)
可以通过调用以下命令进行转换:
$ 2to3 greet.py
默认情况下,这只会将差异打印到屏幕上以供检查。如果看起来没问题,您可以使用-w
选项来实际更改文件:
$ 2to3 -w greet.py
原始文件被更改,旧文件将保存为 greet.py.bak。结果:
def greet(name):
print("Hello, {0}!".format(name))
print("What's your name?")
name = input()
greet(name)
2to3
的一些更有趣的标志是:
旗 | 功能 |
---|---|
-我 | 列出所有修复程序 |
[加在以-u 结尾的法语词源的名词之后构成复数] | 排除选定的定影剂 |
-f | 仅显式运行此修复程序 |
-w | 更新文件而不是打印到标准输出 |
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
在开始转换代码之前,请继续阅读完整的 2to3 文档。
六个
six
是一个 Python 2 和 3 兼容库。该项目帮助代码库同时支持 Python 2 和 3。我建议用2to3
完全迁移到 Python 3,但是如果你不能,不管什么原因,你至少可以让你的代码库在两个版本上都工作。
Six 提供了消除 Python 2 和 3 之间语法差异的函数。一个容易理解的例子是six.print_()
。在 Python 3 中,打印是通过print()
函数完成的。在 Python 2 中,print
不需要括号。通过使用six.print_()
,您可以用一条语句支持两种语言。
几个好听的都知道:
- 六这个名字来源于这样一个事实:二乘以三等于六。
- 对于类似的库,也可以查看
future
包。
升级您的软件包
您可能需要升级您所依赖的包。对于您正在使用的每个包版本,尝试找出它是否已经支持 Python 3。如果不支持,就找一个支持的版本。您可能需要修改一些代码,因为 API 往往会随着时间而变化。
如果你很着急,并且你的包的 API 有了很大的变化,你可以尝试找到支持 Python 3 的包的最低版本。API 在较低版本中可能没有太大的变化。使用最新版本几乎总是更好,但至少这是朝着正确方向迈出的一步!
检查最低要求的 Python 版本
一旦迁移到 Python 3,在代码中添加 Python 版本检查。这样,您可以确保您和您的用户不会使用不兼容的版本运行您的脚本,这将导致不可理解的错误和挫折。使用这样一个简单的检查:
if not sys.version_info > (2, 7):
# berate your user for running a 10 year
# python version
elif not sys.version_info >= (3, 5):
# Kindly tell your user (s)he needs to upgrade
# because you're using 3.5 features
Python 3 的优势
原文:https://python . land/migrating-from-python-2-to-3/python-3-advantages
让我们探索一下 Python 3 相对于 Python 2 的优势吧!
目录
Print 不再是一个语句,而是一个内置函数
在 Python 3 中,print 变成了一个函数调用而不是一个语句。Python 3 中这一变化的一些优点是:
- 印刷品真的没有理由成为一种陈述。如果打印是一种功能,那就更符合了。
- 因为 print 是一个函数,所以它可以作为参数传递给需要函数的函数。例如,将需要另一个函数进一步处理数据的函数作为参数。对于简单的模拟/调试,您现在还可以传递 print()函数。
- 你现在可以像这样使用 print,因为它是一个函数:
[print(x) for x in range(10)]
- 您可以通过赋值给
builtins.print
来覆盖打印功能,但是您不能用语句来这样做。
Python 3 中的 Unicode
Python 3 的另一大优势是,默认情况下,每个字符串都是 Unicode 字符串。在 Python 2 中,字符串默认为 ASCII 字符串,这限制了它可以处理的字符范围。如果您想要一个 Unicode 字符串,您必须显式地创建一个这样的字符串:
# Python 2
unicode_string = u'Ümlaut? Nō prōblem!'
# Python 3
unicode_string = 'Ümlaut? Nō prōblem!'
这是很多国家必备的。
数据类别
从最近的 3.7 版本开始, Python 提供了数据类。与常规类或其他替代方法相比,它有几个优点,比如返回多个值或字典:
- 数据类需要最少的代码。
- 你可以比较数据类,因为
__eq__
是为你实现的。 - 您可以轻松地打印一个用于调试的数据类,因为也实现了
__repr__
。 - 数据类需要类型提示,减少了出错的机会。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
下面是一个数据类工作的例子:
from dataclasses import dataclass
@dataclass
class Card:
rank: str
suit: str
card = Card("Q", "hearts")
print(card == card)
# True
print(card.rank)
# 'Q'
print(card)
Card(rank='Q', suit='hearts')
合并字典(Python 3.5+)
从 Python 3.5 开始,合并字典变得更加容易:
dict1 = { 'a': 1, 'b': 2 }
dict2 = { 'b': 3, 'c': 4 }
merged = { **dict1, **dict2 }
print (merged)
# {'a': 1, 'b': 3, 'c': 4}
如果有重叠的关键字,第一个字典中的关键字将被覆盖。
分歧变得更加可预测
在 Python 2 中,除法运算符/
默认为整数除法,除非其中一个操作数是浮点数。所以你有这样的行为:
# Python 2
5 / 2 = 2
5 / 2.0 = 2.5
在 Python 3 中,除法运算符默认为浮点除法,而//运算符变成了整数除法。所以我们得到:
# Python 3
5 / 2 = 2.5
5 // 2 = 2
对于这一变化背后的完整动机,你应该阅读 PEP-0238。
有意义的比较
在 Python 2 中,您可以将任何东西与任何东西进行比较。以下示例将全部返回True
:
"a string" > 2
None < 5
没有意义,还能藏 bug。在 Python 3 中,这些比较会抛出一个TypeError
异常。
无更多范围对比 x 范围
Python 2 有两个范围函数:range
和xrange
。后者更快,因为它基于迭代器。在 Python 3 中,range
变成了xrange
,而xrange
的名字被去掉了。这是 Python 变得不那么让新手困惑的例子之一。
使用 Unix Shell
用 Python 你几乎可以做任何事情!尽管如此,精通命令行还是很有价值的。一个精心制作的 bash 命令或脚本可以节省您自己编写代码(或手工编写代码)的时间!).
目录
Unix 哲学
Unix 命令行已经存在了几十年,并且仍然很强大。这一成功部分来自 Unix 理念:
- 写程序做一件事,并把它做好。
- 写程序一起工作。
- 编写处理文本流的程序,因为那是一个通用接口。
当使用命令行时,最好记住这个原则。你会发现总有一种工具组合能完全满足你的需要。艺术是找到它们并正确地组合它们。
Linux 命令和脚本
我将主要关注 Linux,但是您将要学习的内容也适用于其他类似 Unix 的操作系统,比如 MacOS 或基于 BSD 的发行版。我建议你从学习最常用、最需要的 Linux 命令开始。之后,你可以学习如何编写 Bash 脚本,例如,自动化你的 Python 开发过程的一部分。
Linux 命令:你必须牢记的 10 个命令
通过学习一些基本的 shell Linux 命令,您可以显著提高您的工作效率并加深对操作系统的理解。本教程中的 Linux 命令在 Bash 中工作,Bash 是 Linux 和 MacOS 上最常见的 shell。然而,替代 shells 大多与 Bash 兼容,因此它们应该在任何地方都可以工作!
如果您非常熟悉这些 linux 命令,请跳到下一节来学习一些更高级的命令和 shell 脚本。例如,学习如何创建 Bash 脚本,或者在 Bash 中做多重处理!
目录
- pwd:返回工作目录名
- Linux 中的 ls 命令:列出目录内容
- Linux 中的 cd 命令:改变目录
- cp Linux 命令:复制一个文件
- Linux 中的 mv 命令:Move
- mkdir Linux 命令:制作目录
- rmdir 命令:删除目录
- rm 命令:移除东西
- 猫,少,尾,头命令:查看内容
- control+r:找到你之前的命令
pwd:返回工作目录名
让我们从知道一旦你打开你的终端你在哪里开始。这是通过 pwd Linux 命令完成的:
$ pwd
/Users/erik
我在我的主目录里(在 Mac 上),你在哪里?
Linux 中的 ls 命令:列出目录内容
现在让我们看看当前目录中有什么:
$ ls
AndroidStudioProjects VirtualBox VMs
Applications Desktop
Documents flutter
Downloads...
这里有很多因为我是一个乱七八糟的家伙,所以我截断了一点。ls 命令有许多方便的选项;这三个我一直在用:
-l
:(小写字母“ell”。)长格式列表。(见下文。)所有文件大小的总和在长列表之前的一行上输出-h
:与-l 选项一起使用,将文件大小改为更易读的大小,比如 10G、42M。在我的脑海里,我把它翻译成“人类可读的”-a
:‘所有’的简称;包括名称以点(.)
您可以根据需要任意组合,如下所示:
$ ls -lha
total: 32
drwxr-xr-x+ 85 erik staff 2.7K May 27 11:11 .
drwxr-xr-x 6 root admin 193B Sep 29 2019 ..
-rw-r--r-- 1 erik staff 2.1K Apr 15 11:06 .gitignore
-rw-r--r-- 1 erik staff 25M May 26 15:44 image.zip
...
您在这里看到的是访问权限、所有者和组、文件大小、文件的最后修改数据以及文件名本身。
在前两行,您可能会注意到一些奇怪的事情:两个名为.
和..
的文件:
.
是当前目录。它甚至有一个大小,这取决于目录中文件的数量..
是底层目录,当前目录是其中的一部分
这些便捷的快捷键可以在每个命令中使用。如果您想查看底层目录的内容,请使用ls ../
。你也可以重复这个,所以如果你想下两个目录,可以用ls ../../
。
Linux 中的 cd 命令:改变目录
现在你知道你在哪里,有什么目录,你想四处导航。假设您的项目存储在项目文件夹中。您可以通过键入以下 Linux 命令来实现:
$ cd Projects
大多数 shells 支持自动完成。只需键入以下命令进行尝试:
$ cd Pro<TAB key>
您的 shell 将自动补全单词,或者,如果多个目录与您键入的内容匹配,它通常会向您显示所有选项。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
cp Linux 命令:复制一个文件
您可能想要拷贝或复制一些文件。Linux 中的 cp 命令就是这么做的:它复制一个文件。它通过创建一个新文件并将所有内容复制到该文件来实现这一点。要将file1.txt
复制到第二个文件,例如备份,使用:
$ cp file1.txt file1.txt.bak
要将文件复制到另一个目录,您可以使用:
$ cp file1.txt ./backups/
./
表示“当前目录”,所以./backups
位于当前工作目录。
Linux 中的 mv 命令:Move
如果需要移动文件,请使用 mv 命令。请注意,移动文件等同于重命名文件。事实上,Bash 命令行上没有 rename 命令。用 mv 和 cp 区别不大。要重命名/移动文件:
$ mv file1.txt file1.txt.bak
要将文件移动到另一个目录,例如当前工作目录中的备份文件夹:
$ mv file1.txt backups/
mkdir Linux 命令:制作目录
这个 Linux 命令将简单地创建一个目录。要创建存储项目的目录,请使用:
$ mkdir Projects
这个命令有一个非常方便的参数,-p
。当您使用它时,任何尚不存在的中间目录也将被创建。如果您想在一个名为Projects
的目录中创建您的第一个项目,您可以使用:
$ mkdir -p Projects/my_first_project
rmdir 命令:删除目录
mkdir 的反义词是 rmdir:它删除一个目录。它甚至允许相同的-p
参数。
$ rm Projects
要删除项目目录和我们刚刚创建的第一个项目:
$ rmdir -p Projects/my_first_project
移除目录仅在该目录为空时有效。如果您想删除一个目录,包括它的内容,请参见下面的 rm 命令!
rm 命令:移除东西
现在我们进入了一个危险的领域。这个 Linux 命令非常强大,有可能破坏您的文件系统。小心使用。
让我们从删除 Linux 中的一个文件开始:
$ rm file1.txt
噗!它不见了。没有你在其他操作系统中可能已经习惯的“垃圾桶”。
现在让我们删除我们的项目目录中所有以.txt
结尾的文件。您需要一个所谓的 glob 模式来匹配多个文件:
$ rm Projects/*.txt
如果要删除所有文件,请使用:
$ rm Projects/*
全球模式*
匹配一切!
如果您想谨慎行事,请使用-i
选项,它会强制您确认删除操作。
现在,作为压轴戏,让我们删除整个目录和文件树。为此,您需要参数-r
,递归的简称。rm -r
有时会拒绝删除特殊文件,或者不断要求确认。你可以使用强制选项-f
清除它能清除的一切;不问任何问题。让我们删除所有项目:
$ rm -rf Projects
噗!你所有的工作都一去不复返了。就像我说的,小心处理!
猫,少,尾,头命令:查看内容
您经常想要快速检查文件的内容。有几个 Linux 命令可以帮助您做到这一点。选择一个符合您的使用案例的:
- 打印屏幕上的所有内容
- 允许你在文件中滚动,甚至在里面搜索
tail
:类似 cat,但只打印最后 10 行(默认)head
:类似 cat,但只显示前 10 行(默认)
尾部和头部都使用-n <num>
选项来改变它显示的行数。
control+r:找到你之前的命令
很容易忘记,所以快速找到您昨天或上周使用的 Linux 命令不是很好吗?你可以的!只需在键盘上点击control
+ r
,开始输入你记得的那部分命令。这可以是任何部分!如果你找到的不完全是你要找的,通过反复点击control
+ r
继续搜索。它会从你的命令历史中不断给你匹配。
如何创建 Bash 脚本,带有示例代码
使用 bash 脚本,您可以运行命令序列。它们防止你重复你自己,并允许你存储冗长乏味的输入命令以备后用。在本文中,您将学习如何创建 Bash 脚本。您还将学习如何读取参数、使用变量以及创建 for 循环来重复操作。
在开始创建脚本之前,确保您熟悉基本的 Linux 命令。
目录
- 创建一个 Bash 脚本
- Bash 变量
- Bash 脚本参数
- Bash for loop
- 【Bash 脚本示例
- 启动 Bash 脚本
- Bash 条件编程
创建一个 Bash 脚本
命令行脚本以 shebang 开始,she bang 是两个字符#!
的序列。在这两个字符之后,你可以命名任何解释器和一串参数,就像这样:
#!interpreter [optional arguments]
一个 shell 脚本通常基于最流行的 shell Bash,因此它可以简单地变成:
#!/bin/bash
有时你会看到这个:
#!/usr/bin/env bash
使用/usr/bin/env
时,提供的命令会在当前$PATH
中搜索,而不是硬编码到/bin/bash
位置。Bash 几乎总是出现在/bin/bash
,所以第一种方法对我们来说很好。
然而,您可能想要使用这个技巧来创建一个可执行的 Python 脚本。Python 可以安装在任何地方,这取决于它的安装方式。我在/usr/bin/
、/bin
、/usr/local/bin
都看过,就举几个吧。如果你运行的是虚拟环境,它可以在任何地方。所以搜索$PATH
是一个好的选择,因为这将从虚拟环境中找到 Python 版本。
我们稍后将创建一个实际的 shell 脚本。但是首先,我们将看看变量和 for 循环,让事情更生动一些。
Bash 变量
就像其他语言一样,Bash 也有变量。Bash 中变量的一个奇特之处在于,您可以用美元符号访问它们,但却不用它来设置它们。为了澄清,这里有一个例子:
myvar="Hello world"
echo $myvar
Bash 有许多为您自动设置的保留变量。下表列出了一些更有用的方法,但并不详尽:
可变的 | 功能 |
---|---|
$0 | 当前脚本的名称。 |
$1 … $9 | 脚本的前 9 个参数。 |
$# | 传递给脚本的参数数量。 |
$@ | 提供给脚本的所有参数。 |
$用户 | 运行脚本的用户的用户名。 |
$主机名 | 运行脚本的机器的主机名。 |
美元秒 | 自脚本启动以来的秒数。 |
$随机 | 每次被引用时返回一个不同的随机数。 |
$行号 | 返回 Bash 脚本中的当前行号。 |
Bash 保留变量
Bash 脚本参数
我们现在能够创建一个 shell 脚本,但是这个脚本将在您每次运行它时做同样的事情。我们可以通过使用论点让它变得更加有趣。
这里有个例子,叫它arguments.sh
:
#!/bin/bash
echo "Hello $1, from $0"
如果我们用一个参数运行它,结果如下:
$ ./arguments.sh Weirdo
Hello Weirdo, from ./arguments.sh
Bash 所做的,是把你的整个命令分割开来,并把它分配给不太有想象力的变量$0
、$1
、$2
等等。如您所见,我们命令的第一部分是脚本本身的名称。这可以派上用场,在下一节中可以看到。
创建一个安全网
在进入正题之前,我想教你一个重要的安全网。
一旦开始创建脚本,就不可避免地会出错。我们都是。脚本中一个非常常见的错误可能是灾难性的。这是未设置的变量。如果你不明白我的意思,只要继续读下去,一会儿就会明白了。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
我们可以通过使用以下命令启动脚本来防止使用未设置的变量:
set -u
仅此而已!从现在开始,当您进行替换时,您的脚本会将未设置的变量视为错误。
例如,此脚本将失败:
#!/bin/bash
set -u
rm -rf /$my_directory
输出:
$ ./test.sh
./test.sh: 3: ./test.sh: my_directory: parameter not set
那是因为我从来没有设置过$my_directory
。没有set -u
,它不会失败,而是忽略空变量,留给我们命令rm -rf /
。我们都知道那是什么,对吧?如果由特权用户(比如root
)执行,这个错误将会清除我的整个文件系统,而不是我想要删除的目录。
养成总是以set -u
开始脚本的习惯。
Bash for loop
一旦掌握了 Bash for-loops,命令行(尤其是脚本)的用处就会成倍增加。尽管它们看起来很吓人,但其实并没有那么难。
Bash for 循环的语法
循环的基本语法是:
for VARIABLE in A LIST
do
command1
command2
commandN
done
【Bash 脚本示例
A LIST
部分可以是任何东西:文件名、数字或字符串。不过,在大多数脚本中,它将是一个文件名列表,因为这是我们在 Bash 中经常使用的。现在我们知道了如何创建一个循环,让我们看看我们的第一个脚本:
#!/bin/bash
echo "You can list numbers and text like this:"
for n in 1 2 3 four
do
echo "Number $n"
done
echo "Or specify a range of numbers:"
for n in {1..5}
do
echo "Number $n"
done
echo "Or use the output of another command:"
for f in $(ls)
do
echo $f
done
在上一个 for 循环中,我使用了表达式$(ls)
。这会执行括号中的命令并替换结果。在这种情况下,ls
被执行,for 循环被输入了ls
打印出的文件名。
启动 Bash 脚本
要启动这个脚本,我们可以做两件事。首先,我们可以运行它:
$ bash loop.sh
我推荐的第二种方法是使文件可执行。操作系统将知道如何执行我们的文件,因为我们的 shebang 行在顶部!通过设置文件的“执行标志”可以使文件成为可执行文件,如下所示:
$ chmod +x loop.sh
现在,您可以使用以下命令运行该脚本:
$ ./loop.sh
在我的例子中,输出是:
$ ./loop.sh
You can just list a bunch of numbers and text like this:
1
2
3
four
Or specify a range of numbers:
1
2
3
4
5
Or use the output of another command:
loop.sh
notes.txt
对您来说可能有所不同,这取决于运行脚本的目录中有哪些文件。
Bash 条件编程
有时,您只想在特定条件为真时运行命令。为此,我们在 bash 中使用了if… then… else…fi
构造。
检查 bash 脚本中的参数
我们可以使用条件编程来改进我们之前的例子arguments.sh
,因为它包含了一个小问题。它期望在$1
中有一个名字,但不检查它是否真的有名字。让我们来解决这个问题:
https://crumb.sh/embed/fawGEMEUYGg
检查 Bash 脚本中的参数
如果这不起作用,下面是相同代码的静态版本:
#!/bin/bash
if test -z "$1"
then
echo "Usage: $0 <Your name>"
else
echo "Hello $1, from $0"
fi
bash 中的测试命令
用test -z
我们可以检查变量的长度是否为零。如果是这种情况,我们打印一些友好的使用说明:
$ ./arguments.sh
Usage: ./arguments.sh <Your name>
test 命令可以测试很多东西,所以它确实有助于条件编程。当你在命令行输入man test
就可以看到完整的列表。没错,你不需要什么都用谷歌!使用手册页是成为命令行忍者的一部分!您会发现,几乎所有您可以在终端中做的事情都有一个手册页。毕竟,在过去,我们没有互联网来搜索一切…
下面是我们比较两个值以确定它们是否相同的另一个例子:
https://crumb.sh/embed/3PAwdsNPFva
比较 Bash 中的两个值
下面是静态版本,以防不起作用:
#!/bin/bash
for i in {1..10}
do
if test $i -eq 3
then
echo "I found the 3!"
else
echo "Not looking for the $i"
fi
done
该循环运行 10 次迭代,每次检查$i
是否等于 3。你能预测产量吗?
尽管else
-部分是可选的,但您总是需要以fi
结尾。
在 Linux 中使用 Find 命令
原文:https://python.land/the-unix-shell/using-the-find-command
有一个强大的 Linux 命令,如果用在正确的地方,可以帮你省去很多麻烦。叫find
。它从提供的路径开始遍历文件树。接下来,它打印每个目录和文件,包括其相对于当前工作目录的路径。让我们一起来探索一下 Linux 中的 find 命令吧!
目录
基础知识
尝试一下,输入以下内容,看看会得到什么样的输出:
$ find .
对我来说,输出是这样的:
.
./images
./images/cover.jpg
./scripts
./scripts/loop.sh
./scripts/arguments.sh
./scripts/words.txt
第一个点就是当前目录。Find
将同时输出目录和文件。如果您只想列出文件,请使用:
$ find . -type f
如果您只想列出目录,请使用:
find . -type d
表情
基本的到此为止。Linux 中的 find 命令也接受表达式,这进一步缩小了它应该返回的文件的范围。我经常使用的一个有用的表达是-mtime
。以下命令返回 5 分钟前修改的所有文件:
$ find . -type f -mtime +5m
m 是一个修饰符,下表列出了所有可用的修饰符:
修饰语 | 意义 |
---|---|
s | 第二 |
m | 分钟(60 秒) |
h | 小时(60 分钟) |
d | 日(24 小时) |
w | 周(7 天) |
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
加号也很重要:
- 前面的加号表示“大于 n”
- 前面的减号表示“小于 n”
- 两者都不意味着“正好是 n”
另一个有用的表达式是-name
,它将根据文件名过滤结果。对于名称,您可以使用 shell 模式。所以命令find . -name "*.txt"
只会返回扩展名为.txt
的文件。
动作:使用结果
有了我们新的 ninja 脚本能力,我们现在可以使用 Linux find
命令返回一堆文件,将它们送入 Bash 脚本中的 for 循环,并用它们做一些很棒的事情,对吗?
没错,但是由于很明显您想要对返回的文件执行一些操作,find 已经覆盖了我们。不需要编写脚本。
假设您想要删除符合您的标准的文件,您可以简单地将-delete
添加到您的find
-命令中。这显然有点危险,所以在使用这个标志之前,总是先检查没有这个标志的find
命令的输出。
给我们更多灵活性的是-exec
标志。它允许我们执行任何命令。你以前可能见过这样一个例子:
$ find /path/to/files* -mtime +5m -exec rm {} +
让我们来分解一下:
- 查找给定路径中 5 分钟前修改过的所有文件
- 执行
rm
命令(删除文件) - 对于给定实用程序的每次调用,
{} +
被替换为尽可能多的路径名(在我们的例子中是ls
)
这实际上是一种非常有效的删除大量文件的方法,比如数千甚至数百万个文件。
该命令的一个简单替代方法是:
$ find /path/to/files* -mtime +5m -exec rm {} \;
不同之处在于,现在对每个文件都执行rm
,而不是系统允许的每批文件数。您使用哪个版本取决于您对结果执行的命令。如果你能用带加号的那个,那就快多了!这是因为该命令同时在许多文件上执行,节省了操作系统打开、启动、运行和退出程序所需的大量 CPU 周期。然而,有些命令一次只接受一个文件,对于这些命令,第二个版本可以解决问题。
同样:如果你输入man find
(或者访问在线手册页,你会得到完整的文档。但是我承认。我也倾向于在谷歌上搜索这些东西,因为你通常会得到有用的例子,而不需要费力地阅读原始文档。然而,在按回车键之前,一定要试着理解你粘贴到终端上的到底是什么!
Unix 管道
管道将两个进程连接在一起。用|
字符编写的管道将一个命令的输出连接到第二个命令的输入。这是 Unix shell 的基本构件之一。
一个非常基本的例子是使用cat
将文件内容输入字数统计工具wc
:
$ cat <filename> | wc
以防你不知道这些命令:
- 打印文件的内容。
- 对输入的所有行、字和字节进行计数。
另一个常用的版本是wc -l
,它只统计一个文件中的行数。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
让我们试试这个。首先,创建一个名为words.txt
的文件,内容如下:
hello
world
python
ninja
are
you
?
3
2
1
然后运行以下命令:
$ cat words.txt | wc -l
输出应该是 10,因为文件中有 10 行。另一个例子是使用sort
对文件中的单词进行排序:
$ cat words.txt | sort
?
are
hello
ninja
python
world
you
1
2
3
正如我们所看到的,来自words.txt
的所有单词都被输入到sort
中,由后者对它们进行排序。先标点,再数字,再字母。
好吧。这是最后一个使用管道的有问题的例子。有一个很棒的工具叫做cowsay
。它会生成一张 ASCII 图片,上面是一头牛在说你喂它的东西:
$ echo "Are we going to learn anything useful?" | cowsay
________________________________________
< Are we going to learn anything useful? >
----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
这是会给你的同事留下持久印象的东西。
用 jq 处理并突出显示 JSON
一旦你掌握了足够的命令行基础知识,是时候实际学习一些可以在日常工作中应用的有用的东西了!由于 JSON 超级无处不在,我想先教你一些有用的命令行 JSON 处理 voodoo。
目录
jq 命令行工具
默认情况下,通常不安装jq
工具。如果没有,请为您的操作系统安装。如果你在苹果电脑上,你可能会想看看家酿啤酒。
jq
用法的一个非常基本的例子是:
$ echo '{ "Price": 10.0, "Name": "Cable" }' | jq
结果是您输入的 JSON 的一个格式良好的彩色版本:
用 jq 着色 JSON
为此,我实际上经常使用它。如果您只想查看 JSON 文件的内容,一个简单的cat filename.json | jq
将立即帮助您。但是jq
可以做得更多——它是一个成熟的命令行 JSON 处理器。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
比如说,你只对价格领域感兴趣。您可以使用以下命令简单地获得价格:
$ echo '{ "Price": 10.0, "Name": "Cable" }' | jq ".Price"
试试看!结果应该是 10。这仍然只是触及最基本的问题。只要记住jq
是存在的,用它你可以从你的 JSON 文件中得到几乎任何东西。阅读手册页或使用搜索引擎来查找更高级的示例。
基于 Python 的替代品
使用 Python 的 JSON 库
Python 的 JSON 库也可以从命令行使用,以验证和美化您的 JSON:
$ echo "{ \"name\": \"Monty\", \"age\": 45 }" | \
python3 -m json.tool
{
"name": "Monty",
"age": 45
}
Python 中的高级 JSON 查询
如果您正在寻找在 Python 中查询 JSON 的高级方法,请访问我们的页面,使用 JmesPath 在 Python 中查询 JSON。
使用 Bash 历史记录
原文:https://python.land/the-unix-shell/using-the-bash-history
现在你知道了所有这些令人敬畏的命令,但是下周你会发现你已经忘记了它们。幸运的是,这里也有工具可以帮助我们!
您可以使用向上和向下键来滚动浏览先前键入的命令。这还不是全部。我在下表中列出了一些基于历史的省时方法:
命令 | 说明 |
---|---|
history n |
查看历史记录中最多n 行 |
!! |
重复最后一个命令 |
!n |
重复历史中的命令编号 n |
!cd |
重新运行您键入的最后一个命令,在本例中从“cd”开始 |
!* |
替换上一个命令中的参数 |
也许使用终端历史最有效的方法是通过搜索它。点击control + r
获得与您的历史记录中的文本相匹配的搜索提示。只要开始输入之前命令的任何部分,你就能找到它!
Bash 多重处理
对于那些到现在还没有得到那种忍者感觉的人,这是给你的。我们将结合所有新学到的超能力,执行多处理,也称为并行计算,所有这些都只需 Bash 中的一个命令!
目录
xargs
xargs 命令从标准输入中读取项目(也就是说,您可以通过管道将数据传递给它)并执行指定的命令。
xargs 的基本语法是:
xargs [options] [command [initial-arguments]]
乍一看,你可能看不到这样做的好处。为什么不创建一个 while 循环并运行每个命令呢?xargs 的好处是,它可以批量处理参数,并对许多文件调用一次命令,而不是对每个文件单独调用。
但是find
也可以这么做!
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
是的,你是对的。尽管如此,还是有更多的优势。Xargs 不需要查找就可以工作。所以这是一个。但是 xargs 有一个特殊的锦囊妙计:它可以与-P
-选项并行运行命令。
该选项采用一个数字来定义需要并行启动多少个进程。你没看错——并行!
例子
使用 xargs 进行 Bash 多处理的一个真实例子是在对大量文件进行视频转换时使用它。让我们一起来剖析下面的命令:
$ find . -name "*.mpeg" | xargs -P 4 -I {} ffmpeg -i {} -o {}.mp4
首先,我们找到所有的 mpeg 文件。我们把这些文件交给 xargs。接下来,我们用-P 4
告诉 xargs 同时使用四个进程。我们还告诉 xargs 用-I
选项替换所有遇到{}
的地方的文件名。所以 xargs 得到第一个视频文件,启动 ffmpeg。xargs 没有等待 ffmpeg 完成,而是启动 ffmpeg 的另一个实例来并行处理第二个文件。这种情况一直持续到四个进程。如果所有四个槽都被占用,xargs 在开始下一个进程之前会等待一个槽完成。
视频转换主要受 CPU 限制。如果您的计算机有四个 CPU 内核,这种转换将比使用常规 find 或 while 循环快四倍。是不是很牛逼?!
更多信息
有关更多信息,请阅读命令行手册或 web 上的。维基百科有一些 xargs 用法的例子,你可能会觉得有用。
如果你更喜欢使用 Python,我们的指南包含了一个关于使用 Python 的多重处理或并发性的综合章节。
部署
在某个时候,你的软件已经完成了,你想把它推向生产。本章向您展示如何运送您的软件!
我们将首先开始创建基于 Python 的 Docker 容器。这是将您的软件交付给许多不同的云提供商的好方法。一旦你知道如何创建 Docker 镜像,我将向你展示如何使用 CI/CD 来自动部署你的 Python 软件。最后,我将向您展示如何将您的 Python 应用程序扩展到数百万用户,而无需花费数百万美元。
如何使用 Docker 来容器化您的 Python 项目
您的 Python 项目是否难以从源代码构建,难以与同事共享,或者难以部署到生产环境中?Docker 是为您的 Python 项目解决这一问题的理想工具。
对于那些难以部署的项目,存在几个原因,这里只是几个:
- 该项目需要大量的依赖。
- 该项目需要一些库的过时版本或一个过时的编译器。一旦您安装了它,其他项目可能会依次中断。
- 你运行的是 Windows 或 Mac,但该软件是为构建和运行 Linux 而设计的
出于类似的原因,在生产环境中运行您的软件也很困难!
如果你正面临这些问题,最好知道有一个简单的解决方法。它不需要虚拟化,而是使用一种叫做容器化的原理。简而言之,容器化是虚拟化的一个非常轻量级的替代方案。容器化世界中的事实标准是 Docker,所以我们将用它来容器化一个示例 Python 项目。
目录
什么是容器?
容器是一个拥有运行软件所需的一切的实体。它包括:
- 你的软件
- 所有依赖项
- 可能需要的所有系统工具和库
容器就像虚拟机,但是更轻量级。例如,他们几乎立即开始。容器只虚拟化操作系统,而 VM 虚拟化整个机器及其所有硬件。如果你想知道这到底是如何工作的,请阅读优秀的官方文档。
Docker 容器有点像虚拟机,但是它们不需要运行整个操作系统
码头集装箱已经成为一种标准。它们可以在任何地方运行:从您的开发 PC 到自托管服务器,再到像 Amazon、Google 和 Azure 这样的云托管服务。容器使得打包和运输你的软件变得容易,并且为它的运行提供了一个定义良好的环境。
什么是图像?
Docker 容器总是基于图像。首先定义一个图像,然后基于它启动一个或多个容器。您可以在一个文件(称为Dockerfile
)中定义一个图像,这个文件可以和您的代码一起被签入一个像 git 这样的 VCS。这允许您记录和创建运行代码所需的确切环境。
你不必从头开始建立一个形象。许多软件项目提供了将软件容器化的映像。对于几乎所有的计算机语言,包括 Python,都有多种基本映像可供选择。
就像 Python 类一样,您可以用自己的细节来扩展这样的图像,我将在下面演示。通过这样做,您可以在现有图像上添加一个新层。由于这种分层,Docker 图像可以非常高效地存储和构建。例如,许多映像可能都共享同一个 Debian Linux 基本映像,并使用它们自己特定的软件需求来扩展它:
Docker 图像由多个层构建而成
将您的 Python 项目归档
为你的软件创建一个容器是非常容易的。做了第一个 Docker 形象后,我的想法大致是这样的:“就这些?我一定是跳过了一步!”
在这里,我将创建一个 Python 项目作为示例,但这对于其他语言来说同样简单。此外,我将有意在 Windows 上工作,尽管我是一个真正的 Linux 迷。最重要的是,我根本不打算在 Windows 上安装 Python,只是为了证明容器化的强大。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
如果你愿意,你可以跟着去。这篇文章包含你需要的一切。或者,您也可以从 Github 中克隆这段代码。
一个简单的网络应用
假设我们想使用 Flask 创建一个 web 服务。它有一些依赖关系,在Pipfile
中定义,与 Pipenv 一起使用。
我从简单的开始,下面的Pipfile
:
[packages]
Flask = "*"
现在,我们将创建一个 Python 文件,只是为了证明事情按预期运行。我称之为app.py
:
print("Hello world")
创建自己的 Docker Python 映像
接下来,我们需要创建一个定义容器的Dockerfile
。如今,大多数项目都以 Docker 容器的形式提供软件的官方版本。在谷歌上快速搜索后,发现 Python 也是如此。他们甚至给了我们一个开始的例子。
他们的例子是基于 virtualenv 的,我们更喜欢 Pipenv,所以它需要一些修改。这是我想到的:
FROM python:3
WORKDIR /usr/src/app
COPY Pipfile ./
RUN pip install --no-cache-dir pipenv && pipenv install
COPY *.py .
CMD [ "python", "./app.py" ]
要了解更多关于Dockerfile
的语法,最好直接去 Dockerfiles 上的官方文档。
要构建您的容器,您需要运行docker build
命令。如果您使用的是高级 IDE,如 VSCode,您将能够安装 Docker 扩展,允许您简单地右键单击Dockerfile
,给它一个名称(称为标记),然后开始构建。在这种情况下,我更喜欢命令行,因为它能准确地显示我们在做什么,并让我们控制所有选项。因此,我们将在命令行上构建我们的映像:
C:\dev\python-docker> docker build -t my_webservice:latest .
让我们来分解一下:
- 我们正在构建一个映像,并用名称 my_service 和版本
latest
对其进行标记(这是默认的,所以您可以忽略它) - 这个点仅仅意味着 Docker 需要在当前目录中查找 Docker 文件
第一次运行时,会有很多活动:
- Docker 首先开始提取 python docker 图像
- 接下来,我们将工作目录设置为/usr/src/app
- 我们将 zip 文件复制到工作目录中
- 我们运行 pip install 来安装 pipenv
- 紧接着,我们运行 pipenv install 来安装我们的依赖项
- 最后,我们将所有 python 文件复制到工作目录中
您应该会看到类似下面截图的内容:
Docker 正在运行,提取图像
我们的图像完成了,我们可以用 docker run 运行它。让我们试试:
PS C:\dev\python-docker> docker run my_webservice
Hello world
PS C:\dev\python-docker>
改善我们的 Python Docker 映像
我们已经做好了基础工作,现在让我们创建一个实际的 web 服务。修改app.py
使其看起来像这个基本的烧瓶示例:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
因为我们现在正在运行 Flask 应用程序,所以我们也需要修改 docker 文件中的 CMD:
CMD [ "pipenv", "run", "python", "-m", "flask", "run", "--host=0.0.0.0" ]
如您所见,我们需要 Flask 来监听所有网络接口。否则,它将只是监听 docker 容器内的 localhost,外部世界将无法访问它。
使用与之前相同的命令重建 Docker 映像:
C:\dev\python-docker> docker build -t my_webservice:latest .
因为我们将在端口 5000 (Flask 的默认端口)上运行实际的服务器,所以我们需要公开这个端口。为了额外的安全和防止端口重叠,Docker 默认情况下不会暴露端口。Docker 提供了强大的隔离,这也更安全!您可以使用-p
命令行选项将一个端口从容器映射到 PC 上的一个端口:
C:\dev\python-docker> docker run -p 5000:5000 my_webservice
* Environment: production
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
现在转到 http://127.0.0.1:5000/来看看这个服务的运行情况。您应该得到一个显示“Hello world”的页面,并且应该在命令行上看到一个日志条目。
使用我预先构建的图像
如果您不想经历所有的步骤,但是仍然想测试运行这个例子,您可以通过使用下面的命令从我的 public Docker Hub account 中提取图像:
docker pull eriky/hello_world:latest
之后就可以用:docker run -p 5000:5000 eriky/hello_world
开始镜像了。
如果你想从 GitHub 查看源代码,请点击这里的进入附带的库。
继续学习
- 了解如何使用持续集成自动构建和部署您的 Python 项目。
- 如果你的应用程序成功了,这里有一些游击战术可以在不花费数百万的情况下扩展应用程序。
使用 CI/CD 自动构建和部署 Python 应用程序
原文:https://python . land/deployment/ci-CD-automatically-build-and-deploy-your-python-application
目录
什么是持续集成/持续交付?
当你完成一个新版本的 Python 应用程序时,你如何构建和部署它?您是否会更改版本号,将新文件上传到生产环境,然后就此结束?有一个更好的方法,叫做持续集成和持续交付。
除了手动执行所有繁琐的步骤来将软件投入生产,您还可以自动执行这些步骤。幸运的是,您不必自己构建这个自动化。相反,您可以使用许多可用的持续集成/持续交付(CI/CD)系统中的一个。
CI/CD 是良好软件工程实践的顶峰。这是所有其他良好实践汇集的地方。CI/CD 在开发和操作之间架起了一座桥梁,因为它自动化并加强了构建和部署软件的重要步骤。它确保了质量,并排除了人为错误。
我们将采用一个现有的应用程序,并为它创建一个 CI/CD 管道。您将看到如何在 15 分钟内建立一个专业的 CI/CD 管道!
要求
要建立一个良好的 CI/CD 渠道,我们首先需要有一些其他的实践:
- 我们必须使用像 Git 这样的代码版本管理系统。
- 我们的应用程序应该有自动化测试(单元测试,集成测试)。
- 我们应该至少有一个软件的开发、测试和生产场所。
- 我们需要一个 CI/CD 系统。
设置 CI/CD 可能看起来像是一次令人生畏的冒险,但实际上并非如此。事实上,你可以在开始的时候给自己留一些捷径。要快速开始:
- 我们的测试和开发地点将是我们自己的电脑。
- 我们将从一个单元测试开始。
- 我们将使用 GitHub 托管我们的代码,我们将使用 Docker Hub 托管我们完成的应用程序。
这给我们留下了最后的选择:使用哪个 CI/CD 系统?
选择哪个 CI/CD 系统?
一些供应商提供 CI/CD 系统。我过去用过的一个著名的开源系统是 Jenkins。它已经存在很长时间了。缺点:它需要知识和时间来设置和运行。我也曾在过去工作过的一家公司被迫使用 Atlassian,但我从未喜欢过该产品。
为了快速启动并运行,并向您展示这有多简单,我将在本文中使用 Buddy 。它是以云服务的形式提供的,他们的免费层仍然允许你创建 5 个项目。他们的界面是荒谬的直观;它甚至在最初分析代码后向您建议合适的操作。
应用程序
对于这个管道,我将使用现有的“hello world”应用程序。可以在我的 GitHub 账号上查看来源。它具有以下特点:
- 这是一个使用 Pipenv 的 Python 项目。
- 它有基本的单元测试。
- 它包括一个 docker 文件来创建一个 docker Python 图像并发布到我的公共 Docker Hub 帐户。
第一步。创建一个账户和项目
开始吧!如果您没有 GitHub 帐户,请先创建一个。然后使用您的 Github 登录名创建一个好友帐户。这样,Buddy 可以直接访问您的存储库。
第二步。添加源库
在 GitHub 中,您应该派生我的" docker-python-example "存储库,这样在下一步中它就可以作为您自己的存储库之一。
完成后,点按 Buddy 中的“创建新项目”大按钮。您可以选择一个 GitHub 存储库。如果一切顺利,那么“docker-python-example”回购也应该存在:
选择“docker-python-example”项目
单击该项目。您应该会看到以下屏幕:
我们已经准备好自动化建筑或我们的应用程序
Buddy 检测到这是一个 Python 应用程序。太棒了。一旦完成了完整管道的设置,就可以下载 buddy.yml 文件来存储管道和代码。我强烈建议您为自己的项目这样做。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
第三步。创建新管道并添加动作
我们准备建造一条新管道。点击“添加新管道”按钮,并填写如下表格:
创建新管道
我们将运行这条管道,将所有东西推送到主分支。或者,您也可以循环或手动构建您的应用程序。单击蓝色大按钮添加新管道。Buddy 已经看过了我们的代码,并将提出一些建议:
巴迪建议采取几项行动
选择 Python 操作,并填写下一个表单。如果您想跟进,您可以复制/粘贴以下内容:
pip install pipenv
pipenv install
pipenv run python3 tests.py
这些行动:
- 使用 pip 安装命令安装 pipenv。
- 安装所有要求,
- 最后在我们新创建的环境中运行单元测试。
创建 Python 操作
您也可以在这里选择 Python 版本。Buddy 在 Docker 容器中运行每个步骤,因此如果需要,您甚至可以使用自己的自定义 Docker 容器。对我们来说,默认的 Python 容器就可以了。我选择了 3.8.6 版本,因为我知道这是这个项目可以使用的版本。如果您愿意,现在就可以尝试构建步骤,只是为了确保在继续之前一切正常。正如你在下面看到的,我需要 4 次尝试,因为我忘了将一些文件签入 Github repo。那没问题。我们可以继续尝试,直到成功:
我们的 CI/CD 构建步骤有效!
现在回到你的管道,点击你的第一个动作下面的小+号。它允许您添加更多的动作。默认情况下,只有在前一个操作没有错误地完成时,下一个操作才会运行。
下一个要添加的动作是另一个建议:Dockerfile linter。它检查我们的 docker 文件的错误。这是一个很好的额外。所有的默认设置都没问题,所以我们需要做的就是点击“添加这个动作”
现在是时候构建 Docker 映像并将其推送到 Docker Hub 了。添加另一个操作,并再次从建议的操作中选择。这次是那个叫“构建映像”(里面有 docker 图标的那个)。
“设置”选项卡上的默认设置是正确的。前往“选项”选项卡,填写您的 Docker Hub 详细信息。这也是您可以定义图像标签的地方。
我们将做一些简单的事情:我们总是将图像标记为latest
。为了保持版本历史,我们添加了一个使用 Buddy 变量的附加标签。
一个什么?!Buddy 会自动定义一个您可以在项目中使用的环境变量列表。完整的名单和更多细节可以在这里找到。其中之一是简写的 git 修订号,可以在${BUDDY_EXECUTION_REVISION_SHORT}
下访问。我们可以用它给我们的图像一个唯一的 id:
设置 Docker 映像构建
您要添加的最后一个操作是通知,这样您就会收到成功构建的通知。我将用一封不错的旧电子邮件来演示,但还有许多其他选项,例如:
- 松弛的
- 电报
- MS 团队
- 不调和
- 短信
过程又简单了。单击最后一个操作下的小加号按钮,滚动浏览可用的操作,然后单击电子邮件。您将看到这样的屏幕:
添加电子邮件通知
如您所见,您可以使用所有可用的环境变量来定制您喜欢的消息。在更高级的设置中,您可能希望用版本号标记您的 git repo,并创建一个在新标记上触发的构建触发器。这是将开发版本与发布版本分开的好方法,因为这个标签也可以用作标记 docker 映像的变量。
第四步。运行完整的管道
你都准备好了。当您将一些东西推送到主分支时,您的管道将开始运行。或者,您也可以手动触发构建。让我们现在就开始吧:
我们的持续集成和持续交付渠道正在运行
如果一切顺利,您应该会看到一个绿色条,上面写着:“通过。”
为了确保这一点,你可以访问你的 Docker Hub 账户,确认是否有新的图片可用。如您所见,我们的图像有正确的标签:latest 和一个短散列,基于最后一次 git 提交:
我们的 Python 应用程序,构建并推送到 Docker Hub
恭喜,您建立了一个 CI/CD 渠道。这并不太难,是吗?
游击式扩展您的 Web 应用
原文:https://python . land/deployment/guerrilla-scaling-your-web-application-to-million-users
你正在运行一个网站或网络应用程序,它正在迅速流行起来。问题是:你在用一个便宜的 VPS 运行你的网站。它使用典型的组件,如 MySQL 或 MongoDB,一个 web 服务器,如 Apache 或 Nginx,一些存储,以及一个 web 框架,如 Python Django 或使用 web 服务的单页面应用程序。现在您需要扩展您的 web 应用程序,因为事情正在迅速分崩离析。怎么办?
你在廉价的 VPS 上运行你的网站,而事情正在分崩离析。怎么办?
这篇文章讨论了游击式的选择来扩展你的网站,服务更多的用户,而不需要花费数百万,从极其简单和便宜的策略开始。从那里,我们将逐步继续更先进和更昂贵的解决方案。
目录
使用 CDN 扩展您的 web 应用
CDNs 或内容交付网络非常棒,应该是你扩大规模的第一步。你可能对 CDN 有一个概念,但是让我们更详细地看看它。
CDN 如何工作
CDN 提供商操作大量的计算机,这些计算机在地理上分布在世界各地。这些通常被称为边缘节点。通常,他们在所有战略上最好的地方都有服务器,这意味着靠近互联网主干网。这些服务器协同工作,向全球用户提供互联网内容。
CDN 通过 DNS 透明地做到这一点,所以你需要让 CDN 为你的网站管理 DNS。现在,如果一个用户在你的网站上请求一个页面,CDN 会确定该用户的位置,并把他指向地理上邻近的服务器的 IP 地址。这个附近的 CDN 服务器识别哪个域地址请求了哪个页面,如果这个页面被缓存,它就向用户提供一个缓存版本。
图片由 cloudflare
这是对事物如何工作的一个简化但仍然准确的描述。当然,事实上,幕后还有更多的事情要做。
CDN 优势
这种方法有几个明显的优点:
- 它减少了原始服务器(你的廉价 VPS)上的负载,因为 CDN 将直接从缓存中提供大多数资产,而不会命中原始服务器
- 原始服务器上的带宽减少是显著的
- 你将有更快的加载时间,因为缓存的页面和资产就在附近,因此每个人的加载速度都更快,不管他们在哪里。另外,cdn 应用了最先进的传输算法、压缩和(可选的)文件缩小。
- 如今,一个更快的网站意味着更好的谷歌排名,当然也意味着更好的用户体验。
此外,还有一些不太明显的好处:
- cdn 非常擅长检测 DDoS 攻击,并将保护您的站点免受攻击。
- 一个好的 CDN 就像一个应用防火墙,阻止可疑的请求或要求验证码,以减少未知和可疑机器人的负载。
- CDN 可以为你提供离线保护:当你的网站宕机或速度太慢时,它仍然会向用户显示网站的缓存副本。
- 最后,CDN 充当你的站点的代理,所以如果配置得好,用户永远不会知道你的站点实际托管在哪里。事实上,您可以禁止 CDN 以外的任何 IP 访问您的 web 服务器,从而大大减少对原始服务器的攻击。
什么时候 CDN 是正确的选择?
从 CDN 中获利的关键是缓存。如果每个网站页面都是为每个访问者单独构建的,那么它通常是不可缓存的。或者说,至少,所谓的缓存命中率会很低。因此,一个大部分是静态的网站是一个理想的候选。
然而,许多网站的页面大部分是静态内容,只有一部分是动态的。有了这样一个网站,您仍然可以通过单独提供可缓存部分并使用 JavaScript 异步加载动态部分来从 CDN 中获益。这种设置的一个很好的例子是当一个网站允许评论时。主要的文章、视频或图像是 HTML 页面的一部分,可以很容易地缓存,但是只有当用户一直滚动到最后时,评论才会通过 JavaScript 加载。这对你的服务器和用户都有好处,因为他需要提前获取更少的数据。
我个人只有使用 Cloudflare 的经验。Cloudflare 的优点在于,他们提供了一个免费层,在带宽和网站页面数量方面没有限制。这非常符合我们的游击队员攀登网站的目标。如果您使用更高级的选项,如特定的防火墙规则或分析,您将开始付费。
如果你在运行 WordPress(像 40%的网站一样),他们也有一个插件,旨在有效地缓存 WordPress 网站,每月 5 美元,这是我用来托管我的 Python 教程的插件。我可以用这种方式缓存大约 70%的网络请求。虽然不需要,但我仍然可以用一个便宜的 VPS,谷歌和网站测量工具如灯塔明显提高了加载速度。
需要澄清的是:我与 Cloudflare 没有任何关系,也没有任何报酬。他们太棒了。
扩展您的网络应用
CDN 可以减轻原始服务器的负载;这是毫无疑问的。但是在某些时候,源服务器仍然会达到它的极限。幸运的是,我们还有另一个简单的诀窍:扩大规模。
我所说的纵向扩展是指为您的应用程序投入更多的硬件,如 CPU 能力和内存。大多数 web 应用程序要么运行在 VPS 上,要么运行在物理硬件上。如果你在一个共享的主机环境中运行,升级到一个专用的 VPS 或服务器将是一个很好的开端。
如果你已经在使用 VPS 或专用硬件,你可以考虑升级它。任何服务器最重要的方面是:
- CPU 能力:CPU 有多快,更重要的是,有多少个内核?
- 内存:有多少工作内存(RAM)可用?
- 磁盘存储:通常您需要高速固态硬盘
因此,要纵向扩展,您可以寻找更快的 SSD 类型的磁盘、更多的内存和更多的 CPU。此外,您可以考虑在不添加更多服务器的情况下分散负载。例如,您可能希望从不同的磁盘提供静态文件(图像、HTML、CSS)和数据库文件,以提高吞吐量。您可能还想将日志写入单独的磁盘。
如果您想做好这一点,您需要分析您的服务器使用情况,以便首先找到瓶颈。是内存、磁盘速度、CPU 速度,还是这些因素的组合导致速度变慢?或者,如果赶时间,你可以扩大规模,然后期待最好的结果。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
将服务分散到更多硬件上
同样,这是一个简单的问题。您可以将 web 服务器、数据库、缓存、邮件、搜索引擎和其他服务分散在多台机器上,而不是托管在一台机器上。
大多数 VPS 主机提供虚拟网络,允许您创建专用的后端网络,使用私有 IP 地址连接多个 VPS。通过将您的数据库分离到一台或多台机器上,并将您的网站逻辑托管在另一台机器上,您可以走得更远。
在这种情况下,您可以使用的另一个技巧是寻找负载平衡器。大多数情况下,VPS 提供商提供开箱即用的服务,设置起来并不困难。负载平衡器将负载均匀地分布在多台服务器上,让您可以为更多的访问者提供服务。例如,这些服务器可以是 web 服务器,也可以是数据库的副本服务器。
HAProxy 平衡到多个 web 服务器的连接
如果你使用专用硬件或想要更多的控制,你可能想看看 HAProxy 。它是事实上的标准开源负载平衡器,可以平衡 HTTP 和 TCP 连接。
到目前为止,我们已经讨论了一些相对简单的方法来扩展您的 web 应用程序,这些方法可以为您提供一些帮助。然而,当你的流量以每周两倍的速度增长时,它们不会带来很大的不同。你可以扩展的范围是有限的,但是如果你不需要太多的努力就可以做到,这是一种为自己争取时间的廉价方式。同时,您可以制定一个计划,开始从 向外扩展 。
扩展您的 web 应用
当所有其他选项都用尽时,您就到达了扩展的终极方式,称为水平扩展,或向外扩展。我们不是增强单个服务器,而是开始添加更多的相同的服务器来做大致相同的事情。因此,我们不再使用几台大型服务器,而是使用数十台甚至数百台普通服务器。从理论上讲,您可以向外扩展的范围是无限的。像谷歌、脸书和亚马逊这样的大型科技公司就是这种方式运转良好的活生生的例子。这些公司向整个世界提供服务,显然是根据它们获得的负载量无缝地扩大和缩小服务规模。
放大和缩小的区别(图像源
最大的技巧是将负载和数据分散到这些服务器上。它需要分布式算法来允许数据库水平扩展。大量数据的并行处理也是如此。除非你有分布式计算的硕士学位,否则你自己实现它的可能性很小。即使你做到了,你自己或一个小团队也很难做到,如果不是不可能的话。这需要很多知识和很多人。
幸运的是,我们生活在云计算时代,这让我们能够站在巨人的肩膀上。使用微软、谷歌和亚马逊提供的服务来构建水平可伸缩的系统变得相对容易。问题是:它并不便宜!因此,为了忠于文章的标题,在完全求助于云提供商之前,我将提供更多的建议供您考虑。
复制和集群
首先成为瓶颈的通常是你的数据库。幸运的是,所有主要的数据库系统都允许某种集群和/或复制。使用这些选项,您可以将数据库负载和存储分布在多台机器上。结合负载均衡器,这可以让你走得更远。
不利的一面是,这样的设置需要大量的工作和系统管理员技能来维护。好处是您可以执行滚动升级之类的操作,这样您就可以在不停机的情况下维护集群。这是可行的,但你需要扎实的 Linux 和网络技能,以及大量的时间来学习和执行,或者花钱雇人来帮你做这件事。
NoSQL
您可能还想考虑从 SQL 类型的数据库切换到 NoSQL 数据库。后者通常具有更好、更容易的可伸缩性。我个人非常喜欢 Elasticsearch,它可以从小型、廉价的单节点系统无缝扩展到拥有一百个节点的系统。它可以用作文档存储和搜索引擎,甚至可以在其中存储小文件。几种流行的编程语言都有官方客户端,它们将负载分散到整个集群中,因此您不需要负载平衡器。
如果你想了解更多关于 Elasticsearch 的知识,一定要看看我关于它的文章:
- " Elasticsearch 教程:动手操作"
- "【Elasticsearch 可以存储数据吗?
使用混合模型
作为节省时间的替代方案,您可以考虑混合模式。一旦您的数据库开始成为瓶颈,您可以研究许多数据库即服务产品。这些都很贵,但是它们扩展性很好,并且不需要您进行任何维护。这很可能是最好的选择。或者,您可能想冒险改用完全云原生的解决方案。
云原生开发
对于云原生开发,我的意思是从底层构建您的应用程序时要考虑到云。基于云的 web 应用程序往往伸缩性很好。在构建云计算时,您可以充分利用云计算的可伸缩性、灵活性和弹性。
容器和微服务
云中的大多数应用程序都是使用容器打包的,所以在为云开发时,您会很快发现您需要使用容器。
本质上,容器包含了应用程序及其所有需求,比如系统包和软件依赖。这种容器可以在任何地方运行,从您自己的 PC 到私有云,到大型云提供商,如 Amazon、Azure、Google、Digital Ocean 等等。
为了使这一点更加明确,让我们来看看 Python Django 应用程序是如何打包到容器中的。这种应用程序的容器将包含:
- 一组最小的 Linux 系统文件
- Python 3 安装
- Django 和其他必需的包
- 代码本身
类似地,另一个容器或一组容器将运行数据库。或许你会有一整套运行微服务的容器,它们都提供特定的功能。
许多大公司都出现了微服务的分离。好处是小团队可以各自处理单独的服务,在团队和代码库之间创建自然的界限。我只会向大公司推荐微服务方法,有多个团队。如果你是一个小的,只有一个团队,甚至只有一个人的公司,创建所谓的“独石”实际上是一个明智的选择。它们更容易管理,也更容易推理。您总是可以在稍后阶段分离功能。
码头工人和库柏工人
Docker 和 Kubernetes 是集装箱领域最著名的品牌。尽管这些系统的使用需要一个学习过程,但这是值得的,因为所有主要的提供商都支持它们,尤其是 Kubernetes。
我以前写过关于容器以及如何创建容器化的 Python REST API 。
利用云服务
云原生应用通常也使用基于云的服务,比如文件存储和基于云的数据库。一个众所周知的文件存储系统是亚马逊的 S3,也称为云对象存储系统。亚马逊上一个流行的 NoSQL 数据库是 DynamoDB。其他云提供商也提供类似的产品。这些产品具有极强的可扩展性,使用起来非常方便。它们允许真正快速的开发,但是它们也会非常迅速地从你的钱包里抢钱。然而,在许多用例中,成本并不是大问题。例如,当你的收入随着访客数量线性增长时,你可能更喜欢增长和降低复杂性,而不是降低成本。
云的优势
对于云解决方案来说,将功能分成容器和微服务是很典型的。基于云的对象存储和数据库的使用也是如此。这样做的好处是,应用程序的所有部分都可以根据需要进行伸缩。每个容器可以作为单个副本运行,但是您可以轻松地添加更多副本,并在所有副本之间平衡负载。
微服务的另一个优势是,它们可以很容易地与新版本交换,只改变整个系统的一部分,从而降低风险。另一个优势是,团队可以各自处理自己的服务,而不会妨碍彼此,从而在团队之间创建自然的界限。例如,一个团队可能更喜欢 Java 栈,而另一个团队更喜欢 Python。只要他们提供一个定义良好的 REST 接口,一切都很好!
继续学习
现在,您应该对如何扩展 web 应用程序有了大致的了解。我们从廉价、简单的黑客技术开始,以全面的云原生开发结束。要了解更多,看看大型科技公司能提供什么,以下是一些帮助你入门的链接(排名不分先后):
此外,红帽还有一页解释云原生开发。最后,还有维基百科。从关于云计算的文章开始。
用于数据科学的 Python:学习路线图
Python 是大部分数据科学社区选择的语言。本文是学习 Python 用于数据科学的路线图。它适合刚起步的数据科学家,也适合那些已经想学习更多关于使用 Python 进行数据科学的人。
我们将浏览数据科学家使用的所有基本元素,同时提供更全面解释的链接。这样,你可以跳过你已经知道的东西,直接进入你不知道的东西。在这个过程中,我将向您介绍数据科学社区使用的基本 Python 包。
我建议你把这一页加入书签,这样你就可以很容易地返回。最后但同样重要的是:这一页是一个持续的工作进展。我会添加内容和链接,我也希望得到您的反馈。所以,如果你在旅途中发现了你认为也属于这里的东西,不要犹豫,给我发信息。
目录
什么是数据科学?
不过,在我们开始之前,我想更正式地描述一下我所看到的数据科学。虽然我假设您对什么是数据科学有一个大致的概念,但更具体地定义它仍然是一个好主意。它还会帮助我们定义一个清晰的学习路径。
您可能知道,很难给数据科学家下一个单一的、包罗万象的定义。如果我们问十个人,我肯定会得出至少十一种不同的数据科学定义。所以这是我的看法。
使用数据
成为一名数据科学家意味着对几个领域了如指掌。但最重要的是,你必须熟悉数据。数据有哪些种类,如何存储,如何检索?是实时数据还是历史数据?可以用 SQL 查询吗?是文本、图像、视频,还是这些的组合?
您如何管理和处理您的数据取决于许多属性或质量,这使我们能够更准确地描述它。这些也称为数据的五个 V:
- 卷:有多少数据?
- 速度:数据流动的速度有多快?它的时效性如何(例如,它是实时数据吗?)
- 多样性:数据有不同的类型和来源,还是只有一种类型?
- 准确性:数据质量;是否完整,是否容易解析,是否源源不断?
- Value :在所有处理结束时,数据给表带来了什么价值?思考对管理有用的见解。
尽管在数据工程和大数据领域,你会更经常地听到这五个 V,但我坚信它们适用于所有专业领域,是看待数据的一种好方法。
编程/脚本
为了读取、处理和存储数据,您需要具备基本的编程技能。你不需要成为一名软件工程师,你可能也不需要了解软件设计之类的知识,但是你需要一定水平的脚本技能。
对于数据科学家来说,有很多奇妙的库和工具。对于许多数据科学工作来说,你需要做的就是结合正确的工具和库。但是,要做到这一点,您需要了解一种或多种编程语言。Python 已经证明了自己是数据科学的理想语言,原因有几个:
- 这很容易学
- 您可以交互地使用它,也可以以脚本的形式使用它
- 有(字面上的)大量有用的库
数据科学界最初接受 Python 是有原因的。然而,在过去几年中,许多新的超级有用的 Python 库专门针对数据科学而出现。
数学和统计学
如果上述技能本身还不够难,你还需要相当好的数学、统计学和科学工作的知识。
形象化
最终,你想要向你的团队、你的经理或全世界展示你的成果!为此,你需要将你的结果可视化。您需要了解如何在地图上创建基本的图表、饼图、直方图和封装数据。
专家知识
每个工作领域都有或要求:
- 特定术语,
- 它自己的规章制度,
- 专家知识。
一般来说,你需要深入了解一个领域是如何形成的。不了解基本术语和规则,就无法分析特定专业领域的数据。
那么什么是数据科学家呢?
回到我们最初的问题:什么是数据科学?或者:是什么让一个人成为数据科学家?你至少需要具备上述所有学科领域的基本技能。每位数据科学家都有不同程度的这些技能。你可以在一个方面很强,在另一个方面很弱。没关系。
例如,如果你有数学背景,你会在数学方面表现出色,但也许你最初会在处理数据时遇到困难。另一方面,一些数据科学家来自人工智能/机器学习领域,将倾向于这部分工作,而不是其他部分。没太大关系:最后,我们都需要学习,需要补缺。这种差异正是这个领域令人兴奋并充满学习机会的原因!
学习 Python
想用 Python 做数据科学的第一站:学习 Python。如果您对 Python 完全陌生,首先开始学习语言本身:
- 从我的免费 Python 教程或高级 Python 初学者课程开始
- 查看我们的 Python 学习资源页面,获取书籍和其他有用的网站
学习命令行
如果您习惯使用命令行,这会很有帮助。这是你必须开始并习惯的事情之一。一旦你这样做了,你会发现你会越来越多地使用它,因为它比任何事情都使用图形用户界面要有效得多。使用命令行将使您成为一个更加多才多艺的计算机用户,并且您将很快发现,一些命令行工具可以完成否则将是一个庞大、丑陋的脚本和一整天的工作。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
好消息是:这并不像你想象的那么难。我们在这个网站上有一个相当广泛的章节,关于使用 Unix 命令行、你需要知道的基本 shell 命令、创建 shell 脚本,甚至 Bash 多重处理!我强烈建议你去看看。
数据科学工作环境
将 Python 用于数据科学大致有两种方式:
- 创建和运行脚本
- 使用交互式外壳,如 REPL 或笔记本
Jupyter Lab
交互式笔记本在数据科学社区中已经变得非常流行,但是您当然不应该排除使用简单的 Python 脚本来完成一些繁重工作的可能性。两者都有自己的位置。
查看我们关于 Jupyter 笔记本优势的详细文章。您将了解将它用于数据科学的优势、它的工作原理以及如何安装它。在那里,您还将了解什么时候笔记本是正确的选择,什么时候写脚本更好。
读取数据
有很多方法可以得到你需要分析的数据。我们将快速浏览获取数据的最常见方法,我将向您推荐一些最好的库来完成这项工作。
来自本地文件的数据
通常,数据会存储在一个文件系统中,所以你需要能够用 Python 打开和读取文件。如果数据是 JSON 格式的,你需要一个 Python JSON 解析器。Python 本身就可以做到这一点。如果你需要读取 YAML 数据,还有一个 Python YAML 解析器。
来自 API 的数据
数据通常会通过 REST API 提供给你。在 Python 的世界中,通过 HTTP 获取数据的最常用和最用户友好的库之一叫做 Requests。对于请求,从 API 获取数据可以像这样简单:
>>> import requests
>>> data = requests.get('https://some-weather-service.example/api/historic/2020-04-06')
>>> data.json()
[{'ts':'2020-04-06T00:00:00', 'temperature': 12.5, .... }]
这是绝对基本的用例,但是当你需要发布数据,当你需要登录一个 API,等等时,请求也包括在内。在请求网站本身和 StackOverflow 这样的网站上会有很多例子。
从万维网抓取数据
有时,数据无法通过易于解析的 API 获得,只能从网站获得。如果数据只能从网站上获得,您将需要从原始的 HTML 和 JavaScript 中检索它。这样做就叫刮,可能会很辛苦。但是就像所有事情一样,Python 生态系统已经覆盖了你!
但是,在考虑收集数据之前,您需要了解一些事情:
- 一个网站的结构可以在没有通知的情况下改变。没有任何保证,所以你的铲运机随时可能坏掉。
- 不是所有的网站都允许你刮。有些网站会主动尝试检测抓取器并屏蔽。
- 即使一个网站允许抓取(或者不关心),你也有责任以一种有序的方式这样做。仅仅通过在短时间内发出许多请求,用一个简单的 Python 脚本关闭一个站点并不困难。请意识到你这样做可能会触犯法律。一个不太极端的结果是,你的 IP 地址将被终身禁止出现在该网站上(也可能出现在其他网站上)
- 大多数网站都提供了一个 robots.txt 文件。你应该尊重这样的文件。
好的抓取器会有选项来限制所谓的抓取率,并会选择尊重 robots.txt 文件。理论上,你可以创建你自己的 scraper,例如,Requests 库,但是我强烈建议不要这样做。工作量很大,而且很容易搞砸,被封杀。
相反,你应该看看 Scrapy ,这是一个成熟的,易于使用的库,用于构建高质量的 web scraper。
捣鼓数据
Python 在数据科学领域如此受欢迎的原因之一是以下两个库:
- NumPy :“用 Python 进行科学计算的基础包。”
- Pandas:“一个快速、强大、灵活且易于使用的开源数据分析和操作工具。”
让我们更详细地看看这两个!
NumPy
NumPy 的优势在于处理数据数组。这些可以是一维数组、多维数组和矩阵。NumPy 还提供了许多可以应用于这些数据结构的数学运算。
NumPy 的核心功能大部分是用 C 实现的,这使得它比普通的 Python 代码快得多。因此,只要您使用 NumPy 数组和操作,您的代码就可以和用快速编译语言做同样操作的人一样快,甚至更快。你可以在我的NumPy介绍中了解更多。
熊猫
像 NumPy 一样,Pandas 为我们提供了高效处理内存数据的方法。这两个库在功能上有重叠。一个重要的区别是熊猫为我们提供了一种叫做数据框架的东西。数据框类似于电子表格的工作方式,您可能知道其他语言的数据框,比如 r。
Pandas 是处理表格数据的合适工具,比如存储在电子表格或数据库中的数据。熊猫将帮助你探索、清理和处理你的数据。
可视化
每个 Python 数据科学家都需要在某个时候可视化他或她的结果,有许多方法可以用 Python 可视化您的工作。然而,如果只允许我推荐一个库,那将是一个相对较新的库:Streamlit。
细流
Streamlit 如此强大,值得单独撰写一篇文章来展示它所提供的功能。总结一下:Streamlit 允许您将任何脚本转换成成熟的交互式 web 应用程序,而无需了解 HTML、CSS 和 JavaScript。所有这些只需要几行代码。它真的很强大。去看看 Streamlit 吧!
Streamlit 在内部使用了许多众所周知的包。你可以选择使用它们,但是 Streamlit 让使用它们变得更加容易。Streamlit 的另一个很酷的特性是,大多数图表允许您轻松地将它们导出为图像或 CSV 文件。
破折号
另一个比较成熟的产品是 Dash。像 Streamlit 一样,它允许您创建和托管 web 应用程序来快速可视化数据。要了解 Dash 能做什么,请阅读他们的文档。
继续学习
你可以在这里免费阅读 Jake Vanderplas 的《数据科学的 Python》一书。这本书是 2016 年的,所以有点过时了。比如当时,Streamlit 还不存在。此外,这本书解释了 IPython ,它是现在 Jupyter 笔记本的核心。功能基本相同,所以还是有用的。
Jupyter 笔记本:如何安装和使用
了解 Jupyter Notebook,它是 Jupyter 实验室交互式 IDE 的一部分,是数据科学的理想选择。我们将探索使用它优于常规 IDE 的优势,向您展示如何安装 Jupyter Notebook 和 Jupyter Lab,并演示它的功能。
目录
- Jupyter 实验室 vs Jupyter 笔记型电脑
- 使用 Jupyter Lab 和笔记本的优势
- 如何安装 Jupyter 笔记本和实验室
- 开始探索木星实验室
- 你在笔记本上的第一步
- JupyterLab plugins
- IPython
- vscode中的 Jupyter 笔记本电脑
- 继续学习
Jupyter 实验室 vs Jupyter 笔记型电脑
JupyterLab 是一个基于 web 的交互式开发环境。它最出名的是提供了一种叫做 Jupyter Notebook 的所谓笔记本,但你也可以用它来创建和编辑其他文件,如代码、文本文件和 markdown 文件。此外,它允许您像大多数 ide 一样打开一个 Python 终端来进行试验和修补。
JupyterLab 非常灵活:您可以配置和安排用户界面,以支持数据科学、科学计算和机器学习中的广泛工作流。它也是可扩展的:每个人都可以编写插件来添加新组件并与现有组件集成。
JupyterLab 支持 40 多种其他编程语言,比如 Java、Scala、Julia 和 r。
另一方面,Jupyter Notebook 是一个类似 REPL 的环境,融合了代码、数据和文档。所以简而言之,JupyterLab 就是基于浏览器的 IDE,而 Jupyter Notebook 就是里面的笔记本组件。如果你愿意,你可以单独安装 Jupyter Notebook,我将在本文中向你展示如何安装。
使用 Jupyter Lab 和笔记本的优势
Python 应用程序开发人员通常更喜欢使用像 VSCode 这样的常规 Python IDE,它有助于调试、单元测试、部署和版本管理。相比之下,(数据)科学家和数据分析师有不同的关注点,通常更喜欢笔记本风格的 IDE。让我们来看看这些笔记本电脑提供的一些优势。
组合并记录代码和结果
使用笔记本电脑的最大优势在于,它允许您将三样东西结合到一个环境中:
- 文档,
- 代码,
- 结果
同时提供一个互动的环境,你或你的同伴可以修补和实验。下图完美地展示了这一点:
结合了文档、输出和代码的 Jupyter 笔记本
互动探索
笔记本允许您以交互方式浏览数据。在这样做的时候,笔记本是你做了什么和你如何做的日志。配合 NumPy 和熊猫使用最理想。所以笔记本比普通的 ide 更适合那些处理和分析数据的人。然而,一些 ide,比如 VSCode,将提供一个内置的 Jupyter Notebook(参见本文后面的内容)。
这是其他人用的
有时候顺其自然会有帮助。在这种情况下,你会注意到数据科学中的许多其他人使用 Jupyter 笔记本。这本身有几个优点:
- 很容易交流分享自己的作品。
- 许多插件是由数据科学家同事创建的,专门针对像你这样的人。
如何安装 Jupyter 笔记本和实验室
像所有 Python 软件一样,安装 Jupyter Lab 轻而易举。我将演示如何安装 Pip 和 Conda。事先要注意一件事:我建议在系统范围内安装这个。对于大多数软件包,我强调使用虚拟环境的重要性。然而,像这样的工具你想安装一次。
用 Pip 安装 Jupyter 实验室和/或笔记本电脑
如上所述,您希望在系统范围内安装 Jupyter Lab,或者至少在您的本地 bin 文件夹中。使用 Pip 全局安装软件包需要**-g**
选项。然而,在许多 Linux 系统上,如果您没有指定 -g ,并且如果您没有使用虚拟环境,Pip 会将软件包安装在一个.local/bin
文件夹中。您可以通过使用--user
选项来强制这种行为。这样做的好处是,您不需要 root 权限就可以访问您正在使用的系统。
因此,安装 Jupyter Lab 最推荐的方法是使用 pip install 命令:
$ pip install --user jupyterlab
如果你只想要 Jupyter 笔记本,用这个代替:
$ pip install --user notebook
用 Conda 安装 JupyterLab
JupyterLab 也可以安装mamba
和conda
:
$ mamba install -c conda-forge jupyterlab
or...
$ conda install -c conda-forge jupyterlab
与 Pip 一样,如果您只想安装 Jupyter 笔记本,请使用以下命令:
$ mamba install -c conda-forge notebook
or...
$ conda install -c conda-forge notebook
开始探索木星实验室
如何从命令行打开 Jupyter 实验室
如果您按照安装说明进行操作,您应该能够使用以下命令启动 Jupyter Lab:
$ jupyter-lab
(a bunch of log message will follow)
如何从命令行打开 Jupyter 笔记本
如果您只想启动一个笔记本,请使用:
$ jupyter-notebook
(a bunch of log message will follow)
添加到您的路径
如果您得到一个类似“命令未找到”的错误,不要惊慌。不幸的是,有些系统在$PATH
变量中没有.local/bin
目录。在 Linux 和 OS X 上,一个快速的解决方法是像这样临时添加它:
PATH="$HOME/.local/bin:$PATH"
但是,这只有在您保持终端窗口打开的情况下才有效。因此,为了永久保存,我建议在您的~/.profile
文件末尾添加以下代码片段:
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/.local/bin" ] ; then
PATH="$HOME/.local/bin:$PATH"
fi
它所做的只是将.local/bin
文件夹添加到路径中,但前提是它确实存在。
当您登录到您的系统时,.profile
文件被加载一次。您可能需要注销并登录才能应用更改。如果您使用 zsh 而不是 Bash(目前 zsh 是 MacOS 上的缺省值),您可能想尝试将 about 的代码片段添加到一个名为.zprofile
的文件中。
你在笔记本上的第一步
如果你启动 Jupyter Lab 或 Notebook,它会自动打开你的浏览器。如果没有,可以手动转到 http://localhost:8888/打开。在那里,创建一个新的笔记本,例如通过点击文件- >新建- >笔记本。
在下面的截图中,我创建了一个名为“第一步”的新笔记本。有几件值得注意的事情:
- 你输入放在一个“单元格”里,每个单元格都有编号
- 您可以通过使用
In
和Out
数组来访问之前的单元格。例如,下面示例中的Out[3]
给出了In[3]
单元格中表达式的输出 - 您还可以将单元格类型更改为 Markdown,有关这方面的更多信息,请参见下文
- 如果你想应用或执行你的单元格,按 Shift + Enter 。
一个简单的 Jupyter 笔记本
细胞类型
在笔记本的顶部,有一个下拉菜单,允许您更改单元格类型。现在你需要关心的是“Python”和“Markdown”类型。
计算机编程语言
Python 单元格包含 Python 代码。这可以是任何有效的代码,因此所有这些都是允许的:
降价
Markdown 是一种创建格式化文本的轻量级标记语言。您可以使用它来创建标题、列表、代码片段和您能想到的大多数其他东西。例如,如果您曾经在 GitHub 上创建过 README.md 文件,您可能会对它很熟悉。
Markdown 非常适合在笔记本上添加文档。对大多数人来说,使用减价备忘单就足以快速入门。
公式
您也可以使用 Markdown 输入好看的公式。在内部,Jupyter 使用 MathJax 来格式化和显示公式。公式以单美元符号或双美元符号开始和结束:
$ .... $
:内嵌;该公式与当前句子的其余部分一致$$ .. $$
:显示模式;这个公式在正文的其余部分中很突出
在里面,你需要输入一个基于 TeX 的等式。如果你想看到所有的选项并玩这些方程,我推荐这个网站,它提供了一个 Tex 方程编辑器。
JupyterLab plugins
如果你点击屏幕左边的拼图图标,你会得到一个可以直接安装的插件列表。有许多插件,其中许多会增加额外的可视化功能或与 Tableau 等产品的集成。
IPython
在内部,Jupyter 笔记本使用了一个名为 IPython 的项目。如果你倾向于经常使用 Python REPL ,我推荐你阅读我们在 IPython 上的页面,因为它是常规 REPL 的一个很好的替代品。我个人用的很多,怎么推荐都不够!例如,仅仅因为它出色的自动完成功能,它就是值得的,如果你习惯于 Jupyter 笔记本,显然你会有宾至如归的感觉。
IPython 在运行
vscode 中的 Jupyter 笔记本电脑
如果你和我一样是 VSCode 的粉丝,你需要看看 VSCode 的扩展,它允许你直接在 VSCode 标签中创建和使用笔记本。如果您打开命令托盘,例如使用 ctrl + shift + p ,开始输入“Jupyter”来创建、打开和导出笔记本。
从 VSCode 创建新的 Jupyter 笔记本
继续学习
访问官方项目网站,获取关于 Jupyter Lab 和 Jupyter Notebook 的最新新闻和文档总是值得的。
numpy:Python 数据科学的基础
Python 的构想只有一个目标,简单。这种简单性使得 Python 成为当今最流行的语言之一。然而,Python 开发人员不得不牺牲性能来让他们的生活更轻松。不过,这种性能损失会对数值和科学计算产生相当大的影响!幸运的是,NumPy 在那里拯救了世界!
本文是对 NumPy 的介绍。读完之后,您将知道如何安装和导入 NumPy,以及如何用一维 NumPy 数组处理数值数据。在 Python 的土地上,很快就会有一个关于 NumPy 的完整课程,涵盖更多的主题和多维数组。如果你想得到它发布的通知,请订阅低容量的 Python Land 简讯。
目录
为什么是 NumPy?
由于 Python 的易用性和可扩展性,科学家们从早期就对它感兴趣。NumPy 源于科学 Python 社区为解决 Python 中数值计算的弱点所做的努力。需要解决的主要问题是:
- 高效的数组创建和操作
- 对这些数组进行运算的数学函数和算法
NumPy 是在 2005 年通过合并当时可用的两个数值包创建的:Numeric 和 Numarray。由于 Python 没有针对速度进行优化,NumPy 的大部分繁重代码都是用 C 编写的,一些 Fortran 代码在边缘处乱涂乱画。这就是为什么 NumPy 速度如此之快!
自创建以来,NumPy 已经成为 Python 可用的最著名的第三方模块之一。关于是否将其作为 Python 的标准模块之一,甚至有过激烈的争论。只有最好的项目才能获得这样的荣誉。
快进到现在,我们已经看到了利用 Python 的数据科学和机器学习工作的爆炸。NumPy 是一个基础包,它提供了许多其他项目所需的坚实基础。许多数据科学和机器学习包在幕后使用 NumPy,特别是:
- 熊猫
- scikit-learn
- Tensorflow
所以掌握 NumPy 至关重要。
安装 NumPy
NumPy 不是默认 Python 发行版的一部分,所以你需要用 pip install (或者poems/Pipenv)来安装它:
pip install numpy
如果您是 Conda 用户,您可以使用:
conda install numpy
导入数字
像所有包一样,您可以导入 NumPy 的一部分,也可以导入整个包。有一个惯例是导入整个包,然后将其重命名为 np。强烈建议也使用这个约定。原因很简单,因为大多数 NumPy 用户以交互方式使用软件包,所以他们只需输入更少的内容:
import numpy as np
在本文中,我们将遵守约定,并假设您已经将 NumPy 作为np
导入。
NumPy 数组
NumPy 的核心是数组。让我们正式定义什么是数组:
array
A data structure with elements of the same type whose position can be accessed by one or more indices is an array. Hence, a programming language implementation of a vector, a matrix, or a tensor is an array.
鉴于数组的广泛定义,Python 有两种类似于数组的内置类型:list 和 tuple。我们可以使用列表的列表或列表的字典来存储多个列表,有效地创建多维数组。然而,无论是列表、元组还是字典都没有针对数值目的进行优化。它们甚至不太符合我们的定义,因为它们可以存储不同类型的值。例如,列表可以包含数字、字符串和任何其他对象类型的混合集合。
NumPy 解决了 Python 在通过数组进行数值计算方面的许多缺点。尤其是 NumPy 中的数组创建和操作非常快,并且得到了很好的优化。
创建一维数组
创建数组最简单的方法是将一个列表传递给 NumPy 的主实用程序来创建数组,np.array
:
a = np.array([1, 2, 3])
该方法接受几个可选的关键字参数,我们将讨论其中的三个:copy
。
复制参数
copy
参数说明是否复制输入对象。当 copy 为True
时,结果数组中的任何变化都不会改变输入对象。但是,如果是False
,数组的变化可以改变输入对象。
当使用列表制作数组时,NumPy 将总是复制对象,而不管参数的值;例如:
lst = [1, 2, 3]
a = np.array(lst, copy=False)
print(a)
# array([1, 2, 3])
如果我们改变数组,列表将保持不变,因为 NumPy 复制了它:
a[0] = 0
print(lst)
# [1, 2, 3]
如果我们创建相同的列表,但是使用另一个 NumPy 数组作为输入:
a_in = np.array([1, 2, 3])
a = np.array(a_in, copy=False)
a
让我们看看如果我们改变结果数组会发生什么:
a[0] = 0
print(a)
# array([0,2,3])
print(a_in)
# array([0,2,3])
两个数组都发生了变化,因为我们将copy
选项设置为False
。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
NumPy 数据类型(dttypes)
函数np.array
的另一个关键字参数是dtype
。此参数指定数组中的数据类型。请记住,数组的一个关键属性是所有元素都具有相同的类型。
NumPy 实现了自己的数据类型,这些数据类型针对高效存储和处理进行了优化。为此,它使用了名为dtype
的基类。我们来看看最常见的dtypes
:
- np.int16
- np.int32
- np.int64
- np.float32
- np.float64
- 浮动 128
- np.bool_
- np.str_
- np 字节 _
- np .对象 _
在本文中,我们将只关注数值类型。
整数
整数数据类型、np.int16
、np.int32
和np.int64
的区别仅在于它们能存储的数的大小:
np.int16
->32762np.int32
->2147483647np.int64
->9223372036854775807
在正常情况下,使用np.int64
是可行的,因为它允许我们存储最大的数字。Int64 是 NumPy 默认使用的dtype
。然而,使用较小的整数也有好处:
- 减少内存使用
- 更快的计算
对于相对较小的阵列,内存使用通常不是问题。如果您认为会,请尝试较小的类型,但要确保所有元素以及对这些元素的未来操作的结果不会超过所选类型的最大大小。
漂浮物
浮点类型也指内存中数字的大小。尺寸越大,数组元素的精度就越高。然而,这种精度是以牺牲内存和性能为代价的。经验法则是默认使用np.float64
。如果您可以节省一些精度,并且性能和内存使用是最重要的,请使用更小的。
让我们探索一下浮动大小如何影响 REPL 的精度:
>>> np.array([1.3738729019013636723763], dtype=np.float16)[0]
1.374
>>> np.array([1.3738729019013636723763], dtype=np.float32)[0]
1.3738729
>>> np.array([1.3738729019013636723763], dtype=np.float64)[0]
1.3738729019013636
>>> np.array([1.3738729019013636723763], dtype=np.float128)[0]
1.3738729019013635746
在一些系统上,甚至有一个float128
类型,如上一个例子所示。如果你在 Windows 上,这可能会出错,但是 Linux 和 MacOS 应该支持它。
使用 NumPy 数组
我们现在将仔细研究如何使用 NumPy 数组,从使用数组索引访问元素开始。
获取单个元素
我们可以访问和修改单个元素:
a = np.array([0.0, 2.0, 3.0, 4.0, 5.0])
print(a[0])
# 0.0
a[0] = 1.0
print(a)
# [1., 2., 3., 4., 5.]
访问多个元素
我们可以一次访问和修改 NumPy 数组中的多个特定元素。注意 Python 列表没有这个特性:
a = np.array([0.0, 2.0, 3.0, 4.0, 5.0])
# Get elements at position 0 and 2
print(a[[0, 2]])
# [0., 3.]
# Change the first two elements
a[[0, 1]] = [0, 3.0]
print(a)
# [0., 3., 3., 4., 5.]
负索引
负索引的工作原理与列表相同;他们倒计数指数。例如,要获取数组末尾的元素,可以使用:
a = np.array([0.0, 2.0, 3.0, 4.0, 5.0])
print(a[-1])
# 5.0
print(a[-2])
4.0
限幅
切片也可以工作,它的行为与列表的常规切片完全一样,例如,格式是a[start: stop: step]
。作为一个例子,让我们得到一个数组的前三个元素:
a = np.array([0.0, 2.0, 3.0, 4.0, 5.0])
print(a[0: 3])
或者除最后一个元素之外的所有元素:
print(a[0: -1])
# [0., 2., 3., 4.]
和列表一样,我们也可以这样反转数组:
print(a[:: -1])
# [5., 4., 3., 2., 0.]
追加、插入、删除和排序
NumPy 数组与列表有更多的共同点。许多常规操作的行为类似于 Python 列表,比如排序、删除、插入和追加数据。注意,这些方法都返回一个新的数组,而不是修改给定的数组。
追加到 NumPy 数组
追加意味着在末尾添加元素。我们可以像处理列表一样将单个元素追加到 NumPy 数组中:
a = np.array([1.0, 2.0])
a = np.append(a, 3.0)
print(a)
# [1., 2., 3.]
我们习惯于使用extend
方法将多个元素添加到一个列表中。但是,NumPy 数组重用相同的 append 函数来添加多个元素:
a = np.array([1.0, 2.0])
np.append(a, [4.0, 5.0])
print(a)
[1., 2., 4., 5.]
插入 NumPy 数组
我们可以使用 insert 在特定的索引位置插入一个或多个元素:
a = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
# Insert one element at position 3
a = np.insert(a, 3, values=3.5)
# a is now [1\. , 2\. , 3\. , 3.5, 4\. , 5\. ]
# Insert a list of elements at position 3
a = np.insert(a, 3, values=[100, 200])
# a is now [1\. , 2\. , 3\. , 3.5, 100, 200, 4\. , 5\. ]
# Insert multiple elements at multiple positions
a = np.insert(a, [3, 5], values=[4.5, 5.5])
# a is nop [1\. , 2\. , 3\. , 4.5, 4\. , 5\. , 5.5]
从 NumPy 数组中删除元素
我们也可以一次删除一个或多个元素:
a = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
np.delete(a, -1)
# a is now [1., 2., 3., 4.]
np.delete(a, [0, 1])
# a is now [3., 4.]
排序 NumPy 数组
有两种方法可以对 NumPy 数组进行排序:就地排序和创建一个新的排序后的数组。从最后一个开始:
a = np.array([1.0, 3.0, 2.0, 4.0, 5.0])
b = np.sort(a)
# b is now [1., 2., 3., 4., 5.]
要进行就地排序,请执行以下操作:
a = np.array([1.0, 3.0, 2.0, 4.0, 5.0])
a.sort()
# a is now [1., 2., 3., 4., 5.]
重申一下:请注意,除了 sort 方法之外,大多数方法都不属于 array 类。因此,我们必须调用接受数组作为参数的np
对象上的方法。因此,这些转换不会就地发生,而是返回一个新数组。
在 NumPy 课程中(即将推出!),我们将学习更多的函数和数组方法,使我们能够用数组做更多的事情。
数学阵列运算
我们将用最常见的数学运算来结束这篇文章,这些运算是人们可能想对数组执行的:和、减、乘、除。
数组像标量一样处理;操作是按元素执行的。因此,数组只能被另一个相同大小的数组或标量加、减、乘或除。
让我们先定义一些数组,注意a
和b
有相同的大小 4,b_wrong_size
有不同大小的 3 个元素:
a = np.array([1.0, 2.0, 3.0, 4.0])
b = np.array([2.0, 2.0, 2.0, 2.0])
b_wrong_size = np.array([2.0, 2.0, 2.0])
如果我们试图操作不同大小的数组,将会出现一个ValueError
异常:
a = np.array([1.0, 2.0, 3.0, 4.0])
b_wrong_size = np.array([2.0, 2.0, 2.0])
# raises ValueError exception
a + b_wrong_size
ValueError: operands could not be broadcast together with shapes (4,) (3,)
加法和减法
我们可以将数组相加,也可以为数组的每个元素添加一个值:
a = np.array([1.0, 2.0, 3.0, 4.0])
b = np.array([2.0, 2.0, 2.0, 2.0])
print(a + b)
[3., 4., 5., 6.]
print(a + 2)
[3., 4., 5., 6.]
print(a - b)
[-1., 0., 1., 2.]
print(a - 2)
[-1., 0., 1., 2.]
乘法和除法
乘法和除法也是如此:我们可以使用单个值或两个数组:
a = np.array([1.0, 2.0, 3.0, 4.0])
b = np.array([2.0, 2.0, 2.0, 2.0])
print(a * b)
[2., 4., 6., 8.]
print(a * 2)
[2., 4., 6., 8.]
print(a / b)
[0.5, 1\. , 1.5, 2\. ]
print(a / 2)
[0.5, 1\. , 1.5, 2\. ]
结论
我们已经了解了一维数组的创建、访问元素、数组操作以及数组上最重要的数学运算。关于 NumPy 还有很多要学的。Python Land 将很快在 NumPy 上发布一个完整的课程,涵盖你想知道的一切。在此之前,我推荐以下资源来了解更多信息:
- 官方 NumPy 手册有一个部分是给绝对初学者的
- 如果你懂 MATLAB,你会喜欢 NumPy for MATLAB 用户
Python 学习资源
这是个人挑选的 Python 学习资源的集合,按知识水平排序。他们都很棒,所以一定要探索这些!这些链接大多是免费(无报酬)资源。甚至这里的一些书都有免费版本。
目录
初学者学习资源
以下链接都是免费的 Python 学习资源,针对初学者。如果你刚刚开始使用 Python,这些是给你的!
- 我们的 Python 初学者教程——这是开始学习 Python 的最好地方!
- ——一本伟大的书,被许多人推荐,如果你想的话,可以免费阅读(知识共享许可)。还有一个付费版本;参见下面的书籍部分。
- 官方 Python 文档——这些文档是按版本组织的,是一个很棒的、相当完整的资源,我发现自己要回顾的比我想承认的还要多。
中级 Python 资源
这些 Python 学习资源是针对中级水平的。如果你能写一个 Python 程序并知道基础知识,这些对提高你的技能和加深你的知识是非常好的。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
- 全栈 Python——了解创建、部署和操作 Python 驱动的应用所需的一切。这个网站也有书的形式!
- Python 风格的元素——本文超越了 PEP8,涵盖了伟大 Python 风格的核心。它有点固执己见,超越了简单的语法和模块布局问题,进入了范例、组织和架构领域。
学习 Python 的书籍
这是我推荐给所有 Python 初学者的精选书籍。如果你决定使用下面的链接购买这本书,Python Land 会从亚马逊获得一小笔佣金,不需要你额外付费。如果你想支持我们,请使用链接!
在这本全面修订的畅销经典的第二版中,你将学习如何使用 Python 编写程序,在几分钟内完成手工需要几个小时才能完成的事情——不需要任何编程经验。您将学习 Python 的基础知识,并探索 Python 丰富的模块库来执行特定的任务,如从网站上抓取数据、阅读 PDF 和 Word 文档,以及自动执行点击和键入任务。
不仅仅是编写干净代码的高级语法和熟练技巧的集合,您将学习如何通过使用命令行和其他专业工具(如代码格式化程序、类型检查器、linters 和版本控制)来提高您的 Python 编程技能。Sweigart 带您了解设置开发环境、命名变量和提高可读性的最佳实践,然后处理文档、组织和性能测量,以及面向对象的设计和编码面试中常用的 Big-O 算法分析。
数据分析 Python 书籍
获取在 Python 中操作、处理、清理和处理数据集的完整说明。针对 Python 3.6 进行了更新,该实践指南的第二版包含大量实际案例研究,向您展示如何有效地解决一系列数据分析问题。在这个过程中你会学到熊猫、NumPy、IPython、Jupyter 的最新版本。
这本书由 Python 熊猫项目的创始人 Wes McKinney 撰写,是对 Python 中的数据科学工具的实用、现代介绍。它非常适合刚接触 Python 的分析师和刚接触数据科学和科学计算的 Python 程序员。