内置函数
python解释器可以使用Tab不全,通过ctrl+c或ctrl+z回车或quit()可以退出解释器,重新回到windows命令行
pass常常作为定义函数、类、循环等操作的占位符,为了满足语法规范,防止报错
enum库 定义常量
一、注释和变量命名规范
1.1 注释
- 单行注释
# - 多行注释 三重引号
- 特殊注释 类似于shell脚本指定shell和字符编码
#!/usr/bin/env python3
# -*- coding: cp1252 -*-1.2 变量命名规则
- 只能包含数字、字母、下划线,且不能以数字开头
- 区分大小写,变量一般使用小写
- 变量、函数名使用蛇形命名法
- 类名使用大驼峰命名法
- 常量命名时字母全大写
- 见名知意
- 命名时谨慎使用小写字母l和大写字母O,以免和数字混淆
- 不能和python关键字重名
二、内置函数
print()函数input()函数 返回用户输入的字符串type()函数 返回数据类型range()函数 生成等差数列,左闭右开区间,返回range对象,这是一个可迭代对象iterablerange(num)返回0到num之间的整数序列range(num1,num2)返回两个数字之间的整数序列range(num1,num2,step)返回两个数字之间的整数序列,通过step指定间隔大小
- 类型转换函数
int()函数 转换为整型,不是四舍五入,而是舍弃小数点之后的部分float()转换成小数str()转换成字符串list()函数str()函数tuple()函数set()函数
三、运算符
3.1 算术运算符和数学计算
+加-减*乘/除 计算结果是浮点数//整除%取余**乘方
如果参与运算的数字中有浮点数,返回的结果也一定是浮点数
交互模式下,上次输出的表达式会赋给变量 _
>>> 2+3
5
>>> 10 + _
15除了 int 和 float,Python 还支持其他数字类型,例如 Decimal 或 Fraction。Python 还内置支持 复数,后缀 j 或 J 用于表示虚数(例如 3+5j )
3.2 赋值运算符
=赋值+=加法赋值运算符-=减法赋值运算符*=乘法赋值运算符/=除法赋值运算符%=取模赋值运算符**=幂赋值运算符//=取整除赋值运算符
3.3 比较运算符
==判断内容是否相等,满足为True,不满足为False!=判断内容是否不相等,满足为True,不满足为False>判断运算符左侧内容是否大于右侧,满足为True,不满足为False<判断运算符左侧内容是否小于右侧,满足为True,不满足为False>=判断运算符左侧内容是否大于等于右侧,满足为True,不满足为False<=判断运算符左侧内容是否小于等于右侧,满足为True,不满足为False
3.4 逻辑运算符
- 与
and - 或
or - 非
not
基本数据类型
四、数据类型
基本数据类型有 整型int 浮点数float 布尔数bool -> True/False 字符串str 复数complex 空NoneType -> None 列表list 集合set 元组tuple 字典dict。整数和浮点数可以适当添加下划线让数字更易读。可以通过使用逗号分割同时为多个变量赋值。
数据容器:一种可以容纳多份数据的数据类型,容纳的每一份数据称之为1个元素,每一个元素,可以是任意类型的数据,如字符串、数字、布尔等。五种数据容器分别是 列表list 集合set 元组tuple 字典dict 字符串str
4.1 空类型 <class 'NoneType'>
None是python中一个特殊的字面量,表示空,无意义。无返回值的函数返回的实际上是None,在if判断语句中None相当于False,定义变量但是暂时不想赋值时使用None当占位符,条件判断中判断是否是None,使用is关键字,而不用相等运算符 ==
4.2 数据容器通用操作
- 遍历 全都可以使用for循环,集合和字典不能使用while循环
- 共有的函数
len()函数max()函数min()函数sorted(容器, [reverse=True])函数 排序,reverse=True表示降序,返回一个排好序的列表
4.3 字符串<class 'str'>
使用单引号、双引号、三重引号包裹,可以使用 + 进行字符串的拼接,使用 * n 进行字符串的重复,n 表示重复次数
三重引号用于包裹多行字符串。一个引号的写法也可以跨行,只需要在每行的末尾加入表示连接的字符 \ 即可
字符串是不可变对象immutable,不能单独修改字符串某个字符,只能修改整体,字符串str更像是元组tuple而不是列表list
转义字符
\n换行符\t制表符
可以切片,也可以使用while循环或for循环遍历
4.3.1 字符串格式化
- f字符串
- 占位符字符串
在不需要控制数字的精度时,采用 f字符串 明显比 占位符字符串 简单。以下重点介绍占位符字符串
占位符
%s将内容转换成字符串,放入占位位置%d将内容转换成整数,放入占位位置%f将内容转换成浮点型,放入占位位置
用于控制精度的符号
m控制宽度,要求是数字(很少使用),设置的宽度小于数字自身,不生效。可以在m前添加0来使用0来补位.n控制小数点精度,要求是数字,会进行小数的四舍五入-配合m使用,控制宽度不够时在右边补空格,默认在左边补空格
f-string 格式控制
基本语法 f"{变量名:格式控制标记}"
格式控制标记 <填充字符><对齐方式><宽度><,><.精度><类型>
- 填充字符 默认是空格,只能是单个字符,需要配合
宽度使用 - 对齐方式 需要配合
宽度使用:^居中>右对齐<左对齐
- 宽度 整数,表示这个字符串的长度
,千位分隔符
4.3.2 字符串操作
常用函数和方法
replace(str1,str2)方法 将字符串内的全部:str1,替换为str2。不改变原字符串split(分隔str)方法 以分隔str为分隔符,将原字符串拆分,返回一个列表。不改变原字符串strip()方法 删除字符串前后的空白字符,传入参数时,删除参数字符串中的字母,比如传入'ab',删除的是字符串前后的字母a和字母brstrip()方法lstrip()方法
upper()方法 将字符串所以字母转换为大写lower()方法 将字符串所有字母转换为小写title()方法 单词首字母大写removeprefix()方法 删除前缀,需要传入需要删除的前缀
字符串特点
- 只可以存储字符串
- 长度任意(取决于内存大小)
- 支持下标索引
- 允许重复字符串存在
- 不可以修改(增加或删除元素等)
- 支持for循环
4.4 列表 <class 'list'>
使用方括号 [] 包裹,元素之间使用逗号分隔,列表中的数据可以是不同的数据类型,有顺序,使用下标获取元素。正向下标从 0 开始,反向下标从 -1 开始,列表命名时建议使用名词复数形式
可以使用for循环或while循环遍历
列表和字符串一样,也可以进行加法运算和乘法运算,列表是可变对象mutable
操作列表
index(元素)方法 返回查询元素的下标(正向下标)列表名[下标] = 新值更改元素的值insert(下标,值)方法 在指定位置插入元素append(值)方法 在列表末尾追加元素extend(数据容器)方法 将其它数据容器的内容取出,依次追加到列表尾部pop(下标)方法 删除元素,不传入参数时删除末尾的元素,返回删除的元素remove(元素)方法 删除某元素在列表中的第一个匹配项,根据值删除元素,可以与while循环配合使用删除所有匹配项clear()方法 将列表转换为空列表[]count(元素)方法 统计某元素在列表中出现次数sort()方法 正序排列,传入reverse=True时倒序排列(按照数字或字母顺序)reverse()方法 倒置,将原顺序倒置
del 列表名[下标] 删除元素
len()函数 返回列表长度sorted()函数 正序排列,传入参数reverse=True时倒序排列max(lst)函数 取列表中的最大值min(lst)函数 取列表中的最小值sum(lst)函数 计算列表元素之和zip(lst1, lst2)函数 同时遍历多个列表enumerate(lst, start)函数 同时遍历列表下标和元素,可选参数start表示列表下标开始值,默认0- 列表推导式
列表特点
- 可以容纳多个元素
- 可以容纳不同类型的元素(混装)
- 数据是有序存储的(有下标序号)
- 允许重复数据存在
- 可以修改(增加或删除元素等)
切片
序列是指:内容连续、有序,可使用下标索引的一类数据容器,列表、元组、字符串,均可以可以视为序列。序列的典型特征就是:有序并可用下标索引,字符串、元组、列表均满足这个要求
切片实际上是浅拷贝
通过类似于切片的写法,列表可以一次性修改多个元素的值
语法:序列[起始下标: 结束下标: 步长] 左闭右开区间
- 起始可以省略,省略从头开始
- 结束可以省略,省略到尾结束
- 步长可以省略,省略步长为1(可以为负数,表示倒序执行)
- 使用切片创建列表副本
序列[:]
4.5 元组 <class 'tuple'>
使用小括号包裹,元素之间使用逗号分隔,单元素元组需要在元素后添加逗号。元组相当于不可修改的列表,元组中的数据可以是不同的数据类型
可以切片,可以使用while循环或for循环遍历
常用函数和方法
- len()函数
- index(元素)方法
- count(元素)方法
元组特点
- 不可以修改元组的内容,否则会直接报错
- 可以修改元组内嵌套的list的内容(修改元素、增加、删除、反转等)但是不可以替换list为其它list或其它类型
- 可以容纳多个数据
- 可以容纳不同类型的数据(混装)
- 数据是有序存储的(下标索引)
- 允许重复数据存在
- 支持for循环
4.6 集合 <class 'set'>
使用花括号 {} 包裹,元素使用逗号分隔。元素无序,互不相同,没有下标索引。
只能使用for循环,不能使用while循环遍历。
常用函数和方法
add(元素)方法 添加元素,会导致集合本身遭到修改remove(元素)方法 移除元素,会导致集合本身遭到修改pop()方法 从集合中随机取出一个元素,会导致集合本身遭到修改clear()方法 清空集合,返回空集set(),会导致集合本身遭到修改变为set()集合1.difference(集合2)方法 取出集合1和集合2的差集(集合1有而集合2没有的),得到一个新集合,不改变原来的两个集合集合1.difference_update(集合2)方法 消除2个集合的差集,对比集合1和集合2,在集合1内,删除和集合2相同的元素。会导致集合1被修改,集合2不变集合1.union(集合2)方法 得到两个集合的并集,不改变原来两个集合len()函数
集合特点
- 可以容纳多个数据
- 可以容纳不同类型的数据(混装)
- 数据是无序存储的(不支持下标索引)
- 不允许重复数据存在
- 可以修改(增加或删除元素等)
- 支持for循环
4.7 字典dict
通过键获取值,使用花括号包裹,键值对使用逗号分隔,键和值使用冒号连接。
常用操作
-
新增元素
字典名[Key] = Value -
更新元素
字典名[Key] = Value对已存在的键使用 -
删除元素
pop(Key)方法 字典被修改,指定Key的键值对被删除,返回键对应的值value -
清空字典
clear()方法 字典被修改为空字典{},元素被清空 -
字典长度
len()函数 -
删除字典中的键值对:使用 del 语句将指定字典名中要删除的键相应的键值对彻底删除。例如 使用
del mydict['name']删除mydict字典中的name对应的键值对 -
get()方法- 用来访问字典某个键对应的值,如果键不存在,返回默认值或
None my_dict.get(key, [default]):根据键获取值,默认值default实参是可选的
- 用来访问字典某个键对应的值,如果键不存在,返回默认值或
-
{==items()==}方法 用于同时遍历字典的键和值??? example
代码 ```py languages = {'python': 'py', 'javascript': 'js', 'c++': 'cpp'} for lang, extension in languages.items(): print(f'{lang}语言的文件后缀名是{extension}') ``` 输出结果 ``` python语言的文件后缀名是py javascript语言的文件后缀名是js c++语言的文件后缀名是cpp ``` -
keys()方法 返回键的列表,一般用于遍历所有键,直接使用字典名遍历,默认是遍历字典的键的列表 -
values()方法,返回值的列表,一般用于遍历所有的值 -
字典推导式 能明显简化代码
???+ example
```py langs = ['python', 'javascripts', 'c++'] extensions = ['py', 'js', 'cpp'] languages = {lang: extension for lang, extension in zip(langs, extensions)} print(languages) # 输出结果 {'python': 'py', 'javascripts': 'js', 'c++': 'cpp'} ```
??? note "字典特点"
- 可以容纳多个数据
- 可以容纳不同类型的数据
- 每一份数据是KeyValue键值对
- 可以通过Key获取到Value,Key不可重复(重复会覆盖)
- 不支持下标索引
- 可以修改(增加或删除更新元素等)
- 支持for循环,不支持while循环
语句和函数
五、判断语句和循环语句
5.1 判断语句
判断语句if,和布尔数以及比较运算符一起使用。关键字in 检查特定值是否在列表、元组中。将数值 0、空值 None、空字符串、空列表[]、空元组()、空字典{} 用作条件表达式时,Python 都会返回 False
三种if语句
if语句if else语句和 双目运算符if elif else语句
match case 语句
5.2 循环语句
while循环 不断地运行,直到指定的条件不再满足为止。用途比for循环更广泛
for循环 适用于可迭代类型(列表,字符串,元组,字典,range()函数生成的数字序列等)
循环中断
continue中断本次循环,进入下一次循环break结束循环
六、函数function和模块
使用 def 关键字定义函数,函数必须先定义再使用。使用 return 关键字设置返回值,没有定义返回值的函数返回 None,使用蛇形命名法
定义函数之后,在 def 的下一行使用三重引号作为函数的文档字符串
使用 global 关键字 可以在函数内部声明变量为全局变量
多返回值 return语句得到返回值,如果想要返回多个可以使用逗号分隔,实际上返回的是元组
传参方式
- "位置参数"
- 调用函数时根据函数定义的参数位置来传递参数
- 传递的参数和定义的参数的顺序及个数必须一致
- "关键字参数"
- 函数调用时通过
"键=值"形式传递参数,不受传参顺序影响,关键字参数必须写在位置参数之后默认值 - 定义函数时可以为参数设置默认值,所有位置参数必须放在默认值参数之前
- 通过将参数的默认值设为
''或None,可以使其成为可选参数,具体用哪个当做占位符取决于参数的数据类型
- 函数调用时通过
- "不定长参数"
- 多个位置参数
*args,args是多个位置参数的习惯性写法。传入的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元组(tuple),args是元组类型,这就是位置传递 - 多个关键字参数
**kwargs,kwargs是习惯性写法,可以自己命名。参数是“键=值”形式的形式的情况下, 所有的“键=值”都会被kwargs接受, 同时会根据“键=值”组成字典.
- 多个位置参数
当函数的参数是列表时,为了防止原列表被函数修改,传参数可以传入原列表的副本进行操作
函数编写指南
- 要给函数和模块一个描述性名称,且只使用小写字母和下划线。
- 每个函数都应包含简要阐述其功能的注释,该注释应紧跟在函数定义后面,并采用文档字符串docstring的格式。
- 给形参指定默认值和调用函数传入实参时,等号两边不要有空格
- 应使用两个空行分开不同的函数块
- 所有的
import语句都应放在文件开头,在文件开头使用注释来描述整个程序。
匿名函数
-
"将函数作为参数"
-
"lambda函数"
lambda匿名函数 临时使用的函数,只能使用一次
语法
lambda 参数 : 函数体传入参数表示匿名函数的形式参数,函数体只能写一行代码
导入模块
import module_name通过module_name.function_name()使用模块中的函数from module_name import function_0, function_1, function_2直接通过function_0()使用模块中的函数,这种用法需要防止有函数重名from module_name import function_name as fn使用as为函数取别名import module_name as mn使用as为模块取别名from module_name import *导入模块中所有函数(不推荐使用)
文件和异常
七、文件操作
7.1 文件操作基本流程
- 打开文件
- 读写文件
- 关闭文件
7.2 通过open函数操作文件
-
打开文件
open(file, mode, encoding)函数,返回文件对象,用于打开文件或创建文件- file参数 文件路径字符串
- mode参数 文件访问模式
- encoding参数 编码方式,推荐使用utf-8
mode参数的可选值
- 'r' 只读模式(默认)
- 'w' 覆写模式,覆盖文件原有内容,写入新内容。文件不存在时,自动创建文件
- 'a' 追加模式,在原文件末尾写入内容。文件不存在时,自动创建文件
- 'rb' 使用只读模式打开二进制文件(图片等)
- 'wb' 使用覆写模式写入二进制文件
+配合以上方法使用,在原有功能上增加同时读写功能
-
读写文件
read()方法 读取文件内容,默认读取全部内容。传入数字可指定读取的字节长度readlines()方法 按行读取,返回列表,如果返回的列表需要遍历,则直接遍历文件对象也可以实现这个功能,并且更高效readline()方法 一次读取一行,此方法和 for i in open()相同,一次读取一行内容,大多数行末尾会有一个换行符write()方法 将内容写入文件flush()方法 刷新内容,使write()函数的内容成功写入文件write()函数将文件内容写到缓冲区,返回写入的字符数,需要使用flush()方法或者close()方法才可以完全写入文件writelines()方法 传入字符串列表,将此列表写入文件seek()方法 传入数字,改变当前文件操作指针的位置,英语占一个字节,GBK编码中文占2个字节,UTF-8编码中文占3个字节
-
关闭文件
close()方法
快捷操作 with open() as f 使用with open语句对文件操作结束后,会自动关闭文件
7.3 通过pathlib操作文件
-
导入标准库
pathlibfrom pathlib import Path -
创建
Path对象path = Path() # 传入文件路径 -
对
Path对象进行操作Path对象的常用方法:read_text()方法 读取path对象路径文件的所有内容,可选参数encoding指定读取文件使用的编码,常用utf-8write_text()方法 将字符串写入文件,如果指定路径的文件不存在,则会创建它,使用此方法写入时会删除文件原有内容exists()方法 判断文件是否存在- path对象可以通过str()函数转换为路径字符串
常和字符串的 splitlines() 方法一起使用,按行分割字符串,返回列表
八、异常
Python 使用称为异常(exception)的特殊对象来管理程序执行期间发生的错误。
try except else finally语句
try语句 可能引发异常的代码except语句 出现异常时,不出现traceback终止程序- 捕获一种异常
except NameError as e: - 捕获多种异常
except (NameError, ZeroDivisionError): - 捕获所有异常
except Exception as e:
- 捕获一种异常
else语句(可选) 不出现异常继续执行的代码finally语句(可选) 无论是否异常都要执行的代码
OOP面向对象
要使用tkinter进行GUI设计,就不得不用到面向对象的编程思想。Tkinter提供的小部件非常有限,而且大多数小部件的功能往往不足以完成相应的任务。在这种情况之下,就不得不使用继承来加强小部件的功能,或者创建新的小部件。
面向对象编程(object-oriented programming,OOP)是最有效的软件编写方法之一,在面向对象编程中,我们可以:编写表示现实世界中事物或情景的类(class),并基于这些类来创建对象(object)。通过类的实例化来根据类创建对象,并在代码中使用类的实例(instance)对象。
class关键字,类名以大写字母开头(大驼峰命名法)。 类名后面不需要加括号,但是子类后面需要加括号
一、属性
使用 dir() 函数可以查看一个对象所有的方法和属性
1.1 类属性
通俗来说,类属性就是定义在类中的变量。不需要创建实例就能直接使用,当然实例也可以使用类属性。通过类名就可以调用,可以更改属性值,还可以创建新属性。
我们来创建一个 Banana 类:
class Banana:
"""一种美味的热带水果"""
food_group = 'fruit'
colors = ['green', 'green-yellow', 'yellow', 'brown spotted', 'black']上面代码中的 food_group 和 colors 就是类属性。它们不需要创建实例就可以使用,对二者调用 print() 函数:
print(Banana.food_group) # fruit
print(Banana.colors) # ['green', 'green-yellow', 'yellow', 'brown spotted', 'black']
1.2 实例属性
使用初始化方法 __init__() 创建实例属性。self 形参:它是一个指向实例本身的引用,实例的每次调用都会自动传入自身,这样使得实例能够访问类中的属性和方法,self 形参必不可少,而且必须位于其他形参的前面。
所有的实例属性定义时都有 self. 的前缀,有些属性无须通过形参来定义,可以在 __init__() 方法中为其指定默认值。
实例属性就是定义在构造方法(又称初始化方法,属于魔术方法的一种) __init__() 中的带有 self. 前缀的变量,必须创建实例之后才能使用。不创建实例通过类名调用会报错。需要先创建实例,才能调用,创建实例后,可以对属性重新赋值,还可以创建新属性。
class Banana:
# --snip--
def __init__(self, color='green'):
self.color = color此处的 self.color 就是实例属性,它是定义在 __init__() 方法中的一个变量。
__init__(self)方法的第一个参数必须是 self,用来表示实例本身- self.color 和 color 并不是同一个变量,而是两个不同的变量,self.color 是一个不可分割的整体
为了使用 self.color 属性,我们需要创建实例:
my_banana = Banana()
print(my_banana.color) # green
二、方法
2.1 实例方法
定义在类中的函数,和 __init__() 方法一样,第一个参数必须是 self。需要先创建实例,才能调用。创建实例后,可以新建方法(先定义一个函数,再使用self.的方式将创建的函数名赋值给self)。
class Banana:
# --snip--
def print_color(self):
print(self.color)需要创建实例才可以使用:
my_banana.print_color() # green
2.2 类方法
使用装饰器 @classmethod 修饰的方法,类方法的第一个参数必须是 cls,表示这个类本身。不能使用实例属性和实例方法,但是可以通过 cls. 来访问类属性,类似与实例属性的访问。
不需要创建实例就可以调用类方法,在类中也可以通过 self. 来调用类方法,类似于实例方法在类中的调用。当然实例也可以调用类方法。
cls() 函数,在类方法中使用可以创建实例对象。
class Banana:
# --snip--
@classmethod
def check_color(cls, color):
return color in cls.colors这个方法通过类名就可以直接调用:
print(Banana.check_color('blue')) # False
2.3 静态方法
使用装饰器@staticmethod修饰的方法,定义静态方法时,不需要传入参数 self或 cls 。静态方法类似于普通的函数。不能在静态方法中使用实例属性和实例方法。
直接通过类名,就可以调用。当然也可以通过实例调用静态方法。
class Banana:
# --snip--
@staticmethod
def estimate_calories(num_bananas):
return num_bananas * 105这个方法通过类名就可以直接调用:
print(Banana.estimate_calories(10)) # 1050
三、类的三个特性
封装、多态、继承
3.1 继承
代码复用。
在编写类时,并非总是要从头开始,如果要编写的类是一个已有类的特殊版本,那么我们就可以使用继承(inheritance)。被继承的类称为父类(parent class)或超类,而继承的类称为子类(child class)。子类可以继承父类的所有属性和方法,也可以定义自己的属性和方法。
父类代码必须放在在子类代码的前面。需要在子类名称后添加括号,并在其中指定父类的名称,括号中指定了当前类继承的父类名称。在子类的初始化方法中, 我们使用了 super() 函数。这是一个特殊的函数,让你能够调用父类的方法,继承父类的实例属性。当父类中定义的一些方法不能满足子类的要求时,可以在子类中随时重写父类的方法:定义一个同名的方法。当调用子类生成的实例时,Python 首先会查找子类中的方法,从而忽略父类中被重写的同名方法。也可以把实例当作属性
单继承 使用 super().__init__() 继承父类属性和方法
多继承 使用 父类类名.__init__ () 继承父类方法和属性
在子类中重写方法 可以使用 super() 从父类继承原方法
class RedBanana(Banana):
def __init__(self, color='green'):
super().__init__(color)通过继承 RedBanana 类获得了 Banana 类的所有属性和方法。
3.2 多态
提高程序的可扩展性。
多个不同的类有同名的方法,通过创建一个参数是类的函数,这个函数中调用这些类同名的方法,可以实现多态(同一个方法,适用于不同的对象产生不同的行为)。
3.3 封装
保护程序的安全。
3.3.1 受保护成员(单下划线)
使用单下划线开头的属性和方法表示受保护的成员,只能在类的内部使用,子类可以通过继承获得受保护成员,实际上在外部也可以使用。
3.3.2 私有成员(双下划线)
使用双下划线开头的变量或方法表示私有成员,只能在类的内部使用,子类无法通过继承获得私有成员
3.3.3 魔术方法和魔术属性(首尾双下划线)
魔术方法
- 构造方法
__init__ __str__对实例调用print()函数时,返回的内容
魔术属性
- obj.__dict__属性 存储对象的属性字典
- obj.__class__属性 存储对象所属于的类,相当于调用了type()函数。在此基础上还可以再使用__name__属性,返类名
- obj.__bases__属性 存储对象父类的元组
- obj.__base__属性 存储对象继承的第一个父类
- obj.__mro__属性 存储直接或间接继承的父类的元组
四、类型注解
类的编写风格
- 类名中的每个单词的首字母都应大写,并且不使用下划线。这种命名规则叫做大驼峰式命名法(也称为 Pascal 命名法)。实例名和模块名都应该使用全小写,并在单词之间加上下划线(_)。 这种命名规则叫做下划线命名法(也称为 蛇形命名法)
- 每个类定义后的第一行和模块文件的首行,都应该包含一个文档字符串,简要地描述类的功能,以帮助其他人理解我们的代码
- 使用两个空行来分隔不同类,使用一个空行分隔类内的不同方法
- 当使用 import 语句时,使用一个空行分隔标准库和自己的库
适用类型注解可以让 PyCharm 为自定义的方法、变量等添加代码提示。
类型注解在代码简单时,几乎不使用,但是当代码十分复杂时,可以使用类型注解帮助自己更好的记住自己的思路,从而提高自己的工作效率。
4.1 对类中的变量添加类型注解
写法一
my_list: list = [1, 2, 3]
my_tuple: tuple = (1,2,3)写法二
my_list = [1, 2, 3] # type: list
my_tuple = (1,2,3) # type: tuple分别对my_list和my_tuple两个变量进行了类型注解。
4.2 对类中的方法添加类型注解
例如:
@staticmethod
def estimate_calories(num_bananas: int) -> int:
return num_bananas * 105为传入的参数 num_bananas 和 方法的返回值添加了类型注解。
4.3 Union联合类型注解
如果一个变量中存储了不同类型的数据,那就需要使用联合类型注解,常用于字典、列表等。
使用前先导入模块 from typing import Union
# 旧版本Python
from typing import Union
my_list: list[Union[int, str]] = [1, 2, "hello", "world"]
# 新版本Python可以不导入typing
my_list2: list[int | str] = [1, 2, "hello", "world"]因为 my_list 变量存储的数据同时有 int 和 str,所以需要使用联合类型注解。
测试
十、测试unittest/pytest
可以使用 标准库unittest 或 第三方库pytest 进行单元测试。一种最简单的测试是单元测试(unit test),用于核实函数的某个方面没有问题。测试用例(test case)是一组单元测试,这些单元测试一道核实函数在各种情况下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情况的测试。全覆盖(full coverage)测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。
使用 pytest 进行测试,会让单元测试编写起来非常简单。我们将编写一个测试函数,它会调用要测试的函数,并做出有关返回值的断言 (assert 语句)。如果断言正确,表示测试通过;如果断言不正确,表示测试未通过。
测试函数和测试文件必须以 test_ 开头
几种常用的断言(类似于if语句的条件判断):
| unittest断言 | pytest断言 | 用途 |
|---|---|---|
| assertEqual(a, b) | assert a == b | 断言两个值相等 |
| assertNotEqual(a, b) | assert a != b | 断言两个值不等 |
| assertTrue(a) | assert a | 断言 a 的布尔求值为 True |
| assertFalse(a) | assert not a | 断言 a 的布尔求值为 False |
| assertIn(element, list) | assert element in list | 断言元素在列表中 |
| assertNotIn(element, list) | assert element not in list | 断言元素不在列表中 |
10.1 对函数进行单元测试pytest
测试文件和被测试文件要放在同一个目录下,此处都放置在 code/08-pytest 目录下。
需要被测试的函数
name_func.py
def get_format_name(first: str, last: str) -> str:
"""返回完整格式的姓名"""
return first + ' ' + last测试函数
test_name_func.py
运行测试
Pycharm运行测试非常简单,测试函数旁边会出现运行按钮,点击即可运行测试
通用方法:终端使用cd将当前工作目录切换到 code/08-pytest,输入pytest,回车
10.1 对函数进行单元测试unittest
测试文件和被测试文件要放在同一个目录下,此处都放置在 code/08-unittest 目录下。
需要被测试的函数
name_func.py
测试函数
test_name_func.py
运行测试
直接运行 test_name_func.py
如果我们修改被测试函数,使它能够处理有中间名的姓名:
def get_format_name(first, middle, last):
"""返回完整格式的姓名"""
return first + ' ' + middle + ' ' + last此时运行测试,测试将不会通过
测试不通过,说明我们编写的用于获取全名的函数 name_func() 并没有完成我们预期的行为:将只有姓和名的名字合成为全名,以及将有姓、名以及中间名的名字合成为全名。这种情况下我们应该修改 name_func() 这个函数,而不是修改测试。
修改后的 name_func.py
def get_format_name(first, last, middle=''):
"""返回完整格式的姓名"""
if middle:
return first + ' ' + middle + ' ' + last
else:
return first + ' ' + last此时运行测试,测试将通过。但是我们编写的测试函数只会测试只有姓和名的情况,并不会测试有中间名的情况,因此我们要添加一个用于测试含有中间名名字的测试。
10.2 对类进行单元测试
-
一个需要被测试的类
survey.py -
编写测试函数
test_survey.py在测试中,夹具(fixture)可帮助我们搭建测试环境。这通常意味着创建供多个测试使用的资源。在 pytest 中,要创建夹具,可编写一个使用装饰器 @pytest.fixture装饰的函数。装饰器(decorator)是放在函数定义前面的指令。在运行函数前,Python将该指令应用于函数,以修改函数代码的行为。
-
运行测试