第 2 章 Python 基本元素:数字、字符串和变量

本章会从 Python 最基本的内置数据类型开始学习,这些类型包括:

  • 布尔型(表示真假的类型,仅包含 TrueFalse 两种取值)

  • 整型(整数,例如 42100000000

  • 浮点型(小数,例如 3.14159,或用科学计数法表示的数字,例如 1.0e8,它表示 1 乘以 10 的 8 次方,也可写作 100000000.0

  • 字符串型(字符组成的序列)

这些基本类型就像组成 Python 的原子一样。本章将学习如何单独使用这些基本“原子”,而第 3 章则会介绍如何将这些“原子”组合成更大的“分子”。

每一种类型都有自己的使用规则,计算机对它们的处理方式也不尽相同。此外我们还会学到变量的概念(与实际数据相关联的名字,后面有更详细的介绍)。

本章中的代码虽然都只是小的片段,但它们都是有效的 Python 程序。为了方便快速测试,我们将使用 Python 的交互式解释器,这样在输入代码的同时就可以获得执行结果。尝试在你自己搭建的 Python 环境中运行书中的代码片段,它们会由提示符 >>> 标出。第 4 章将开始编写能独立执行的 Python 程序。

2.1 变量、名字和对象

Python 里所有数据——布尔值、整数、浮点数、字符串,甚至大型数据结构、函数以及程序——都是以对象(object)的形式存在的。这使得 Python 语言具有很强的统一性(还有许多其他有用的特性),而这恰恰是许多其他语言所缺少的。

对象就像一个塑料盒子,里面装的是数据(图 2-1)。对象有不同类型,例如布尔型和整型,类型决定了可以对它进行的操作。现实生活中的“陶器”会暗含一些信息(例如它可能很重,注意不要掉到地上,等等)。类似地,Python 中一个类型为 int 的对象会告诉你:可以把它与另一个 int 对象相加。

{%}

图 2-1:对象就像一个盒子

对象的类型还决定了它装着的数据是允许被修改的变量(可变的)还是不可被修改的常量(不可变的)。你可以把不可变对象想象成一个透明但封闭的盒子:你可以看到里面装的数据,但是无法改变它。类似地,可变对象就像一个开着口的盒子,你不仅可以看到里面的数据,还可以拿出来修改它,但你无法改变这个盒子本身,即你无法改变对象的类型。

Python 是强类型的(strongly typed),你永远无法修改一个已有对象的类型,即使它包含的值是可变的(图 2-2)。

第 2 章 Python 基本元素:数字、字符串和变量 - 图2

图 2-2:Strong Typing 不是指用力敲打键盘

编程语言允许你定义变量(variable)。所谓变量就是在程序中为了方便地引用内存中的值而为它取的名称。在 Python 中,我们用 = 来给一个变量赋值。

第 2 章 Python 基本元素:数字、字符串和变量 - 图3 我们都在数学课上学过 = 代表“等于”,那么为什么计算机语言(包括 Python)要用 = 代表赋值操作呢?一种解释是标准键盘没有像左箭头一样的逻辑上能代表赋值操作的键,与其他键相比,= 显得相对不那么令人困惑;而在程序中,赋值出现的频率又要远远超过等于,因此把 = 分给了赋值操作来使用。

下面这段仅两行的 Python 程序首先将整数 7 赋值给了变量 a,之后又将 a 的值打印了出来:

  1. >>> a = 7
  2. >>> print(a)
  3. 7

注意,Python 中的变量有一个非常重要的性质:它仅仅是一个名字。赋值操作并不会实际复制值,它只是为数据对象取个相关的名字。名字是对对象的引用而不是对象本身。你可以把名字想象成贴在盒子上的标签(见图 2-3)。

第 2 章 Python 基本元素:数字、字符串和变量 - 图4

图 2-3:贴在对象上的名字

试着在交互式解释器中执行下面的操作:

(1) 和之前一样,将 7 赋值给名称 a,这样就成功创建了一个包含整数 7 的对象;

(2) 打印 a 的值;

(3) 将 a 赋值给 b,这相当于给刚刚创建的对象又贴上了标签 b

(4) 打印 b 的值。

  1. >>> a = 7
  2. >>> print(a)
  3. 7
  4. >>> b = a
  5. >>> print(b)
  6. 7

在 Python 中,如果想知道一个对象(例如一个变量或者一个字面值)的类型,可以使用语句:type( thing )。试试对不同的字面值(5899.9abc)以及不同的变量(ab)执行 type 操作:

  1. >>> type(a)
  2. <class 'int'>
  3. >>> type(b)
  4. <class 'int'>
  5. >>> type(58)
  6. <class 'int'>
  7. >>> type(99.9)
  8. <class 'float'>
  9. >>> type('abc')
  10. <class 'str'>

(class)是对象的定义,第 6 章会详细介绍。在 Python 中,“类”和“类型”一般不加区分。

变量名只能包含以下字符:

  • 小写字母(a~z

  • 大写字母(A~Z

  • 数字(0~9

  • 下划线(_

名字不允许以数字开头。此外,Python 中以下划线开头的名字有特殊的含义(第 4 章会解释)。下面是一些合法的名字:

  • a

  • a1

  • a_b_c___95

  • _abc

  • _1a

下面这些名字则是非法的:

  • 1

  • 1a

  • 1_

最后要注意的是,不要使用下面这些词作为变量名,它们是 Python 保留的关键字

  1. False class finally is return
  2. None continue for lambda try
  3. True def from nonlocal while
  4. and del global not with
  5. as elif if or yield
  6. assert else import pass
  7. break except in raise

这些关键字以及其他的一些标点符号是用于描述 Python 语法的。在本书中,你会慢慢学到它们各自的作用。

2.2 数字

Python 本身支持整数(比如 51000000000)以及浮点数(比如 3.141614.991.87e4)。你可以对这些数字进行下表中的计算。

运算符 描述 示例 运算结果
+ 加法 5 + 8 13
- 减法 90 - 10 80
* 乘法 4 * 7 28
/ 浮点数除法 7 / 2 3.5
// 整数除法 7 // 2 3
% 模(求余) 7 % 3 1
** 3 ** 4 81

接下来会给你展示一些示例,这些示例体现了 Python 作为一个计算机器的非凡特性。

2.2.1 整数

任何仅含数字的序列在 Python 中都被认为是整数

  1. >>> 5
  2. 5

你可以单独使用数字零(0):

  1. >>> 0
  2. 0

但不能把它作为前缀放在其他数字前面:

  1. >>> 05
  2. File "<stdin>", line 1
  3. 05
  4. ^
  5. SyntaxError: invalid token

第 2 章 Python 基本元素:数字、字符串和变量 - 图5 这是你第一次看见 Python 异常——程序错误。在上面的例子中,解释器抛出了一个警告,提示 05 是一个“非法标识”(invalid token)。2.2.3 节会解释这个警告的意义。你会在本书中见到许多种异常,这是 Python 主要的错误处理机制。

一个数字序列定义了一个正整数。你也可以显式地在前面加上正号 +,这不会使数字发生任何改变:

  1. >>> 123
  2. 123
  3. >>> +123
  4. 123

在数字前添加负号 - 可以定义一个负数:

  1. >>> -123
  2. -123

你可以像使用计算器一样使用 Python 来进行常规运算。Python 支持的运算参见之前的表格。试试进行加法和减法运算,运算结果和你预期的一样:

  1. >>> 5 + 9
  2. 14
  3. >>> 100 - 7
  4. 93
  5. >>> 4 - 10
  6. -6

可以连续运算任意个数:

  1. >>> 5 + 9 + 3
  2. 17
  3. >>> 4 + 3 - 2 - 1 + 6
  4. 10

格式提示:数字和运算符之间的空格不是强制的,你也可以写成下面这种格式:

  1. >>> 5+9 + 3
  2. 17

只不过添加空格会使代码看起来更规整更便于阅读。

乘法运算的实现也很直接:

  1. >>> 6 7
  2. 42
  3. >>> 7 6
  4. 42
  5. >>> 6 7 2 * 3
  6. 252

除法运算比较有意思,可能与你预期的有些出入,因为 Python 里有两种除法:

  • / 用来执行浮点除法(十进制小数)

  • // 用来执行整数除法(整除)

与其他语言不同,在 Python 中即使运算对象是两个整数,使用 / 仍会得到浮点型的结果:

  1. >>> 9 / 5
  2. 1.8

使用整除运算得到的是一个整数,余数会被截去:

  1. >>> 9 // 5
  2. 1

如果除数为 0,任何一种除法运算都会产生 Python 异常:

  1. >>> 5 0
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. ZeroDivisionError: division by zero
  5. >>> 7 / 0
  6. Traceback (most recent call last):
  7. File "<stdin>", line 1, in <module>
  8. ZeroDivisionError: integer division or modulo by z

之前的例子中我们都在使用立即数进行运算,你也可以在运算中将立即数和已赋值过的变量混合使用:

  1. >>> a = 95
  2. >>> a
  3. 95
  4. >>> a - 3
  5. 92

上面代码中出现了 a - 3,但我们并没有将结果赋值给 a,因此 a 的值并未发生改变:

  1. >>> a
  2. 95

如果你想要改变 a 的值,可以这样写:

  1. >>> a = a - 3
  2. >>> a
  3. 92

对于初学者来说,上面这行式子可能很费解,因为小学的数学知识让我们根深蒂固地认为 = 代表等于,因此上面的式子显然是不成立的。但在 Python 里并非如此,Python 解释器会首先计算 = 右侧的表达式,然后将其结果赋值给左侧的变量。

试着这样理解看看是否有帮助。

  • 计算 a-3

  • 将运算结果保存在一个临时变量中

  • 将这个临时变量的值赋值给 a

  1. >>> a = 95
  2. >>> temp = a - 3
  3. >>> a = temp

因此,当输入:

  1. >>> a = a - 3

Python 实际上先计算了右侧的减法,暂时记住运算结果,然后将这个结果赋值给了 = 左侧的 a。这种写法比使用临时变量要更加迅速、简洁。

你还可以进一步将运算过程与赋值过程进行合并,只需将运算符放到 = 前面。例如,a -= 3 等价于 a = a - 3

  1. >>> a = 95
  2. >>> a -= 3
  3. >>> a
  4. 92

下面的代码等价于执行 a = a + 8

  1. >>> a += 8
  2. >>> a
  3. 100

以此类推,下面的代码等价于 a = a * 2

  1. >>> a *= 2
  2. >>> a
  3. 200

再试试浮点型除法,例如 a = a / 3

  1. >>> a /= 3
  2. >>> a
  3. 66.66666666666667

接着将 13 赋值给 a,然后试试执行 a = a // 4(整数除法)的简化版:

  1. >>> a = 13
  2. >>> a //= 4
  3. >>> a
  4. 3

百分号(%)在 Python 里有多种用途,当它位于两个数字之间时代表求模运算,得到的结果是第一个数除以第二个数的余数:

  1. >>> 9 % 5
  2. 4

使用下面的方法可以同时得到商和余数:

  1. >>> divmod(9,5)
  2. (1, 4)

或者你也可以分别计算:

  1. >>> 9 // 5
  2. 1
  3. >>> 9 % 5
  4. 4

上面的代码出现了一些你没见过的新东西:一个叫作 divmod函数。这个函数接受了两个整数:95,并返回了一个包含两个元素的结果,我们称这种结构为元组(tuple)。第 3 章会学习元组的使用,而关于函数的内容将在第 4 章进行讲解。

2.2.2 优先级

想一想下面的表达式会产生什么结果?

  1. >>> 2 + 3 * 4

如果你先进行加法运算 2 + 3 = 5,然后计算 5 * 4,最终得到 20。但如果你先进行乘法运算,3 * 4 = 12,接着 2 + 12,结果等于 14。与其他编程语言一样,在 Python 里,乘法的优先级要高于加法,因此第二种运算结果是正确的:

  1. >>> 2 + 3 * 4
  2. 14

如何了解优先级规则?我在附录 F 中为你准备了一张优先级表,但在实际编程中我几乎从来没有查看过它,因为我们总可以使用括号来保证运算顺序与我们期望的一致:

  1. >>> 2 + (3 * 4)
  2. 14

这样书写的代码也可让阅读者无需猜测代码的意图,免去了检查优先级表的麻烦。

2.2.3 基数

在 Python 中,整数默认使用十进制数(以 10 为底),除非你在数字前添加前缀,显式地指定使用其他基数(base)。也许你永远都不会在自己的代码中用到其他基数,但你很有可能在其他人编写的 Python 代码里见到它们。

我们大多数人都有 10 根手指 10 根脚趾(我家里倒是有只猫多了几根指头,但我从来没见过它用自己的指头数数)。因此,我们习惯这样计数:0,1,2,3,4,5,6,7,8,9。到了 9 之后,我们用光了所有的数字,于是将数字 1 放到“十位”,并把 0 放到“个位”。因此,10 代表“1 个十加 0 个一”。我们无法用一个字符代表数字“十”。接着是 11,12,一直到 19,然后仿照之前的做法,我们将新多出来的 1 加到十位来组成 20(2 个十加 0 个一),以此类推。

基数指的是在必须进位前可以使用的数字的最大数量。以 2 为底(二进制)时,可以使用的数字只有 0 和 1。这里的 0 和十进制的 0 代表的意义相同,1 和十进制的 1 所代表的意义也相同。然而以 2 为底时,1 与 1 相加得到的将是 10(1 个二加 0 个一)。

在 Python 中,除十进制外你还可以使用其他三种进制的数字:

  • 0b0B 代表二进制(以 2 为底)

  • 0o0O 代表八进制(以 8 为底)

  • 0x0X 代表十六进制(以 16 为底)

Python 解释器会打印出它们对应的十进制整数。我们来试试这些不同进制的数。首先是单纯的十进制数字 10,代表“1 个十加 0 个一”。

  1. >>> 10
  2. 10

接着,试试二进制(以 2 为底),代表“1(十进制)个二加上 0 个一”:

  1. >>> 0b10
  2. 2

八进制(以 8 为底),代表“1(十进制)个八加上 0 个一”:

  1. >>> 0o10
  2. 8

十六进制(以 16 为底),代表“1(十进制)个 16 加上 0 个一”:

  1. >>> 0x10
  2. 16

可能你会好奇,十六进制用的是哪 16 个“数字”,它们是:0,1,2,3,4,5,6,7,8,9,a,b,c,d,e 以及 f。因此,0xa 代表十进制的 100xf 代表十进制的 150xf1 等于 0x10(十进制 16)。

为什么要使用 10 以外的基数?因为它们在进行运算时非常有用。有关位运算以及不同进制之间的转换将在第 7 章进行介绍。

2.2.4 类型转换

我们可以方便地使用 int() 函数将其他的 Python 数据类型转换为整型。它会保留传入数据的整数部分并舍去小数部分。

Python 里最简单的数据类型是布尔型,它只有两个可选值:TrueFalse。当转换为整数时,它们分别代表 10

  1. >>> int(True)
  2. 1
  3. >>> int(False)
  4. 0

当将浮点数转换为整数时,所有小数点后面的部分会被舍去:

  1. >>> int(98.6)
  2. 98
  3. >>> int(1.0e4)
  4. 10000

也可以将仅包含数字和正负号的字符串(如果你不知道字符串是什么,不用着急,先往后读,很快就会了解)转换为整数,下面有几个例子:

  1. >>> int('99')
  2. 99
  3. >>> int('-23')
  4. -23
  5. >>> int('+12')
  6. 12

将一个整数转换为整数没有太多意义,这既不会产生任何改变也不会造成任何损失:

  1. >>> int(12345)
  2. 12345

如果你试图将一个与数字无关的类型转化为整数,会得到一个异常:

  1. >>> int('99 bottles of beer on the wall')
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. ValueError: invalid literal for int() with base 10: '99 bottles of beer on the wall'
  5. >>> int('')
  6. Traceback (most recent call last):
  7. File "<stdin>", line 1, in <module>
  8. ValueError: invalid literal for int() with base 10: ''

尽管上面例子中的字符串的确是以有效数字(99)开头的,但它没有就此截止,后面的内容不是纯数字,无法被 int() 函数识别,因此抛出异常。

第 2 章 Python 基本元素:数字、字符串和变量 - 图6 第 4 章会详细介绍异常。现在,你只需知道异常是 Python 处理程序错误的方式(不像有些语言不做处理直接造成程序崩溃)。在本书里,我会经常展示各种出现异常的情况,而不是假定程序总是正确运行的,这样你可以更加了解 Python 是如何处理程序错误的。

int() 可以接受浮点数或由数字组成的字符串,但无法接受包含小数点或指数的字符串:

  1. >>> int('98.6')
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. ValueError: invalid literal for int() with base 10: '98.6'
  5. >>> int('1.0e4')
  6. Traceback (most recent call last):
  7. File "<stdin>", line 1, in <module>
  8. ValueError: invalid literal for int() with base 10: '1.0e4'

如果混合使用多种不同的数字类型进行计算,Python 会自动地进行类型转换:

  1. >>> 4 + 7.0
  2. 11.0

与整数或浮点数混合使用时,布尔型的 False 会被当作 00.0Ture 会被当作 11.0

  1. >>> True + 2
  2. 3
  3. >>> False + 5.0
  4. 5.0

2.2.5 一个int型有多大

在 Python 2 里,一个 int 型包含 32 位,可以存储从 -2 147 483 648 到 2 147 483 647 的整数。

一个 long 型会占用更多的空间:64 位,可以存储从 -9 223 372 036 854 775 808 到 9 223 372 036 854 775 807 的整数。

到了 Python 3,long 类型已不复存在,而 int 类型变为可以存储任意大小的整数,甚至超过 64 位。因此,你可以进行像下面一样计算(10**100 被赋值给名为 googol 的变量,这是 Google 最初的名字,但由于其拼写困难而被现在的名字所取代):

  1. >>>
  2. >>> googol = 10**100
  3. >>> googol
  4. 100000000000000000000000000000000000000000000000000000000000000000000000000000
  5. 00000000000000000000000
  6. >>> googol * googol
  7. 100000000000000000000000000000000000000000000000000000000000000000000000000000
  8. 000000000000000000000000000000000000000000000000000000000000000000000000000000
  9. 000000000000000000000000000000000000000000000

在许多其他编程语言中,进行类似上面的计算会造成整数溢出,这是因为计算中的数字或结果需要的存储空间超过了计算机所提供的(例如 32 位或 64 位)。在程序编写中,溢出会产生许多负面影响。而 Python 在处理超大数计算方面不会产生任何错误,这也是它的一个加分点。

2.2.6 浮点数

整数全部由数字组成,而浮点数(在 Python 里称为 float)包含非数字的小数点。浮点数与整数很像:你可以使用运算符(+-*///**%)以及 divmod() 函数进行计算。

使用 float() 函数可以将其他数字类型转换为浮点型。与之前一样,布尔型在计算中等价于 1.00.0

  1. >>> float(True)
  2. 1.0
  3. >>> float(False)
  4. 0.0

将整数转换为浮点数仅仅需要添加一个小数点:

  1. >>> float(98)
  2. 98.0
  3. >>> float('99')
  4. 99.0

此外,也可以将包含有效浮点数(数字、正负号、小数点、指数及指数的前缀 e)的字符串转换为真正的浮点型数字:

  1. >>> float('98.6')
  2. 98.6
  3. >>> float('-1.5')
  4. -1.5
  5. >>> float('1.0e4')
  6. 10000.0

2.2.7 数学函数

Python 包含许多常用的数学函数,例如平方根、余弦函数,等等。这些内容被放到了附录 C,在那里我还会讨论 Python 的科学应用。

2.3 字符串

不是程序员的人经常会认为程序员一定都非常擅长数学,因为他们整天和数字打交道。但事实上,大多数程序员在处理字符串上花费的时间要远远超过处理数字的时间。逻辑思维(以及创造力)的重要性要远远超过数学能力。

对 Unicode 的支持使得 Python 3 可以包含世界上任何书面语言以及许多特殊符号。对于 Unicode 的支持是 Python 3 从 Python 2 分离出来的重要原因之一,也正是这一重要特性促使人们转向使用 Python 3。处理 Unicode 编码有时会非常困难,因此我将相关内容分散在了本书的不同地方。而本节只会使用 ASCII 编码的例子。

字符串型是我们学习的第一个 Python 序列类型,它的本质是字符序列。

与其他语言不同的是,Python 字符串是不可变的。你无法对原字符串进行修改,但可以将字符串的一部分复制到新字符串,来达到相同的修改效果。很快你就会学到如何实现。

2.3.1 使用引号创建

将一系列字符包裹在一对单引号或一对双引号中即可创建字符串,就像下面这样:

  1. >>> 'Snap'
  2. 'Snap'
  3. >>> "Crackle"
  4. 'Crackle'

交互式解释器输出的字符串永远是用单引号包裹的,但无论使用哪种引号,Python 对字符串的处理方式都是一样的,没有任何区别。

既然如此,为什么要使用两种引号?这么做的好处是可以创建本身就包含引号的字符串,而不用使用转义符。可以在双引号包裹的字符串中使用单引号,或者在单引号包裹的字符串中使用双引号:

  1. >>> "'Nay,' said the naysayer."
  2. "'Nay,' said the naysayer."
  3. >>> 'The rare double quote in captivity: ".'
  4. 'The rare double quote in captivity: ".'
  5. >>> 'A "two by four" is actually 1 1/2" × 3 1/2".'
  6. 'A "two by four is" actually 1 1/2" × 3 1/2".'
  7. >>> "'There's the man that shot my paw!' cried the limping hound."
  8. "'There's the man that shot my paw!' cried the limping hound."

你还可以使用连续三个单引号 ''',或者三个双引号 """ 创建字符串:

  1. >>> '''Boom!'''
  2. 'Boom'
  3. >>> """Eek!"""
  4. 'Eek!'

三元引号在创建短字符串时没有什么特殊用处。它多用于创建多行字符串。下面的例子 中,创建的字符串引用了 Edward Lear 的经典诗歌:

  1. >>> poem = '''There was a Young Lady of Norway,
  2. ... Who casually sat in a doorway;
  3. ... When the door squeezed her flat,
  4. ... She exclaimed, "What of that?"
  5. ... This courageous Young Lady of Norway.'''
  6. >>>

(上面这段代码是在交互式解释器里输入的,第一行的提示符为 >>>,后面行的提示符为 ,直到再次输入三元引号暗示赋值语句的完结,此时光标跳转到下一行并再次以 >>> 提示输入。)

如果你尝试通过单独的单双引号创建多行字符串,在你完成第一行并按下回车时,Python 会弹出错误提示:

  1. >>> poem = 'There was a young lady of Norway,
  2. File "<stdin>", line 1
  3. poem = 'There was a young lady of Norway,
  4. ^
  5. SyntaxError: EOL while scanning string literal
  6. >>>

在三元引号包裹的字符串中,每行的换行符以及行首或行末的空格都会被保留:

  1. >>> poem2 = '''I do not like thee, Doctor Fell.
  2. ... The reason why, I cannot tell.
  3. ... But this I know, and know full well:
  4. ... I do not like thee, Doctor Fell.
  5. ... '''
  6. >>> print(poem2)
  7. I do not like thee, Doctor Fell.
  8. The reason why, I cannot tell.
  9. But this I know, and know full well:
  10. I do not like thee, Doctor Fell.
  11. >>>

值得注意的是,print() 函数的输出与交互式解释器的自动响应输出存在一些差异:

  1. >>> poem2
  2. 'I do not like thee, Doctor Fell.\n The reason why, I cannot tell.\n But
  3. this I know, and know full well:\n I do not like thee, Doctor Fell.\n'

print() 会把包裹字符串的引号截去,仅输出其实际内容,易于阅读。它还会自动地在各 个输出部分之间添加空格,并在所有输出的最后添加换行符:

  1. >>> print(99, 'bottles', 'would be enough.')
  2. 99 bottles would be enough.

如果你不希望 print() 自动添加空格或换行,随后将学会如何避免它们。

解释器可以打印字符串以及像 \n转义符,关于转义符的内容你会在 2.3.3 节看到。

最后要指出的是 Python 允许空串的存在,它不包含任何字符且完全合法。你可以使用前面提到的任意一种方法创建一个空串:

  1. >>> ''
  2. ''
  3. >>> ""
  4. ''
  5. >>> ''''''
  6. ''
  7. >>> """"""
  8. ''
  9. >>>

为什么会用到空字符串?有些时候你想要创建的字符串可能源自另一字符串的内容,这时需要先创建一个空白的模板,也就是一个空字符串。

  1. >>> bottles = 99
  2. >>> base = ''
  3. >>> base += 'current inventory: '
  4. >>> base += str(bottles)
  5. >>> base
  6. 'current inventory: 99'

2.3.2 使用str()进行类型转换

使用 str() 可以将其他 Python 数据类型转换为字符串:

  1. >>> str(98.6)
  2. '98.6'
  3. >>> str(1.0e4)
  4. '10000.0'
  5. >>> str(True)
  6. 'True'

当你调用 print() 函数或者进行字符串插值(string interpolation)时,Python 内部会自动使用 str() 将非字符串对象转换为字符串。第 7 章会了解到相关内容。

2.3.3 使用\转义

Python 允许你对某些字符进行转义操作,以此来实现一些难以单纯用字符描述的效果。在字符的前面添加反斜线符号 \ 会使该字符的意义发生改变。最常见的转义符是 \n,它代表换行符,便于你在一行内创建多行字符串。

  1. >>> palindrome = 'A man,\nA plan,\nA canal:\nPanama.'
  2. >>> print(palindrome)
  3. A man,
  4. A plan,
  5. A canal:
  6. Panama.

转义符 \t(tab 制表符)常用于对齐文本,之后会经常见到:

  1. >>> print('\tabc')
  2. abc
  3. >>> print('a\tbc')
  4. a bc
  5. >>> print('ab\tc')
  6. ab c
  7. >>> print('abc\t')
  8. abc

(上面例子中,最后一个字符串的末尾包含了一个制表符,当然你无法在打印的结果中看到它。)

有时你可能还会用到 \'\" 来表示单、双引号,尤其当该字符串由相同类型的引号包裹时:

  1. >>> testimony = "\"I did nothing!\" he said. \"Not that either! Or the other
  2. thing.\""
  3. >>> print(testimony)
  4. "I did nothing!" he said. "Not that either! Or the other thing."
  5. >>> fact = "The world's largest rubber duck was 54'2\" by 65'7\" by 105'"
  6. >>> print(fact)
  7. The world's largest rubber duck was 54'2" by 65'7" by 105'

如果你需要输出一个反斜线字符,连续输入两个反斜线即可:

  1. >>> speech = 'Today we honor our friend, the backslash: \\.'
  2. >>> print(speech)
  3. Today we honor our friend, the backslash: \.

2.3.4 使用+拼接

在 Python 中,你可以使用 + 将多个字符串或字符串变量拼接起来,就像下面这样:

  1. >>> 'Release the kraken! ' + 'At once!'
  2. 'Release the kraken! At once!'

也可以直接将一个字面字符串(非字符串变量)放到另一个的后面直接实现拼接:

  1. >>> "My word! " "A gentleman caller!"
  2. 'My word! A gentleman caller!'

进行字符串拼接时,Python 并不会自动为你添加空格,需要显式定义。但当我们调用 print() 进行打印时,Python 会在各个参数之间自动添加空格并在结尾添加换行符:

  1. >>> a = 'Duck.'
  2. >>> b = a
  3. >>> c = 'Grey Duck!'
  4. >>> a + b + c
  5. 'Duck.Duck.Grey Duck!'
  6. >>> print(a, b, c)
  7. Duck. Duck. Grey Duck!

2.3.5 使用*复制

使用 * 可以进行字符串复制。试着把下面这几行输入到交互式解释器里,看看结果是什么:

  1. >>> start = 'Na ' * 4 + '\n'
  2. >>> middle = 'Hey ' * 3 + '\n'
  3. >>> end = 'Goodbye.'
  4. >>> print(start + start + middle + end)

2.3.6 使用[]提取字符

在字符串名后面添加 [],并在括号里指定偏移量可以提取该位置的单个字符。第一个字符(最左侧)的偏移量为 0,下一个是 1,以此类推。最后一个字符(最右侧)的偏移量也可以用 -1 表示,这样就不必从头数到尾。偏移量从右到左紧接着为 -2、-3,以此类推。

  1. >>> letters = 'abcdefghijklmnopqrstuvwxyz'
  2. >>> letters[0]
  3. 'a'
  4. >>> letters[1]
  5. 'b'
  6. >>> letters[-1]
  7. 'z'
  8. >>> letters[-2]
  9. 'y'
  10. >>> letters[25]
  11. 'z'
  12. >>> letters[5]
  13. 'f'

如果指定的偏移量超过了字符串的长度(记住,偏移量从 0 开始增加到字符串长度 -1),会得到一个异常提醒:

  1. >>> letters[100]
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. IndexError: string index out of range

位置索引在其他序列类型(列表和元组)中的使用也是如此,你将在第 3 章见到。

由于字符串是不可变的,因此你无法直接插入字符或改变指定位置的字符。看看当我们试图将 'Henny' 改变为 'Penny' 时会发生什么:

  1. >>> name = 'Henny'
  2. >>> name[0] = 'P'
  3. Traceback (most recent call last):
  4. File "<stdin>", line 1, in <module>
  5. TypeError: 'str' object does not support item assignment

为了改变字符串,我们需要组合使用一些字符串函数,例如 replace(),以及分片操作(很快就会学到):

  1. >>> name = 'Henny'
  2. >>> name.replace('H', 'P')
  3. 'Penny'
  4. >>> 'P' + name[1:]
  5. 'Penny'

2.3.7 使用[start:end:step]分片

分片操作(slice)可以从一个字符串中抽取子字符串(字符串的一部分)。我们使用一对方括号、起始偏移量 start、终止偏移量 end 以及可选的步长 step 来定义一个分片。其中一些可以省略。分片得到的子串包含从 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,分片会默认使用偏移量 -1(结尾)。

我们来创建一个由小写字母组成的字符串:

  1. >>> letters = 'abcdefghijklmnopqrstuvwxyz'

仅仅使用 : 分片等价于使用 0 :(也就是提取整个字符串):

  1. >>> letters[:]
  2. 'abcdefghijklmnopqrstuvwxyz'

下面是一个从偏移量 20 提取到字符串结尾的例子:

  1. >>> letters[20:]
  2. 'uvwxyz'

现在,从偏移量 10 提取到结尾:

  1. >>> letters[10:]
  2. 'klmnopqrstuvwxyz'

下一个例子提取了偏移量从 12 到 14 的字符(Python 的提取操作不包含最后一个偏移量对应的字符):

  1. >>> letters[12:15]
  2. 'mno'

提取最后三个字符:

  1. >>> letters[-3:]
  2. 'xyz'

下面一个例子提取了从偏移量为 18 的字符到倒数第 4 个字符。注意与上一个例子的区别:当偏移量 -3 作为开始位置时,将获得字符 x;而当它作为终止位置时,分片实际上会在偏移量 -4 处停止,也就是提取到字符 w

  1. >>> letters[18:-3]
  2. 'stuvw'

接下来,试着提取从倒数第 6 个字符到倒数第 3 个字符:

  1. >>> letters[-6:-2]
  2. 'uvwx'

如果你需要的步长不是默认的 1,可以在第二个冒号后面进行指定,就像下面几个例子所示。

从开头提取到结尾,步长设为 7:

  1. >>> letters[::7]
  2. 'ahov'

从偏移量 4 提取到偏移量 19,步长设为 3:

  1. >>> letters[4:20:3]
  2. 'ehknqt'

从偏移量 19 提取到结尾,步长设为 4:

  1. >>> letters[19::4]
  2. 'tx'

从开头提取到偏移量 20,步长设为 5:

  1. >>> letters[:21:5]
  2. 'afkpu'

(记住,分片中 end 的偏移量需要比实际提取的最后一个字符的偏移量多 1。)

是不是非常方便?但这还没有完。如果指定的步长为负数,机智的 Python 还会从右到左反向进行提取操作。下面这个例子便从右到左以步长为 1 进行提取:

  1. >>> letters[-1::-1]
  2. 'zyxwvutsrqponmlkjihgfedcba'

事实上,你可以将上面的例子简化为下面这种形式,结果完全一致:

  1. >>> letters[::-1]
  2. 'zyxwvutsrqponmlkjihgfedcba'

分片操作对于无效偏移量的容忍程度要远大于单字符提取操作。在分片中,小于起始位置的偏移量会被当作 0,大于终止位置的偏移量会被当作 -1,就像接下来几个例子展示的一样。

提取倒数 50 个字符:

  1. >>> letters[-50:]
  2. 'abcdefghijklmnopqrstuvwxyz'

提取从倒数第 51 到倒数第 50 个字符:

  1. >>> letters[-51:-50]
  2. ''

从开头提取到偏移量为 69 的字符:

  1. >>> letters[:70]
  2. 'abcdefghijklmnopqrstuvwxyz'

从偏移量为 70 的字符提取到偏移量为 71 的字符:

  1. >>> letters[70:71]
  2. ''

2.3.8 使用len()获得长度

到目前为止,我们已经学会了使用许多特殊的标点符号(例如 +)对字符串进行相应操作。但标点符号只有有限的几种。从现在开始,我们将学习使用 Python 的内置函数。所谓函数指的是可以执行某些特定操作的有名字的代码。

len() 函数可用于计算字符串包含的字符数:

  1. >>> len(letters)
  2. 26
  3. >>> empty = ""
  4. >>> len(empty)
  5. 0

也可以对其他的序列类型使用 len(),这些内容都被放在第 3 章里讲解。

2.3.9 使用split()分割

与广义函数 len() 不同,有些函数只适用于字符串类型。为了调用字符串函数,你需要输入字符串的名称、一个点号,接着是需要调用的函数名,以及需要传入的参数string.function(arguments)。4.7 节会学习更多关于函数的内容。

使用内置的字符串函数 split() 可以基于分隔符将字符串分割成由若干子串组成的列表。所谓列表(list)是由一系列值组成的序列,值与值之间由逗号隔开,整个列表被方括号所包裹。

  1. >>> todos = 'get gloves,get mask,give cat vitamins,call ambulance'
  2. >>> todos.split(',')
  3. ['get gloves', 'get mask', 'give cat vitamins', 'call ambulance']

上面例子中,字符串名为 todos,函数名为 split(),传入的参数为单一的分隔符 ','。如果不指定分隔符,那么 split() 将默认使用空白字符——换行符、空格、制表符。

  1. >>> todos.split()
  2. ['get', 'gloves,get', 'mask,give', 'cat', 'vitamins,call', 'ambulance']

即使不传入参数,调用 split() 函数时仍需要带着括号,这样 Python 才能知道你想要进行函数调用。

2.3.10 使用join()合并

可能你已经猜到了,join() 函数与 split() 函数正好相反:它将包含若干子串的列表分解,并将这些子串合成一个完整的大的字符串。join() 的调用顺序看起来有点别扭,与 split() 相反,你需要首先指定粘合用的字符串,然后再指定需要合并的列表:string.join(list)。因此,为了将列表 lines 中的多个子串合并成完整的字符串,我们应该使用语句:'\n'.join(lines)。下面的例子将列表中的名字通过逗号及空格粘合在一起:

  1. >>> crypto_list = ['Yeti', 'Bigfoot', 'Loch Ness Monster']
  2. >>> crypto_string = ', '.join(crypto_list)
  3. >>> print('Found and signing book deals:', crypto_string)
  4. Found and signing book deals: Yeti, Bigfoot, Loch Ness Monster

2.3.11 熟悉字符串

Python 拥有非常多的字符串函数。这一节将探索其中最常用的一些。我们的测试对象是下面的字符串,它源自纽卡斯尔伯爵 Margaret Cavendish 的不朽名篇 What Is Liquid?

  1. >>> poem = '''All that doth flow we cannot liquid name
  2. Or else would fire and water be the same;
  3. But that is liquid which is moist and wet
  4. Fire that property can never get.
  5. Then 'tis not cold that doth the fire put out
  6. But 'tis the wet that makes it die, no doubt.'''

先做个小热身,试着提取开头的 13 个字符(偏移量为 0 到 12):

  1. >>> poem[:13]
  2. 'All that doth'

这首诗有多少个字符呢?(计入空格和换行符。)

  1. >>> len(poem)
  2. 250

这首诗是不是以 All 开头呢?

  1. >>> poem.startswith('All')
  2. True

它是否以 That's all, folks! 结尾?

  1. >>> poem.endswith('That\'s all, folks!')
  2. False

接下来,查一查诗中第一次出现单词 the 的位置(偏移量):

  1. >>> word = 'the'
  2. >>> poem.find(word)
  3. 73

以及最后一次出现 the 的偏移量 :

  1. >>> poem.rfind(word)
  2. 214

the 在这首诗中出现了多少次?

  1. >>> poem.count(word)
  2. 3

诗中出现的所有字符都是字母或数字吗?

  1. >>> poem.isalnum()
  2. False

并非如此,诗中还包括标点符号。

2.3.12 大小写与对齐方式

在这一节,我们将介绍一些不那么常用的字符串函数。我们的测试字符串如下所示:

  1. >>> setup = 'a duck goes into a bar...'

将字符串收尾的 . 都删除掉:

  1. >>> setup.strip('.')
  2. 'a duck goes into a bar'

第 2 章 Python 基本元素:数字、字符串和变量 - 图7 由于字符串是不可变的,上面这些例子实际上没有一个对 setup 真正做了修改。它们都仅仅是获取了 setup 的值,进行某些操作后将操作结果赋值给了另一个新的字符串而已。

让字符串首字母变成大写:

  1. >>> setup.capitalize()
  2. 'A duck goes into a bar...'

让所有单词的开头字母变成大写:

  1. >>> setup.title()
  2. 'A Duck Goes Into A Bar...'

让所有字母都变成大写:

  1. >>> setup.upper()
  2. 'A DUCK GOES INTO A BAR...'

将所有字母转换成小写:

  1. >>> setup.lower()
  2. 'a duck goes into a bar...'

将所有字母的大小写转换:

  1. >>> setup.swapcase()
  2. 'A DUCK GOES INTO A BAR...'

再来看看与格式排版相关的函数。这里,我们假设例子中的字符串被排版在指定长度(这里是 30 个字符)的空间里。

在 30 个字符位居中:

  1. >>> setup.center(30)
  2. ' a duck goes into a bar... '

左对齐:

  1. >>> setup.ljust(30)
  2. 'a duck goes into a bar... '

右对齐:

  1. >>> setup.rjust(30)
  2. ' a duck goes into a bar...'

第 7 章更加详细地介绍字符串格式以及转换,包括如何使用 % 以及 format()

2.3.13 使用replace()替换

使用 replace() 函数可以进行简单的子串替换。你需要传入的参数包括:需要被替换的子串,用于替换的新子串,以及需要替换多少处。如果你省略了最后的参数 count,replace() 将替换所有实例(匹配到的字符串)。在这个例子中,只匹配到并且替换了一个字符串:

  1. >>> setup.replace('duck', 'marmoset')
  2. 'a marmoset goes into a bar...'

修改最多 100 处:

  1. >>> setup.replace('a ', 'a famous ', 100)
  2. 'a famous duck goes into a famous bar...'

当你准确地知道想要替换的子串是什么样子时,replace() 是个非常不错的选择。但使用时一定要小心!在上面第二个例子中,如果我们粗心地把需要替换的子串写成了单个字符的 'a' 而不是两个字符的 'a 'a 后面跟着一个空格)的话,会错误地将所有单词中出现的 a 也一并替换了:

  1. >>> setup.replace('a', 'a famous', 100)
  2. 'a famous duck goes into a famous ba famousr...'

有时,你可能想确保被替换的子串是一个完整的词,或者某一个词的开头,等等。在这种情况下,你需要借助正则表达式,相关内容将在第 7 章进行介绍。

2.3.14 更多关于字符串的内容

Python 还内置了许多在此没有涉及的字符串函数,其中有一些你可以在之后的几章见到。除此之外,你可以在标准文档链接(https://docs.python.org/3/library/stdtypes.html#string-methods)获取所有字符串函数的细节。

2.4 练习

本章介绍了 Python 最基本的元素:数字、字符串以及变量。我们试着在交互式解释器里完成一些相关的练习。

(1) 一个小时有多少秒?这里,请把交互式解释器当作计算器使用,将每分钟的秒数(60)乘以每小时的分钟数(60)得到结果。

(2) 将上一个练习得到的结果(每小时的秒数)赋值给名为 seconds_per_hour 的变量。

(3) 一天有多少秒?用你的 seconds_per_hour 变量进行计算。

(4) 再次计算每天的秒数,但这一次将结果存储在名为 seconds_per_day 的变量中。

(5) 用 seconds_per_day 除以 seconds_per_hour,使用浮点除法(/)。

(6) 用 seconds_per_day 除以 seconds_per_hour,使用整数除法(//)。除了末尾的 .0,本练习所得结果是否与前一个练习用浮点数除法得到的结果一致?