原文:
annas-archive.org/md5/4b0fd2cf0da7c8edae4b5ecfd40159bf
译者:飞龙
前言
正如标题所承诺的,本书将向你介绍世界上最流行的编程语言之一:Python。它旨在面向初学者和希望将 Python 添加到他们已经掌握的语言中的更有经验的程序员。
在大多数情况下,学习计算机语言比学习人类语言更容易。没有那么多歧义和例外需要记住。Python 是最一致和清晰的计算机语言之一。它平衡了学习的简易性、使用的便捷性和表达力量。
计算机语言由数据(类似口语中的名词)和指令或代码(类似动词)组成。你需要两者兼具。在交替的章节中,你将被介绍 Python 的基本代码和数据结构,学会如何结合它们,并逐步构建更高级的结构。你阅读和编写的程序将变得越来越长、越来越复杂。用木工的类比来说,我们将从锤子、钉子和木片开始。在本书的前半部分,我们将引入更多专业的组件,直到等同于车床和其他电动工具的水平。
你不仅会学习语言,还会学习如何使用它。我们将从 Python 语言及其“一揽子”标准库开始,但我也会向你展示如何查找、下载、安装和使用一些优秀的第三方包。我强调的是我在超过 10 年的 Python 开发中实际发现有用的东西,而不是边缘话题或复杂的技巧。
尽管这是一个介绍,但我会包含一些高级主题,因为我希望向你们介绍。像数据库和网络这样的领域仍然涵盖在内,但技术变化很快。现在,一个 Python 程序员可能期望了解云计算、机器学习或事件流等内容。在这里,你将找到所有这些内容。
Python 具有一些特殊的功能,它们比从其他语言适应的风格更有效。例如,使用for
和迭代器比手动递增某些计数器变量更直接地进行循环。
当你学习新东西时,很难分辨哪些术语是特定的而不是口头的,哪些概念实际上很重要。换句话说,“这个会出现在考试中吗?”我会突出显示在 Python 中具有特定含义或重要性的术语和思想,但不会一次性介绍太多。真实的 Python 代码早早就包含在内。
注意
当有些内容可能令人困惑或者存在更适合Pythonic的解决方式时,我会包含这样的注释。
Python 并不完美。我会向你展示一些看似奇怪或应该避免的东西,并提供你可以使用的替代方案。
有时,我的一些观点(例如对象继承,或者用于 Web 的 MVC 和 REST 设计)可能与常见的智慧有所不同。看看你的想法。
受众
本书适用于任何对学习世界上最流行的计算语言之一感兴趣的人,无论您以前是否学过任何编程。
第二版的变化
与第一版相比有什么变化?
-
还有大约一百页,包括猫的图片。
-
两倍于之前的章节,每一章现在都更短。
-
一个早期专门讨论数据类型、变量和名称的章节。
-
新的标准 Python 功能,如 f-strings。
-
新的或改进的第三方软件包。
-
全书穿插着新的代码示例。
-
一个关于基本硬件和软件的附录,适用于新程序员。
-
一个关于 asyncio 的附录,适用于不那么新的程序员。
-
“新的堆栈”覆盖范围:容器、云、数据科学和机器学习。
-
关于如何在 Python 编程中找到工作的提示。
什么没有改变?使用糟糕诗歌和鸭子的示例。这些是不朽的。
大纲
第一部分(第 1-11 章)解释了 Python 的基础知识。您应该按顺序阅读这些章节。我从最简单的数据和代码结构开始工作,逐步将它们结合到更详细和现实的程序中。第二部分(第 12-22 章)展示了 Python 在特定应用领域(如网络、数据库、网络等)中的使用方式;您可以按任意顺序阅读这些章节。
这里是章节和附录的简要预览,包括一些您将在其中遇到的术语:
第一章,Python 的一点味道
计算机程序与您每天看到的指南并没有太大的区别。一些小的 Python 程序让您一窥该语言的外观、能力和在现实世界中的用途。您将看到如何在其交互式解释器(或shell)中运行 Python 程序,或者从计算机上保存的文本文件中运行。
第二章,数据:类型、值、变量和名称
计算机语言将数据和指令混合在一起。不同的数据类型会以不同的方式被计算机存储和处理。它们可能允许它们的值被更改(可变的)或不允许(不可变的)。在 Python 程序中,数据可以是文字(例如 78
这样的数字,文本 字符串,例如 "waffle"
)或由命名的变量表示。Python 将变量视为名称,这与许多其他语言不同,并且具有一些重要的后果。
第三章,数字
本章展示了 Python 的最简单的数据类型:布尔、整数 和 浮点 数。您还将学习基本的数学运算。示例使用 Python 的交互式解释器就像一个计算器一样。
第四章,使用 if 进行选择
我们将在几章之间往返于 Python 的名词(数据类型)和动词(程序结构)。Python 代码通常一次运行一行,从程序的开头到结尾。if
代码结构允许您根据某些数据比较运行不同的代码行。
第五章,文本字符串
回到名词和文本字符串的世界。学习如何创建、组合、更改、检索和打印字符串。
第六章,循环:while 和 for
再谈动词,以及创建循环的两种方法:for
和 while
。您将了解到 Python 的核心概念:迭代器。
第七章,元组和列表
现在是 Python 更高级内置数据结构的第一步:列表
和 元组
。这些是值序列,就像 LEGO 一样,用于构建更复杂的数据结构。通过迭代器快速浏览它们,并通过推导式快速构建列表。
第八章,字典和集合
字典(又称dicts)和集合允许您通过其值而不是位置保存数据。这是非常方便的功能,也将成为您喜爱的 Python 特性之一。
第九章,函数
编织前几章的数据和代码结构,以便比较、选择或重复。将代码打包在函数中,并使用异常处理错误。
第十章,哦哦:对象和类
对象这个词有点模糊,在许多计算机语言中都很重要,包括 Python。如果您在其他语言中做过面向对象编程,Python 会更加灵活。本章解释如何使用对象和类,以及在何时更好地使用替代方法。
第十一章,模块、包和好东西
本章演示如何扩展到更大的代码结构:模块、包 和 程序。您将了解到代码和数据的存放位置,如何输入和输出数据,处理选项,浏览 Python 标准库,并略观未来。
第十二章,整理和操纵数据
学会像专家一样管理(或操纵)数据。本章涵盖文本和二进制数据,Unicode 字符的乐趣以及正则表达式文本搜索。还介绍了字节和字节数组这两种数据类型,它们是字符串的对应物,包含的是原始的二进制值而不是文本字符。
第十三章,日历和时钟
处理日期和时间可能会很混乱。本章展示了常见问题和实用解决方案。
第十四章,文件和目录
基本数据存储使用文件和目录。本章将向您展示如何创建和使用它们。
第十五章,时间数据:进程和并发
这是第一个硬核系统章节。它的主题是时间数据——如何使用程序、进程和线程同时执行多项任务(并发)。提到了 Python 最近的async增强功能,详细信息在附录 C 中。
第十六章,盒中的数据:持久存储
数据可以通过基本的平面文件和文件系统中的目录进行存储和检索。它们通过常见的文本格式(如 CSV、JSON 和 XML)获得一些结构。随着数据变得更大更复杂,它们需要传统的 关系型 数据库和一些较新的 NoSQL 数据存储提供的服务。
第 17 章, 空间中的数据:网络
通过 网络 和 服务,使用 协议 和 API 将您的代码和数据发送到空间。示例涵盖从低级别的 TCP 套接字 到 消息传递 库和排队系统,再到 云 部署。
第 18 章, Web, 解析
Web 有专门的章节——客户端、服务器、API 和框架。您将会 爬取 和 抓取 网站,并使用 请求 参数和 模板 构建真实的网站。
第 19 章, 成为 Python 爱好者
本章包含 Python 开发者的技巧——使用 pip
和 virtualenv
进行安装、使用集成开发环境、测试、调试、日志记录、源代码控制和文档编写。它还帮助您找到并安装有用的第三方软件包,将您自己的代码打包以供重用,并了解获取更多信息的途径。
第 20 章, Py 艺术
人们在艺术领域正在用 Python 做一些很酷的事情:图形、音乐、动画和游戏。
第 21 章, Py 在工作中
Python 在商业领域有特定的应用:数据可视化(绘图、图表和地图)、安全性和监管。
第 22 章, Py Sci
在过去的几年中,Python 已经成为科学领域的顶级语言:数学和统计学、物理科学、生物科学和医学。数据科学 和 机器学习 是显著的优势。本章涉及 NumPy、SciPy 和 Pandas。
附录 A, 初学者的硬件和软件
如果您是编程新手,这部分描述了硬件和软件的实际工作原理。它介绍了一些术语,您将经常遇到它们。
附录 B, 安装 Python 3
如果您的计算机上还没有安装 Python 3,本附录将向您展示如何安装,无论您使用的是 Windows、macOS、Linux 还是其他 Unix 变种。
附录 C, 截然不同的事情:异步
Python 在不同版本中不断添加异步特性,这些特性并不容易理解。我会在各个章节中提到它们,但详细讨论留在附录中。
附录 D, 练习答案
这里包含了章节末尾练习的答案。在您自己尝试练习之前,请勿偷看,否则可能会变成一只螃蟹。
附录 E, 速查表
本附录包含了作为快速参考使用的速查表。
Python 版本
随着开发人员增加功能并修复错误,计算机语言随时间而变化。本书中的示例是在运行 Python 3.7 时编写和测试的。在本书编辑时,版本 3.7 是最新版本。我会谈一谈它的显著增强功能。版本 3.8 预计将于 2019 年底正式发布,并将包含一些预期功能。如果您想了解 Python 的新功能及其发布时间,请查看Python 的新特性页面。这是一个技术参考,当您刚开始使用 Python 时可能会感到有些繁重,但如果未来需要在不同 Python 版本的计算机上运行程序,它可能会有用。
本书使用的约定
本书使用以下排版约定:
斜体
表示新术语、网址、电子邮件地址、文件名和文件扩展名。
等宽字体
用于程序清单,以及段落中引用程序元素,如变量、函数和数据类型。
恒定宽度加粗
显示用户应直接输入的命令或其他文本。
等宽斜体
显示应替换为用户提供值或根据上下文确定值的文本。
注意
这个图标表示提示、建议或一般说明。
警告
这个图标表示警告或注意事项。
使用代码示例
本书中的大量代码示例和练习可以在线下载。本书旨在帮助您完成工作。通常情况下,您可以在您的程序和文档中使用本书中的代码,无需征得我们的许可。除非您复制了本书中的大部分代码,否则无需许可。例如,编写一个使用本书中几个代码块的程序不需要许可。销售或分发奥莱利图书中的示例需要许可。引用本书并引用示例代码回答问题不需要许可。将本书中大量示例代码整合到产品文档中需要许可。
我们感谢但不需要归属。归属通常包括标题、作者、出版商和 ISBN。例如:“Python 入门,作者 Bill Lubanovic(奥莱利)。版权 2020 年 Bill Lubanovic,978-1-492-05136-7。”
如果您觉得使用代码示例超出了公平使用范围或这里给出的权限,请随时联系我们,邮箱是 permissions@oreilly.com。
奥莱利在线学习
注意
奥莱利媒体(O’Reilly Media)已经提供了 40 多年的技术和商业培训、知识和见解,帮助企业取得成功。
我们独特的专家和创新者网络通过书籍、文章、会议以及我们的在线学习平台分享他们的知识和专长。奥莱利的在线学习平台为您提供按需访问的实时培训课程、深度学习路径、交互式编码环境,以及来自奥莱利及其他 200 多家出版商的大量文本和视频。欲了解更多信息,请访问http://oreilly.com。
如需联系我们
请将有关本书的评论和问题发送至出版商:
-
奥莱利传媒公司
-
1005 Gravenstein Highway North
-
Sebastopol, CA 95472
-
800-998-9938(美国或加拿大)
-
707-829-0515(国际或当地)
-
707-829-0104(传真)
我们为这本书设立了一个网页,列出勘误、示例和任何额外信息。您可以访问此页面:https://oreil.ly/introducing-python-2e。
欲就本书提出评论或技术问题,请发送电子邮件至bookquestions@oreilly.com。
欲了解更多关于我们的图书、课程、会议和新闻,请访问我们的网站:http://www.oreilly.com。
在 Facebook 上找到我们:http://facebook.com/oreilly
在 Twitter 上关注我们:http://twitter.com/oreillymedia
观看我们的 YouTube 频道:http://www.youtube.com/oreillymedia
致谢
衷心感谢帮助使这本书更好的审稿人和读者们:
Corbin Collins, Charles Givre, Nathan Stocks, Dave George, and Mike James
第一部分:Python 基础
第一章:Python 的一点味道
只有丑陋的语言才会流行。Python 是唯一的例外。
唐纳德·克努斯
谜
让我们从两个迷你谜题及其解决方案开始。你认为以下两行文字是什么意思?
(Row 1): (RS) K18,ssk,k1,turn work.
(Row 2): (WS) Sl 1 pwise,p5,p2tog,p1,turn.
它看起来很技术化,像某种计算机程序。实际上,它是一个编织图案;具体来说,是一个描述如何转弯袜子的脚后跟的片段,就像图 1-1 中的那只袜子一样。
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0101.png
图 1-1. 针织袜子
这对我来说一点都不合理,就像数独对我的猫一样,但我的妻子完全理解。如果你是一个编织者,你也会的。
让我们尝试另一个神秘的文本,发现在一张索引卡上。你会立即理解它的目的,尽管你可能不知道它的最终产品:
1/2 c. butter or margarine
1/2 c. cream
2 1/2 c. flour
1 t. salt
1 T. sugar
4 c. riced potatoes (cold)
Be sure all ingredients are cold before adding flour.
Mix all ingredients.
Knead thoroughly.
Form into 20 balls. Store cold until the next step.
For each ball:
Spread flour on cloth.
Roll ball into a circle with a grooved rolling pin.
Fry on griddle until brown spots appear.
Turn over and fry other side.
即使你不会做饭,你可能也认出了它是一个食谱¹:一份食物配料列表,后面是制作指南。但它制作的是什么?这是lefse,一种类似于玉米饼的挪威美食(见图 1-2)。涂上一些黄油和果酱或你喜欢的任何东西,卷起来,享受吧。
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0102.png
图 1-2. Lefse
编织图案和食谱共享一些特征:
-
一种常规的词汇,包括单词、缩写和符号。有些可能很熟悉,其他的可能令人困惑。
-
关于可以说什么和在哪里说的语法规则。
-
需要按顺序执行的操作序列。
-
有时,会重复执行一些操作(一个循环),比如油煎每片 lefse 的方法。
-
有时,参考另一个操作序列(在计算机术语中,是一个函数)。在食谱中,你可能需要参考另一个关于土豆泥的食谱。
-
假设你对上下文有所了解。这个配方假设你知道水是什么,以及如何把它煮沸。这个编织图案假设你能够编织和针织而不经常刺伤自己。
-
一些要使用、创建或修改的数据——土豆和纱线。
-
用于处理数据的工具——锅、搅拌机、烤箱、编织棒。
-
一个预期的结果。在我们的例子中,是给你的脚和你的胃的东西。只是别把它们搞混了。
无论你如何称呼它们——成语、行话、小语言——你都会在各处看到它们的例子。这些行话为懂它的人节省了时间,而对我们其他人则使事情变得神秘。试着解密一篇关于桥牌的报纸专栏,如果你不玩这个游戏,或者解释一篇科学论文,如果你不是科学家(甚至如果你是,但在不同领域)。
小程序
你会在计算机程序中看到所有这些想法,它们本身就像小语言,专门为人类告诉计算机要做什么而设计。我用编织图案和食谱来说明编程并不神秘。这在很大程度上取决于学习正确的词汇和规则。
现在,如果单词和规则不多,并且你不需要一次学习太多东西,这会帮助很多。我们的大脑一次只能记住那么多东西。
让我们最终看一个真正的计算机程序(示例 1-1)。你觉得这个程序做什么?
示例 1-1. countdown.py
for countdown in 5, 4, 3, 2, 1, "hey!":
print(countdown)
如果你猜测这是一个 Python 程序,它打印以下行
5
4
3
2
1
hey!
那么你就知道 Python 比食谱或编织图案更容易学习。而且你可以在舒适和安全的桌子旁练习编写 Python 程序,远离热水和尖锐的棍子的危险。
Python 程序有一些特殊的单词和符号——for
、in
、print
、逗号、冒号、括号等等,这些是语言语法(规则)的重要部分。好消息是,Python 具有更好的语法,而且需要记住的东西比大多数计算机语言少。它似乎更自然——几乎像一个食谱。
示例 1-2 是另一个微小的 Python 程序;它从一个 Python 列表 中选择一个哈利波特法术并打印它。
示例 1-2. spells.py
spells = [
"Riddikulus!",
"Wingardium Leviosa!",
"Avada Kedavra!",
"Expecto Patronum!",
"Nox!",
"Lumos!",
]
print(spells[3])
个体法术是 Python 字符串(文本字符序列,用引号括起来)。它们由逗号分隔,并且放在由方括号([
和]
)定义的 Python 列表中。单词spells
是一个变量,给列表命名,这样我们就可以对其进行操作。在这种情况下,程序会打印第四个法术:
Expecto Patronum!
为什么我们说3
,如果我们想要第四个?Python 列表(如spells
)是从列表开始的值的序列,通过它们从列表开始的偏移量来访问。第一个值在偏移量0
处,第四个值在偏移量3
处。
注意
人们从 1 开始计数,所以从 0 开始计数可能看起来很奇怪。以偏移量而不是位置来思考有助于理解。是的,这是计算机程序有时与普通语言用法不同的示例。
列表在 Python 中是非常常见的数据结构,并且第七章展示了如何使用它们。
示例 1-3 中的程序打印了一个 Three Stooges 的引用,但是通过说它的人而不是在列表中的位置来引用。
示例 1-3. quotes.py
quotes = {
"Moe": "A wise guy, huh?",
"Larry": "Ow!",
"Curly": "Nyuk nyuk!",
}
stooge = "Curly"
print(stooge, "says:", quotes[stooge])
如果你运行这个小程序,它会打印以下内容:
Curly says: Nyuk nyuk!
quotes
是一个变量,它命名了一个 Python 字典——一组唯一的键(在这个示例中是三个小丑的名字)和相关的值(这里是这些小丑的著名说法)。使用字典,你可以通过名称存储和查找东西,这通常是列表的有用替代方法。
spells
示例使用方括号([
和]
)创建了一个 Python 列表,而quotes
示例使用花括号({
和}
,与 Curly 无关)创建了一个 Python 字典。此外,冒号(:
)用于将字典中的每个键与其值关联起来。你可以在第八章中读到更多关于字典的内容。
希望这不会让你一下子记住太多语法规则。在接下来的几章中,你将逐步了解更多这些小规则。
更大的程序
现在,让我们完全不同的内容:示例 1-4 展示了一个执行更复杂任务的 Python 程序。暂时不要期望能理解程序的工作原理;这正是本书的目的!它的目的是让你了解典型的非平凡 Python 程序的外观和感觉。如果你懂其他编程语言,可以评估一下 Python 的差异。即使还不了解 Python,你能在阅读程序解释之前大致猜到每行代码的作用吗?你已经见过 Python 列表和字典的示例,这里还包含了更多功能。
在本书早期版本中,示例程序连接到 YouTube 网站并获取其最受欢迎视频的信息,如“查理咬我的手指”。这个功能在第二版印刷后不久停止工作。那时,Google 停止支持此服务,导致标志性的示例程序无法继续运行。我们的新 示例 1-4 前往另一个网站,这个网站应该能持续更长时间——位于 互联网档案馆 的 Wayback Machine,这是一个免费服务,保存了二十年来数十亿个网页(以及电影、电视节目、音乐、游戏和其他数字文物)。你将在 第 18 章 中看到更多这样的 网络 API 示例。
程序会要求你输入一个 URL 和一个日期。然后,它询问 Wayback Machine 是否在那个日期左右保存了该网站的副本。如果找到了,它将把信息返回给 Python 程序,打印出 URL 并在你的网络浏览器中显示。这个例子展示了 Python 如何处理各种任务:获取你输入的内容、通过互联网与网站进行通信、获取一些内容、从中提取一个 URL,并说服你的网络浏览器显示该 URL。
如果我们得到一个充满 HTML 格式文本的普通网页,我们需要弄清如何显示它,这是一项需要交给网络浏览器处理的大量工作。我们也可以尝试提取我们想要的部分(详见 第 18 章 关于 网页抓取 的更多细节)。无论选择哪种方式,都会增加工作量和程序的复杂度。相反,Wayback Machine 返回的数据是 JSON 格式。JSON(JavaScript 对象表示法)是一种人类可读的文本格式,描述了其中数据的类型、值和顺序。它是另一种小语言,已成为不同计算机语言和系统之间交换数据的流行方式。你将在 第 12 章 进一步了解 JSON。
Python 程序可以将 JSON 文本转换为 Python 数据结构 —— 就像你自己编写程序创建它们一样。我们的小程序只选择了一部分(来自 Internet Archive 网站的旧页面的 URL)。再次说明,这是一个完整的 Python 程序,你可以自己运行。我们只包含了少量的错误检查,只是为了保持示例的简洁。行号不是程序的一部分;它们包含在内,以帮助你跟随我们在程序后提供的描述。
示例 1-4. archive.py
1 import webbrowser
2 import json
3 from urllib.request import urlopen
4
5 print("Let's find an old website.")
6 site = input("Type a website URL: ")
7 era = input("Type a year, month, and day, like 20150613: ")
8 url = "http://archive.org/wayback/available?url=%s×tamp=%s" % (site, era)
9 response = urlopen(url)
10 contents = response.read()
11 text = contents.decode("utf-8")
12 data = json.loads(text)
13 try:
14 old_site = data["archived_snapshots"]["closest"]["url"]
15 print("Found this copy: ", old_site)
16 print("It should appear in your browser now.")
17 webbrowser.open(old_site)
18 except:
19 print("Sorry, no luck finding", site)
这个小 Python 程序在几行中做了很多事情,而且相当易读。你现在还不知道所有这些术语,但在接下来的几章中你会了解到。以下是每一行正在发生的事情:
-
导入(使这个程序可用)来自 Python 标准库模块名为
webbrowser
的所有代码。 -
导入 Python 标准库模块
json
的所有代码。 -
仅从标准库模块
urllib.request
中导入urlopen
函数。 -
一个空行,因为我们不想感到拥挤。
-
将一些初始文本打印到您的显示器上。
-
打印一个关于 URL 的问题,读取你输入的内容,并保存在一个名为
site
的程序 变量 中。 -
打印另一个问题,这次读取年、月和日,并将其保存在名为
era
的变量中。 -
构造一个名为
url
的字符串变量,以使 Wayback Machine 查找您输入的站点和日期的副本。 -
连接到该 URL 的 Web 服务器并请求特定的Web 服务。
-
获取响应数据并将其分配给变量
contents
。 -
解码
contents
为 JSON 格式的文本字符串,并将其分配给变量text
。 -
将
text
转换为data
—— Python 数据结构。 -
错误检查:
try
运行接下来的四行,如果任何一个失败,则运行程序的最后一行(在except
之后)。 -
如果我们找到了这个站点和日期的匹配项,则从一个三级 Python 字典中提取其值。注意,这一行和接下来的两行是缩进的。这就是 Python 如何知道它们属于前面的
try
行。 -
打印我们找到的 URL。
-
打印下一行执行后会发生什么。
-
在你的浏览器中显示我们找到的 URL。
-
如果前面的四行有任何失败,Python 跳到这里。
-
如果失败了,打印一条消息和我们正在查找的站点。这是缩进的,因为它只能在前面的
except
行运行时运行。
当我在终端窗口中运行这个命令时,我输入了一个站点 URL 和一个日期,并得到了这段文本输出:
$ python archive.py
Let's find an old website.
Type a website URL: lolcats.com
Type a year, month, and day, like 20150613: 20151022
Found this copy: http://web.archive.org/web/20151102055938/http://www.lolcats.com/
It should appear in your browser now.
并且图 1-3 显示了在我的浏览器中显示的内容。
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0103.png
图 1-3. 来自 Wayback Machine
在前面的示例中,我们使用了 Python 的一些标准库模块(安装 Python 时包含的程序),但它们并非神圣不可侵犯。Python 还拥有大量优秀的第三方软件。示例 1-5 是一个重写,使用名为requests
的外部 Python 软件包访问 Internet Archive 网站。
示例 1-5. archive2.py
1 import webbrowser
2 import requests
3
4 print("Let's find an old website.")
5 site = input("Type a website URL: ")
6 era = input("Type a year, month, and day, like 20150613: ")
7 url = "http://archive.org/wayback/available?url=%s×tamp=%s" % (site, era)
8 response = requests.get(url)
9 data = response.json()
10 try:
11 old_site = data["archived_snapshots"]["closest"]["url"]
12 print("Found this copy: ", old_site)
13 print("It should appear in your browser now.")
14 webbrowser.open(old_site)
15 except:
16 print("Sorry, no luck finding", site)
新版本更短,我猜对大多数人来说更易读。您将在 第十八章 中详细了解requests
,以及在 第十一章 中了解 Python 的外部编写软件。
Python 在现实世界中
那么,学习 Python 是否值得时间和精力?Python 自 1991 年以来就存在(比 Java 老,比 C 新),一直稳居最受欢迎的五大计算机语言之列。人们受雇编写 Python 程序——比如您每天使用的严肃内容,如 Google、YouTube、Instagram、Netflix 和 Hulu。我在许多领域的生产应用中都使用了它。Python 以其高效率而著称,适合快节奏的组织。
您会发现 Python 存在于许多计算环境中,包括以下几种:
-
监视器或终端窗口中的命令行
-
包括 Web 在内的图形用户界面(GUI)
-
Web 的客户端和服务器端
-
支持大型流行站点的后端服务器
-
云(由第三方管理的服务器)
-
移动设备
-
嵌入式设备
Python 程序从一次性脚本(比如本章中到目前为止看到的那些)到百万行系统都有。
2018 年 Python 开发者调查 提供了有关 Python 在计算世界中当前地位的数据和图表。
我们将看到它在网站、系统管理和数据处理中的应用。在最后的章节中,我们将看到 Python 在艺术、科学和商业中的具体用途。
Python 与来自 X 星球的语言相比
Python 与其他语言相比如何?在何时何地选择一种而不是另一种?在本节中,我展示了其他语言的代码示例,这样您就可以看到竞争情况。如果您没有使用过这些语言,您不需要理解它们。(当您看到最后的 Python 示例时,您可能会因为没有必要与其他语言一起工作而感到宽慰。)
每个程序都应打印一个数字,并对语言做简要介绍。
如果您使用终端或终端窗口,那么读取您输入的内容并运行它并显示结果的程序称为shell程序。Windows 的 shell 称为cmd
,它运行带有后缀.bat
的批处理文件。Linux 和其他类 Unix 系统(包括 macOS)有许多 shell 程序。最流行的称为bash
或sh
。Shell 具有简单的功能,如简单逻辑和将通配符符号如*
扩展为文件名。您可以将命令保存在称为shell 脚本的文件中,以后运行它们。这些可能是您作为程序员遇到的第一个程序。问题在于 shell 脚本在超过几百行后不容易扩展,并且比其他语言慢得多。下一段显示了一个小的 shell 程序:
#!/bin/sh
language=0
echo "Language $language: I am the shell. So there."
如果您将这段代码保存为test.sh
并用sh test.sh
运行它,您会在显示器上看到以下内容:
Language 0: I am the shell. So there.
旧有的C和C++是相当底层的语言,当速度最重要时使用。您的操作系统及其许多程序(包括计算机上的python
程序)可能是用 C 或 C++编写的。
这两种语言更难学习和维护。您需要注意许多细节,如内存管理,这可能导致程序崩溃和难以诊断的问题。这是一个小的 C 程序:
#include <stdio.h>
int main(int argc, char *argv[]) {
int language = 1;
printf("Language %d: I am C! See? Si!\n", language);
return 0;
}
C++具有 C 家族的特征,但也发展了一些独特的功能:
#include <iostream>
using namespace std;
int main() {
int language = 2;
cout << "Language " << language << \
": I am C++! Pay no attention to my little brother!" << \
endl;
return(0);
}
Java和C#是 C 和 C++的继任者,避免了它们前辈的一些问题——特别是内存管理问题——但有时会显得有些冗长。接下来的示例展示了一些 Java:
public class Anecdote {
public static void main (String[] args) {
int language = 3;
System.out.format("Language %d: I am Java! So there!\n", language);
}
}
如果您还没有用过这些语言编写程序,您可能会想知道:所有那些东西是什么?我们只想打印一行简单的代码。一些语言带有大量的语法装备。您将在第二章中了解更多信息。
C、C++和 Java 是静态语言的示例。它们要求您在使用变量之前指定一些低级细节,如数据类型。附录 A 显示了像整数这样的数据类型在计算机中有特定数量的位,并且只能执行整数操作。相比之下,动态语言(也称为脚本语言)在使用变量之前不强制您声明变量类型。
多年来,通用的动态语言是Perl。Perl 非常强大,拥有广泛的库。然而,它的语法可能有些笨拙,在过去几年中似乎失去了向 Python 和 Ruby 的势头。这个例子向您展示了 Perl 的一个名言:
my $language = 4;
print "Language $language: I am Perl, the camel of languages.\n";
Ruby是一种较新的语言。它从 Perl 借鉴了一些,主要因为Ruby on Rails,一个 Web 开发框架而流行。它在很多和 Python 相同的领域中使用,选择其中之一可能归结为品味或者您特定应用程序的可用库。这是一个 Ruby 片段:
language = 5
puts "Language #{language}: I am Ruby, ready and aglow."
PHP,如下例所示,非常流行于 Web 开发,因为它方便地结合了 HTML 和代码。然而,PHP 语言本身有一些陷阱,并且在 Web 之外的通用语言中并不流行。这是它的样子:
<?PHP
$language = 6;
echo "Language $language: I am PHP, a language and palindrome.\n";
?>
Go(或者Golang,如果你想要在 Google 中搜索)是一种近来的语言,试图在高效和友好之间找到平衡:
package main
import "fmt"
func main() {
language := 7
fmt.Printf("Language %d: Hey, ho, let's Go!\n", language)
}
另一种现代的 C 和 C++的替代品是Rust:
fn main() {
println!("Language {}: Rust here!", 8)
还剩下谁?哦对了,Python:
language = 9
print(f"Language {language}: I am Python. What's for supper?")
为什么选择 Python?
其中一个原因,虽然不一定是最重要的,是它的流行度。通过各种指标来看,Python 是:
-
作为最快增长的主要编程语言,正如你在图 1-4 中看到的那样。
-
2019 年 6 月的TIOBE 指数的编辑们说:“本月 Python 在 TIOBE 指数中再次达到了历史新高的 8.5%。如果 Python 能保持这样的增长速度,它可能在 3 到 4 年内取代 C 和 Java,成为世界上最流行的编程语言。”
-
2018 年的年度编程语言(TIOBE),并且在IEEE Spectrum和PyPL中排名靠前。
-
在顶级美国学院中,最流行的初级计算机科学课程语言。
-
法国高中的官方教学语言。
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0104.png
图 1-4. Python 在主要编程语言增长中处于领先地位
近年来,在数据科学和机器学习领域变得极其流行。如果你想在一个有趣的领域找到一份高薪的编程工作,Python 现在是一个不错的选择。而且,如果你在招聘,有越来越多经验丰富的 Python 开发人员。
但是为什么它如此流行?编程语言并不是非常有吸引力。有哪些潜在原因呢?
Python 是一种优秀的通用高级语言。其设计使得它非常可读,这比听起来更重要。每个计算机程序只写一次,但要被多次阅读和修订,通常由多人完成。可读性高使得它更容易学习和记忆;因此,更可写。与其他流行的语言相比,Python 具有渐进的学习曲线,使您更快进入生产状态,但它又有深度,您可以随着经验的积累探索。
Python 相对简洁的语法使你能够写出比静态语言中等价程序更小的程序。研究表明,无论使用何种语言,程序员每天产生的代码行数大致相同,因此,减半代码行数就会使你的生产力翻倍,就这么简单。Python 是许多公司的“不那么秘密武器”,这些公司认为这很重要。
当然,Python 是免费的,无论是像啤酒(价格)还是言论(自由)。用 Python 写任何你想要的东西,在任何地方都可以自由使用。没有人能读懂你的 Python 程序然后说,“你写的这个小程序不错啊,要是出了什么问题就太可惜了。”
Python 几乎可以在任何地方运行,并且“电池已包括”——其标准库中包含大量有用的软件。本书介绍了许多标准库和有用的第三方 Python 代码示例。
但是,也许使用 Python 的最好理由是一个意外的理由:人们通常享受使用它进行编程,而不是把它看作是必要的恶来完成工作。它不会妨碍你。一个常见的说法是它“符合你的思维方式”。通常,开发人员会说,在需要使用其他语言时,他们会错过一些 Python 的设计。这将 Python 与其大多数同行区分开来。
为什么选择 Python?
Python 并不是适合每种情况的最佳语言。
Python 并非默认安装在所有地方。附录 B 向您展示如何在计算机上安装 Python(如果您尚未安装)。
对于大多数应用来说,Python 的速度已经足够快,但对于一些要求更高的应用来说可能还不够快。如果你的程序大部分时间都在计算东西(技术术语是CPU-bound),那么用 C、C++、C#、Java、Rust 或 Go 写的程序通常会比 Python 等效程序运行得更快。但并非总是如此!
这里有一些解决方案:
-
有时在 Python 中,一个更好的算法(逐步解决方案)胜过 C 中的一个低效算法。Python 更快的开发速度让你有更多时间尝试不同的解决方案。
-
在许多应用程序中(特别是 Web 应用程序),程序在等待来自网络上某个服务器的响应时会无所事事。CPU(中央处理单元,计算机的芯片,负责所有计算)几乎没有参与;因此,静态和动态程序的端到端时间会非常接近。
-
标准 Python 解释器是用 C 编写的,可以通过 C 代码进行扩展。我在第十九章中稍作讨论。
-
Python 解释器变得越来越快。Java 在早期非常慢,进行了大量的研究和资金投入来加快速度。Python 不归任何公司所有,因此其改进更为渐进。在“PyPy”中,我谈到了PyPy项目及其影响。
-
你可能有一个非常苛刻的应用程序,无论你做什么,Python 都无法满足你的需求。通常的替代方案是 C、C++ 和 Java。Go(感觉像 Python,但性能像 C)或 Rust 也值得一试。
Python 2 与 Python 3
一个中等大小的复杂性是 Python 有两个版本。Python 2 已经存在很长时间,预装在 Linux 和 Apple 计算机上。它是一种优秀的语言,但没有十全十美。在计算机语言中,像许多其他领域一样,有些错误是表面的,容易修复,而其他则很难。难以修复的问题是不兼容的:用这些问题编写的新程序将无法在旧的 Python 系统上工作,而在修复之前编写的旧程序将无法在新系统上工作。
Python 的创始人(Guido van Rossum)和其他人决定将难以修复的问题一起打包,并在 2008 年作为 Python 3 引入。Python 2 是过去,Python 3 是未来。Python 2 的最终版本是 2.7,它将在一段时间内存在,但这是终点;不会有 Python 2.8。Python 2 的语言支持终止日期是 2020 年 1 月。安全和其他修复将不再进行,许多知名的 Python 软件包将在那时放弃对 Python 2 的支持。操作系统很快将放弃 Python 2 或将 Python 3 设为默认版本。流行的 Python 软件向 Python 3 的转换是逐步进行的,但现在我们已经远远超过了临界点。所有新的开发将使用 Python 3。
本书关注的是 Python 3。它几乎与 Python 2 相同。最明显的变化是在 Python 3 中print
是一个函数,因此你需要用括号将其参数括起来。最重要的变化是对Unicode字符的处理,这在第十二章中有详细介绍。我会在其他重要的不同点出现时指出它们。
安装 Python
为了不使本章混乱,你可以在附录 B 中找到关于如何安装 Python 3 的详细信息。如果你没有 Python 3,或者不确定,去那里看看你需要为你的计算机做什么。是的,这有点麻烦(具体来说,是右前部的麻烦),但你只需做一次。
运行 Python
安装了 Python 3 的工作副本后,你可以用它来运行本书中的 Python 程序以及你自己的 Python 代码。你到底如何运行 Python 程序?主要有两种方式:
-
Python 的内置交互解释器(也称为shell)是尝试小型程序的简便方法。你逐行输入命令并立即看到结果。由于输入和显示紧密耦合,你可以更快地进行实验。我将使用交互解释器演示语言特性,你也可以在自己的 Python 环境中输入相同的命令。
-
对于其他所有内容,将你的 Python 程序存储在文本文件中,通常使用*.py*扩展名,并通过输入
python
后跟这些文件名来运行它们。
让我们现在尝试这两种方法。
使用交互式解释器
本书中的大多数代码示例使用内置的交互式解释器。当你输入与示例中相同的命令并获得相同的结果时,你就知道你走在正确的道路上了。
你可以通过在计算机上输入主 Python 程序的名称来启动解释器:它应该是python
、python3
或类似的名称。在本书的其余部分,我们假设它称为python
;如果你的命名不同,请在代码示例中看到python
时输入那个名称。
交互式解释器几乎与 Python 文件的工作方式完全相同,只有一个例外:当你输入有值的东西时,交互式解释器会自动为你打印其值。这不是 Python 语言的一部分,只是解释器的一个特性,可以帮助你省去每次都输入print()
的麻烦。例如,如果你在解释器中输入数字27
,它将被回显到你的终端(如果你在文件中有一行27
,Python 不会感到沮丧,但当你运行程序时不会看到任何打印输出):
$ python
Python 3.7.2 (v3.7.2:9a3ffc0492, Dec 24 2018, 02:44:43)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 27
27
注意
在上一个示例中,$
是一个样本系统提示符,用于在终端窗口中输入类似python
的命令。我们在本书的代码示例中使用它,尽管你的提示符可能不同。
顺便说一句,在解释器中随时使用print()
也可以打印输出:
>>> print(27)
27
如果你在交互式解释器中尝试了这些示例,并看到了相同的结果,那么你刚刚运行了一些真实(虽然很小)的 Python 代码。在接下来的几章中,你将从一行代码逐步过渡到更长的 Python 程序。
使用 Python 文件
如果你将27
单独放在一个文件中并通过 Python 运行它,它会运行,但不会打印任何东西。在正常的非交互式 Python 程序中,你需要调用print
函数来打印输出:
print(27)
让我们创建一个 Python 程序文件并运行它:
-
打开你的文本编辑器。
-
输入
print(27)
这一行,就像这里显示的一样。 -
将此保存为名为test.py的文件。确保将其保存为纯文本而不是 RTF 或 Word 等“丰富”格式。你不需要为 Python 程序文件使用*.py*后缀,但这有助于你记住它们的类型。
-
如果你使用的是 GUI——几乎所有人都是——打开一个终端窗口。²
-
通过输入以下内容来运行你的程序:
$ python test.py
你应该看到一行输出:
27
成功了吗?如果是这样,恭喜你运行了你的第一个独立 Python 程序。
下一步是什么?
你将向一个真实的 Python 系统输入命令,它们需要遵循合法的 Python 语法。与一次性倾泻语法规则不同,我们将在接下来的几章中逐步介绍它们。
开发 Python 程序的基本方法是使用纯文本编辑器和终端窗口。我在本书中使用纯文本显示,有时显示交互式终端会话,有时显示 Python 文件片段。你应该知道还有许多优秀的 集成开发环境(IDE)适用于 Python。这些可能具有带有高级文本编辑和帮助显示的 GUI。你可以在第十九章中了解其中一些的详细信息。
你的禅意时刻
每种计算机语言都有自己的风格。在前言中,我提到过表达自己的 Pythonic 方法通常会有。Python 中嵌入了一点自由诗,简洁地表达了 Python 的哲学(据我所知,Python 是唯一一个包含这样一个彩蛋的语言)。只需在交互式解释器中键入 import this
,然后在需要这个禅意时按下回车键:
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one--and preferably only one--obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea--let's do more of those!
我会在整本书中举例说明这些情感。
即将发生的事情
下一章将讨论 Python 的数据类型和变量。这将为接下来的章节做准备,这些章节将详细介绍 Python 的数据类型和代码结构。
要做的事情
本章是 Python 语言的介绍——它的功能、外观以及它在计算世界中的位置。在每章末尾,我建议一些迷你项目,帮助你记住你刚刚阅读的内容,并为即将到来的内容做好准备。
1.1 如果你的计算机上还没有安装 Python 3,请立即安装。阅读附录 B 获取有关你的计算机系统的详细信息。
1.2 启动 Python 3 交互式解释器。同样的详情请参阅附录 B。它应该打印几行关于自身的信息,然后是一行以 >>>
开头的单行。这就是你输入 Python 命令的提示。
1.3 与解释器玩一会儿。像使用计算器一样使用它,键入 8 * 9
。按回车键查看结果。Python 应该打印 72
。
1.4 输入数字 47
,然后按回车键。下一行是否打印出 47
?
1.5 现在,键入 print(47)
,然后按 Enter 键。这也在下一行为你打印出 47
吗?
¹ 通常只在食谱书和舒适的推理小说中找到。
² 如果你不确定这意味着什么,请参阅附录 B 获取不同操作系统的详细信息。
第二章:数据:类型、值、变量和名称
选一个好名字胜过大财。
箴言 22:1
在计算机底层,一切都只是一串比特(见附录 A)。计算的一个洞察是,我们可以按任意方式解释这些比特——作为不同大小和类型的数据(数字、文本字符),甚至是计算机代码本身。我们使用 Python 来定义这些比特的块,以及在 CPU 中的输入输出。
我们从 Python 的数据类型和它们可以包含的值开始。然后我们看如何将数据表示为字面值和变量。
Python 数据是对象。
你可以将计算机的内存视为一系列长长的货架。每个货架上的插槽宽度为一个字节(八比特),编号从 0(第一个)到末尾。现代计算机具有数十亿字节的内存(吉字节),因此这些货架将填满一个巨大的虚构仓库。
你的操作系统为 Python 程序提供对计算机内存的一些访问权限。该内存用于程序本身的代码以及它使用的数据。操作系统确保程序无法读取或写入其他内存位置而不通过某种方式获得权限。
程序跟踪它们的比特位于何处(内存位置),以及它们是什么类型(数据类型)。对于计算机来说,一切都只是比特。同样的比特模式可能表示整数 65
或文本字符 A
。
不同类型可能使用不同数量的比特。当你听说“64 位机器”时,这意味着整数使用 64 比特(8 字节)。
有些语言在内存中直接取出和拨弄这些原始值,并跟踪它们的大小和类型。Python 不直接处理这些原始数据值,而是将每个数据值包装在内存中作为一个对象——布尔值、整数、浮点数、字符串,甚至大型数据结构、函数和程序。关于如何在 Python 中定义自己的对象,有一个完整的章节(第十章)。但现在,我们只是讨论处理基本内置数据类型的对象。
使用内存货架的类比,你可以把对象想象成变量大小的盒子,占据这些货架上的空间,如图 2-1 所示。Python 制作这些对象盒子,将它们放在货架上的空位上,并在它们不再使用时移除它们。
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0201.png
图 2-1. 一个对象就像一个盒子;这个是一个值为7
的整数。
在 Python 中,对象是一个包含至少以下内容的数据块:
-
定义对象能做什么的type(见下一节)。
-
一个独特的id,以区别于其他对象。
-
与其类型一致的value。
-
reference count 跟踪这个对象被使用的频率。
它的标识就像它在架子上的位置,一个唯一的标识符。它的类型就像盒子上的工厂印记,说明它能做什么。如果一个 Python 对象是整数,它的类型是 int
,并且可以(除了其他事情,你将在第 3 章中看到)加到另一个 int
上。如果我们把盒子想象成由透明塑料制成,我们可以看到里面的值。在不久的几节中,当我们谈论变量和名称时,你将学到引用计数的用法。
类型
表 2-1 展示了 Python 中的基本数据类型。第二列(类型)包含该类型的 Python 名称。第三列(可变?)指示值在创建后是否可以更改,这在下一节中有更详细的解释。示例显示了该类型的一个或多个字面示例。最后一列(章节)指向本书中详细讲述该类型的章节。
表 2-1. Python 的基本数据类型
名称 | 类型 | 可变? | 示例 | 章节 |
---|---|---|---|---|
布尔值 | bool | 否 | True , False | 第 3 章 |
整数 | int | 否 | 47 , 25000 , 25_000 | 第 3 章 |
浮点数 | float | 否 | 3.14 , 2.7e5 | 第 3 章 |
复数 | complex | 否 | 3j , 5 + 9j | 第 22 章 |
文本字符串 | str | 否 | 'alas' , "alack" , '''a verse attack''' | 第 5 章 |
列表 | list | 是 | ['Winken', 'Blinken', 'Nod'] | 第 7 章 |
元组 | tuple | 否 | (2, 4, 8) | 第 7 章 |
字节 | bytes | 否 | b'ab\xff' | 第 12 章 |
字节数组 | bytearray | 是 | bytearray(...) | 第 12 章 |
集合 | set | 是 | set([3, 5, 7]) | 第 8 章 |
冻结集合 | frozenset | 否 | frozenset(['Elsa', 'Otto']) | 第 8 章 |
字典 | dict | 是 | {'game': 'bingo', 'dog': 'dingo', 'drummer': 'Ringo'} | 第 8 章 |
在这些基本数据类型的章节后,您将看到如何在第 10 章中创建新类型。
可变性
无物能长存,唯变性永恒。
Percy Shelley
类型还决定了盒子中包含的数据值是否可以更改(可变)或是常量(不可变)。把一个不可变对象想象成一个密封的盒子,但有透明的侧面,就像图 2-1;你可以看到值,但不能改变它。按同样的类比,可变对象就像有盖子的盒子:不仅可以看到里面的值,还可以改变它;但是,你不能改变它的类型。
Python 是强类型的,这意味着对象的类型即使其值是可变的,也不会改变(图 2-2)。
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0202.png
图 2-2. 强类型并不意味着要更用力按键
字面值
有两种方法在 Python 中指定数据值:
-
字面
-
变量
在接下来的章节中,你将看到如何为不同的数据类型指定字面值的详细信息——整数是一串数字,浮点数包含小数点,文本字符串被引号包围,等等。但是,在本章的其余部分,为了避免手指起茧,我们的示例将仅使用短小的十进制整数和一两个 Python 列表。十进制整数就像数学中的整数一样:从0
到9
的一串数字。还有一些额外的整数细节(例如符号和非十进制基数),我们将在第三章中讨论。
变量
现在,我们来到了计算语言的一个关键概念。
Python,像大多数计算机语言一样,允许您定义变量——即计算机内存中值的名称,您希望在程序中使用。
Python 变量名有一些规则:
-
它们只能包含以下这些字符:
-
小写字母(
a
到z
) -
大写字母(
A
到Z
) -
数字(
0
到9
) -
下划线(
_
)
-
-
它们是区分大小写的:
thing
、Thing
和THING
是不同的名称。 -
它们必须以字母或下划线开头,不能以数字开头。
-
以下划线开头的名称被特殊对待(你可以在第九章中阅读有关此处的内容)。
-
它们不能是 Python 的保留词(也称为关键字)之一。
保留词¹有:
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
在 Python 程序中,您可以找到保留词与
>>> help("keywords")
或:
>>> import keyword
>>> keyword.kwlist
以下是有效的名称:
-
a
-
a1
-
a_b_c___95
-
_abc
-
_1a
然而,这些名称是无效的:
-
1
-
1a
-
1_
-
name!
-
another-name
分配
在 Python 中,您使用 =
来分配一个值给一个变量。
注意
我们都在小学算术中学到了 =
的意思是等于。那么为什么许多计算机语言,包括 Python,使用 =
进行赋值?一个原因是标准键盘缺乏逻辑的替代品,如左箭头键,并且 =
似乎不会太令人困惑。另外,在计算机程序中,您使用分配的频率比测试相等要高得多。
程序不像代数。当您在学校学习数学时,您看到了这样的方程式:
y = x + 12
您可以通过“插入”一个值来解决方程式中的等式。如果您给 x
赋值 5
,5 + 12
就是 17
,所以 y
的值将是 17
。为 x
插入 6
以获得 y
的 18
,依此类推。
计算机程序行看起来可能像方程式,但它们的含义是不同的。在 Python 和其他计算机语言中,x
和 y
是变量。Python 知道像 12
或 5
这样的裸数字是字面整数。因此,这里有一个小小的 Python 程序模仿这个方程式,打印出 y
的结果值:
>>> x = 5
>>> y = x + 12
>>> y
17
这是数学和程序之间的重大区别:在数学中,=
表示两边的相等,但在程序中表示赋值:将右边的值赋给左边的变量。
在程序中,右侧的每一部分都需要有一个值(这称为初始化)。右侧可以是字面值,或者已经被赋值的变量,或者它们的组合。Python 知道5
和12
是字面整数。第一行将整数值5
赋给变量x
。现在我们可以在下一行中使用变量x
。当 Python 读取y = x + 12
时,它执行以下操作:
-
看到中间的
=
号 -
知道这是一个任务。
-
计算右侧(获取由
x
引用的对象的值并将其添加到12
) -
将结果分配给左侧变量
y
然后在交互解释器中输入变量y
的名称将打印其新值。
如果您在程序中以y = x + 12
开头,Python 会生成一个异常(一个错误),因为变量x
还没有值:
>>> y = x + 12
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
您将在第九章中详细了解异常。在计算机术语中,我们会说这个x
是未初始化的。
在代数中,您可以反向工作,并将一个值分配给y
来计算x
。要在 Python 中做到这一点,您需要在赋值给左侧的x
之前获取右侧的字面值和初始化变量。
>>> y = 5
>>> x = 12 - y
>>> x
7
变量是名称,不是位置
现在是时候提一个关于 Python 变量的关键点了:变量只是名称。这与许多其他计算机语言不同,这是 Python 特别要知道的一点,特别是当我们涉及到像列表这样的可变对象时。赋值不会复制一个值;它只是将一个名称绑定到包含数据的对象上。名称是对事物的引用,而不是事物本身。将名称想象成一个标签,附在计算机内存中的对象框上 (图 2-3)。
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0203.png
图 2-3. 名称指向对象(变量a
指向一个值为7
的整数对象)
在其他语言中,变量本身有一个类型,并绑定到一个内存位置。您可以更改该位置的值,但它必须是相同的类型。这就是静态语言要求您声明变量类型的原因。Python 不需要,因为一个名称可以引用任何东西,我们通过“跟随字符串”到数据对象本身来获取值和类型。这节省了时间,但也有一些缺点:
-
可能会拼错一个变量并且得到一个异常,因为它并不引用任何东西,而且 Python 并不像静态语言那样自动检查。第十九章展示了提前检查变量以避免这种情况的方法。
-
Python 的原始速度比像 C 这样的语言慢。它让计算机做更多的工作,这样你就不必亲自去做。
在交互解释器中尝试这个(在图 2-4 中可视化):
-
如前所述,将值
7
分配给名称a
。这创建了一个包含整数值7
的对象框。 -
打印
a
的值。 -
将
a
分配给b
,使b
也指向包含7
的对象盒子。 -
打印变量
b
的值:>>> a = 7 >>> print(a) 7 >>> b = a >>> print(b) 7
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0204.png
图 2-4. 复制一个名称(现在变量b
也指向相同的整数对象)
在 Python 中,如果您想知道任何东西的类型(变量或字面值),可以使用type(*`thing`*)
。 type()
是 Python 的内置函数之一。如果要检查变量是否指向特定类型的对象,请使用isinstance(*`type`*)
:
>>> type(7)
<class 'int'>
>>> type(7) == int
True
>>> isinstance(7, int)
True
注意
当我提到函数时,我会在其后加上括号(()
),以强调它是一个函数而不是变量名或其他东西。
让我们试试更多的字面值(58
,99.9
,'abc'
)和变量(a
,b
):
>>> a = 7
>>> b = a
>>> type(a)
<class 'int'>
>>> type(b)
<class 'int'>
>>> type(58)
<class 'int'>
>>> type(99.9)
<class 'float'>
>>> type('abc')
<class 'str'>
类是对象的定义;第十章详细介绍了类。在 Python 中,“类”和“类型”几乎意味着相同的事情。
正如您所见,当您在 Python 中使用变量时,它会查找它所引用的对象。在幕后,Python 正在忙碌地创建临时对象,这些对象将在一两行后被丢弃。
让我们重复一个早期的例子:
>>> y = 5
>>> x = 12 - y
>>> x
7
在这段代码片段中,Python 执行了以下操作:
-
创建一个值为
5
的整数对象 -
使变量
y
指向该5
对象 -
增加了值为
5
的对象的引用计数 -
创建另一个值为
12
的整数对象 -
从
y
指向的对象的值(5
)中减去具有该值的(匿名)对象中的12
的值 -
将此值(
7
)分配给新的(到目前为止未命名的)整数对象 -
让变量
x
指向这个新对象 -
增加了
x
指向的这个新对象的引用计数 -
查找
x
指向的对象的值(7
)并打印它
当对象的引用计数达到零时,没有名称指向它,因此它不需要保留。 Python 有一个名为垃圾收集器的迷人工具,可以重新使用不再需要的内存。想象有人在那些内存架子后面,拉取过时的盒子进行回收利用。
在这种情况下,我们不再需要具有值5
,12
或7
的对象,也不需要变量x
和y
。 Python 垃圾收集器可以选择将它们送往对象天堂²,或者出于性能原因保留一些,因为小整数通常被广泛使用。
分配给多个名称
您可以同时将值分配给多个变量名:
>>> two = deux = zwei = 2
>>> two
2
>>> deux
2
>>> zwei
2
重新分配名称
因为名称指向对象,所以更改分配给名称的值只是使名称指向新对象。旧对象的引用计数将减少,新对象的引用计数将增加。
复制
正如您在图 2-4 中看到的,将现有变量a
赋值给名为b
的新变量只是使b
指向与a
相同的对象。如果您选择任一a
或b
标签并跟随它们的字符串,您将到达同一个对象。
如果对象是不可变的(比如整数),其值不能被更改,因此这两个名称本质上是只读的。试试这个:
>>> x = 5
>>> x
5
>>> y = x
>>> y
5
>>> x = 29
>>> x
29
>>> y
5
当我们将 x
分配给 y
时,这使得名称 y
指向 x
也指向的值为 5
的整数对象。更改 x
使其指向值为 29
的新整数对象。它没有更改 y
所指向的仍然是 5
的那个对象。
但如果两个名称都指向一个可变对象,则可以通过任一名称更改对象的值,并在使用任一名称时看到更改后的值。如果你一开始不知道这一点,可能会感到惊讶。
列表是一个可变的值数组,第七章详细介绍了它们。在本例中,a
和b
分别指向具有三个整数成员的列表:
>>> a = [2, 4, 6]
>>> b = a
>>> a
[2, 4, 6]
>>> b
[2, 4, 6]
这些列表成员(a[0]
、a[1]
和 a[2]
)本身就像名称,指向具有值 2
、4
和 6
的整数对象。列表对象按顺序保留其成员。
现在通过名称 a
更改第一个列表元素,并看到 b
也已更改:
>>> a[0] = 99
>>> a
[99, 4, 6]
>>> b
[99, 4, 6]
当更改第一个列表元素时,它不再指向值为 2
的对象,而是指向值为 99
的新对象。列表仍然是 list
类型,但其值(列表元素及其顺序)是可变的。
选择好的变量名称
他说了真实的事情,但用了错误的名字来称呼它们。
伊丽莎白·巴雷特·勃朗宁
选择好变量名称是多么重要啊。在迄今为止的许多代码示例中,我一直在使用像 a
和 x
这样的临时名称。在实际程序中,你将同时跟踪更多的变量,并且需要在简洁性和清晰性之间取得平衡。例如,键入 num_loons
要比 number``_of_loons
或 gaviidae_inventory
快,但比 n
更具解释性。
即将发生
数字!它们像你期望的那样令人兴奋。嗯,也许没有那么糟糕。³ 你将看到如何将 Python 用作计算器,以及一只猫如何创立了数字系统。
要做的事情
2.1 将整数值 99
分配给变量 prince
,并打印它。
2.2 值 5
的类型是什么?
2.3 值 2.0
的类型是什么?
2.4 表达式 5 + 2.0
的类型是什么?
¹ async
和 await
是 Python 3.7 中的新功能。
² 或者是不合格对象之岛。
³ 8
看起来像一个雪人!
第三章:数字
行动是最大程度地为最大数量的人带来最大幸福的行动。
弗朗西斯·哈切森
在本章中,我们首先看一下 Python 最简单的内置数据类型:
-
布尔值(其值为
True
或False
) -
整数(如
42
和100000000
) -
浮点数(带有小数点的数字,如
3.14159
,或者有时指数形式如1.0e8
,表示10 的 8 次方,或者100000000.0
)
从某种意义上说,它们就像原子一样。本章中我们单独使用它们,而在后续章节中,您将看到如何将它们组合成像列表和字典这样更大的“分子”。
每种类型都有其使用的特定规则,并由计算机以不同方式处理。我还展示了如何使用像97
和3.1416
这样的字面值值,以及我在第二章中提到的变量。
本章的代码示例都是有效的 Python 代码,但它们只是片段。我们将使用 Python 交互解释器,键入这些片段并立即查看结果。尝试在您的计算机上使用所安装的 Python 版本来运行它们。您可以通过>>>
提示符识别这些示例。
布尔值
在 Python 中,布尔数据类型的唯一值为True
和False
。有时直接使用它们,其他时候从它们的值评估“真假性”。特殊的 Python 函数bool()
可以将任何 Python 数据类型转换为布尔值。
函数在第九章中有专门的章节,但现在您只需要知道函数有一个名称,零个或多个用逗号分隔的参数,用括号括起来,以及零个或多个返回值。bool()
函数接受任何值作为其参数,并返回布尔等价值。
非零数字被视为True
:
>>> bool(True)
True
>>> bool(1)
True
>>> bool(45)
True
>>> bool(-45)
True
零值也被视为False
:
>>> bool(False)
False
>>> bool(0)
False
>>> bool(0.0)
False
您将在第四章中看到布尔值的实用性。在后续章节中,您将看到如何将列表、字典和其他类型视为True
或False
。
整数
整数是整数——没有分数,没有小数点,没有花哨的东西。嗯,除了可能的初始符号。如果您希望以除了通常的十进制(10 进制)以外的其他方式表示数字,还有基数。
整数字面值
在 Python 中,任何数字序列都表示一个整数字面值:
>>> 5
5
纯零(0
)是有效的:
>>> 0
0
但是您不能以0
开头,后面跟着介于1
到9
之间的数字:
>>> 05
File "<stdin>", line 1
05
^
SyntaxError: invalid token
注意
此 Python 异常警告您输入了违反 Python 规则的内容。我在“基数”中解释了这意味着什么。在本书中,您将看到更多异常的示例,因为它们是 Python 的主要错误处理机制。
您可以以0b
、0o
或0x
开头一个整数。参见“基数”。
数字序列指定一个正整数。如果在数字前面加上+
号,则数字保持不变:
>>> 123
123
>>> +123
123
要指定负整数,在数字前面插入一个-
:
>>> -123
-123
整数中不能有逗号:
>>> 1,000,000
(1, 0, 0)
与百万不同,你会得到一个元组(参见第七章了解更多关于元组的信息),包含三个值。但是你可以使用下划线(_
)作为数字分隔符:¹
>>> million = 1_000_000
>>> million
1000000
实际上,你可以在第一个数字后的任何位置放置下划线;它们会被忽略:
>>> 1_2_3
123
整数运算
在接下来的几页中,我展示了 Python 作为一个简单计算器的示例。你可以通过使用表格中的数学操作符在 Python 中进行常规算术运算:
操作符 | 描述 | 示例 | 结果 |
---|---|---|---|
+ | 加法 | 5 + 8 | 13 |
- | 减法 | 90 - 10 | 80 |
* | 乘法 | 4 * 7 | 28 |
/ | 浮点数除法 | 7 / 2 | 3.5 |
// | 整数(截断)除法 | 7 // 2 | 3 |
% | 取模(余数) | 7 % 3 | 1 |
** | 指数运算 | 3 ** 4 | 81 |
加法和减法的工作方式与你期望的一样:
>>> 5 + 9
14
>>> 100 - 7
93
>>> 4 - 10
-6
你可以包含任意数量的数字和操作符:
>>> 5 + 9 + 3
17
>>> 4 + 3 - 2 - 1 + 6
10
请注意,每个数字和操作符之间不需要有空格:
>>> 5+9 + 3
17
在风格上看起来更好,而且更容易阅读。
乘法也很简单:
>>> 6 * 7
42
>>> 7 * 6
42
>>> 6 * 7 * 2 * 3
252
除法更有趣一些,因为它有两种形式:
-
/
执行浮点数(十进制)除法 -
//
执行整数(截断)除法
即使你将整数除以整数,使用 /
也会给你一个浮点结果(浮点数稍后在本章讨论):
>>> 9 / 5
1.8
截断整数除法返回一个整数答案,丢弃任何余数:
>>> 9 // 5
1
无论使用哪种除法,将整数除以零都会引发 Python 异常,而不是撕开时空维度:
>>> 5 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 7 // 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by z
整数和变量
所有前面的例子都使用了文字整数。你可以混合使用文字整数和已经赋予整数值的变量:
>>> a = 95
>>> a
95
>>> a - 3
92
你会记得从第二章中,a
是一个指向整数对象的名称。当我说 a - 3
时,我没有将结果再赋给 a
,因此 a
的值没有改变:
>>> a
95
如果想要改变 a
,你需要这样做:
>>> a = a - 3
>>> a
92
再次强调,这不是一个合法的数学方程,而是在 Python 中重新分配变量值的方式。在 Python 中,等号右侧的表达式先计算,然后再赋给左侧的变量。
如果有帮助,可以这样考虑:
-
从
a
减去3
-
将减法的结果赋给一个临时变量
-
将临时变量的值赋给
a
:
>>> a = 95
>>> temp = a - 3
>>> a = temp
因此,当你说
>>> a = a - 3
Python 在等号右侧计算减法,记住结果,然后将其分配给 a
在加号左侧。这比使用临时变量更快、更整洁。
你可以通过在等号前放置操作符将算术运算符与赋值结合起来。例如,a -= 3
相当于 a = a - 3
:
>>> a = 95
>>> a -= 3
>>> a
92
这就像 a = a + 8
一样:
>>> a = 92
>>> a += 8
>>> a
100
这就像 a = a * 2
一样:
>>> a = 100
>>> a *= 2
>>> a
200
这是一个浮点除法的例子,例如 a = a / 3
:
>>> a = 200
>>> a /= 3
>>> a
66.66666666666667
现在让我们试试 a = a // 4
的缩写(截断整数除法):
>>> a = 13
>>> a //= 4
>>> a
3
%
字符在 Python 中有多种用法。当它在两个数字之间时,它产生第一个数除以第二个数的余数:
>>> 9 % 5
4
这里是如何同时获得截断的商和余数:
>>> divmod(9,5)
(1, 4)
否则,你可以分别计算它们:
>>> 9 // 5
1
>>> 9 % 5
4
你刚刚在这里看到了一些新东西:一个名为 divmod
的函数被给定整数 9
和 5
,并返回一个两项的元组。正如我之前提到的,元组将在第七章亮相;函数则在第九章首次登场。
最后一个数学特性是指数运算符 **
,它还允许你混合整数和浮点数:
>>> 2**3
8
>>> 2.0 ** 3
8.0
>>> 2 ** 3.0
8.0
>>> 0 ** 3
0
优先级
如果你输入以下内容,会得到什么?
>>> 2 + 3 * 4
如果你先做加法,2 + 3
是 5
,然后 5 * 4
是 20
。但如果你先做乘法,3 * 4
是 12
,然后 2 + 12
是 14
。在 Python 中,与大多数语言一样,乘法比加法具有更高的优先级,所以第二个版本是你会看到的:
>>> 2 + 3 * 4
14
你如何知道运算符的优先级规则?在附录 E 中有一张大表列出了所有规则,但实际上我从不查看这些规则。只需通过添加括号来组合代码以执行你意图的计算,会更容易得多:
>>> 2 + (3 * 4)
14
这个带有指数的例子
>>> -5 ** 2
-25
是相同的
>>> - (5 ** 2)
-25
而且可能不是你想要的。括号可以明确表达:
>>> (-5) ** 2
25
这样,任何阅读代码的人都不需要猜测它的意图或查阅优先级规则。
基数
假设整数默认为十进制(十进制),除非你使用前缀来指定另一个基数。你可能永远不需要使用这些其他基数,但在某个时候你可能会在 Python 代码中见到它们。
通常我们有 10 个手指和 10 个脚趾,所以我们数 0
, 1
, 2
, 3
, 4
, 5
, 6
, 7
, 8
, 9
。然后,我们用完了个位数,并把一个1
进位到“十位”,在个位数放一个0
:10
表示“1 十和 0 个”。不像罗马数字,阿拉伯数字没有单独表示10
的字符。然后是11
、12
,一直到19
,进位成20
(2 十和 0 个),以此类推。
基数是你可以使用多少位数,直到需要“进位为一”的数。在二进制(binary)中,唯一的位数是0
和1
。这就是著名的比特位。0
与普通的十进制 0 相同,而1
与十进制 1 相同。然而,在二进制中,如果你将1
加到1
上,你会得到10
(1 十进制二加 0 十进制一)。
在 Python 中,除了十进制以外,你可以用这些整数前缀表示字面整数的三种基数:
-
0b
或0B
代表二进制(基数 2)。 -
0o
或0O
代表八进制(基数 8)。 -
0x
或0X
代表十六进制(基数 16)。
这些基数都是二的幂,某些情况下很方便,尽管你可能永远不需要使用除了好旧的十进制整数之外的任何东西。
解释器将这些打印出来作为十进制整数。让我们尝试每一个基数。首先是一个普通的十进制 10
,这意味着1 十和 0 个一:
>>> 10
10
现在,二进制(基数 2) 0b10
表示1(十进制)二和 0 个一:
>>> 0b10
2
八进制(基数 8) 0o10
代表1(十进制)八和 0 个一:
>>> 0o10
8
十六进制(基数 16) 0x10
表示1(十进制)十六和 0 个一:
>>> 0x10
16
你可以反向操作,使用任何这些基数将整数转换为字符串:
>>> value = 65
>>> bin(value)
'0b1000001'
>>> oct(value)
'0o101'
>>> hex(value)
'0x41'
chr()
函数将一个整数转换为其单字符字符串等效物:
>>> chr(65)
'A'
而 ord()
反向操作:
>>> ord('A')
65
如果你想知道十六进制中所使用的“数字”,它们是:0
, 1
, 2
, 3
, 4
, 5
, 6
, 7
, 8
, 9
, a
, b
, c
, d
, e
, 和 f
。 0xa
是十进制 10
,而 0xf
是十进制 15
。在 0xf
上加 1,你得到 0x10
(十进制 16)。
为什么要使用不同于十进制的基数?它们在位级操作中非常有用,详见第十二章,以及有关从一种进制转换到另一种进制的更多细节。
猫通常每只前爪有五个趾,每只后爪有四个趾,总共有 18 个。如果你曾经在他们穿着实验服的猫科学家们之间,他们经常讨论十八进制算术。我的猫切斯特,看到他在图 3-1 中悠闲地躺着,是一只多趾,给了他大约 22 或更多(很难分辨)的脚趾。如果他想要用它们来计算围绕碗的食物碎片,他可能会使用一个基数 22 的系统(这里称为切斯特数字系统),使用 0
到 9
和 a
到 l
。
https://github.com/OpenDocCN/ibooker-python-zh/raw/master/docs/intd-py-2e/img/inp2_0301.png
图 3-1. 切斯特——一只出色的毛茸茸小伙子,切斯特数字系统的发明者
类型转换
要将其他 Python 数据类型转换为整数,请使用 int()
函数。
int()
函数接受一个输入参数并返回一个值,即输入参数的整数化等效物。这将保留整数部分并丢弃任何小数部分。
正如你在本章开头所见,Python 最简单的数据类型是布尔值,它只有 True
和 False
两个值。当转换为整数时,它们分别表示 1
和 0
:
>>> int(True)
1
>>> int(False)
0
相反地,bool()
函数返回一个整数的布尔等效物:
>>> bool(1)
True
>>> bool(0)
False
将浮点数转换为整数只需去掉小数点后的所有内容:
>>> int(98.6)
98
>>> int(1.0e4)
10000
将浮点数转换为布尔值并不奇怪:
>>> bool(1.0)
True
>>> bool(0.0)
False
最后,这里有一个示例,从一个只包含数字的文本字符串中获取整数值(第五章)可能包含 _
数字分隔符或初始 +
或 -
号:
>>> int('99')
99
>>> int('-23')
-23
>>> int('+12')
12
>>> int('1_000_000')
1000000
如果字符串代表一个非十进制整数,你可以包括基数:
>>> int('10', 2) # binary
2
>>> int('10', 8) # octal
8
>>> int('10', 16) # hexadecimal
16
>>> int('10', 22) # chesterdigital
22
将整数转换为整数不会改变任何内容,但也不会造成伤害:
>>> int(12345)
12345
如果你试图转换一个看起来不像是数字的东西,你会得到一个异常:
>>> int('99 bottles of beer on the wall')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '99 bottles of beer on the wall'
>>> int('')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: ''
之前的文本字符串以有效的数字字符(99
)开头,但随后出现了其他字符,这些字符使得int()
函数无法处理。
int()
可以从浮点数或数字字符串创建整数,但不能处理包含小数点或指数的字符串:
>>> int('98.6')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '98.6'
>>> int('1.0e4')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '1.0e4'
如果你混合使用数值类型,Python 有时会尝试自动进行类型转换:
>>> 4 + 7.0
11.0
布尔值False
在与整数或浮点数混合时被视为0
或0.0
,而True
则被视为1
或1.0
:
>>> True + 2
3
>>> False + 5.0
5.0
一个整数有多大?
在 Python 2 中,int
的大小可以限制为 32 位或 64 位,具体取决于你的 CPU;32 位可以存储任何从–2,147,483,648 到 2,147,483,647 的整数。
64 位的long
类型允许存储范围从–9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。在 Python 3 中,long
类型已经消失,而int
可以是任意大小,甚至可以比 64 位还大。你可以处理像谷歌(一个后面跟着一百个零的数,在 1920 年被一个九岁的男孩命名)这样的大数:
>>>
>>> googol = 10**100
>>> googol
100000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000
>>> googol * googol
100000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000
谷歌宇宙是10**googol
(如果你想自己尝试的话,有一千个零)。这是谷歌在决定用谷歌之前的一个建议名称,但他们在注册域名google.com
之前没有检查其拼写。
在许多语言中,尝试这样做会导致称为整数溢出的问题,其中数字需要比计算机允许的空间更多,会产生各种不良影响。Python 处理谷歌般大的整数毫无问题。
浮点数
整数是整数,但 Python 中的浮点数(称为floats)带有小数点:
>>> 5.
5.0
>>> 5.0
5.0
>>> 05.0
5.0
浮点数可以在字母e
后包含一个十进制整数指数:
>>> 5e0
5.0
>>> 5e1
50.0
>>> 5.0e1
50.0
>>> 5.0 * (10 ** 1)
50.0
你可以使用下划线(_
)分隔数字以增加清晰度,就像你可以用于整数一样:
>>> million = 1_000_000.0
>>> million
1000000.0
>>> 1.0_0_1
1.001
浮点数的处理方式与整数类似:你可以使用操作符(+
、–
、*
、/
、//
、**
和%
)以及divmod()
函数。
要将其他类型转换为浮点数,你可以使用float()
函数。和以前一样,布尔值表现得像小整数一样:
>>> float(True)
1.0
>>> float(False)
0.0
将整数转换为浮点数只会使其成为一个带有小数点的自豪所有者:
>>> float(98)
98.0
>>> float('99')
99.0
你可以将包含字符的字符串转换为有效的浮点数(数字、符号、小数点或e
后跟一个指数):
>>> float('98.6')
98.6
>>> float('-1.5')
-1.5
>>> float('1.0e4')
10000.0
当你混合整数和浮点数时,Python 会自动将整数值提升为浮点数值:
>>> 43 + 2.
45.0
Python 还会将布尔值提升为整数或浮点数:
>>> False + 0
0
>>> False + 0.
0.0
>>> True + 0
1
>>> True + 0.
1.0
数学函数
Python 支持复数,并且具有常见的数学函数,如平方根、余弦等等。我们将它们留到第二十二章中,在那里我们还将讨论在科学环境中使用 Python。
即将发生
在下一章中,你将终于从单行的 Python 示例中毕业。通过if
语句,你将学会如何使用代码做出决策。
要做的事情
本章介绍了 Python 的基本组成部分:数字、布尔值和变量。让我们在交互式解释器中尝试一些与它们相关的小练习。
3.1 一个小时有多少秒?使用交互式解释器作为计算器,将每分钟的秒数(60
)乘以每小时的分钟数(同样是60
)。
3.2 将前一个任务(每小时的秒数)的结果赋给名为seconds_per_hour
的变量。
3.3 一天有多少秒?使用你的seconds_per_hour
变量。
3.4 再次计算每天的秒数,但这次将结果保存在名为seconds_per_day
的变量中。
3.5 将seconds_per_day
除以seconds_per_hour
。使用浮点数(/
)除法。
3.6 将seconds_per_day
除以seconds_per_hour
,使用整数(//
)除法。除了最后的.0
,这个数字是否与前一个问题中的浮点值一致?
¹ 适用于 Python 3.6 及更新版本。
第四章:选择使用 if
如果你能保持头脑冷静
如果你能保持头脑冷静,当周围的一切都在失控
鲁道德·吉卜林,《如果——》
在前面的章节中,你已经看到了许多数据示例,但还没有深入研究过。大多数代码示例都使用交互式解释器,并且很简短。在这一章中,你将学习如何构建 Python 代码,而不仅仅是数据。
许多计算机语言使用诸如花括号 ({
和 }
) 或关键字如 begin
和 end
来标记代码段落。在这些语言中,使用一致的缩进习惯是提高代码可读性的良好实践,不仅适合自己阅读,也便于他人理解。甚至有工具可以帮助你使代码排列整齐。
当他设计成为 Python 的语言时,吉多·范罗苏姆决定使用缩进来定义程序结构,避免使用所有那些括号和花括号。Python 在使用空格来定义程序结构方面非常不同寻常。这是新手注意到的第一个方面,对有其他语言经验的人来说可能会感觉奇怪。但事实证明,使用 Python 一段时间后,这种方式会变得自然,你甚至不再注意到它。你甚至会习惯于在输入更少的情况下做更多事情。
我们最初的代码示例都是单行代码。让我们先看看如何进行注释和多行命令。
使用 #
进行注释
注释 是程序中被 Python 解释器忽略的文本片段。你可以使用注释来澄清附近的 Python 代码,做笔记提醒自己以后修复问题,或者任何你喜欢的目的。通过使用 #
字符标记注释;从该点到当前行末尾的所有内容都是注释。通常你会在单独的一行上看到注释,如下所示:
>>> # 60 sec/min * 60 min/hr * 24 hr/day
>>> seconds_per_day = 86400
或者,在代码同一行上进行注释:
>>> seconds_per_day = 86400 # 60 sec/min * 60 min/hr * 24 hr/day
#
字符有很多名称:井号、sharp、pound 或听起来邪恶的 octothorpe。¹ 无论你如何称呼它,² 它的效果仅限于出现在该行的末尾。
Python 没有多行注释。你需要明确地用 #
开始每一行或每一节注释:
>>> # I can say anything here, even if Python doesn't like it,
... # because I'm protected by the awesome
... # octothorpe.
...
>>>
然而,如果它在文本字符串中,强大的井号将恢复其作为普通旧#
字符的角色:
>>> print("No comment: quotes make the # harmless.")
No comment: quotes make the # harmless.
使用 \
继续多行
当行长度合理时,程序更易读。推荐(非必须)的最大行长度为 80 个字符。如果你无法在这个长度内表达所有想要说的内容,你可以使用续行字符:\
(反斜杠)。只需在行尾加上 \
,Python 就会认为你仍然在同一行上。
例如,如果我想要添加前五个数字,我可以一行一行地进行:
>>> sum = 0
>>> sum += 1
>>> sum += 2
>>> sum += 3
>>> sum += 4
>>> sum
10
或者,我可以使用续行字符一步到位:
>>> sum = 1 + \
... 2 + \
... 3 + \
... 4
>>> sum
10
如果我们在表达式中跳过中间的反斜杠,我们会得到一个异常:
>>> sum = 1 +
File "<stdin>", line 1
sum = 1 +
^
SyntaxError: invalid syntax
这里有一个小技巧——如果你在成对的括号(或方括号或花括号)中间,Python 不会对行结束发出警告:
>>> sum = (
... 1 +
... 2 +
... 3 +
... 4)
>>>
>>> sum
10
在第五章中,你还会看到成对的三重引号让你创建多行字符串。
与 if、elif 和 else 比较
现在,我们终于迈出了进入编程的第一步,这是一个小小的 Python 程序,检查布尔变量disaster
的值,并打印相应的注释:
>>> disaster = True
>>> if disaster:
... print("Woe!")
... else:
... print("Whee!")
...
Woe!
>>>
if
和else
行是 Python 的语句,用于检查条件(这里是disaster
的值)是否为布尔True
值,或者可以评估为True
。记住,print()
是 Python 的内置函数,用于打印东西,通常打印到屏幕上。
注意
如果你在其他语言中编程过,请注意,对于if
测试,不需要括号。例如,不要写像if (disaster == True)
这样的内容(相等操作符==
在几段后面描述)。但是需要在末尾加上冒号(:
)。如果像我一样有时会忘记输入冒号,Python 会显示错误消息。
每个print()
行在其测试下缩进。我使用四个空格来缩进每个子节。虽然你可以使用任何你喜欢的缩进方式,但 Python 期望你在一个部分内保持一致——每行都需要缩进相同的数量,左对齐。推荐的风格,称为PEP-8,是使用四个空格。不要使用制表符,也不要混合制表符和空格;这会搞乱缩进计数。
在本节逐渐展开时,我们做了很多事情,我会详细解释:
-
将布尔值
True
赋给名为disaster
的变量。 -
通过使用
if
和else
执行条件比较。 -
调用
print()
函数来打印一些文本。
你可以进行嵌套测试,需要多少层都可以:
>>> furry = True
>>> large = True
>>> if furry:
... if large:
... print("It's a yeti.")
... else:
... print("It's a cat!")
... else:
... if large:
... print("It's a whale!")
... else:
... print("It's a human. Or a hairless cat.")
...
It's a yeti.
在 Python 中,缩进决定了如何配对if
和else
部分。我们的第一个测试是检查furry
。因为furry
是True
,Python 进入缩进的if large
测试。因为我们将large
设为True
,if large
评估为True
,忽略以下的else
行。这使得 Python 运行缩进在if large:
下的行,并打印It's a yeti.
。
如果有超过两个可能性需要测试,使用if
来进行第一个测试,elif
(意为else if)来进行中间的测试,else
用于最后一个:
>>> color = "mauve"
>>> if color == "red":
... print("It's a tomato")
... elif color == "green":
... print("It's a green pepper")
... elif color == "bee purple":
... print("I don't know what it is, but only bees can see it")
... else:
... print("I've never heard of the color", color)
...
I've never heard of the color mauve
在上面的例子中,我们使用==
操作符进行了相等性测试。这里是 Python 的比较操作符:
等于 | == |
---|---|
不等于 | != |
小于 | < |
小于或等于 | <= |
大于 | > |
大于或等于 | >= |
这些返回布尔值True
或False
。让我们看看它们如何工作,但首先,给x
赋一个值:
>>> x = 7
现在,让我们尝试一些测试:
>>> x == 5
False
>>> x == 7
True
>>> 5 < x
True
>>> x < 10
True
注意,两个等号 (==
) 用于测试相等性;记住,单个等号 (=
) 用于给变量赋值。
如果你需要同时进行多个比较,可以使用逻辑(或布尔)运算符 and
、or
和 not
来确定最终的布尔结果。
逻辑运算符比它们比较的代码块具有较低的优先级。这意味着首先计算这些代码块,然后再比较。在这个例子中,因为我们将 x
设置为 7
,5 < x
计算为 True
,x < 10
也是 True
,所以最终我们得到 True and True
:
>>> 5 < x and x < 10
True
正如“优先级”所指出的,避免关于优先级混淆的最简单方法是添加括号:
>>> (5 < x) and (x < 10)
True
这里有一些其他的测试:
>>> 5 < x or x < 10
True
>>> 5 < x and x > 10
False
>>> 5 < x and not x > 10
True
如果你在一个变量上进行多个 and
运算的比较,Python 允许你这样做:
>>> 5 < x < 10
True
这与 5 < x and x < 10
是一样的。你也可以编写更长的比较:
>>> 5 < x < 10 < 999
True
什么是真?
如果我们检查的元素不是布尔值,Python 认为什么是 True
和 False
?
一个 false
值并不一定需要显式地是布尔 False
。例如,下面这些都被认为是 False
:
布尔 | False |
---|---|
空 | None |
零整数 | 0 |
零浮点数 | 0.0 |
空字符串 | '' |
空列表 | [] |
空元组 | () |
空字典 | {} |
空集合 | set() |
其他任何情况都被认为是 True
。Python 程序使用这些“真实性”和“虚假性”的定义来检查空数据结构以及 False
条件:
>>> some_list = []
>>> if some_list:
... print("There's something in here")
... else:
... print("Hey, it's empty!")
...
Hey, it's empty!
如果你要测试的是一个表达式而不是一个简单的变量,Python 会评估该表达式并返回一个布尔结果。因此,如果你输入:
if color == "red":
Python 评估 color == "red"
。在我们之前的例子中,我们将字符串 "mauve"
分配给 color
,所以 color == "red"
是 False
,Python 继续下一个测试:
elif color == "green":
使用 in
进行多个比较
假设你有一个字母,并想知道它是否是元音字母。一种方法是编写一个长长的 if
语句:
>>> letter = 'o'
>>> if letter == 'a' or letter == 'e' or letter == 'i' \
... or letter == 'o' or letter == 'u':
... print(letter, 'is a vowel')
... else:
... print(letter, 'is not a vowel')
...
o is a vowel
>>>
每当你需要进行大量使用 or
分隔的比较时,使用 Python 的成员运算符 in
更加 Pythonic。下面是如何使用由元音字符组成的字符串与 in
结合来检查元音性:
>>> vowels = 'aeiou'
>>> letter = 'o'
>>> letter in vowels
True
>>> if letter in vowels:
... print(letter, 'is a vowel')
...
o is a vowel
下面是如何在接下来的几章节中详细阅读的一些数据类型的使用示例:
>>> letter = 'o'
>>> vowel_set = {'a', 'e', 'i', 'o', 'u'}
>>> letter in vowel_set
True
>>> vowel_list = ['a', 'e', 'i', 'o', 'u']
>>> letter in vowel_list
True
>>> vowel_tuple = ('a', 'e', 'i', 'o', 'u')
>>> letter in vowel_tuple
True
>>> vowel_dict = {'a': 'apple', 'e': 'elephant',
... 'i': 'impala', 'o': 'ocelot', 'u': 'unicorn'}
>>> letter in vowel_dict
True
>>> vowel_string = "aeiou"
>>> letter in vowel_string
True
对于字典,in
查看的是键(:
的左边),而不是它们的值。
新内容:我是海象
在 Python 3.8 中引入了海象运算符,它看起来像这样:
*`name`* := *`expression`*
看到海象了吗?(像笑脸一样,但更多了一些象牙。)
通常,赋值和测试需要两个步骤:
>>> tweet_limit = 280
>>> tweet_string = "Blah" * 50
>>> diff = tweet_limit - len(tweet_string)
>>> if diff >= 0:
... print("A fitting tweet")
... else:
... print("Went over by", abs(diff))
...
A fitting tweet
通过我们的新的分配表达式,我们可以将这些组合成一个步骤:
>>> tweet_limit = 280
>>> tweet_string = "Blah" * 50
>>> if diff := tweet_limit - len(tweet_string) >= 0:
... print("A fitting tweet")
... else:
... print("Went over by", abs(diff))
...
A fitting tweet
“海象运算符”还与 for
和 while
很好地配合,我们将在第六章中详细讨论。
即将到来
玩弄字符串,并遇见有趣的字符。
要做的事情
4.1 选择一个 1 到 10 之间的数字,并将其赋给变量 secret
。然后,再选择另一个 1 到 10 之间的数字,并将其赋给变量 guess
。接下来,编写条件测试(if
、else
和elif
)来打印字符串'too low'
,如果 guess
小于 secret
,打印'too high'
,如果 guess
大于 secret
,打印'just right'
,如果 guess
等于 secret
。
4.2 为变量 small
和 green
赋值True
或False
。编写一些if
/else
语句来打印这些选择匹配哪些选项:cherry(樱桃)、pea(豌豆)、watermelon(西瓜)、pumpkin(南瓜)。
¹ 就像那只八脚的绿色东西就在你后面!
² 请不要打电话给它。它可能会回来。
第五章:文本字符串
我总是喜欢奇怪的字符。
蒂姆·伯顿
计算机书籍通常给人一种编程都是关于数学的印象。实际上,大多数程序员更常用于处理文本的字符串,而不是数字。逻辑(和创造性!)思维通常比数学技能更重要。
字符串是 Python 的第一个序列示例。在这种情况下,它们是字符的序列。但是什么是字符?它是书写系统中的最小单位,包括字母、数字、符号、标点符号,甚至空格或类似换行符的指令。字符由其含义(如何使用它)来定义,而不是它的外观。它可以有多个视觉表示(在不同字体中),而且多个字符可以具有相同的外观(比如在拉丁字母表中表示 H
音的视觉 H
,但在西里尔字母表中表示拉丁 N
音)。
本章集中讨论如何制作和格式化简单文本字符串,使用 ASCII(基本字符集)示例。两个重要的文本主题推迟到第十二章:Unicode 字符(如我刚提到的 H
和 N
问题)和正则表达式(模式匹配)。
与其他语言不同,Python 中的字符串是不可变的。你不能直接改变一个字符串,但你可以将字符串的部分复制到另一个字符串以达到相同的效果。我们马上看看如何做到这一点。
用引号创建
通过将字符包含在匹配的单引号或双引号中,你可以创建一个 Python 字符串:
>>> 'Snap'
'Snap'
>>> "Crackle"
'Crackle'
交互式解释器用单引号回显字符串,但 Python 对所有字符串处理都是完全相同的。
注意
Python 有几种特殊类型的字符串,第一个引号前面的字母指示。f
或 F
开始一个f 字符串,用于格式化,在本章末尾描述。r
或 R
开始一个原始字符串,用于防止字符串中的转义序列(参见“用 \ 进行转义” 和 第十二章 中有关它在字符串模式匹配中的用法)。然后,有组合 fr
(或 FR
、Fr
或 fR
)开始一个原始 f-string。u
开始一个 Unicode 字符串,它与普通字符串相同。b
开始一个 bytes
类型的值(参见第十二章)。除非我提到这些特殊类型之一,我总是在谈论普通的 Python Unicode 文本字符串。
为什么要有两种引号字符?主要目的是创建包含引号字符的字符串。你可以在双引号字符串中放单引号,或在单引号字符串中放双引号:
>>> "'Nay!' said the naysayer. 'Neigh?' said the horse."
"'Nay!' said the naysayer. 'Neigh?' said the horse."
>>> 'The rare double quote in captivity: ".'
'The rare double quote in captivity: ".'
>>> 'A "two by four" is actually 1 1⁄2" × 3 1⁄2".'
'A "two by four" is actually 1 1⁄2" × 3 1⁄2".'
>>> "'There's the man that shot my paw!' cried the limping hound."
"'There's the man that shot my paw!' cried the limping hound."
你也可以使用三个单引号('''
)或三个双引号("""
):
>>> '''Boom!'''
'Boom'
>>> """Eek!"""
'Eek!'
三重引号对于这些短字符串并不是很有用。它们最常见的用途是创建多行字符串,就像爱德华·利尔的这首经典诗歌:
>>> poem = '''There was a Young Lady of Norway,
... Who casually sat in a doorway;
... When the door squeezed her flat,
... She exclaimed, "What of that?"
... This courageous Young Lady of Norway.'''
>>>
(这是在交互式解释器中输入的,第一行我们用 >>>
提示,接着是 ...
直到我们输入最后的三重引号并进入下一行。)
如果你尝试在没有三重引号的情况下创建那首诗,当你转到第二行时,Python 会抱怨:
>>> poem = 'There was a young lady of Norway,
File "<stdin>", line 1
poem = 'There was a young lady of Norway,
^
SyntaxError: EOL while scanning string literal
>>>
如果在三重引号中有多行文本,行尾字符将保留在字符串中。如果有前导或尾随空格,它们也将被保留:
>>> poem2 = '''I do not like thee, Doctor Fell.
... The reason why, I cannot tell.
... But this I know, and know full well:
... I do not like thee, Doctor Fell.
... '''
>>> print(poem2)
I do not like thee, Doctor Fell.
The reason why, I cannot tell.
But this I know, and know full well:
I do not like thee, Doctor Fell.
>>>
顺便提一下,print()
的输出与交互式解释器的自动回显是有区别的。
>>> poem2
'I do not like thee, Doctor Fell.\n The reason why, I cannot tell.\n But
this I know, and know full well:\n I do not like thee, Doctor Fell.\n'
print()
会去除字符串的引号并打印它们的内容。它适用于人类输出。它会在打印的每个内容之间添加一个空格,并在末尾添加一个换行符:
>>> print('Give', "us", '''some''', """space""")
Give us some space
如果你不想要空格或换行符,请参阅第十四章中的说明以避免它们。
交互式解释器打印字符串时带有单独的引号和转义字符,例如\n
,这些在“使用\进行转义”中有解释。
>>> """'Guten Morgen, mein Herr!'
... said mad king Ludwig to his wig."""
"'Guten Morgen, mein Herr!'\nsaid mad king Ludwig to his wig."
最后,还有空字符串,它完全没有字符但却是完全有效的。你可以用前述任何引号创建空字符串:
>>> ''
''
>>> ""
''
>>> ''''''
''
>>> """"""
''
>>>
使用str()
创建字符串。
你可以使用str()
函数从其他数据类型创建字符串:
>>> str(98.6)
'98.6'
>>> str(1.0e4)
'10000.0'
>>> str(True)
'True'
在调用print()
时,Python 在对象不是字符串且在字符串格式化时内部使用str()
函数,稍后在本章节中你会看到。
使用\
进行转义
Python 允许你转义字符串中某些字符的含义,以实现其他难以表达的效果。通过在字符前加上反斜杠(\
),你赋予它特殊的含义。最常见的转义序列是\n
,表示开始新的一行。这样你可以从单行字符串创建多行字符串:
>>> palindrome = 'A man,\nA plan,\nA canal:\nPanama.'
>>> print(palindrome)
A man,
A plan,
A canal:
Panama.
你会看到\t
(制表符)的转义序列用于对齐文本:
>>> print('\tabc')
abc
>>> print('a\tbc')
a bc
>>> print('ab\tc')
ab c
>>> print('abc\t')
abc
(最终字符串具有终止的制表符,当然,你看不到。)
你可能还需要\'
或\"
来指定一个由相同字符引用的字符串中的字面单引号或双引号:
>>> testimony = "\"I did nothing!\" he said. \"Or that other thing.\""
>>> testimony
'"I did nothing!" he said. "Or that other thing."'
>>> print(testimony)
"I did nothing!" he said. "Or that other thing."
>>> fact = "The world's largest rubber duck was 54'2\" by 65'7\" by 105'"
>>> print(fact)
The world's largest rubber duck was 54'2" by 65'7" by 105'
如果你需要一个字面上的反斜杠,请输入两个(第一个转义第二个):
>>> speech = 'The backslash (\\) bends over backwards to please you.'
>>> print(speech)
The backslash (\) bends over backwards to please you.
>>>
正如本章开头所提到的,原始字符串会取消这些转义。
>>> info = r'Type a \n to get a new line in a normal string'
>>> info
'Type a \\n to get a new line in a normal string'
>>> print(info)
Type a \n to get a new line in a normal string
(第一个info
输出中的额外反斜杠是交互式解释器添加的。)
原始字符串不会取消任何真正的(不是'\n'
)换行符:
>>> poem = r'''Boys and girls, come out to play.
... The moon doth shine as bright as day.'''
>>> poem
'Boys and girls, come out to play.\nThe moon doth shine as bright as day.'
>>> print(poem)
Boys and girls, come out to play.
The moon doth shine as bright as day.
通过+
进行组合
在 Python 中,你可以通过使用+
运算符来组合字面字符串或字符串变量。
>>> 'Release the kraken! ' + 'No, wait!'
'Release the kraken! No, wait!'
你还可以通过简单地将一个字符串放在另一个字符串后面来组合字面字符串(而不是字符串变量):
>>> "My word! " "A gentleman caller!"
'My word! A gentleman caller!'
>>> "Alas! ""The kraken!"
'Alas! The kraken!'
如果有很多这样的内容,你可以通过将其用括号括起来来避免转义行尾。
>>> vowels = ( 'a'
... "e" '''i'''
... 'o' """u"""
... )
>>> vowels
'aeiou'
Python 在连接字符串时不会为你添加空格,因此在一些早期的示例中,我们需要显式地包含空格。Python 在print()
语句的每个参数之间添加一个空格,并在末尾添加一个换行符。
>>> a = 'Duck.'
>>> b = a
>>> c = 'Grey Duck!'
>>> a + b + c
'Duck.Duck.Grey Duck!'
>>> print(a, b, c)
Duck. Duck. Grey Duck!
使用*
进行复制
你可以使用*
运算符来复制一个字符串。尝试在交互式解释器中输入这些行,并查看它们打印出什么:
>>> start = 'Na ' * 4 + '\n'
>>> middle = 'Hey ' * 3 + '\n'
>>> end = 'Goodbye.'
>>> print(start + start + middle + end)
请注意,*
比 +
优先级更高,因此在换行符附加之前字符串被复制。
通过 [] 获取一个字符
要从字符串中获取单个字符,请在字符串名称后的方括号内指定其 offset。第一个(最左边的)偏移量是 0,接下来是 1,依此类推。最后一个(最右边的)偏移量可以用 -1 指定,因此你不必计数;向左是 -2,-3,等等:
>>> letters = 'abcdefghijklmnopqrstuvwxyz'
>>> letters[0]
'a'
>>> letters[1]
'b'
>>> letters[-1]
'z'
>>> letters[-2]
'y'
>>> letters[25]
'z'
>>> letters[5]
'f'
如果指定的偏移量等于或超过字符串的长度(记住,偏移量从 0 到长度减 1),将会引发异常:
>>> letters[100]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range
索引在其他序列类型(列表和元组)中的工作方式相同,我在第七章中介绍。
因为字符串是不可变的,您无法直接插入字符或更改特定索引处的字符。让我们尝试将 'Henny'
更改为 'Penny'
看看会发生什么:
>>> name = 'Henny'
>>> name[0] = 'P'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
相反,您需要使用 replace()
或 slice 的某些组合(我们马上会看到的)等字符串函数:
>>> name = 'Henny'
>>> name.replace('H', 'P')
'Penny'
>>> 'P' + name[1:]
'Penny'
我们没有更改 name
的值。交互式解释器只是打印替换的结果。
使用 slice 提取一个 substring
您可以通过使用 slice 从字符串中提取 substring(字符串的一部分)。您可以通过使用方括号、start
偏移量、end
偏移量和它们之间的可选 step
计数来定义 slice。您可以省略其中一些。切片将包括从 start
偏移量到 end
偏移量之前的字符:
-
[:]
提取从开头到结尾的整个序列。 -
[
start
:]
指定从start
偏移量到结尾。 -
[:
end
]
指定从开头到end
偏移量减 1。 -
[
start
:
end
]
表示从start
偏移量到end
偏移量减 1。 -
[
start
:
end
:
step
]
提取从start
偏移量到end
偏移量减 1,跳过step
个字符。
如前所述,偏移量从左到右为 0、1 等等,从右到左为 -1、-2 等等。如果不指定 start
,则切片使用 0(开头)。如果不指定 end
,则使用字符串的末尾。
让我们创建一个包含小写英文字母的字符串:
>>> letters = 'abcdefghijklmnopqrstuvwxyz'
使用一个普通的 :
等同于 0:
(整个字符串):
>>> letters[:]
'abcdefghijklmnopqrstuvwxyz'
这里是一个从偏移量 20 开始到结尾的示例:
>>> letters[20:]
'uvwxyz'
现在,从偏移量 10 到结尾:
>>> letters[10:]
'klmnopqrstuvwxyz'
另一个,偏移量从 12 到 14。Python 不包括切片中的结束偏移量。开始偏移量是 包含 的,结束偏移量是 不包含 的:
>>> letters[12:15]
'mno'
最后三个字符:
>>> letters[-3:]
'xyz'
在下一个示例中,我们从偏移量 18 到倒数第 4 个提取;请注意与上一个示例的区别,在上一个示例中,从 -3 开始获取 x
,但在 -3 结束实际上停在 -4,w
:
>>> letters[18:-3]
'stuvw'
在以下示例中,我们从倒数第 6 个到倒数第 3 个提取:
>>> letters[-6:-2]
'uvwx'
如果要使用除 1 外的步长大小,请在第二个冒号后指定它,如下一系列示例所示。
从开头到结尾,步长为 7 个字符:
>>> letters[::7]
'ahov'
从偏移量 4 到 19,步长为 3:
>>> letters[4:20:3]
'ehknqt'
从偏移量 19 到末尾,步进为 4:
>>> letters[19::4]
'tx'
从开头到偏移量 20 加 5:
>>> letters[:21:5]
'afkpu'
(同样,结束需要比实际偏移量多一位。)
这还不是全部!给定一个负步长,这个方便的 Python 切片器还可以向后步进。它从末尾开始,直到开头,跳过一切:
>>> letters[-1::-1]
'zyxwvutsrqponmlkjihgfedcba'
结果表明,你可以通过以下方式获得相同的结果:
>>> letters[::-1]
'zyxwvutsrqponmlkjihgfedcba'
切片对于错误的偏移量更宽容,不像单索引查找[]
那样严格。一个早于字符串开始的切片偏移量将被视为0
,一个超过末尾的将被视为-1
,正如在下面的示例中展示的那样。
从倒数第 50 位到末尾:
>>> letters[-50:]
'abcdefghijklmnopqrstuvwxyz'
从倒数第 51 位到倒数第 50 位之前:
>>> letters[-51:-50]
''
从开头到开头后的第 69 位:
>>> letters[:70]
'abcdefghijklmnopqrstuvwxyz'
从开头后的第 70 位到开头后的第 70 位:
>>> letters[70:71]
''
使用 len()获取长度
到目前为止,我们已经使用特殊的标点字符如+
来操作字符串。但这些字符并不多。现在让我们开始使用一些 Python 内置的函数:这些是执行特定操作的命名代码片段。
len()
函数用于计算字符串中的字符数:
>>> len(letters)
26
>>> empty = ""
>>> len(empty)
0
你可以像在第七章中看到的那样,使用len()
处理其他序列类型。
使用 split()分割
与len()
不同,有些函数专门用于字符串。要使用字符串函数,输入字符串名称,一个点,函数名称和函数需要的参数:*string*.*function*(*arguments*)
。关于函数的更长讨论请参见第九章。
你可以使用内置的字符串split()
函数根据某个分隔符将字符串分割成一个列表。我们将在第七章中讨论列表。列表是一系列由逗号分隔并用方括号括起来的值:
>>> tasks = 'get gloves,get mask,give cat vitamins,call ambulance'
>>> tasks.split(',')
['get gloves', 'get mask', 'give cat vitamins', 'call ambulance']
在前面的例子中,字符串称为tasks
,字符串函数称为split()
,带有单一的分隔符参数','
。如果不指定分隔符,split()
将使用任何连续的空白字符——换行符、空格和制表符:
>>> tasks.split()
['get', 'gloves,get', 'mask,give', 'cat', 'vitamins,call', 'ambulance']
在不带参数调用split
时,你仍然需要括号——这是 Python 知道你在调用函数的方式。
使用 join()合并
不太意外的是,join()
函数是split()
的反向操作:它将字符串列表合并成一个单独的字符串。看起来有点反向,因为你首先指定将所有东西粘合在一起的字符串,然后是要粘合的字符串列表:string
.join(
list
)
。所以,要使用换行符将列表lines
连接起来,你会说'\n'.join(lines)
。在下面的示例中,让我们用逗号和空格将列表中的一些名字连接起来:
>>> crypto_list = ['Yeti', 'Bigfoot', 'Loch Ness Monster']
>>> crypto_string = ', '.join(crypto_list)
>>> print('Found and signing book deals:', crypto_string)
Found and signing book deals: Yeti, Bigfoot, Loch Ness Monster
使用 replace()替换
你用replace()
进行简单的子字符串替换。给它旧的子字符串、新的子字符串,以及要替换的旧子字符串的实例数量。它返回更改后的字符串,但不修改原始字符串。如果省略这个最后的计数参数,它会替换所有实例。在这个例子中,只有一个字符串('duck'
)在返回的字符串中被匹配并替换:
>>> setup = "a duck goes into a bar..."
>>> setup.replace('duck', 'marmoset')
'a marmoset goes into a bar...'
>>> setup
'a duck goes into a bar...'
更改多达 100 个:
>>> setup.replace('a ', 'a famous ', 100)
'a famous duck goes into a famous bar...'
当你知道确切的子字符串要更改时,replace()
是一个很好的选择。但要小心。在第二个例子中,如果我们替换为单个字符字符串'a'
而不是两个字符字符串'a '
(a
后跟一个空格),我们也会改变其他单词中间的a
:
>>> setup.replace('a', 'a famous', 100)
'a famous duck goes into a famous ba famousr...'
有时,你想确保子字符串是一个完整的单词,或者是一个单词的开头等。在这些情况下,你需要正则表达式,在第十二章中详细描述。
用 strip() 去除
从字符串中去除前导或尾随的“填充”字符,尤其是空格,这是非常常见的。这里显示的strip()
函数假设你想要去除空白字符(' '
, '\t'
, '\n'
),如果你不给它们参数的话。strip()
会去除两端,lstrip()
只从左边,rstrip()
只从右边。假设字符串变量world
包含字符串"earth"
浮动在空格中:
>>> world = " earth "
>>> world.strip()
'earth'
>>> world.strip(' ')
'earth'
>>> world.lstrip()
'earth '
>>> world.rstrip()
' earth'
如果字符不在那里,什么也不会发生:
>>> world.strip('!')
' earth '
除了没有参数(意味着空白字符)或单个字符外,你还可以告诉strip()
去除多字符字符串中的任何字符:
>>> blurt = "What the...!!?"
>>> blurt.strip('.?!')
'What the'
附录 E 显示了一些对于strip()
有用的字符组的定义:
>>> import string
>>> string.whitespace
' \t\n\r\x0b\x0c'
>>> string.punctuation
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
>>> blurt = "What the...!!?"
>>> blurt.strip(string.punctuation)
'What the'
>>> prospector = "What in tarnation ...??!!"
>>> prospector.strip(string.whitespace + string.punctuation)
'What in tarnation'
搜索和选择
Python 有一组庞大的字符串函数。让我们探讨它们中最常见的一些如何工作。我们的测试对象是以下字符串,其中包含了玛格丽特·卡文迪许,纽卡斯尔公爵夫人的不朽诗作“液体是什么?”的文字:
>>> poem = '''All that doth flow we cannot liquid name
... Or else would fire and water be the same;
... But that is liquid which is moist and wet
... Fire that property can never get.
... Then 'tis not cold that doth the fire put out
... But 'tis the wet that makes it die, no doubt.'''
鼓舞人心!
首先,获取前 13 个字符(偏移量 0 到 12):
>>> poem[:13]
'All that doth'
这首诗有多少个字符?(空格和换行符都包括在计数中。)
>>> len(poem)
250
它以All
开头吗?
>>> poem.startswith('All')
True
它以That's all, folks!
结尾吗?
>>> poem.endswith('That\'s all, folks!')
False
Python 有两个方法(find()
和index()
)用于找到子字符串的偏移量,并且有两个版本(从开始或结尾)。如果找到子字符串,它们的工作方式相同。如果找不到,find()
返回-1
,而index()
引发异常。
让我们找到诗中单词the
的第一次出现的偏移量:
>>> word = 'the'
>>> poem.find(word)
73
>>> poem.index(word)
73
最后一个the
的偏移量:
>>> word = 'the'
>>> poem.rfind(word)
214
>>> poem.rindex(word)
214
但如果子字符串不在其中:
>>> word = "duck"
>>> poem.find(word)
-1
>>> poem.rfind(word)
-1
>>> poem.index(word)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: substring not found
>>> poem.rfind(word)
-1
>>> poem.rindex(word)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: substring not found
三个字母序列the
出现了多少次?
>>> word = 'the'
>>> poem.count(word)
3
诗中的所有字符都是字母或数字吗?
>>> poem.isalnum()
False
不,有一些标点字符。
案例
在这一部分,我们将看一些内置字符串函数的更多用法。我们的测试字符串再次是以下内容:
>>> setup = 'a duck goes into a bar...'
从两端去除.
序列:
>>> setup.strip('.')
'a duck goes into a bar'
注意
由于字符串是不可变的,这些示例中没有一个实际上更改了setup
字符串。每个示例只是取setup
的值,对其进行处理,并将结果作为新字符串返回。
将第一个单词大写:
>>> setup.capitalize()
'A duck goes into a bar...'
将所有单词大写:
>>> setup.title()
'A Duck Goes Into A Bar...'
将所有字符转换为大写:
>>> setup.upper()
'A DUCK GOES INTO A BAR...'
将所有字符转换为小写:
>>> setup.lower()
'a duck goes into a bar...'
交换大写和小写:
>>> setup.swapcase()
'A DUCK GOES INTO A BAR...'
对齐
现在,让我们使用一些布局对齐函数。字符串在指定的总空间内(这里为30
)居中对齐。
在 30 个空格内居中对齐字符串:
>>> setup.center(30)
' a duck goes into a bar... '
左对齐:
>>> setup.ljust(30)
'a duck goes into a bar... '
右对齐:
>>> setup.rjust(30)
' a duck goes into a bar...'
接下来,我们看一下更多如何对齐字符串的方法。
格式化
您已经看到可以使用+
来连接字符串。让我们看看如何使用各种格式将数据值插值到字符串中。您可以用此方法生成需要外观精确的报告、表格和其他输出。
除了上一节中的函数外,Python 还有三种格式化字符串的方法:
-
旧风格(支持 Python 2 和 3)
-
新风格(Python 2.6 及更高版本)
-
f-strings(Python 3.6 及更高版本)
旧风格:%
旧式字符串格式化的形式为*format_string
* %
data
。格式字符串中包含插值序列。表 5-1 说明了最简单的序列是一个%
后跟一个指示要格式化的数据类型的字母。
表 5-1. 转换类型
%s | 字符串 |
---|---|
%d | 十进制整数 |
%x | 十六进制整数 |
%o | 八进制整数 |
%f | 十进制浮点数 |
%e | 指数浮点数 |
%g | 十进制或指数浮点数 |
%% | 一个字面量% |
您可以使用%s
来表示任何数据类型,Python 会将其格式化为无额外空格的字符串。
以下是一些简单的示例。首先,一个整数:
>>> '%s' % 42
'42'
>>> '%d' % 42
'42'
>>> '%x' % 42
'2a'
>>> '%o' % 42
'52'
一个浮点数:
>>> '%s' % 7.03
'7.03'
>>> '%f' % 7.03
'7.030000'
>>> '%e' % 7.03
'7.030000e+00'
>>> '%g' % 7.03
'7.03'
一个整数和一个字面量%
:
>>> '%d%%' % 100
'100%'
让我们尝试一些字符串和整数插值:
>>> actor = 'Richard Gere'
>>> cat = 'Chester'
>>> weight = 28
>>> "My wife's favorite actor is %s" % actor
"My wife's favorite actor is Richard Gere"
>>> "Our cat %s weighs %s pounds" % (cat, weight)
'Our cat Chester weighs 28 pounds'
字符串中的%s
表示插入一个字符串。字符串中%
的数量需要与跟随字符串之后的数据项的数量匹配。单个数据项,如actor
,直接放在最后一个%
之后。多个数据必须分组成元组(详细信息见第七章; 由括号界定,逗号分隔),如(cat, weight)
。
尽管weight
是一个整数,但字符串中的%s
将其转换为字符串。
您可以在格式字符串的%
和类型说明符之间添加其他值来指定最小宽度、最大宽度、对齐和字符填充。这本质上是一种小语言,比接下来的两个部分的语言更有限。让我们快速看看这些值:
-
初始
'%'
字符。 -
可选的对齐字符:没有或
'+'
表示右对齐,'-'
表示左对齐。 -
可选的最小宽度字段宽度。
-
可选的
'.'
字符用于分隔最小宽度和最大字符数。 -
可选的maxchars(如果转换类型为
s
)指定要从数据值中打印多少个字符。如果转换类型为f
,则指定精度(小数点后要打印多少位数)。 -
早期表格中的转换类型字符。
这很令人困惑,所以这里有一些字符串的示例:
>>> thing = 'woodchuck'
>>> '%s' % thing
'woodchuck'
>>> '%12s' % thing
' woodchuck'
>>> '%+12s' % thing
' woodchuck'
>>> '%-12s' % thing
'woodchuck '
>>> '%.3s' % thing
'woo'
>>> '%12.3s' % thing
' woo'
>>> '%-12.3s' % thing
'woo '
再来一次,和一个带有%f
变体的浮点数:
>>> thing = 98.6
>>> '%f' % thing
'98.600000'
>>> '%12f' % thing
' 98.600000'
>>> '%+12f' % thing
' +98.600000'
>>> '%-12f' % thing
'98.600000 '
>>> '%.3f' % thing
'98.600'
>>> '%12.3f' % thing
' 98.600'
>>> '%-12.3f' % thing
'98.600 '
和一个整数与%d
:
>>> thing = 9876
>>> '%d' % thing
'9876'
>>> '%12d' % thing
' 9876'
>>> '%+12d' % thing
' +9876'
>>> '%-12d' % thing
'9876 '
>>> '%.3d' % thing
'9876'
>>> '%12.3d' % thing
' 9876'
>>> '%-12.3d' % thing
'9876 '
对于整数,%+12d
只是强制打印符号,并且带有.3
的格式字符串对其无效,就像对浮点数一样。
新风格:{} 和 format()
仍然支持旧风格格式化。在 Python 2 中,将冻结在版本 2.7 上,将永远支持。对于 Python 3,请使用本节中描述的“新风格”格式化。如果你使用的是 Python 3.6 或更新版本,f-strings(“最新风格:f-strings”)更加推荐。
“新风格”格式化的形式为*format_string*.format(*data*)
。
格式字符串与前一节不完全相同。这里演示了最简单的用法:
>>> thing = 'woodchuck'
>>> '{}'.format(thing)
'woodchuck'
format()
函数的参数需要按照格式字符串中的{}
占位符的顺序:
>>> thing = 'woodchuck'
>>> place = 'lake'
>>> 'The {} is in the {}.'.format(thing, place)
'The woodchuck is in the lake.'
使用新风格格式,你还可以像这样按位置指定参数:
>>> 'The {1} is in the {0}.'.format(place, thing)
'The woodchuck is in the lake.'
值0
指的是第一个参数place
,1
指的是thing
。
format()
的参数也可以是命名参数
>>> 'The {thing} is in the {place}'.format(thing='duck', place='bathtub')
'The duck is in the bathtub'
或者是一个字典:
>>> d = {'thing': 'duck', 'place': 'bathtub'}
在以下示例中,{0}
是format()
的第一个参数(字典d
):
>>> 'The {0[thing]} is in the {0[place]}.'.format(d)
'The duck is in the bathtub.'
这些示例都使用默认格式打印它们的参数。新风格格式化与旧风格的格式字符串定义略有不同(示例如下):
-
初始冒号(
':'
)。 -
可选的填充字符(默认为
' '
)以填充值字符串,如果比minwidth短。 -
可选的对齐字符。这次,左对齐是默认的。
'<'
也表示左对齐,'>'
表示右对齐,'^'
表示居中。 -
数字的可选符号。没有意味着仅为负数添加减号(
'-'
)。' '
表示负数前添加减号,正数前添加空格(' '
)。 -
可选的minwidth。一个可选的句点(
'.'
)用于分隔minwidth和maxchars。 -
可选的maxchars。
-
转换类型。
>>> thing = 'wraith'
>>> place = 'window'
>>> 'The {} is at the {}'.format(thing, place)
'The wraith is at the window'
>>> 'The {:10s} is at the {:10s}'.format(thing, place)
'The wraith is at the window '
>>> 'The {:<10s} is at the {:<10s}'.format(thing, place)
'The wraith is at the window '
>>> 'The {:¹⁰s} is at the {:¹⁰s}'.format(thing, place)
'The wraith is at the window '
>>> 'The {:>10s} is at the {:>10s}'.format(thing, place)
'The wraith is at the window'
>>> 'The {:!¹⁰s} is at the {:!¹⁰s}'.format(thing, place)
'The !!wraith!! is at the !!window!!'
最新的风格:f-strings
f-strings出现在 Python 3.6 中,现在是推荐的字符串格式化方式。
制作 f-string:
-
直接在初始引号之前输入字母
f
或F
。 -
在大括号(
{}
)中包含变量名或表达式,以将它们的值放入字符串中。
这就像前一节的“新风格”格式化,但没有format()
函数,并且格式字符串中没有空括号({}
)或位置参数({1}
)。
>>> thing = 'wereduck'
>>> place = 'werepond'
>>> f'The {thing} is in the {place}'
'The wereduck is in the werepond'
正如我之前提到的,大括号内也允许表达式:
>>> f'The {thing.capitalize()} is in the {place.rjust(20)}'
'The Wereduck is in the werepond'
这意味着在前一节的format()
中可以做的事情,在主字符串的{}
内部现在也可以做到。这看起来更容易阅读。
f-strings 使用与新式格式化相同的格式化语言(宽度、填充、对齐),在':'
之后。
>>> f'The {thing:>20} is in the {place:.²⁰}'
'The wereduck is in the ......werepond......'
从 Python 3.8 开始,f-strings 增加了一个新的快捷方式,当你想打印变量名及其值时非常有帮助。在调试时非常方便。窍门是在 f-string 的{}
括号内的变量名后面加上一个单独的=
:
>>> f'{thing =}, {place =}'
thing = 'wereduck', place = 'werepond'
名称实际上可以是一个表达式,并且会按字面意思打印出来:
>>> f'{thing[-4:] =}, {place.title() =}'
thing[-4:] = 'duck', place.title() = 'Werepond'
最后,=
后面可以跟着一个:
和格式化参数,如宽度和对齐方式:
>>> f'{thing = :>4.4}'
thing = 'were'
更多字符串事项
Python 有比我展示的更多字符串函数。一些将出现在稍后的章节中(尤其是第十二章),但你可以在标准文档链接找到所有细节。
即将到来
你会在杂货店找到 Froot Loops,但 Python 循环在下一章的第一个柜台上。
待办事项
5.1 将以m
开头的单词大写:
>>> song = """When an eel grabs your arm,
... And it causes great harm,
... That's - a moray!"""
5.2 打印每个列表问题及其正确匹配的答案,格式为:
Q: 问题
A: 答案
>>> questions = [
... "We don't serve strings around here. Are you a string?",
... "What is said on Father's Day in the forest?",
... "What makes the sound 'Sis! Boom! Bah!'?"
... ]
>>> answers = [
... "An exploding sheep.",
... "No, I'm a frayed knot.",
... "'Pop!' goes the weasel."
... ]
5.3 通过旧式格式编写以下诗歌。将字符串'roast beef'
、'ham'
、'head'
和'clam'
替换为此字符串中的内容:
My kitty cat likes %s,
My kitty cat likes %s,
My kitty cat fell on his %s
And now thinks he's a %s.
5.4 使用新式格式化编写一封表单信。将以下字符串保存为letter
(你将在下一个练习中使用它):
Dear {salutation} {name},
Thank you for your letter. We are sorry that our {product}
{verbed} in your {room}. Please note that it should never
be used in a {room}, especially near any {animals}.
Send us your receipt and {amount} for shipping and handling.
We will send you another {product} that, in our tests,
is {percent}% less likely to have {verbed}.
Thank you for your support.
Sincerely,
{spokesman}
{job_title}
5.5 为字符串变量'salutation'
、'name'
、'product'
、'verbed'
(过去时动词)、'room'
、'animals'
、'percent'
、'spokesman'
和'job_title'
分配值。使用letter.format()
打印letter
。
5.6 在公众投票之后为事物命名,出现了一个模式:英国潜艇(Boaty McBoatface)、澳大利亚赛马(Horsey McHorseface)和瑞典火车(Trainy McTrainface)。使用%
格式化来打印国家集市上的获奖名字,以及鸭子、葫芦和 spitz 的奖品。
5.7 使用format()
格式化方法做同样的事情。
5.8 再来一次,使用f strings。