Python学习笔记
Python学习笔记摘要
1.起步
1.1Python的安装等
略
1.2设置代码格式
PEP 8建议每级缩进都使用四个空格,这既可提高可读性,又留下了足够的多级缩进空间。
在程序中混合使用制表符和空格可能导致极难解决的问题。如果你混合使用了制表符和空格,可将文件中所有的制表符转换为空格,大多数编辑器都提供了这样的功能。
很多Python程序员都建议每行不超过80字符。
PEP 8还建议注释的行长都不超过72字符,因为有些工具为大型项目自动生成文档时,会在每行注释开头添加格式化字符。
要将程序的不同部分分开,可使用空行,但是也不应该滥用,空行不会影响代码的运行,但会影响代码的可读性。
在vscode中设置80字符参考线:
设置-文本编辑器-Rulers-在settings.json中编辑加入下列这段代码
1 |
|
1.3编码风格
类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线。
实例名和模块名都采用小写格式,并在单词之间加上下划线。
对于每个类,都应紧跟在类定义后面包含一个文档字符串
可使用空行来组织代码,但不要滥用
需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的import语句,再添加一个空行,然后编写导入你自己编写的模块的import语句
2.变量和数据类型
2.1变量
变量的命名规则:
- 变量名只能包含字母、数字和下划线。变量名可以字母或下划线打头,但不能以数字打头,例如,可将变量命名为message_1,但不能将其命名为1_message
- 变量名不能包含空格,但可使用下划线来分隔其中的单词。例如,变量名greeting_message可行,但变量名greeting message会引发错误
- 不要将Python关键字和函数名用作变量名,即不要使用Python保留用于特殊用途的单词,如print(大部分编辑器会有提示)
- 变量名应既简短又具有描述性。例如,name比n好,student_name比s_n好,name_length比length_of_persons_name好
- 慎用小写字母l和大写字母O,因为它们可能被人错看成数字1和0
2.2字符串
字符串在python中表示为被引号括起来的,引号可以是单引号也可以是双引号。因此如果一个字符串内是有单引号或者撇号的,外面使用双引号,如果字符串内是要包含双引号的,外面用单引号。
字符串的一些方法:
title()
,将每个单词的首字母改为大写upper()
,将所有字母都改为大写lower()
,将所有字母都改为小写rstrip()
,在输出时删除字符串末尾的空白(空白包括空格、制表符\t
、换行符\n
等),只在输出是去除空白,实际并没有改变字符串的值>>> favorite_language = ‘python ‘
>>> favorite_language
‘python ‘
>>> favorite_language.rstrip()
‘python’
>>> favorite_language
‘python ‘
lstrip()
,在输出时删除字符串开头的空白strip()
,在输出时删除字符串前后的空白
2.3数字
整数
Python使用两个乘号表示乘方运算,例如:
1 |
|
浮点数
Python也存在小数位的精度问题,例如存在下面这些情况:
1 |
|
str()函数
通过str()函数来避免类型错误,在输出包含字符串及整数的信息时,将整数转换为字符串,从而实现信息的拼接
2.3注释
养成写注释的习惯!养成写注释的习惯!养成写注释的习惯!重要的事情说三遍。
3列表
列表由一系列按特定顺序排列的元素组成,在Python中,用方括号([])来表示列表,并用逗号来分隔其中的元素。
3.1访问列表
列表是有序集合,只需要将该元素的位置或索引告诉Python即可,当你请求获取列表元素时,Python只返回该元素,而不包括方括号和引号。
- 可以对从列表取出的元素使用字符串方法。
- 列表元素的索引从0开始,而不是从 1开始,这点要注意。
- -1表示列表的最后一个元素
3.2列表的增、删、改
- 修改:修改列表元素的语法与访问列表元素的语法类似。要修改列表元素,可指定列表名和要修改的元素的索引,再指定该元素的新值。
- 末尾添加元素:使用append()函数,可以在列表的末尾添加元素
- 列表中插入元素:使用insert()函数,可以在列表的任何位置添加新元素
- 列表中删除元素:使用del语句可以删除列表中的任何元素,前提是你要知道你删除的元素索引
- 使用pop()删除元素:pop()函数可删除列表末尾的元素,并允许使得它的值。
- 使用pop()删除任何位置元素:只需在括号中指定要删除的元素的索引即可。
- 根据值删除元素:不知道要从列表中删除的值所处的位置。如果你只知道要删除的元素的值,可使用方法remove()。
- 注意:remove()只删除第一个指定的值。如果要删除的值可能在列表中出现多次,就需要使用循环来判断是否删除了所有这样的值
3.3组织列表
创建的列表中,元素的排列顺序常常是无法预测的,因为你并非总能控制用户提供数据的顺序。这虽然在大多数情况下都是不可避免的,但你经常需要以特定的顺序呈现信息。有时候,你希望保留列表元素最初的排列顺序,而有时候又需要调整排列顺序。Python提供了很多组织列表的方式,可根据具体情况选用。
永久排序
可以通过sort()函数对列表进行排序,并且是永久性地修改了列表元素的排列顺序。
sort()
:按字母顺序进行排列sort(reverse=True)
:按字母顺序相反的顺序排列
临时排序
要保留列表元素原来的排列顺序,同时以特定的顺序呈现它们,可使用函数sorted()。
倒序打印
要反转列表元素的排列顺序,可使用方法reverse(),永久性地修改列表元素的排列顺序,但可随时恢复到原来的排列顺序,为此只需对列表再次调用reverse()即可。
确定列表的长度
使用函数len()可快速获悉列表的长度
4.操作列表
如何遍历整个列表,这只需要几行代码,无论列表有多长。循环让你能够对列表的每个元素都采取一个或一系列相同的措施,从而高效地处理任何长度的列表,包括包含数千乃至数百万个元素的列表。
4.1遍历整个列表
for循环
- 通过使用for循环,实现对列表的快速遍历。
- 在for循环中,可对每个元素执行任何操作。
- 在for循环后面,没有缩进的代码都只执行一次,而不会重复执行。
4.2避免缩进错误
Python根据缩进来判断代码行与前一个代码行的关系。
4.3创建数值列表
列表非常适合用于存储数字集合,而Python提供了很多工具,可帮助你高效地处理数字列表。
range()函数
函数range()让你能够轻松地生成一系列的数字。例如下面的语句:
1 |
|
括号内的参数表示从1开始包括1到5结束但不包括5。
使用range()创建数字列表
可使用函数list()将range()的结果直接转换为列表,如果将range()作为list()的参数,输出将是一个数字列表。例如:
1 |
|
numbers是一个列表,预期结果将会是:
[1, 2, 3, 4, 5]
range()函数指定步长
range()函数还可以指定步长,例如下面的代码将打印1-10内的偶数:
1 |
|
使用函数range()几乎能够创建任何需要的数字集,例如,如何创建一个列表,其中包含前10个整数(即1~10)的平方呢?在Python中,两个星号(**)表示乘方运算。下面的代码演示了如何将前10个整数的平方加入到一个列表中:
1 |
|
对数字列表执行简单的统计计算
有几个专门用于处理数字列表的Python函数。例如,你可以轻松地找出数字列表的最大值、最小值和总和:
- min()
- max()
- sum()
- ···
列表解析
列表解析将for循环和创建新元素的代码合并成一行,并自动附加新元素,下面这两段代码的结果是一样的:
使用for循环
1 |
|
使用列表解析
1 |
|
要使用这种语法,首先指定一个描述性的列表名,如squares;然后,指定一个左方括号,并定义一个表达式,用于生成你要存储到列表中的值。在这个示例中,表达式为value**2,它计算平方值。接下来,编写一个for循环,用于给表达式提供值,再加上右方括号。在这个示例中,for循环为for value in range(1,11),它将值1~10提供给表达式value**2。请注意,这里的for语句末尾没有冒号。
4.4使用列表的一部分
以处理列表的部分元素——Python称之为切片。
切片
要创建切片,可指定要使用的第一个元素和最后一个元素的索引要创建切片,可指定要使用的第一个元素和最后一个元素的索引,下面的范例:
1 |
|
print(players[0:3])表示打印列表的一个切片,包含1-3名队员,输出的也是一个列表;
print(players[1:4])表示提取列表第2-4个元素
print(players[:4])表示从列表开头到第4个元素
print(players[2:])表示从第3个元素到最后
print(players[-3:])表示末尾的3个元素
遍历切片
如果要遍历列表的部分元素,可在for循环中使用切片。
1 |
|
复制列表
要复制列表,可创建一个包含整个列表的切片,方法是同时省略起始索引和终止索引([:])。这让Python创建一个始于第一个元素,终止于最后一个元素的切片,即复制整个列表。
如果使用“=”将一个列表赋给另一个列表,其表示的并非是将列表的内容复制到新的列表,仅是将两个列表指向同一个列表的内容。例如:
1 |
|
这里将my_foods赋给friend_foods,而不是将my_foods的副本存储到friend_foods。这种语法实际上是让Python将新变量friend_foods关联到包含在my_foods中的列表,因此这两个变量都指向同一个列表。鉴于此,当我们将’cannoli’添加到my_foods中时,它也将出现在friend_foods中;同样,虽然’ice cream’好像只被加入到了friend_foods中,但它也将出现在这两个列表中。输出表明,两个列表是相同的。
4.5元组
Python将不能修改的值称为不可变的,而不可变的列表被称为元组。
定义元组
元组看起来犹如列表,但使用圆括号而不是方括号来标识。定义元组后,就可以使用索引来访问其元素,就像访问列表元素一样。
遍历元组中的所有值
像列表一样,也可以使用for循环来遍历元组中的所有值。
修改元组变量
虽然不能修改元组的元素,但可以给存储元组的变量赋值,从而实现重新定义元组。
相比于列表,元组是更简单的数据结构。如果需要存储的一组值在程序的整个生命周期内都不变,可使用元组。
5.if语句
5.1条件测试
每条if语句的核心都是一个值为True或False的表达式,这种表达式被称为条件测试。
Python根据条件测试的值为True还是False来决定是否执行if语句中的代码。如果条件测试的值为True,Python就执行紧跟在if语句后面的代码;如果为False,Python就忽略这些代码。
检查是否相等
使用两个等(==)来进行相等判断,==
运算符两边的值相等时返回True,否则返回False。
检查是否相等时不考虑大小写
在Python中检查是否相等时区分大小写,如果在判断时不需要考虑大小写,那我们可使用lower()函数后再进行比较。常见的场景类似如下:
网站采用类似的方式让用户输入的数据符合特定的格式。例如,网站可能使用类似的测试来确保用户名是独一无二的,而并非只是与另一个用户名的大小写不同。用户提交新的用户名时,将把它转换为小写,并与所有既有用户名的小写版本进行比较。执行这种检查时,如果已经有用户名’john’(不管大小写如何),则用户提交用户名’John’时将遭到拒绝。
检查是否不相等
使用惊叹号和等号(!=)来表示不等于。
比较数字
条件语句中可包含各种数学比较,如小于、小于等于、大于、大于等于:
- =
- <=
- >
- >=
- !=
检查多个条件
你可能想同时检查多个条件,例如,有时候你需要在两个条件都为True时才执行相应的操作,而有时候你只要求一个条件为True时就执行相应的操作。在这些情况下,关键字and和or可助你一臂之力。
and
:只有两个都为True时才为Trueor
:只要有一个为True时就为True,两个条件都为False是才返回False
检查特定值是否包含在列表中
要判断特定的值是否已包含在列表中,可使用关键字in
检查特定值是否不包含在列表中
要判断特定的值是否不包含在列表中,可使用关键字not in
布尔表达式
布尔表达式,是条件测试的别名。与条件表达式一样,布尔表达式的结果要么为True,要么为False。
5.2 if语句
if
在if语句中,缩进的作用与for循环中相同。如果测试通过了,将执行if语句后面所有缩进的代码行,否则将忽略它们
if-else
if-else语句块类似于简单的if语句,但其中的else语句让你能够指定条件测试未通过时要执行的操作。
if-else结构非常适合用于要让Python执行两种操作之一的情形。在这种简单的if-else结构中,总是会执行两个操作中的一个。
if-elif-else
经常需要检查超过两个的情形,为此可使用Python提供的if-elif-else结构。
Python只执行if-elif-else结构中的一个代码块,它依次检查每个条件测试,直到遇到通过了的条件测试。
省略else代码块
Python并不要求if-elif结构后面必须有else代码块。
else是一条包罗万象的语句,只要不满足任何if或elif中的条件测试,其中的代码就会执行,这可能会引入无效甚至恶意的数据。
测试多个条件
在可能有多个条件为True,且你需要在每个条件为True时都采取相应措施时,适合使用这种方法。
5.4使用if语句处理列表
通过结合使用if语句和列表,可完成一些有趣的任务:对列表中特定的值做特殊处理。
5.5设置if语句的格式
在条件测试的格式设置方面,PEP 8提供的唯一建议是,在诸如==、>=和<=等比较运算符两边各添加一个空格。
6.字典
在Python中,字典是一系列键—值对。每个键都与一个值相关联,你可以使用键来访问与之相关联的值。与键相关联的值可以是数字、字符串、列表乃至字典。事实上,可将任何Python对象用作字典中的值。
在Python中,字典用放在花括号{}中的一系列键—值对表示
键—值对是两个相关联的值。指定键时,Python将返回与之相关联的值。键和值之间用冒号分隔,而键—值对之间用逗号分隔。在字典中,你想存储多少个键—值对都可以。
6.1使用字典
添加键-值对
字典是一种动态结构,可随时在其中添加键—值对。要添加键—值对,可依次指定字典名、用方括号括起的键和相关联的值。
修改字典的值
要修改字典中的值,可依次指定字典名、用方括号括起的键以及与该键相关联的新值。
删除键-值对
对于字典中不再需要的信息,可使用del语句将相应的键—值对彻底删除。使用del语句时,必须指定字典名和要删除的键。
由类似对象组成的字典
字典存储的是一个对象的多种信息,但你也可以使用字典来存储众多对象的同一种信息。
确定需要使用多行来定义字典时,在输入左花括号后按回车键,再在下一行缩进四个空格,指定第一个键—值对,并在它后面加上一个逗号
6.2遍历字典
通过for语句结合字典的方法items(),实现对字典的遍历,范例代码如下:
1 |
|
在不需要使用字典的值时,可以使用方法keys()获取字典中的key,当然在实际应用中,可以不加上keys()方法。
按顺序遍历字典中的所有键
字典总是明确地记录键和值之间的关联关系,但获取字典的元素时,获取顺序是不可预测的。要以特定的顺序返回元素,一种办法是在for循环中对返回的键进行排序,为此,可使用函数sorted()来获得按特定顺序排列的键列表的副本。
1 |
|
遍历字典中的所有值
如果你感兴趣的主要是字典包含的值,可使用方法values(),它返回一个值列表,而不包含任何键。
1 |
|
这种做法提取字典中所有的值,而没有考虑是否重复。涉及的值很少时,这也许不是问题,但如果被调查者很多,最终的列表可能包含大量的重复项。为剔除重复项,可使用集合(set)。
集合类似于列表,但每个元素都必须是独一无二的:
1 |
|
通过对包含重复元素的列表调用set(),可让Python找出列表中独一无二的元素,并使用这些元素来创建一个集合.
6.4嵌套
有时候,需要将一系列字典存储在列表中,或将列表作为值存储在字典中,这称为嵌套。你可以在列表中嵌套字典、在字典中嵌套列表甚至在字典中嵌套字典。
字典列表
经常需要在列表中包含大量的字典,而其中每个字典都包含特定对象的众多信息。例如,你可能需要为网站的每个用户创建一个字典,并将这些字典存储在一个名为users的列表中。在这个列表中,所有字典的结构都相同,因此你可以遍历这个列表,并以相同的方式处理其中的每个字典。
在字典中存储列表
有时候,需要将列表存储在字典中,而不是将字典存储在列表中。
每当需要在字典中将一个键关联到多个值时,都可以在字典中嵌套一个列表
在字典中存储字典
可在字典中嵌套字典,但这样做时,代码可能很快复杂起来。
7.用户输入和while循环
7.1函数input()的工作原理
函数input()让程序暂停运行,等待用户输入一些文本。获取用户输入后,Python将其存储在一个变量中,以方便你使用。
编写清晰的程序
每当你使用函数input()时,都应指定清晰而易于明白的提示,准确地指出你希望用户提供什么样的信息——指出用户该输入任何信息的提示都行。
有时候,提示可能超过一行,例如,你可能需要指出获取特定输入的原因。在这种情况下,可将提示存储在一个变量中,再将该变量传递给函数input(),例如:
1 |
|
使用int()来获取数值输入
使用函数input()时,Python将用户输入解读为字符串,如何输入的数字需要进行后续的计算时,可使用函数int(),它让Python将输入视为数值。
函数int()将数字的字符串表示转换为数值表示。
求模运算符
处理数值信息时,求模运算符(%)是一个很有用的工具,它将两个数相除并返回余数。
求模运算符不会指出一个数是另一个数的多少倍,而只指出余数是多少。
7.2while循环
for循环用于针对集合中的每个元素都执行一个代码块,而while循环不断地运行,直到指定的条件不满足为止。
在要求很多条件都满足才继续运行的程序中,可定义一个变量,用于判断整个程序是否处于
活动状态。这个变量被称为标志,充当了程序的交通信号灯。你可让程序在标志为True时继续运行,并在任何事件导致标志的值为False时让程序停止运行。这样,在while语句中就只需检查一个条件——标志的当前值是否为True,并将所有测试(是否发生了应将标志设置为False的事件)都放在其他地方,从而让程序变得更为整洁。
使用break退出循环
要立即退出while循环,不再运行循环中余下的代码,也不管条件测试的结果如何,可使用break语句。break语句用于控制程序流程,可使用它来控制哪些代码行将执行,哪些代码行不执行,从而让程序按你的要求执行你要执行的代码。
注意 在任何Python循环中都可使用break语句。例如,可使用break语句来退出遍历列表或字典的for循环。
在循环中使用 continue
要返回到循环开头,并根据条件测试结果决定是否继续执行循环,可使用continue语句,它不像break语句那样不再执行余下的代码并退出整个循环,而是退出当前循环,并继续执行下一个循环
避免无限循环
每个while循环都必须有停止运行的途径,这样才不会没完没了地执行下去
7.3使用while循环来处理列表和字典
for循环是一种遍历列表的有效方式,但在for循环中不应修改列表,否则将导致Python难以跟踪其中的元素。要在遍历列表的同时对其进行修改,可使用while循环。通过将while循环同列表和字典结合起来使用,可收集、存储并组织大量输入,供以后查看和显示。
在列表之间移动元素
1 |
|
删除包含特定值的所有列表元素
我们使用函数remove()来删除列表中的特定值,这之所以可行,是因为要删除的值在列表中只出现了一次。如果要删除列表中所有包含特定值的元素,该怎么办呢?
假设你有一个宠物列表,其中包含多个值为’cat’的元素。要删除所有这些元素,可不断运行一个while循环,直到列表中不再包含值’cat’,如下所示:
1 |
|
使用用户输入来填充字典
看范例:
1 |
|
这个程序首先定义了一个空字典(responses),并设置了一个标志(polling_active),用于指出调查是否继续。只要polling_active为True,Python就运行while循环中的代码。
8.函数
函数让你能够将程序分成多个很小的部分,其中每部分都负责完成一项具体任务。你可以根据需要调用同一个函数任意次,还可将函数存储在独立的文件中。
使用函数可让你编写的代码效率更高,更容易维护和排除故障,还可在众多不同的程序中重用。
8.1定义函数
文档字符串用三引号括起(”””这是函数的说明”””),Python使用它们来生成有关程序中函数的文档。
- 形参——函数完成其工作所需的一项信息
- 实参——是调用函数时传递给函数的信息
8.2传递参数
鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多,可使用位置实参,这要求实参的顺序与形参的顺序相同;也可使用关键字实参,其中每个实参都由变量名和值组成;还可使用列表和字典。
位置实参
你调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此,最简单的关联方式是基于实参的顺序。这种关联方式被称为位置实参。
关键字实参
关键字实参是传递给函数的名称—值对。你直接在实参中将名称和值关联起来了,因此向函数传递实参时不会混淆(不会得到名为Hamster的harry这样的结果)。关键字实参让你无需考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。
关键字实参的顺序无关紧要,因为Python知道各个值该存储到哪个形参中。
默认值
编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python将使用指定的实参值;否则,将使用形参的默认值。因此,给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值可简化函数调用,还可清楚地指出函数的典型用法。
8.3返回值
函数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值。函数返回的值被称为返回值。
在函数中,可使用return语句将值返回到调用函数的代码行。返回值让你能够将程序的大部分繁重工作移到函数中去完成,从而简化主程序。
有时候,需要让实参变成可选的,这样使用函数的人就只需在必要时才提供额外的信息。可使用默认值来让实参变成可选的。
函数可返回任何类型的值,包括列表和字典等较复杂的数据结构。
8.4传递列表
向函数传递列表很有用,这种列表包含的可能是名字、数字或更复杂的对象(如字典)。将列表传递给函数后,函数就能直接访问其内容。
在函数中修改列表
将列表传递给函数后,函数就可对其进行修改。在函数中对这个列表所做的任何修改都是永久性的,这让你能够高效地处理大量的数据。
- 每个函数都应只负责一项具体的工作
禁止函数修改列表
可向函数传递列表的副本而不是原件;这样函数所做的任何修改都只影响副本,而丝毫不影响原件。
1 |
|
8.5传递任意数量的实参
有时候,你预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参
用*形参名
的形参表示可以接收任意数量的实参,并装到元组中。注意,Python将实参封装到一个元组中,即便函数只收到一个值也如此。范例如下:
1 |
|
如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。
1 |
|
有时候,需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键—值对——调用语句提供了多少就接受多少。
用**形参
接收任意数量的关键字实参,范例如下:
1 |
|
形参**user_info中的两个星号让Python创建一个名为user_info的空字典,并将收到的所有名称—值对都封装到这个字典中
8.6将函数存储在模块中
函数的优点之一是,使用它们可将代码块与主程序分离。通过给函数指定描述性名称,可让主程序容易理解得多。你还可以更进一步,将函数存储在被称为模块的独立文件中,再将模块导入到主程序中。import语句允许在当前运行的程序文件中使用模块中的代码。
导入整体模块
要让函数是可导入的,得先创建模块。模块是扩展名为.py的文件,包含要导入到程序中的代码。
只需编写一条import语句并在其中指定模块名,就可在程序中使用该模块中的所有函数。如果你使用这种import语句导入了名为module_name.py的整个模块,就可使用下面的语法来使用其中任何一个函数:
1 |
|
导入特定的函数
还可以导入模块中的特定函数,语法如下:
1 |
|
若使用这种语法,调用函数时就无需使用句点,与使用主体程序中的函数是一样的用法。
使用as给函数指定别名
如果要导入的函数的名称可能与程序中现有的名称冲突,或者函数的名称太长,可指定简短而独一无二的别名——函数的另一个名称,类似于外号。
1 |
|
使用as给模块指定别名
还可以给模块指定别名。通过给模块指定简短的别名(如给模块pizza指定别名p),让你能够更轻松地调用模块中的函数。引用模块内的函数的名称还是没有变。
1 |
|
导入模块中的所有函数
使用星号(*)运算符可让Python导入模块中的所有函数。
import语句中的星号让Python将模块pizza中的每个函数都复制到这个程序文件中。由于导入了每个函数,可通过名称来调用每个函数,而无需使用句点表示法。但是强烈不建议这么做。
最佳的做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法
8.7函数编写指南
- 编写函数时,需要牢记几个细节。应给函数指定描述性名称,且只在其中使用小写字母和下划线。
- 每个函数都应包含简要地阐述其功能的注释,该注释应紧跟在函数定义后面,并采用文档字符串格式。
- 给形参指定默认值时,等号两边不要有空格。
- 如果程序或模块包含多个函数,可使用两个空行将相邻的函数分开,这样将更容易知道前一个函数在什么地方结束,下一个函数从什么地方开始。
- 所有的import语句都应放在文件开头,唯一例外的情形是,在文件开头使用了注释来描述整个程序。
9.类
Python 中的类(Class)是一种面向对象编程(Object-Oriented Programming,简称 OOP)的概念,它允许我们通过创建对象来组织和管理代码。类是一种抽象的数据类型,用于封装数据和行为。
类定义了一组属性和方法,描述了对象的特征和行为。对象是类的实例,通过实例化类可以创建具体的对象。
下面是一个简单的类的示例,以便更好地理解:
1 |
|
在上面的示例中,我们定义了一个名为 Person
的类。它具有类属性 species
,表示人类这个类别的物种。类还定义了一个初始化方法 __init__()
,它在创建对象时被调用,用于设置对象的初始状态。__init__()
方法的第一个参数是 self
,它表示类的实例本身。通过 self
可以访问和设置对象的属性。
类还定义了一个实例方法 greet()
,用于打印个人信息。在调用实例方法时,不需要显式传递 self
参数,Python 会自动将对象实例作为第一个参数传递给方法。
通过创建类的实例,我们可以创建具体的对象。在上面的示例中,我们创建了两个 Person
类的对象 person1
和 person2
。每个对象都具有自己的属性值,可以调用对象的方法来执行特定的操作。
类是面向对象编程中的核心概念之一,它提供了一种组织和封装代码的方式,使得代码更具可维护性、可扩展性和复用性。通过定义类和创建对象,我们可以按照面向对象的思想来编写更优雅和灵活的代码。
9.1创建和使用类
- 根据约定,在Python中,首字母大写的名称指的是类。
- 类中的函数称为方法;你前面学到的有关函数的一切都适用于方法,就目前而言,唯一重要的差别是调用方法的方式
- _init_()是一个特殊的方法,创建新实例时,Python都会自动运行它
- _init_()的形参self必不可少,还必须位于其他形参的前面
- 每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法
- 以self为前缀的变量都可供类中的所有方法使用,我们还可以通过类的任何实例来访问这些变量
- 可将类视为有关如何创建实例的说明
- 首字母大写的名称指的是类,而小写的名称指的是根据类创建的实例
9.2使用类和实例
类中的每个属性都必须有初始值,哪怕这个值是0或空字符串。在有些情况下,如设置默认值时,在方法__init__()内指定这种初始值是可行的;如果你对某个属性这样做了,就无需包含为它提供初始值的形参。
可以以三种不同的方式修改属性的值:直接通过实例进行修改;通过方法进行设置;通过方法进行递增(增加特定的值)。
直接修改属性的值
要修改属性的值,最简单的方式是通过实例直接访问它。
通过方法修改属性的值
通过方法对属性的值进行递增
有时候需要将属性值递增特定的量,而不是将其设置为全新的值
9.3继承
编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
子类的方法_init_()
创建子类的实例时,Python首先需要完成的任务是给父类的所有属性赋值。为此,子类的方法__init__()需要父类施以援手。
看下面这个范例:
1 |
|
- 创建子类时,父类必须包含在当前文件中,且位于子类前面
- 定义子类时,必须在括号内指定父类的名称
- 方法__init__()接受创建Car实例所需的信息
- super()是一个特殊函数,帮助Python将父类和子类关联起来。这行代码让Python调用ElectricCar的父类的方法__init__(),让ElectricCar实例包含父类的所有属性。父类也称为超类(superclass),名称super因此而得名
给子类定义属性和方法
让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法。
重写父类的方法
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。
使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕
将实例用作属性
使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。你可以将大型类拆分成多个协同工作的小类。
例如,不断给ElectricCar类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。在这种情况下,我们可将这些属性和方法提取出来,放到另一个名为Battery的类中,并将一个Battery实例用作ElectricCar类的一个属性:
1 |
|
- 我们定义了一个名为Battery的新类,它没有继承任何类
- 法__init__()除self外,还有另一个形参battery_size
- 方法describe_battery()也移到了这个类中
- 在ElectricCar类中,我们添加了一个名为self.battery的属性。这行代码让Python创建一个新的Battery实例(由于没有指定尺寸,因此为默认值70),并将该实例存储在属性self.battery中
9.4导入类
Python允许你将类存储在模块中,然后在主程序中导入所需的模块。
- 导入单个类;
import module_name import class_name
- 在一个模块中存储多个类
- 从一个模块中导入多个类,从一个模块中导入多个类时,用逗号分隔了各个类;
import module_name import class_name1,class_name2
- 还可以导入整个模块,再使用句点表示法访问需要的类;使用语法module_name.class_name访问需要的类
import module_name
- 导入模块中的所有类;
from module_name import *
,强烈不推荐使用这种方式导入类 - 在一个模块中导入另一个模块
9.5Python标准库
Python标准库是一组模块,安装的Python都包含它。
可使用标准库中的任何函数和类,为此只需在程序开头包含一条简单的import语句
10.文件和异常
学习处理文件和保存数据可让你的程序使用起来更容易:用户将能够选择输入什么样的数据,以及在什么时候输入;用户使用你的程序做一些工作后,可将程序关闭,以后再接着往下做。
学习处理异常可帮助你应对文件不存在的情形,以及处理其他可能导致程序崩溃的问题。这让你的程序在面对错误的数据时更健壮——不管这些错误数据源自无意的错误,还是源自破坏程序的恶意企图。
打开文件时,可指定读取模式(’r’)、写入模式(’w’)、附加模式(’a’)或让你能够读取和写入文件的模式(’r+’)。如果你省略了模式实参,Python将以默认的只读模式打开文件。
10.1从文件中读取数据
读取整个文件
函数open()接受一个参数:要打开的文件的名称
1 |
|
关键字with在不再需要访问文件后将其关闭。在这个程序中,注意到我们调用了open(),但没有调用close();你也可以调用open()和close()来打开和关闭文件,但这样做时,如果程序存在bug,导致close()语句未执行,文件将不会关闭。
read()到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除多出来的空行,可在print语句中使用rstrip():
1 |
|
要让Python打开不与程序文件位于同一个目录中的文件,需要提供文件路径,它让Python到系统的特定位置去查找。
逐行读取
要以每次一行的方式检查文件,可对文件对象使用for循环:
1 |
|
使用关键字with时,open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行存储在一个列表中,并在with代码块外使用该列表:你可以立即处理文件的各个部分,也可推迟到程序后面再处理。
1 |
|
使用文件的内容
strip()可以删除空格
读取文本文件时,Python将其中的所有文本都解读为字符串。如果你读取的是数字,并要将其作为数值使用,就必须使用函数int()将其转换为整数,或使用函数float()将其转换为浮点数。
对于你可处理的数据量,Python没有任何限制;只要系统的内存足够多,你想处理多少数据都可以
10.2写入文件
保存数据的最简单的方式之一是将其写入到文件中。
1 |
|
如果你要写入的文件不存在,函数open()将自动创建它。然而,以写入(’w’)模式打开文件时千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
函数write()不会在你写入的文本末尾添加换行符,因此如果你写入多行时没有指定换行符,像显示到终端的输出一样,还可以使用空格、制表符和空行来设置这些输出的格式
如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件。你以附加模式打开文件时,Python不会在返回文件对象前清空文件,而你写入到文件的行都将添加到文件末尾。如果指定的文件不存在,Python将为你创建一个空文件。
10.3异常
Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理该异常的代码,程序将继续运行;如果你未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。
异常是使用try-except代码块处理的。try-except代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办。使用了try-except代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的traceback。
通过将可能引发错误的代码放在try-except代码块中,可提高这个程序抵御错误的能力
try-except-else代码块的工作原理大致如下:Python尝试执行try代码块中的代码;只有可能引发异常的代码才需要放在try语句中。有时候,有一些仅在try代码块成功执行时才需要运行的代码;这些代码应放在else代码块中。except代码块告诉Python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办。
处理 FileNotFoundError 异常
使用文件时,一种常见的问题是找不到文件:你要查找的文件可能在其他地方、文件名可能不正确或者这个文件根本就不存在。对于所有这些情形,都可使用try-except代码块以直观的方式进行处理。
分析文本
使用try-except代码块提供了两个重要的优点:避免让用户看到traceback;让程序能够继续分析能够找到的其他文件。
失败时一声不吭
有时候你希望程序在发生异常时一声不吭,就像什么都没有发生一样继续运行。要让程序在失败时一声不吭,可像通常那样编写try代码块,但在except代码块中明确地告诉Python什么都不要做。Python有一个pass语句,可在代码块中使用它来让Python什么都不要做。
pass语句还充当了占位符,它提醒你在程序的某个地方什么都没有做,并且以后也许要在这里做些什么
编写得很好且经过详尽测试的代码不容易出现内部错误,如语法或逻辑错误,但只要程序依赖于外部因素,如用户输入、存在指定的文件、有网络链接,就有可能出现异常。凭借经验可判断该在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。
10.4存储数据
模块json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。你还可以使用json在Python程序之间分享数据。更重要的是,JSON数据格式并非Python专用的,这让你能够将以JSON格式存储的数据与使用其他编程语言的人分享。这是一种轻便格式,很有用,也易于学习。
使用json.dump()和json.load()
函数json.dump()接受两个实参:要存储的数据以及可用于存储数据的文件对象。
重构
你经常会遇到这样的情况:代码能够正确地运行,但可做进一步的改进——将代码划分为一系列完成具体工作的函数。这样的过程被称为重构。重构让代码更清晰、更易于理解、更容易扩展。
11.测试
11.1测试函数
编写函数或类时,还可为其编写测试。通过测试,可确定代码面对各种输入都能够按要求的那样工作。
Python标准库中的模块unittest提供了代码测试工具。单元测试用于核实函数的某个方面没有问题;
测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。
全覆盖式测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。
创建测试用例的语法需要一段时间才能习惯,但测试用例创建后,再添加针对函数的单元测试就很简单了
要为函数编写测试用例,可先导入模块unittest以及要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试
使用unittest类最有用的功能之一:一个断言方法。断言方法用来核实得到的结果是否与期望的结果一致
测试未通过时,不要修改测试,而应修复导致测试不能通过的代码:检查刚对函数所做的修改,找出导致函数行为不符合预期的修改
范例:
1 |
|
Python在unittest.TestCase类中提供了很多断言方法:
方法 | 用途 |
---|---|
assertEqual(a, b) | 核实a == b |
assertNotEqual(a, b) | 核实a != b |
assertTrue(x) | 核实x为True |
assertFalse(x) | 核实x为False |
assertIn(item, list) | 核实item在list中 |
assertNotIn(item, list) | 核实item不在list中 |
11.2测试类
类的测试与函数的测试相似——你所做的大部分工作都是测试类中方法的行为