9  Python 入门

参考资料

Note

本章内容源自上海对外经贸大学司继春老师的讲义,略有改动。

参考教程:

9.1 数值类型和计算

Python 语言简单易懂,多数语句都非常符合人类语言的直觉。比如,计算两个数字相加:

3+4
7

我们也可以把它赋值给一个变量,然后再调用 (比如,打印出来):

a = 3 + 4
print(a)
7

此处的 print() 是一个函数,表示打印出括号内的内容。Python 中的多数运算和操作都是通过函数来实现的。这里,我们采用 print() 函数来打印一些常用的数学运算的结果:

print(2*5)     # 乘法
print(2**5)    # 乘方
print(2**0.5)  # 平方根

print(30/4)    # 除法
print(30//4)   # 整数除法, 两个斜杠
print(30%4)    # 取余
10
32
1.4142135623730951
7.5
7
2

不同于 C 和 Java 等语言,Python 不需要预先声明变量的类型,它会根据赋值的内容自动推断变量的类型:

a = 2
b = 'BMW'

print(a*10)   # 整数乘法
print(b*10)   # 字符串重复
20
BMWBMWBMWBMWBMWBMWBMWBMWBMWBMW

9.1.1 变量类型

Python 支持整数和浮点数两种数值类型。整数是没有小数点的数字,比如 123 等;而浮点数是带有小数点的数字,比如 1.02.53.14 等。 我们可以使用 type() 函数来查看一个变量的类型:

type(10)   # int
type(10.0) # float

很多时候我们也会用到字符型变量:

print("Hello, World!")
str1 = 'Python'
str2 = "is amazing"
str3 = str1 + str2

print(str3)                   # 字符串拼接,不会添加空格
print(str1, str2, '=', str3)  # 字符串拼接, 自动添加空格
Hello, World!
Pythonis amazing
Python is amazing = Pythonis amazing

说明:字符串需要用单引号或双引号括起来,Python 会自动识别。比如,'hello'"hello" 是等价的。如果你在字符串中需要使用单引号,可以用双引号括起来,反之亦然。比如,"I'm a student"'I\'m a student' 是等价的。

9.2 逻辑运算与比较

在数据清洗阶段,经常需要根据特定的条件删除或筛选变量和观察值,这就需要使用 ><>=<===!= 等比较运算符来进行逻辑判断。

1 > 2
False
2 >= 2
True

注意以上程序的结果返回的是 TrueFalse,这两个值在 Python 中分别代表 ‘逻辑真’ 和 ‘逻辑假’。逻辑真、假之间可以有与、或、非的运算:

True and True
True
(5==5) and (3>4)   # True and False
False

有兴趣的读者可以测试一下如下表达式的返回值:

(5<5) and (3>4)            # False and False
(5==5) or ('Cat'=='Dog')   # True or False
not ('Cat'!='Dog')         # not True

注意: - Python 中的逻辑运算符是 andornot,而不是 &&||!。这与 C 和 Java 等语言不同。 - 判断是否相等需要用双等号 ==,判断不相等要使用 !=,否则会报错:

print('真的!') if ('Cat' != 'cat') else print('假的!')
真的!

9.3 运算优先级

以上我们介绍了几种常见的运算,包括:

  • 算数运算
    • +
    • -
    • \*
    • /
    • //
    • %
    • \*\*
  • 比较运算
    • ==
    • !=
    • <
    • <=
    • >
    • >=
  • 逻辑运算
    • and
    • or
    • not

这些运算符号并不是从左到右依次执行的,而是具有优先级顺序,比如:

3 + 5*7 - 2

运算时,会先进行乘法运算,再执行加减运算:

Python 中运算符的优先级从高到低依次为:

  1. **
  2. +-: 正负号
  3. */%
  4. +-:加减运算
  5. ==!=<><=>=
  6. not
  7. and
  8. or

请问,按照以上规则,如下表达式的运算顺序是什么?会得到什么结果?

1>2 and 3>2

此外,需要注意幂运算的等级比正负号要高,所以如下两个表达式的结果是不同的:

-2**2      # -4
(-2)**2    #  4

对于 -2**2,其计算顺序是:先计算 \(2^2\),再取负,得到了 \(-4\)。因此,在编写代码时,如果不确定变量 x 的值是正数还是负数,且需要进行类似 x**2 这样的运算,更稳妥的写法上 (x)**2

9.4 变量

变量(variable)是几乎所有编程语言都具有的特性。变量实质上是内存中的一个区域,其中存储着某一些值。在 Python 中,由于不用像 C、Java 一样显示地指明变量类型,因而用起来非常简单,直接使用等号 “=” 赋值即可。比如,我们可以使用以下语句将 3 这个值保存在变量 x 中,并在后面引用这个变量:

x = 3
print(x)
x = 'Python is great' # 变量可以重新赋值
print(x)
3
Python is great

变量命名的基本规则如下:

  • 变量名不能以数字开头
  • 变量名不能是 Python 的保留字符,比如 isin
  • 不能含有 +-*、空格等特殊字符,但可以包含下划线 _
  • 变量名区分大小写

虽然 Python 的变量名要求相对比较宽松,但是还是有一些良好的习惯需要注意:

  • 尽量不要使用中文做变量名
  • 变量名要有意义,方便阅读
  • 尽量不要使用下划线开头,因为下划线开头的变量在 Python 中可能有特殊含义
  • 如果变量名包含几个单词,可以用下划线区分,或使用驼峰规则,如:cat_weight,或者 CatWeight

除了简单的使用等号赋值之外,Python 还有其他几个比较方便的赋值语句,比如:

  • a += b 等价于 a = a + b
  • a -= b 等价于 a = a - b
  • a *= b 等价于 a = a * b
  • a /= b 等价于 a = a / b
  • a **= b 等价于 a = a ** b
  • a //= b 等价于 a = a // b
  • a %= b 等价于 a = a % b
a = 2

a += 1
print(a)

a -= 1
print(a)

a *= 2
print(a)

a /= 2
print(a)

a **= 2
print(a)
3
2
4
2.0
4.0

9.5 字符串

除了数值类型,字符串也是一种常用的数据类型。在 Python 中,即可以用单引号 '',也可以用双引号 "" 来标记字符串,二者等价:

a = "Hello,"
b = 'Python!'
print(a, b)
Hello, Python!

采用两种标记符,有助于处理一些特殊情形,比如

a = "Tom's viewpoint: "
b = '"Python" is amazing!'
print(a)
print(b)
print(a + b)
Tom's viewpoint: 
"Python" is amazing!
Tom's viewpoint: "Python" is amazing!

也就是说,字符串内包含 ' 时,我们可以用双引号括起来,反之亦然。当然,有时候字符串内会同时包含单引号和双引号,此时我们可以使用转义字符 \ 来解决这个问题,比如:

c = "Tom's idea: \"Python\" is amazing, let's learn it."
print(c)
Tom's idea: "Python" is amazing, let's learn it.

此处,我们用 \" 来表示字符串中的双引号,以避免与外围的双引号冲突。类似地,我们也可以用 \' 来表示字符串中的单引号。

除了单引号之外,还有一些其他的字符需要转义,比如,斜杠 \ 本身就需要转义,因为如果不对 \ 转义,解释器无法知晓这个斜杠是一个纯粹的斜杠,还是与后面的字符链接起来的转义字符,比如:

a = "a 工作路径为 D:\mydata\python"
b = "b 工作路径为 D:\\mydata\\python"
print(a)
print(b)
a 工作路径为 D:\mydata\python
b 工作路径为 D:\mydata\python
<>:1: SyntaxWarning: invalid escape sequence '\m'
<>:1: SyntaxWarning: invalid escape sequence '\m'
C:\Users\arlio\AppData\Local\Temp\ipykernel_31208\1298254725.py:1: SyntaxWarning: invalid escape sequence '\m'
  a = "a 工作路径为 D:\mydata\python"

除此之外,Python 中还有其他转义字符:

  • 每一行结尾处的 \:续行符
  • \\:反斜杠
  • \':单引号
  • \":双引号
  • \b:退格
  • \v:纵向制表符
  • \t:横向制表符
  • \r:回车
  • \n:换行
  • \f:换页
  • ……

举几个例子:

print("Name:\tTom\t谭萱\nAge:\t30\t25\nSex:\t\t女")
Name:   Tom 谭萱
Age:    30  25
Sex:    男   女

在 Python 中,\n 表示 换行符,而 \r 表示 回车符。它们的作用略有不同,具体如下:

  • \n 会将光标移到下一行的开头,相当于“换行并定位至该行行首”;
  • \r 只会将光标移到当前行的行首,但不会换行,因此它会从当前行的开头开始重新输出字符,可能会覆盖原来的内容。

来看一个例子:

print("Downloading...\n搞定了")  # 使用 \n(换行符)
print('-'*30)
print("Downloading...\r搞定了")  # 使用 \r(回车符) 
Downloading...
搞定了
------------------------------
搞定了nloading...

第二个输出结果有点奇怪,是因为 \r 把光标拉回了行首 (没有换行),然后开始打印 搞定了。这会覆盖原有字符 Downloading... 中开头 3 个字符 (Dow),最终变成了 搞定了nloading...

这种 \r 的行为在一些进度条或动态打印时非常常见。下面的代码中,由于每次打印都用 \r 把光标拉回了行首,覆盖原内容,所以你看到的其实只有一行在不断刷新

import time

# 模拟一个简单的进度条
for i in range(0, 101, 10):
    print(f"下载进度:{i}%".ljust(20), end="\r")
    time.sleep(0.2)  # 暂停 0.2 秒,模拟下载过程

print("下载完成!            ")  # 打印最终状态
下载完成!               

除了使用单引号和双引号表示字符串之外,Python 还支持长字符串、原始字符串两种表示方法:长字符串使用三个单引号 ''' 或者三个双引号 """ 包裹,可以用来表示跨越多行的字符串。比如:

a = """这是一个可以跨行的字符串
使用三个单引号
或者三个双引号包裹
"""

b = '''
Hello, my friend!
I am learning Python.
'''
print(a)
print(b)
这是一个可以跨行的字符串
使用三个单引号
或者三个双引号包裹


Hello, my friend!
I am learning Python.

而原始字符串即不对反斜杠进行转义,比如一个路径可能为:

a = "C:\network\table"

然而注意到,由于 \n 有转义,所以 Python 解释器在碰到 \n 时将其解释为回车。同理,\t 则会被解释为 Tab

a = "C:\network"
print(a)
C:
etwork

如果需要声明该字符串为原始字符串,可以直接在字符串前面加一个“r”,即:

a=r"C:\network"
print(a)
C:\network

此时,Python解释器就不会将“\n”进行转义了。

此外,Python3中所有的字符串都是以Unicode进行编码的,因而极大地规避了在Python2以及其他语言中可能碰到的乱码问题,因而使用Python3是非常方便的。也正因为如此,我们可以很方便的使用Unicode字符,并使用“\N{unicode_name}”来表示Unicode字符,比如:

a="This is a cat: \N{cat}"
print(a)
This is a cat: 🐈

Unicode字符可以从 http://unicode-table.com 中找到。

如果需要拼接两个字符串,可以简单的使用加号:

a="This is a cat: "
b="\N{cat}"
c=a+b
print(c)
This is a cat: 🐈

最后需要额外注意的是,作为字符串的 “3” 和作为整型数据的 3 是完全不一样的。因此,我们不能使用如下表达式:

"3" + 2

正确的做法是:先使用 int() 函数将字符串 “3” 转化为数值类型,再进行计算:

a = int("3") + 2
print(a)
5

同理,如果希望将一个数值型数字处理为字符串,也需要使用 str() 函数先将数字转化为字符串:

a = 3
b = "There are " + str(a) + " cats."
print(b)
There are 3 cats.

9.6 列表和元组

以上我们初步介绍了 Python 的三种基本数据类型:整型、浮点型以及字符串。接下来我们引入 Python 中的两种最基本也是最常用的数据结构:列表(list)和 元组(tuple)。

列表和元组都是序列(sequence)的一种,可以看成是一些元素的集合,每个元素都有其位置编号,编号从 0 开始。列表使用方括号 “[ ]” 进行声明,而元组使用小括号 “( )” 进行声明,或者不用括号直接声明。列表和元组所包含的内容可以是任何 Python 允许的数据类型、对象等等。

9.6.1 元组

最简单也是最基本的是元组。元组不可更改,一旦创建,只能读取而不能写入 (定义后不能修改),比如:

name_list = "Messi", 10
print(name_list)
('Messi', 10)

我们可以使用元组名后面加一个方括号读取相应编号位的元素:

print(name_list[0])
print(name_list[1])
## 以下语句会报错:
# name_list[1]=7
Messi
10

由于编号从 0 开始,因而 name_list[0] 代表的是元组中的第一个元素,而 name_list[1] 代表第二个元素。方括号中也可以是负数,代表倒数第几个元素:

print(name_list[-1])
print(name_list[-2])
10
Messi

此外,如果需要声明只有一个元素的元组,需要额外加一个逗号,否则解释器无法判断需要声明的是一个值还是一个元组:

name = 'Messi',
print(name)

name1 = 'Messi'
print(name1)
('Messi',)
Messi

当然,在声明元组时,一个良好的习惯是加上括号,使得程序更具有可读性。

由于元组也是 Python 中的对象,因而元组中的元素也可以是元组,并可以使用两个方括号对作为元组成员的元组中的元素进行读取操作:

name_list=(('Messi',10),
           ('Xavi',6),
           ('Iniesta',8),
           ('Puyol',5)
          )
print(name_list[0][0])
print(name_list[1][0],":",name_list[1][1])
Messi
Xavi : 6

元组虽然不能修改,但支持切片(slicing)操作:可以从元组中取出一个子集。切片操作使用元组名后加方括号,在方括号中用冒号“:”指定起始和结束位置(注意是左闭右开区间):

print(name_list[0:2])
print(name_list[-3:-1])
print(name_list[-3:])
print(name_list[2:])
(('Messi', 10), ('Xavi', 6))
(('Xavi', 6), ('Iniesta', 8))
(('Xavi', 6), ('Iniesta', 8), ('Puyol', 5))
(('Iniesta', 8), ('Puyol', 5))

实际上,切片操作还可以支持步进,切片的通用语法为:

x[start:end:step]

其中start的默认值为0,step的默认值为1,而end的默认值为x的维数的大小。如果我们只想取出奇数位的元素,我们可以使用:

print(name_list[::2])
(('Messi', 10), ('Iniesta', 8))

以上切片操作被翻译为:从0开始,一直到结束,隔一个取一个元素。

9.6.2 列表

列表的很多操作跟元组类似,但是列表允许被修改,因而更加灵活,也有更多的操作。

我们可以很方便的使用方括号定义一个列表:

player_list=[('Messi',10),
           ('Xavi',6),
           ('Iniesta',8),
           ('Puyol',5)
          ]
print(player_list)
[('Messi', 10), ('Xavi', 6), ('Iniesta', 8), ('Puyol', 5)]

注意以上声明的过程中,与元组唯一的不同是我们在最外面使用了方括号而非圆括号。

或者,我们可以使用 list() 将一个可迭代iterable)的对象(包括字符串、元组、列表等)转化为一个列表,比如:

print(name_list)
name_list_list=list(name_list)
print(name_list_list)
messi=list('Messi')
print(messi)
(('Messi', 10), ('Xavi', 6), ('Iniesta', 8), ('Puyol', 5))
[('Messi', 10), ('Xavi', 6), ('Iniesta', 8), ('Puyol', 5)]
['M', 'e', 's', 's', 'i']

与列表不同的是,我们可以对列表进行修改操作:

print(player_list)
player_list[3]=('ter Stegen',1)
print(player_list)
[('Messi', 10), ('Xavi', 6), ('Iniesta', 8), ('Puyol', 5)]
[('Messi', 10), ('Xavi', 6), ('Iniesta', 8), ('ter Stegen', 1)]

以及使用del语句进行删除操作:

del player_list[2]
print(player_list)
[('Messi', 10), ('Xavi', 6), ('ter Stegen', 1)]

当然,也可以进行新增,使用列表的append()方法可以在列表最后添加一个元素,比如:

player_list.append(('Busquets',5))
print(player_list)
[('Messi', 10), ('Xavi', 6), ('ter Stegen', 1), ('Busquets', 5)]

如果需要添加的元素比较多,可以使用extend()方法,比如:

player_list_new=[('Pique',3),('Suárez',9)]
player_list.extend(player_list_new)
print(player_list)
[('Messi', 10), ('Xavi', 6), ('ter Stegen', 1), ('Busquets', 5), ('Pique', 3), ('Suárez', 9)]

如果需要在某一个位置插入元素,可以使用insert()方法:

player_list.insert(1,('Alba',18))
print(player_list)
[('Messi', 10), ('Alba', 18), ('Xavi', 6), ('ter Stegen', 1), ('Busquets', 5), ('Pique', 3), ('Suárez', 9)]

还可以使用count()方法计算某个元素出现的次数、使用index()方法找到某个元素第一次出现的位置,比如:

messi=list('Messi')
count_s=messi.count('s')
first_s=messi.index('s')
print(count_s)
print(first_s)
2
2

需要特别注意的是,在Python中,使用等号将一个对象赋值给另一个对象,并不会导致对象的拷贝,而仅仅是给了一个别名,比如:

another_play_list=player_list
print(player_list)
del another_play_list[1]
print(player_list)
[('Messi', 10), ('Alba', 18), ('Xavi', 6), ('ter Stegen', 1), ('Busquets', 5), ('Pique', 3), ('Suárez', 9)]
[('Messi', 10), ('Xavi', 6), ('ter Stegen', 1), ('Busquets', 5), ('Pique', 3), ('Suárez', 9)]

在以上程序中,我们会发现,虽然我们删除的是another_play_list的第2个元素,但是实际上,another_player_list和play_list只是同一个变量的不同别名而已,并没有重新复制一个新的list。

如果我们需要的是list的一个新的拷贝,需要使用list的copy()方法:

another_play_list=player_list.copy()
print(player_list)
del another_play_list[1]
print(player_list)
print(another_play_list)
[('Messi', 10), ('Xavi', 6), ('ter Stegen', 1), ('Busquets', 5), ('Pique', 3), ('Suárez', 9)]
[('Messi', 10), ('Xavi', 6), ('ter Stegen', 1), ('Busquets', 5), ('Pique', 3), ('Suárez', 9)]
[('Messi', 10), ('ter Stegen', 1), ('Busquets', 5), ('Pique', 3), ('Suárez', 9)]

最后,列表、元组、字符串还支持in操作符,该操作符判断in之前的元素是否属于之后的列表、元组或者字符串,比如:

print('Messi' in player_list[0][0])
print('Messi' in player_list[1][0])
print(('Pique', 3) in player_list)
print('d' in 'Messi')
True
False
True
False

如果要判断in之前的元素“不属于”之后的列表、元组或字符串,可以使用“not in”:

print('Messi' not in player_list[0][0])
print('Messi' not in player_list[1][0])
print(('Pique', 3) not in player_list)
print('d' not in 'Messi')
False
True
False
True

最后,我们还可以使用 sort() 方法对列表进行排序,比如:

a=[1,6,2,4,7]
a.sort()
print(a)
a.sort(reverse=True)
print(a)
[1, 2, 4, 6, 7]
[7, 6, 4, 2, 1]

在比较复杂的情况下,还可以使用key选项制定排序的方式,比如,我们可能需要对player_list中的球员号码进行排序,可以使用:

player_list.sort(key=lambda x: x[1])
print(player_list)
[('ter Stegen', 1), ('Pique', 3), ('Busquets', 5), ('Xavi', 6), ('Suárez', 9), ('Messi', 10)]

当然,以上的语法已经超出了目前所学范围,我们将在后面介绍lambda的含义。

9.7 集合

无论是列表还是元组,都允许有重复的元素存在,但是有时我们可能需要不重复的元素,此时可以使用集合set)类。

集合的声明与列表类似,区别在于集合使用大括号{},而非中括号。

与list()类似,可以使用使用set()构建集合:

letters=set("Messi")
print(letters)
{'e', 's', 'i', 'M'}

以上可以看到,set()构建了一个不重复元素组成的集合。可以使用add()方法以及update()方法为集合新增元素,比如:

letters.add('a')
print(letters)
letters.add(('a',))
print(letters)
letters.update({'c','d'})
print(letters)
{'M', 'e', 's', 'i', 'a'}
{('a',), 'M', 'e', 's', 'i', 'a'}
{('a',), 'M', 'e', 'd', 's', 'i', 'a', 'c'}

值得注意的是,在上面的程序中,我们添加了字符串’a’,以及一个元组(‘a’,),两者一个是字符串,一个是元组,是不同的,因而在集合中两者并不冲突。

如果需要删除,可以使用remove()方法以及discard()方法,两者的区别在于:remove()方法不能删除不存在的元素,否则报错;而discard()方法如果要删除的元素不存在,不会报错。

letters.remove(('a',))
print(letters)
letters.discard('z')
print(letters)
{'M', 'e', 'd', 's', 'i', 'a', 'c'}
{'M', 'e', 'd', 's', 'i', 'a', 'c'}

最后,作为集合,还可以使用issubset()和issuperset()方法判断某一个集合是不是另外一个集合的子集或者超集:

print(letters.issuperset({'a','b'}))
print(letters.issuperset({'a','d'}))
print({'a','d'}.issubset(letters))
print({'a','d'}.issuperset(letters))
False
True
True
False

9.8 控制语句:循环

有时我们需要重复一条类似的命令很多次,此时我们可以使用循环命令。

在Python中有两个循环语句:while和for,两者很大程度上是等价的,但是在不同情况下方便程度时不一样的。

for作为关键字,其基本语法为:

for var in iterable_obj:
    ## code

其中var为一个变量,iterable_obj为一个可迭代对象,如列表、元组或者字符串,其后面紧接着跟着一个冒号。

在这里需要注意的是,不像C或者Java使用大括号区分代码块,在Python中,主要靠缩进区分代码块,因而需要循环执行的代码,要写在for语句的下一行,并使用Tab键或者几个(一般为4/8个)空格进行缩进。

比如,以下语句把字符串中的每个字符都分别打印出来:

name='Messi'
for n in name:
    print(n)
M
e
s
s
i

而以下代码将player_list中所有的人名及号码打印出来:

for player in player_list:
    print(player[0],end='')
    print(" : ",end='')
    print(player[1])
print("---the end---")
ter Stegen : 1
Pique : 3
Busquets : 5
Xavi : 6
Suárez : 9
Messi : 10
---the end---

注意到最后一行并没有被缩进,因而不属于需要循环执行的代码块,因而只执行了一次。

此外,经常遇到的一个情形是对数字进行循环,此时可以使用range()函数。如果使用range(N),将会返回一个可迭代的对象,其值为0,…,N-1。比如:

list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

也可以使用range(N,M)的形式,此时返回迭代对象的值为N,N+1,…,M-1,比如:

list(range(1,11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

使用以上特性,我们可以方便的使用range()函数来写for 循环,比如:

name='Messi'
for i in range(len(name)):
    print(name[i])
M
e
s
s
i

其中len()函数取得name中元素的个数,由于len(name)的值为5,因而range(len(name))就产生了一个可以迭代的、值为0,1,2,3,4的对象。

以下程序计算了从1到101的所有奇数的和:

sum_odd=0
for i in range(0,51):
    odd=2*i+1
    sum_odd+=odd
print(sum_odd)
2601

而while循环的语法为:

while logic_expression:
    ## code

同样,while循环的代码块也需要用缩进表示,而logic_expression是一个逻辑判断语句,只有当logic_expression值为真时,循环才继续执行,否则跳出循环。比如刚刚从1到101的所有奇数的和的代码也可以写为:

sum_odd=0
odd=1
while odd<=101:
    sum_odd+=odd
    odd+=2
print(sum_odd)
2601

特别需要注意的是一定不要忘了及时更新odd,否则很容易造成无限循环。

9.9 控制语句:条件

条件语句用于判断某一个逻辑表达式,如果为真,则执行某个代码块。其最基本的形式如下:

if logic_expression:
    ## code

同样,代码块也需要用缩进表示,其中的代码块只有当logic_expression的值为真时才执行,否则不执行。

比如,如下的代码中,首先判断球员姓名是否为Messi或者Suárez,如果是,则打印其位置:

for p in player_list:
    if p[0]=='Messi' or p[0]=='Suárez':
        print(p[0],":前锋")
Suárez :前锋
Messi :前锋

此外,if 后面还可以跟elif语句:

if logic_expression1:
    ## code1
elif logic_expression2:
    ## code2
elif  logic_expression3:
    ## code3
....

即“else if”,如果logic_expression1满足,则执行code1,如果不满足,则继续判断logic_expression2是否满足,若满足,则执行code2,以此类推。比如:

for p in player_list:
    if p[0] in ('Messi', 'Suárez'):
        print(p[0],":前锋")
    elif p[0] in ('Xavi', 'Arthur','Busquets'):
        print(p[0],":中场")
    elif p[0] in ('Pique'):
        print(p[0],":后卫")
Pique :后卫
Busquets :中场
Xavi :中场
Suárez :前锋
Messi :前锋

最后,还可以加else语句,用于所有的if或者elif的逻辑表达式都不满足时执行

for p in player_list:
    if p[0] in ('Messi', 'Suárez'):
        print(p[0],":前锋")
    elif p[0] in ('Xavi', 'Iniesta','Busquets'):
        print(p[0],":中场")
    elif p[0] in ('Pique'):
        print(p[0],":后卫")
    else:
        print(p[0],"守门员")
ter Stegen 守门员
Pique :后卫
Busquets :中场
Xavi :中场
Suárez :前锋
Messi :前锋

此外,if语句可以与break、continue、pass等一起控制循环。其中:

  • break:跳出循环不再执行
  • countinue:跳出本次循环后面的代码,但是循环继续执行
  • pass:什么都不做,继续执行

比如,计算从1到101的所有奇数的和的代码也可以写为:

sum_odd=0
odd=1
while True:
    sum_odd+=odd
    odd+=2
    if odd>101:
        break
print(sum_odd)
2601

在以上代码中,while True代表循环会一直执行,但是if语句会判断odd是否大于了101,如果odd一旦大于101,就会使用break退出循环。

如果我们需要计算从1到101的所有不能被5整除的奇数的和,可以使用pass语句:

sum_odd=0
odd=1
while odd<=101:
    if odd%5==0:
        pass
    else:
        sum_odd+=odd
    odd+=2
print(sum_odd)
2101

以下代码我们使用continue语句将一个字符串中所有的’s’都给去掉

a='Messi'
b=''
for s in a:
    if s=='s':
        continue
    b+=s
print(b)
Mei

在以上代码中,当碰到’s’时,循环跳过了b+=s这一句,而是继续执行循环,直到循环结束。与之相比的是break命令直接跳出了循环:

a='Messi'
b=''
for s in a:
    if s=='s':
        break
    b+=s
print(b)
Me

9.10 实例

import pandas as pd

# 构造一个简单的学生成绩数据
students = [
    {"name": "Alice", "score": 85},
    {"name": "Bob", "score": 58},
    {"name": "Charlie", "score": 92},
    {"name": "David", "score": 40},
    {"name": "Eva", "score": 60}
]

# 只保留成绩及格(>=60)的学生
passed_students = [s for s in students if s["score"] >= 60]

# 转换为 pandas DataFrame 并打印为表格
df_passed = pd.DataFrame(passed_students)

# 打印格式化的表格
print("及格学生名单:")
print(df_passed.to_string(index=False))
及格学生名单:
   name  score
  Alice     85
Charlie     92
    Eva     60

在上例中,我们使用了 列表推导式(List Comprehension)写法:passed_students 是一个新列表,它通过列表推导式从 students 列表中筛选出所有 "score" 大于等于 60 的学生字典 s。这种写法逻辑简洁、表达紧凑:

passed_students = [s for s in students if s["score"] >= 60]

它等价于下面的传统写法:

passed_students = []
for s in students:
    if s["score"] >= 60:
        passed_students.append(s)

两者实现的功能完全一致,都是遍历 students 列表,对每个学生字典 s 检查其 "score" 值是否大于等于 60,如果是,就将该学生加入新的列表 passed_students

列表推导式的优势在于:

  • 写法简洁,更适合一行逻辑较清晰的筛选任务;
  • 常用于数据清洗、筛选、转换等操作,阅读性和执行效率较高。

当逻辑比较复杂(如嵌套循环或多个条件判断)时,仍推荐使用传统的 for 循环方式,更利于维护和调试。

9.11 控制语句:异常

理想与现实总是有差距的。在程序现实运行过程中,出于很多原因,总是可能会存在异常exception)。比如,一个最简单的异常是,允许用户输入两个数字相除,但是用户却输入了0作为分母;或者,用户输入的不是数字,而是字母。此时,正常的程序可能会发生错误。

任何高级语言几乎都带有处理异常的功能,Python也不例外。

在Python中,可以使用try…except…else…finally语句来处理异常,语法为:

try:
    # code1
except Exception1:
    # code2
except Exception2:
......

else:
    # code3
finally:
    #code4

首先,解释器会执行code1,如果没有错误,就执行code3,并继续下去。如果发生了错误,会查看具体发生何种错误,执行相应的except中的命令。最终,无论错误是否发生,code4都会被执行,并继续执行finally后面的代码。

比如,下面一个例子展示了分母为0时的处理方法:

a=1
b=0
try:
    c=a/b
except ZeroDivisionError as e:
    print("分母为0:,错误信息:", e)
else:
    print("没有错误发生。")
finally:
    print("C'est la vie!")
    print(c)
print("生活还要继续。")
分母为0:,错误信息: division by zero
C'est la vie!
This is a cat: 🐈
生活还要继续。
a=0
b=1
try:
    c=a/b
except ZeroDivisionError as e:
    print("分母为0:,错误信息:", e)
else:
    print("没有错误发生。")
finally:
    print("C'est la vie!")
    print(c)
print("生活还要继续。")
没有错误发生。
C'est la vie!
0.0
生活还要继续。

except可以不止有一个,比如,我们可能还需要处理类型错误,比如将数字和字符串进行运算:

a='0'
b=1
try:
    c=a/b
except ZeroDivisionError as e:
    print("分母为0:,错误信息:", e)
except TypeError as e:
    print("类型错误:", e)
else:
    print("没有错误发生。")
finally:
    print("C'est la vie!")
    print(c)
类型错误: unsupported operand type(s) for /: 'str' and 'int'
C'est la vie!
0.0

或者可以将两者合并:

a='0'
b=1
try:
    c=a/b
except (ZeroDivisionError,TypeError) as e:
    print("错误:", e)
else:
    print("没有错误发生。")
finally:
    print("C'est la vie!")
    print(c)
错误: unsupported operand type(s) for /: 'str' and 'int'
C'est la vie!
0.0

如果不知道会发生什么错误,也可以什么也不加,捕获所有错误:

a='0'
b=1
try:
    c=a/b
except:
    print("错误:")
else:
    print("没有错误发生。")
finally:
    print("C'est la vie!")
    print(c)
错误:
C'est la vie!
0.0

更好的办法是将错误捕获,并打印出来方便调试:

a='0'
b=1
try:
    c=a/b
except Exception as e:
    print("错误:", e)
else:
    print("没有错误发生。")
finally:
    print("C'est la vie!")
    print(c)
错误: unsupported operand type(s) for /: 'str' and 'int'
C'est la vie!
0.0

9.12 列表推导

在Python中,可以结合循环语句for、判断语句if、else等,写出更加简洁的程序。

比如,如果我们希望生成一个1…99中所有能被3整除的奇数的表达式,一般可以通过如下的程序:

a=[]
for i in range(50):
    if (2*i+1)%3==0:
        a.append(2*i+1)
print(a)
[3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 87, 93, 99]

以上代码已经足够简洁,但是Python还支持以下更简洁的方式:

a=[2*i+1 for i in range(50) if (2*i+1)%3==0]
print(a)
[3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 87, 93, 99]

当然,也支持双重循环,比如列出一个奇数元组,第一个分类能被3整除,第二个分类能被5整除:

b=[(2*i+1,2*j+1) for i in range(50) for j in range(50) if (2*i+1)%3==0 and (2*j+1)%5==0]
print(b)
[(3, 5), (3, 15), (3, 25), (3, 35), (3, 45), (3, 55), (3, 65), (3, 75), (3, 85), (3, 95), (9, 5), (9, 15), (9, 25), (9, 35), (9, 45), (9, 55), (9, 65), (9, 75), (9, 85), (9, 95), (15, 5), (15, 15), (15, 25), (15, 35), (15, 45), (15, 55), (15, 65), (15, 75), (15, 85), (15, 95), (21, 5), (21, 15), (21, 25), (21, 35), (21, 45), (21, 55), (21, 65), (21, 75), (21, 85), (21, 95), (27, 5), (27, 15), (27, 25), (27, 35), (27, 45), (27, 55), (27, 65), (27, 75), (27, 85), (27, 95), (33, 5), (33, 15), (33, 25), (33, 35), (33, 45), (33, 55), (33, 65), (33, 75), (33, 85), (33, 95), (39, 5), (39, 15), (39, 25), (39, 35), (39, 45), (39, 55), (39, 65), (39, 75), (39, 85), (39, 95), (45, 5), (45, 15), (45, 25), (45, 35), (45, 45), (45, 55), (45, 65), (45, 75), (45, 85), (45, 95), (51, 5), (51, 15), (51, 25), (51, 35), (51, 45), (51, 55), (51, 65), (51, 75), (51, 85), (51, 95), (57, 5), (57, 15), (57, 25), (57, 35), (57, 45), (57, 55), (57, 65), (57, 75), (57, 85), (57, 95), (63, 5), (63, 15), (63, 25), (63, 35), (63, 45), (63, 55), (63, 65), (63, 75), (63, 85), (63, 95), (69, 5), (69, 15), (69, 25), (69, 35), (69, 45), (69, 55), (69, 65), (69, 75), (69, 85), (69, 95), (75, 5), (75, 15), (75, 25), (75, 35), (75, 45), (75, 55), (75, 65), (75, 75), (75, 85), (75, 95), (81, 5), (81, 15), (81, 25), (81, 35), (81, 45), (81, 55), (81, 65), (81, 75), (81, 85), (81, 95), (87, 5), (87, 15), (87, 25), (87, 35), (87, 45), (87, 55), (87, 65), (87, 75), (87, 85), (87, 95), (93, 5), (93, 15), (93, 25), (93, 35), (93, 45), (93, 55), (93, 65), (93, 75), (93, 85), (93, 95), (99, 5), (99, 15), (99, 25), (99, 35), (99, 45), (99, 55), (99, 65), (99, 75), (99, 85), (99, 95)]

此外还支持else语句,比如,以下代码将1…10的所有奇数都取负数,所有偶数保持不变:

c=[i+1 if (i+1)%2==0 else -(i+1) for i in range(10)]
print(c)
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

注意如果需要使用else,if…else…要写在for前面。

9.13 字典

字典dictionary)在Python中是一种映射关系,即特定的单词(key)以及其value)之间的关系。字典可以使用大括号配合冒号进行声明,比如之前的球员号码:

players={"Messi":10,
        "Xavi":6,
        "Iniesta":8,
        "Pique":3}
print(players)
print(players['Messi'])
{'Messi': 10, 'Xavi': 6, 'Iniesta': 8, 'Pique': 3}
10

在以上的代码中,我们使用大括号以及冒号声明了一个字典:playsers,其中键为”Messi”、“Xavi”等等,其值分别为10、6等等。接着,使用players[‘Messi’]取得了键为’Messi’的值。另外也可以使用dict()进行创建:

players=dict(Messi=10,Xavi=6,Iniesta=8,Pique=3)
print(players)
print(players['Messi'])
{'Messi': 10, 'Xavi': 6, 'Iniesta': 8, 'Pique': 3}
10

与上面的结果一样。

在循环语句中,可以使用通常的循环方法获得一个字典的keys:

for k in players:
    print("Player: ",k,": ",players[k])
Player:  Messi :  10
Player:  Xavi :  6
Player:  Iniesta :  8
Player:  Pique :  3

在以上循环中,我们使用循环遍历了players中所有的键,并将其值打印了出来。

此外,字典还支持如下操作:

  • len(players):字典players的键-值对的个数
  • del players(k):删除键k
  • k in players:判断字典players中是否有键k

比如:

print("Players中有",len(players),"个球员")
print(players)
if "Iniesta" in players:
    del players["Iniesta"]
print(players)
Players中有 4 个球员
{'Messi': 10, 'Xavi': 6, 'Iniesta': 8, 'Pique': 3}
{'Messi': 10, 'Xavi': 6, 'Pique': 3}

9.14 Python中的None

在Python中还有一个非常特殊的类型:None,即什么都没有。注意None并不是空的元组、列表,也不是空的字符串,就是None:

type(None)
NoneType
type('')
str
type([])
list
type(False)
bool

由于None表示空的、没有,因而None跟其他任何数据类型比较都是返回False。

如果要判断某个变量是否是None,可以使用is关键字:

a=None
b=[]
print(a is None)
print(b is None)
True
False

9.15 小结

最后,我们使用一个综合的例子,回顾一下目前未知所学的内容。

在接下来的程序中,我们使用input()函数从用户的输入中得到数字,知道用户输入“end”为止,并将所有的数字相加。

summ=0
while True:
    text=input("请输入一个数字,或者end结束:")
    if text=="end":
        break
    summ+=float(text)
print(summ)
请输入一个数字,或者end结束:3
请输入一个数字,或者end结束:3
请输入一个数字,或者end结束:2.3
请输入一个数字,或者end结束:end
8.3

在以上的程序中,我们首先使用input()函数获得用户输入,接着判断用户输入的是否为”end”,如果是,则退出,否则,由于input()获得的是一个字符串,因而我们使用float()函数将该字符串转换为一个浮点数字,再将其相加。

但是以上程序并不完美,比如,如果用户输入的不是“end”也不是数字,就会报错,因而一个更完善的版本是加入错误处理:

summ=0
while True:
    text=input("请输入一个数字,或者end结束:")
    if text=="end":
        break
    try:
        summ+=float(text)
    except Exception as e:
        print("请输入数字或end!")
        continue
print(summ)
请输入一个数字,或者end结束:5
请输入一个数字,或者end结束:5
请输入一个数字,或者end结束:bug
请输入数字或end!
请输入一个数字,或者end结束:end
10.0