Python入门实践

​ IDE:集成开发环境。一般包括代码编辑器,调试器和图形用户界面工具。 ![Thonny](https://cdn.jsdelivr.net/gh/OldSuns/Figurebed@main/img/2021/11/13_163116.png)

​ IDLE:开发Python程序的基本IDE(包括交互式编辑器Python Shell和编辑器)

Powershell

在我们接下来的学习中,代码的格式大多是交互式编辑器的格式(以>>>开头),而我们在实际应用中则需要使用文本编辑器来编写Python代码。在目前的使用中,这两者最大的区别在于:交互式编辑器中使用一个变量会直接输出变量中存储的结果,但文本编辑器不行。示例如下:

image-20211113163529060

可以看到,命令行里并没有出现该变量的内容。我们需要加入print语句以实现正常的输出。

image-20211113163710607

那么在交互式编辑器如何呢?

image-20211113163645327

正常输出,没有问题。

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

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

  • 布尔型(表示真假的类型,仅包含 True 和 False 两种取值)
  • 整型(整数,例如 42、100000000)
  • 浮点型(小数,例如 3.14159,或用科学计数法表示的数字,例如 1.0e8,它表示 1 乘以 10 的 8 次方,也可写作100000000.0)
  • 字符串型(字符组成的序列)

变量、名字和对象

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

变量

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

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

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

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

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

对象盒子

试着在交互式解释器中执行下面的操作:
(1) 和之前一样,将 7 赋值给名称 a,这样就成功创建了一个包含整数 7 的对象;
(2) 打印 a 的值;
(3) 将 a 赋值给 b,这相当于给刚刚创建的对象又贴上了标签 b;
(4) 打印 b 的值。

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

变量名不允许以数字开头。此外,Python 中以下划线开头的名字有特殊的含义。变量名只能包含以下字符:

  • 小写字母(a~z)
  • 大写字母(A~Z)
  • 数字(0~9)
  • 下划线(_)

运算符

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

基数

在 Python 中,整数默认使用十进制数(以 10 为底),除非你在数字前添加前缀,显式地指定使用其他基数(base)。

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

  • 0b 或 0B 代表二进制(以 2 为底)
  • 0o 或 0O 代表八进制(以 8 为底)
  • 0x 或 0X 代表十六进制(以 16 为底)

Python 解释器会打印出它们对应的十进制整数。我们来试试这些不同进制的数。如:

1
2
3
4
5
6
>>>0b10	#二进制
2
>>>0o10 #八进制
8
>>>0x10 #十六进制
16

类型转换

我们可以方便地使用 int() 函数将其他的 Python 数据类型转换为整型。它会保留传入数据的整数部分并舍去小数部分。 Python 里最简单的数据类型是布尔型,它只有两个可选值:True 和 False。当转换为整数时,它们分别代表 1 和 0:

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

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

1
2
3
4
5
6
>>> int(98.6)	#浮点数
98
>>> int(1.0e4) #科学计数法
10000
>>> int("-23") #字符串
-23

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

1
2
3
4
>>> int("OldSun")
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'OldSun'

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

1
2
3
4
>>> int("98.6")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '98.6'
1
2
>>> int(float("98.6"))
98

字符串

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

1
2
>>> "OldSun"
'OldSun'

交互式解释器输出的字符串永远是用单引号包裹的,但无论使用哪种引号,Python 对字符串的处理方式都是一样的,没有任何区别。
既然如此,为什么要使用两种引号?这么做的好处是可以创建本身就包含引号的字符串,而不用使用转义符。可以在双引号
包裹的字符串中使用单引号,或者在单引号包裹的字符串中使用双引号:

1
2
>>> "'wow,',said the OldSun"
"'wow,',said the OldSun"

你还可以使用连续三个单引号 ‘’’,或者三个双引号 “”” 创建多行字符串:

image-20211113165956709

你会发现解释器打印出来的和print不太一样,这是因为print会自动转换转义符,比如把“\n”自动替换成换行。关于转义符的内容你会在之后看到。

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

1
2
>>> str(98.6)
'98.6'

转义符:使用\转义

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

1
2
3
4
5
6
>>> me = 'OldSun\nhave\nno\nPython' 
>>> print(me)
OldSun
have
no
Python

转义符 \t(tab 制表符)常用于对齐文本:

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

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

1
2
>>> testimony = "\"I did nothing!\" he said. \"Not that either! Or the other thing.\"" >>> print(testimony) 
"I did nothing!" he said. "Not that either! Or the other thing."

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

使用[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 个字符提取一个

字符串函数

使用len( )获得长度

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

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

使用split()分割

使用内置的字符串函数 split() 可以基于分隔符将字符串分割成由若干子串组成的列表。

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

使用join()合并

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

1
2
3
4
>>> list = ['OldSun', 'Love', 'Youngun']
>>> string = ', '.join(list)
>>> print(string)
OldSun, Love, Youngun

Python 容器:列表、元组、字典与集合

列表

使用[]或list()创建列表

列表可以由零个或多个元素组成,元素之间用逗号分开,整个列表被方括号所包裹:

1
>>> a=[1,2,3]

也可以使用 list() 函数来创建一个空列表:

1
2
3
>>> list=list()
>>> list
[]

包含列表的列表

1
2
3
4
5
6
7
8
9
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> c=[a,b]
>>> c
[[1, 2, 3], [4, 5, 6]]
>>> c[0]
[1, 2, 3]
>>> c[0][1]
2

切片

我们可以使用[offset](偏移量)查找或修改元素:

1
2
3
4
5
6
7
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a[1]
2
>>> b[0]=13
>>> b
[13, 5, 6]

列表的偏移量必须是合法有效的。

同时,我们也可以切片查找元素:

1
2
3
4
5
6
7
8
>>> a=[1,2,3]
>>> a[0:2]
[1, 2]
>>> a[0:2]=[3,4]
>>> a
[3, 4, 3]
>>> b[::-1]
[6, 5, 13]

添加元素

使用append()添加元素至尾部

传统的向列表中添加元素的方法是利用 append() 函数将元素一个个添加到尾部。例如,我们想把5添加到a列表中:

1
2
3
4
5
>>> a
[3, 4, 3]
>>> a.append(5)
>>> a
[3, 4, 3, 5]

使用insert()在指定位置插入元素

append() 函数只能将新元素插入到列表尾部,而使用 insert() 可以将元素插入到列表的任意位置。指定偏移量为 0 可以插入列表头部。如果指定的偏移量超过了尾部,则会插入到列表最后,就如同 append() 一样,这一操作不会产生 Python 异常。

1
2
3
4
5
>>> a
[3, 4, 3, 5]
>>> a.insert(2,'8')
>>> a
[3, 4, '8', 3, 5]

删除元素

使用del删除指定位置的元素

1
2
3
4
5
6
7
8
9

1
2
3
4
>>> a=[3,4,'8',3,5]
>>> del a[3]
>>> a
[3, 4, '8', 5]

使用remove()删除具有指定值的元素

如果不确定或不关心元素在列表中的位置,可以使用 remove() 根据指定的值删除第一个被匹配到的元素。

1
2
3
4
5
>>> a
[3, 4, '8', 5]
>>> a.remove(4)
>>> a
[3, '8', 5]

使用pop()获取并删除指定位置的元素

使用 pop() 同样可以获取列表中指定位置的元素,但在获取完成后,该元素会被自动删除。 如果你为 pop() 指定了偏移量,它会返回偏移量对应位置的元素;如果不指定,则默认使用 -1。因此,pop(0) 将返回列表的头元素,而 pop() 或pop(-1) 则会返回列表的尾元素:

1
2
3
4
5
6
7
8
9
>>> a=[1,2,3,4,5,8]
>>> a.pop()
8
>>> a
[1, 2, 3, 4, 5]
>>> a.pop(3)
4
>>> a
[1, 2, 3, 5]

使用index()查询具有特定值的元素位置

1
2
3
4
>>> a
[1, 2, 3, 5]
>>> a.index(2)
1

使用count()记录特定值出现的次数

1
2
3
4
5
>>> a=[1,2,3,3,5,3,4,6]
>>> a.count(3)
3
>>> a.count(6)
1

使用sort()重新排列元素

在实际应用中,经常需要将列表中的元素按值排序,而不是按照偏移量排序。Python 为此提供了两个函数:

  • 列表方法 sort() 会对原列表进行排序,改变原列表内容。
  • 通用函数 sorted() 则会返回排好序的列表副本,原列表内容不变。

如果列表中的元素都是数字,它们会默认地被排列成从小到大的升序。如果元素都是字符串,则会按照字母表顺序排列。

1
2
3
4
>>> a=[27,13,89,5]
>>> a.sort()
>>> a
[5, 13, 27, 89]
1
2
3
4
5
6
>>> a=[27,13,89,5]
>>> b=sorted(a)
>>> a
[27, 13, 89, 5]
>>> b
[5, 13, 27, 89]

使用len()获取长度

len() 可以返回列表长度:

1
2
>>> len(a)
4

使用=赋值,使用copy()复制

如果将一个列表赋值给了多个变量,改变其中的任何一处会造成其他变量对应的值也被修改,如下所示:

1
2
3
4
5
6
7
8
9
>>> a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> b = a
>>> b
[1, 2, 3]
>>> a[0] = 'surprise'
>>> a
['surprise', 2, 3]

现在,b 的值是什么?它会保持 [1, 2, 3],还是改变为 [‘surprise’, 2, 3] ?试一试:

1
2
>>> b
['surprise', 2, 3]

还记得第 2 章中贴标签的比喻吗? b 与 a 实际上指向的是同一个对象,因此,无论我们是通过 a 还是通过 b 来修改列表的内容,其结果都会作用于双方。

通过下面任意一种方法,都可以将一个列表的值复制到另一个新的列表中:

  • 列表 copy()
  • 函数 list() 转换函数
  • 列表分片 [:]
1
2
3
4
>>> a = [1, 2, 3]
>>> b = a.copy()
>>> c = list(a)
>>> d = a[:]

改变 a 不影响 b、c 和 d 的复制

1
2
3
4
5
6
7
8
>>> a[0] = 'integer lists are boring'
>>> a ['integer lists are boring', 2, 3]
>>> b
[1, 2, 3]
>>> c
[1, 2, 3]
>>> d
[1, 2, 3]

元组

与列表类似,元组也是由任意类型元素组成的序列。与列表不同的是,元组是不可变的,这意味着一旦元组被定义,将无法再进行增加、删除或修改元素等操作。因此,元组就像是一个常量列表。

下面的例子展示了创建元组的过程,它的语法与我们直观上预想的有一些差别。
可以用 () 创建一个空元组:

1
2
3
>>> empty_tuple = ()
>>> empty_tuple
()

创建包含一个或多个元素的元组时,每一个元素后面都需要跟着一个逗号,即使只包含一个元素也不能省略:

1
2
3
>>> king = 'OldSun',
>>> king
('OldSun',)

如果创建的元组所包含的元素数量超过 1,最后一个元素后面的逗号可以省略:

1
2
3
>>> tuple = 'max', 'mid', 'min'
>>> tuple
('max', 'mid', 'min')

Python 的交互式解释器输出元组时会自动添加一对圆括号。你并不需要这么做——定义元组真正靠的是每个元素的后缀逗号 ——但如果你习惯添加一对括号也无可厚非。可以用括号将所有元素包裹起来,这会使得程序更加清晰:

1
2
3
>>> tuple = ('max', 'mid', 'min')
>>> tuple
('max', 'mid', 'min')

可以一口气将元组赋值给多个变量:

1
2
3
4
5
6
7
8
>>> tuple = ('max', 'mid', 'min')
>>> a, b, c=tuple
>>> a
'max'
>>> b
'mid'
>>> c
'min'

有时这个过程被称为元组解包。
可以利用元组在一条语句中对多个变量的值进行交换,而不需要借助临时变量:

1
2
3
4
5
6
7
>>> a="Tuple"
>>> b="OldSun"
>>> a,b=b,a
>>> a
'OldSun'
>>> b
'Tuple'

字典

字典(dictionary)与列表类似,但其中元素的顺序无关紧要,因为它们不是通过像 0 或 1 的偏移量访问的。取而代之,每个元素拥有与之对应的互不相同的键(key),需要通过键来访问元素。键通常是字符串,但它还可以是 Python 中其他任意的不可变类型:布尔型、整型、浮点型、元组、字符串,以及其他一些在后面的内容中会见到的类型。字典是可变的,因此你可以增加、删除或修改其中的键值对。字典是无序的,它没有固定的顺序。

用大括号({})将一系列以逗号隔开的键值对(key:value)包裹起来即可进行字典的创建。记住,字典的键必须保证互不相同,否则后加入的同键的值会覆盖前一个。

1
2
3
4
5
6
>>> a={"name":"OldSun","age":16}
>>> a['name']
'OldSun'
>>> a["age"]=17
>>> a
{'name': 'OldSun', 'age': 17}

集合

集合就像舍弃了值,仅剩下键的字典一样。键与键之间也不允许重复。如果你仅仅想知道某一个元素是否存在而不关心其他的,使用集合是个非常好的选择。如果需要为键附加其他信息的话,建议使用字典。
很久以前,当你还在小学时,可能就学到过一些关于集合论的知识。当然,如果你的学校恰好跳过了这一部分内容(或者实际上教了,但你当时正好盯着窗外发呆,我小时候就总是这样开小差),可以仔细看看下图,它展示了我们对于集合进行的最基本的操作——交和并。

image-20211113182449113

你可以使用 set() 函数创建一个集合,或者用大括号将一系列以逗号隔开的值包裹起来,如下所示:

1
2
3
4
5
6
7
8
9
>>> empty_set = set()
>>> empty_set
set()
>>> even_numbers = {0, 2, 4, 6, 8}
>>> even_numbers
{0, 8, 2, 4, 6}
>>> odd_numbers = {1, 3, 5, 7, 9}
>>> odd_numbers
{9, 3, 1, 5, 7}

与字典的键一样,集合是无序的。

由于 [ ] 能创建一个空列表,你可能期望 {} 也能创建空集。但事实上,{ } 会创建一个空字典,这也是为什么
交互式解释器把空集输出为 set() 而不是 {}。为何如此?没有什么特殊原因,仅仅是因为字典出现的比较早并抢先占据 了花括号。

使用set()将其他类型转换为集合

你可以利用已有列表、字符串、元组或字典的内容来创建集合,其中重复的值会被丢弃。

首先来试着转换一个包含重复字母的字符串:

1
2
>>> set( 'letters' )
{'l', 'e', 't', 'r', 's'}

注意,上面得到的集合中仅含有一个 ‘e’ 和一个 ‘t’,尽管字符串 ‘letters’ 里各自包含两个。列表和元组都可以用set()来建立集合。要注意,当字典作为参数传入 set() 函数时,只有键会被使用:

1
2
>>> set( {'apple': 'red', 'orange': 'orange', 'cherry': 'red'} )
{'apple', 'cherry', 'orange'}

集合运算符

之后的例子会涵盖所有的集合运算符。有些运算使用特殊定义过的标点,另一些则使用函数,还有一些运算两者都可使用。
这里使用测试集合 a(包含 1 和 2),以及 b(包含 2 和 3):

1
2
>>> a = {1,2}
>>> b = {2,3}

可以通过使用特殊标点符号 & 或者集合函数 intersection() 获取集合的交集(两集合共有元素),如下所示:

1
2
3
4
>>> a & b
{2}
>>> a.intersection(b)
{2}

下面的例子中,使用 | 或者 union() 函数来获取集合的并集(至少出现在一个集合中的元素):

1
2
3
4
>>> a | b
{1, 2, 3}
>>> a.union(b)
{1, 2, 3}

使用字符 - 或者 difference() 可以获得两个集合的差集(出现在第一个集合但不出现在第二个集合):

1
2
3
4
>>> a - b
{1}
>>> a.difference(b)
{1}

使用 ^ 或者 symmetric_difference() 可以获得两个集合的异或集(仅在两个集合中出现一次):

1
2
3
4
>>> a ^ b
{1, 3}
>>> a.symmetric_difference(b)
{1, 3}

使用 <= 或者 issubset() 可以判断一个集合是否是另一个集合的子集(第一个集合的所有元素都出现在第二个集合中):

1
2
3
4
>>> a <= b
False
>>> a.issubset(b)
False

同理,< 可以判断真子集,>= 可以判断超集,> 可以判断真超集。

代码结构

循环

我们这里略过了if、elif和else的分支判断。相信大家都会的。

While循环

Python 中最简单的循环机制是 while。打开交互式解释器,执行下面的从 1 打印到 5 的简单循环:

1
2
3
4
5
6
7
8
9
10
11
>>> count=1
>>> while count<=5:
... print(count)
... count+=1
...
1
2
3
4
5
>>>

首先将变量 count 的值赋为 1,while 循环比较 count 的值和 5 的大小关系,如果 count 小于等于 5 的话继续执行。在 环内部,打印 count 变量的值,然后使用语句 count += 1 对 count 进行自增操作,返回到循环的开始位置,继续比较count 和 5 的大小关系。现在,count 变量的值为 2,因此 while 循环内部的代码会被再次执行,count 值变为 3 。

在 count 从 5 自增到 6 之前循环一直进行。然后下次判断时,count <= 5 的条件不满足,while 循环结束。Python 跳到循环下面的代码。

使用break跳出循环

如果你想让循环在某一条件下停止,但是不确定在哪次循环跳出,可以在无限循环中声明 break 语句。这次,我们通过Python 的 input() 函数从键盘输入一行字符串,然后将字符串转换成整数形式后加一。当输入的一行仅含有字符 q 时,跳出循环 :

1
2
3
4
5
6
7
8
9
10
11
>>> while True:
stuff = input("Number to plus 1 [ type q to quit ]: ")
if stuff == "q":
break
print(int(stuff)+1)

Number to plus 1 [ type q to quit ]: 3
4
Number to plus 1 [ type q to quit ]: 4
5
Number to plus 1 [ type q to quit ]: q

使用continue跳到循环开始

有时我们并不想结束整个循环,仅仅想跳到下一轮循环的开始。下面是一个编造的例子:读入一个整数,如果它是奇数则输出它的平方数;如果是偶数则跳过。同样使用 q 来结束循环,代码中加上了适当的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> while True:
... value = input("Int , please [q to quit]: ")
... if value == "q": #停止循环
... break
... num = int(value)
... if num % 2 == 0: #判断偶数
... continue
... print(num, "squared is", num*num)
...
Int , please [q to quit]: 3
3 squared is 9
Int , please [q to quit]: 4
Int , please [q to quit]: 5
5 squared is 25
Int , please [q to quit]: q
>>>

循环外使用else

如果 while 循环正常结束(没有使用 break 跳出),程序将进入到可选的 else 段。当你使用循环来遍历检查某一数据结构时,找到满足条件的解使用 break 跳出;循环结束,即没有找到可行解时,将执行 else 部分代码段:

1
2
3
4
5
6
7
8
9
10
>>> while position < len(numbers):
... number = numbers[position]
... if number %2 == 0:
... print("Found even number",number)
... break
... position+=1
... else:
... print("No even number found")
...
No even number found

使用for迭代

Python 频繁地使用迭代器。它允许在数据结构长度未知和具体实现未知的情况下遍历整个数据结构,并且支持迭代快速读写中的数据,以及允许不能一次读入计算机内存的数据流的处理。

1
2
3
4
5
6
7
8
>>> a=[1,3,5,7]
>>> for i in a:
print(i)

1
3
5
7

列表、字符串、元组、字典、集合等都是 Python 中可迭代的对象。元组或者列表在一次迭代过程产生一项,而字符串迭代会产生一个字符,如下所示:

1
2
3
4
5
6
>>> for i in "cat":
print(i)

c
a
t

对一个字典(或者字典的 keys() 函数)进行迭代将返回字典中的键。

1
2
3
4
5
6
7
>>> dic={"a":3,"b":7,"me":"you"}
>>> for i in dic: #等同于for i in dic.keys():
print(i)

a
b
me
1
2
3
4
5
6
>>> for i in dic.values():
print(i)

3
7
you
1
2
3
4
5
6
7
8
9
10
11
12
>>> for i in dic.items():
print(i)

('a', 3)
('b', 7)
('me', 'you')
>>> for i,j in dic.items():
print("key=",i,"value=",j)

key= a value= 3
key= b value= 7
key= me value= you

在 for 循环中跳出的用法和在 while 循环中是一样的。

在一个循环中使用 continue 会跳到下一次的迭代开始,这一点和 while 循环也是类似的。

类似于 while,for 循环也可以使用可选的 else 代码段,用来判断 for 循环是否正常结束(没有调用 break 跳出)。若正常结束,则会执行 else 段。

推导式

推导式是从一个或者多个迭代器快速简洁地创建数据结构的一种方法。它可以将循环和条件判断结合,从而避免语法冗长的代码。会使用推导式有时可以说明你已经超过 Python 初学者的水平。也就是说,使用推导式更像 Python 风格。

列表推导式

你可以从 1 到 5 创建一个整数列表,每次增加一项,如下所示:

1
2
3
4
5
6
7
8
>>> number_list = []
>>> number_list.append(1)
>>> number_list.append(2)
>>> number_list.append(3)
>>> number_list.append(4)
>>> number_list.append(5)
>>> number_list
[1, 2, 3, 4, 5]

或者,可以结合 range() 函数使用一个迭代器:

1
2
3
4
5
6
>>> number_list = []
>>> for number in range(1, 6):
... number_list.append(number)
...
>>> number_list
[1, 2, 3, 4, 5]

或者,直接把 range() 的返回结果放到一个列表中:

1
2
3
>>> number_list = list(range(1, 6))
>>> number_list
[1, 2, 3, 4, 5]

上面这些方法都是可行的 Python 代码,会得到相同的结果。然而,更像 Python 风格的创建列表方式是使用列表推导。最简单的形式如下所示:

1
[ expression for item in iterable ]

下面的例子将通过列表推导创建一个整数列表:

1
2
3
>>> number_list = [number for number in range(1,6)]
>>> number_list
[1, 2, 3, 4, 5]

在第一行中,第一个 number 变量为列表生成值,也就是说,把循环的结果放在列表 number_list 中。第二个 number 为循环变量。其中第一个 number 可以为表达式,试试下面改编的例子:

1
2
3
>>> number_list = [number**2 for number in range(1,6)]
>>> number_list
[1, 4, 9, 16, 25]

列表推导把循环放在方括号内部。这种例子和之前碰到的不大一样,但却是更为常见的方式。同样,列表推导也可以像下面的例子加上条件表达式:

1
[expression for item in iterable if condition]

现在,通过推导创建一个在 1 到 5 之间的奇数列表(当 number % 2 为真时,代表奇数;为假时,代表偶数):

1
2
3
>>> a_list = [number for number in range(1,6) if number % 2 == 1]
>>> a_list
[1, 3, 5]

如果在对应的推导中有多个 for … 语句,可以使用以下的循环嵌套:

1
2
3
4
5
6
7
8
9
10
>>> cells = [(row, col) for row in range(1,4) for col in range(1,3)] 
>>> for cell in cells:
print(cell)

(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)

另外,在对 cells 列表进行迭代时可以通过元组拆封将变量 row 和 col 的值分别取出:

1
2
3
4
5
6
7
8
9
>>> for row,col in cells:
print(row,col)

1 1
1 2
2 1
2 2
3 1
3 2

其中,在列表推导中 for row … 和 for col … 都可以有自己单独的 if 条件判断。

字典推导式

除了列表,字典也有自己的推导式。最简单的例子就像:

1
{ key_expression : value_expression for expression in iterable if condition}

类似于列表推导,字典推导也有 if 条件判断以及多个 for 循环迭代语句:

1
2
3
4
>>> word = 'letters'
>>> letter_counts = {letter: word.count(letter) for letter in word}
>>> letter_counts
{'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}

程序中,对字符串 ‘letters’ 中出现的字母进行循环,计算出每个字母出现的次数。对于程序执行来说,两次调用word.count(letter) 浪费时间,因为字符串中 t 和 e 都出现了两次,第一次调用 word.count() 时已经计算得到相应的值。下面的例子会解决这个小问题,更符合 Python 风格:

1
2
3
4
>>> word = 'letters'
>>> letter_counts = {letter: word.count(letter) for letter in set(word)}
>>> letter_counts
{'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}

字典键的顺序和之前的例子是不同的,因为是对 set(word) 集合进行迭代的,而前面的例子是对 word 字符串迭代。

集合推导式

集合也不例外,同样有推导式。最简单的版本和之前的列表、字典推导类似:

1
{expression for expression in iterable if condition }
1
2
3
>>> a_set = {number for number in range(1,6) if number % 3 == 1} 
>>> a_set
{1,4}

元组是没有推导式的。你可能认为将列表推导式中的方括号变成圆括号就可以定义元组推导式,但事实并非如此。这会生成一个生成器推导式。感兴趣的可以自行搜索。

用户输入

函数input()

函数input()让程序暂停运行,等待用户输入一些文本。获取用户输入后,Python将其存储在一个变量中,以方便你使用。

例如,下面的程序让用户输入一些文本,再将这些文本呈现给用户:

1
2
message = input("Tell me something, and I will repeat it back to you: ") 
print(message)

函数input()接受一个参数:即要向用户显示的提示或说明,让用户知道该如何做。在这个示例中,Python运行第1行代码时,用户将看到提示Tell me something, and I will repeat it back to you:。程序等待用户输入,并在用户按回车键后继续运行。输入存储在变量message中,接下来的print(message)将输入呈现给用户:

1
2
Tell me something, and I will repeat it back to you: Hello everyone!
Hello everyone!

函数

定义函数

下面是一个打印问候语的简单函数,名为greet_user():

1
2
3
4
5
def greet_user(): 
"""显示简单的问候语"""
print("Hello!")

greet_user()

这个示例演示了最简单的函数结构。第一行代码使用关键字def来告诉Python你要定义一个函数。这是函数定义,向Python 指出了函数名,还可能在括号内指出函数为完成其任务需要什么样的信息。在这里,函数名为greet_user(),它不需要任何信息就能完成其工作,因此括号是空的(即便如此,括号也必不可少)。最后,定义以冒号结尾。

紧跟在def greet_user():后面的所有缩进行构成了函数体。处的文本是被称为文档字符串(docstring)的注释,描述了函数是做什么的。文档字符串用三引号括起,Python使用它们来生成有关程序中函数的文档。

代码行print("Hello!")是函数体内的唯一一行代码,greet_user()只做一项工作:打印Hello!。

要使用这个函数,可调用它。函数调用让Python执行函数的代码。要调用函数,可依次指定函数名以及用括号括起的必要信息。由于这个函数不需要任何信息,因此调用它时只需输入greet_user()即可。和预期的一样,它打印Hello!:

1
2
>>> greet_user()
Hello!

向函数传递信息

只需稍作修改,就可以让函数greet_user()不仅向用户显示Hello!,还将用户的名字用作抬头。为此,可在函数定义def greet_user()的括号内添加username。通过在这里添加username,就可让函数接受你给username指定的任何值。现在,这个函数要求你调用它时给username指定一个值。调用greet_user()时,可将一个名字传递给它,如下所示:

1
2
3
def greet_user(username):
"""显示简单的问候语"""
print("Hello, " + username.title() + "!") #.title()方法可以是字符串开头大写
1
2
>>> greet_user('oldsun')
Hello, Oldsun!

实参和形参

前面定义函数greet_user()时,要求给变量username指定一个值。调用这个函数并提供这种信息(人名)时,它将打印相应的问候语。

在函数greet_user()的定义中,变量username是一个形参——函数完成其工作所需的一项信息。在代码greet_user(‘oldsun’)中,值’oldsun’是一个实参。实参是调用函数时传递给函数的信息。我们调用函数时,将要让函数使用的信息放在括号内。在greet_user(‘oldsun’)中,将实参’oldsun’传递给了函数greet_user(),这个值被存储在形参username中。

传递实参

鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多,可使用位置实参,这要求实参的顺序与形参的顺序相同;也可使用关键字实参,其中每个实参都由变量名和值组成;还可使用列表和字典。下面来依次介绍这些方式。

位置实参

你调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此,最简单的关联方式是基于实参的顺序。这种关联方式被称为位置实参。

为明白其中的工作原理,来看一个字符串相加的函数。这个函数将两个字符串进行拼接,如下所示:

1
2
3
4
5
6
def str_sum(str1,str2):
sum = str1+str2
return sum

a=" I am "
b="OldSun"

使用位置实参来调用函数时,如果实参的顺序不正确,结果可能出乎意料:

1
2
3
4
>>> str_sum(a,b)
' I am OldSun'
>>> str_sum(b,a)
'OldSun I am '

因此,请务必确认函数调用中实参的顺序与函数定义中形参的顺序一致。

关键字实参

关键字实参是传递给函数的名称—值对。你直接在实参中将名称和值关联起来了,因此向函数传递实参时不会混淆。关键字实参让你无需考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。以上面定义的函数为例:

1
2
3
4
>>> str_sum(str1=a,str2=b)
' I am OldSun'
>>> str_sum(str2=b,str1=a)
' I am OldSun'

关键字实参的顺序无关紧要,因为Python知道各个值该存储到哪个形参中。下面两个函数调用是等效的:

使用关键字实参时,务必准确地指定函数定义中的形参名。

默认值

编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python将使用指定的实参值;否则,将使用形参的默认值。因此,给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值可简化函数调用,还可清楚地指出函数的典型用法。

我们定义一个两数相加的函数,在其中我们设置形参arg2默认为10:

1
2
3
def sum(arg1,arg2=10):
sum =arg1+arg2
return sum
1
2
3
4
>>> sum(10)
20
>>> sum(10,30)
40

使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参。这让Python依然能够正确地解读位置实参。

返回值

函数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值。函数返回的值被称为返回值。在函数中,可使用return语句将值返回到调用函数的代码行。返回值让你能够将程序的大部分繁重工作移到函数中去完成,从而简化主程序。简单的例子便是上面的str_sum函数和sum函数。当然,我们也可以让return带上表达式:

1
2
3
def sum(arg1,arg2=10):
sum =arg1+arg2
return sum+10
1
2
>>> sum(13,40)
63