Python介绍
python常用于回归测试。
python+selenium web自动化(功能测试转换为代码)
python+appium 移动端(手机端app)自动化
python+ requests 接口
Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。python是轻量级语言。
Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。
- Python 是一种解释型语言: 这意味着开发过程中没有编译这个环节。类似于PHP和Perl语言。
- Python 是交互式语言: 这意味着,您可以在一个 Python 提示符 >>> 后直接执行代码。
- Python 是面向对象语言: 这意味着Python支持面向对象的风格或代码封装在对象的编程技术。
- Python 是初学者的语言:Python 对初级程序员而言,是一种伟大的语言,它支持广泛的应用程序开发,从简单的文字处理到 WWW 浏览器再到游戏。
Python基础代码
标识符
第一个字符必须是字母表中字母或下划线**_** ,不能以数字开头。
驼峰命名法:
大驼峰(又称帕斯卡命名法Pascal Case):通常用于标识符(如变量名、函数名、类名等)的命名。在帕斯卡命名法中,每个单词的首字母都大写,单词之间没有下划线或其他分隔符。:MyName
小驼峰(Camel Case):通常用于命名变量、函数、方法等标识符。在小驼峰命名法中,除了第一个单词的首字母小写外,后续单词的首字母都大写,单词之间没有下划线或其他分隔符。,其他大写:myName
下划线连接法(Snake Case):每个单词之间用下划线连接my_name
变量一般使用下划线法
标识符的其他的部分由字母、数字和下划线组成。
标识符对大小写敏感。
在 Python 3 中,可以用中文作为变量名,非 ASCII 标识符也是允许的了。
python保留字
1
2
3
4 >['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del',
>'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
>'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with',
>'yield']
多行语句
Python 通常是一行写完一条语句,但如果语句很长,我们可以使用反斜杠 ** 来实现多行语句,例如:
1 | total = item_one + \ |
在 [], {}, 或 () 中的多行语句,不需要使用反斜杠 ****,例如:
1 | total = ['item_one', 'item_two', 'item_three', |
数字类型
- int (整数), 如 1, 只有一种整数类型 int,表示为长整型,没有 python2 中的 Long。
- bool (布尔), 如 True。
- float (浮点数), 如 1.23、3E-2
- complex (复数), 如 1 + 2j、 1.1 + 2.2j
输出print()
1 | # print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end="" |
格式化输出
1 | print("%s",%str) #格式化输出字符串,%s可以填充任意类型的数据 |
1 | name = 'ana' |
1 | num = 90 |
F-string(f字符串的格式化方法)
1 | 1. 需要在字符串前面加上f""或者F"" |
1 | stu_num = 1 |
输入input
1 | a = input('输入你心中想的内容:') |
输入的内容类型是字符串类型
类型转换
1 | a = input('输入你的年龄:') |
int()
可以将其他类型转换成int类型 可以将float类型转换成整型
可以将整数类型字符串转换为整型
float()
可以将其他类型转换成float类型- 可以将int类型转换成浮点型
- 可以将数字类型字符串(整数、小数)转换为浮点型
str()
可以将其他类型转换成字符串类型- 任何类型都可以使用str()将其转换成字符串,一般都是直接加引号
import与from…import
在 python 用 import
或者 from...import
来导入相应的模块。
- 将整个模块(somemodule)导入,格式为:
import somemodule
- 从某个模块中导入某个函数,格式为:
from somemodule import somefunction
- 从某个模块中导入多个函数,格式为:
from somemodule import firstfunc, secondfunc, thirdfunc
- 将某个模块中的全部函数导入,格式为:
from somemodule import *
import somemodule as alias
导入模块指定别名alias
python中的三种波浪线和pep
红色:表示代码的错误,代码没有写完也会出现
灰色:不会影响代码的正常执行,pep8为代码的书写规范。可以在写完后使用ctrl+alt+L格式化
绿色:在引号中,认为书写的内容不是英文单词时会出现
Python基本数据类型
Python 中的变量不需要声明。每个变量在使用前都必须赋值,如果事先不知道赋什么数值,可以先赋值None,variable = None
进行初始化。变量赋值以后该变量才会被创建。
在 Python 中,变量就是变量,它没有类型,我们所说的”类型”是变量所指的内存中对象的类型。变量名在赋值时,实际上是将对象的引用(即内存地址)赋值给变量名。因此,变量名实际上是对象的引用,而不是直接对内存地址的引用。
1 | import ctypes |
Python允许你同时为多个变量赋值,连续赋值从右往左,分别赋值按位置进行赋值。例如:
1 | a = b = c =1 |
标准数据类型
Python3 中常见的数据类型有:
Number(数字)
- 整数(int): 表示整数值,例如
42
或-10
。 - 浮点数(float): 表示带有小数点的数值,例如
3.14
或-0.001
。
- 整数(int): 表示整数值,例如
String(字符串):表示文本数据,用单引号
' '
或双引号" "
括起来,例如'hello'
或"world"
。bool(布尔类型):表示逻辑值,只有两个值:
True
和False
。List(列表):表示有序的可变集合,可以包含任意类型的元素,例如
[1, 2, 'hello']
。Tuple(元组): 表示有序的不可变集合,可以包含任意类型的元素,例如
(1, 2, 'hello')
。Set(集合):表示无序的不重复元素的集合,例如
{1, 2, 3}
。Dictionary(字典):表示键值对的集合,其中每个键都对应一个值,例如
{'name': 'Alice', 'age': 30}
。NoneType(空值): 表示空值或者缺失值,用
None
表示。bytes(字节串): 表示二进制数据,例如
b'hello'
。-
import sys s = "hello" b = b'hello' print(sys.getsizeof(s)) # 获取字符串对象 s 占用的内存空间大小 print(sys.getsizeof(b)) # 获取字符串对象 b 占用的内存空间大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
![](image-20240403135732256.png)
Python3 的六个标准数据类型中:
- **不可变类型(3 个):**可变类型在创建后其内容无法被修改,每次对不可变类型进行操作时,都会创建一个新的对象
Number(数字)、String(字符串)、Tuple(元组);
- **可变类型(3 个):**在 Python 中,可变类型是指可以在创建后修改其内容的数据类型。可变类型的数据结构在内存中的内容可以改变,而不会改变其在内存中的地址。这意味着,对可变类型对象进行操作时,不会创建新的对象,而是直接修改原始对象。
List(列表)、Dictionary(字典)、Set(集合)。
-
- ```python
list1 = [1, 2, 3]
print('list1的内存地址:', id(list1))
print('list1[0]:1的内存地址:', id(list1[0]))
print('list1[1]:2的内存地址:', id(list1[1]))
print('list1[2]:3的内存地址:', id(list1[2]))
list1.append(5)
print('list1的内存地址:', id(list1))
list1 = [1, 3, 4] # 指向了一个新的列表对象,原来的列表对象因为没有变量指向它,会被回收
print('list1的内存地址:', id(list1))
print('list1[0]:1的内存地址:', id(list1[0]))
print('list1[1]:3的内存地址:', id(list1[1]))
print('list1[2]:4的内存地址:', id(list1[2]))
x = 42
print('x的内存地址', id(x))
print('x+1的内存地址', id(x + 1))
number数字
Python3 支持 int、float、bool、complex(复数)。
注意:
- 数值的除法包含两个运算符:**/** 返回一个浮点数,**//** 返回一个整数。
- 在混合计算时,Python会把整型转换成为浮点数。
查看类型
type()
type(obj)
函数返回对象obj
的类型对象。- 例如,
type(5)
返回<class 'int'>
,表示整数对象的类型是int
类型。
1 | a,b,c,d=2,3.14,True,3+4j |
isinstance()
isinstance(obj, classinfo)
函数用于检查对象obj
是否是指定类型classinfo
的实例,或者是其子类的实例。classinfo
参数可以是类型对象、类型元组或类型列表。如果obj
是classinfo
类型的实例或其子类的实例,则返回True
;否则返回False
。- 例如,
isinstance(5, int)
返回True
,表示整数对象5
是int
类型的实例。
1 | print(isinstance(a,int)) |
一定要注意二者的区别和使用方法
1 | class A(): |
String字符串
Python 中单引号 ‘ 和双引号 “ 使用完全相同。
- 使用三引号**’’’** 或 **”””**可以指定一个多行字符串。
转义符\。
- 反斜杠可以用来转义,使用 r 可以让反斜杠不发生转义。 如 r”this is a line with \n” 则 \n 会显示,并不是换行。
- 按字面意义级联字符串,如 “this “ “is “ “string” 会被自动转换为 this is string。
- 字符串可以用 + 运算符连接在一起,用 ***** 运算符重复。
print('123\n123') print(r'123\n123') print('this''is''a''string') print('a' + 'bc') print('a' * 5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- ![](image-20240403153219978.png)
- Python 中的字符串有两种索引方式,从左往右以 **0** 开始,从右往左以 **-1** 开始。
- Python 中的字符串不能改变。
- Python 没有单独的字符类型,一个字符就是长度为 1 的字符串。
```python
word = '字符串'
sentence = "这是一个句子。"
paragraph = """这是一个段落,
可以由多行组成"""
字符串的截取
字符串的截取的语法格式如下:
变量[头下标:尾下标]
索引值以 0 为开始值,-1 为从末尾的开始位置。
尾下标对应的字符不包含
1 | str = 'abcdefghijk' |
相关函数
len()
len()可以用来获取字符串长度
查找方法find()
1 | 字符串.find(sub_str,start,end) |
1 | str = 'abcdefghijk' |
1 | str = 'ana and luke and lei' |
1 | # 过7游戏 |
字符串替换replace()
1 | 字符串.replace(old_str,new_str,count) |
1 | # 将str1中所有的g改为G |
1 | # 将str1中第一个good改成GOOD |
1 | # 将str1中第二个good改成GOOD |
字符串的拆分spilt()
1 | 字符串.split.(sep,maxsplit) |
1 | str = "Hello,my name is Elora" |
字符串的连接join()
1 | 字符串1.join(列表) # 括号中的内容主要是列表,可以是其他容器 |
1 | list1 = ['hello', 'world', 'you'] |
字符串格式化
1 | 字符串.format() |
1 | stu_num = 1 |
1 | name = 'Alice' |
bool布尔类型
在 Python 中,True 和 False 都是关键字,表示布尔值。
布尔类型特点:
- 布尔类型只有两个值:True 和 False。
- 布尔类型可以和其他数据类型进行比较,比如数字、字符串等。在比较时,Python 会将 True 视为 1,False 视为 0。
- 布尔类型可以和逻辑运算符一起使用,包括 and、or 和 not。这些运算符可以用来组合多个布尔表达式,生成一个新的布尔值。
- 布尔类型也可以被转换成其他数据类型,比如整数、浮点数和字符串。在转换时,True 会被转换成 1,False 会被转换成 0。
注意: 在 Python 中,所有非零的数字和非空的字符串、列表、元组等数据类型都被视为 True,只有 0、空字符串、空列表、空元组等被视为 False。因此,在进行布尔类型转换时,需要注意数据类型的真假性。
list列表
- 列表可以完成大多数集合类的数据结构实现。列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(嵌套)。
- 列表是写在方括号 [] 之间、用逗号分隔开的元素列表。
- 和字符串一样,列表同样可以被索引和截取,列表被截取后返回一个包含所需元素的新列表。截取的语法格式:
变量[头下标:尾下标]
索引值以 0 为开始值,**-1** 为从末尾的开始位置。
1 | list1 = list() # 定义一个空的列表 |
1 | list1 = list() # 定义一个空的列表 |
列表的切片
1 | list1 = ['abcd', 78910, 2.233333, 'hello', 351] |
列表查询相关操作
数据下标index()
1 | 列表.index(el,start,end) |
1 | mylist = [1, 3, 5, 7, 2, 3] |
判断是否存在in()
1 | el in 容器 |
统计出现次数count()
1 | 列表.count(el) |
列表修改数据
1 | # 直接更改list元素值 |
1 | list1 = ['abcd', 78910, 2.233333, 'hello', 351] |
列表添加数据
尾部添加(最常用)
1 | 列表.append(数据) |
指定下标位置添加
1 | 列表.insert(下标,数据) |
1 | mylist = ['hello', 'world', 'and'] |
列表合并extend()
1 | 列表1.extend(列表2) # 将列表2中的所有数据逐个添加到列表1的尾部,不会改变列表1的内存地址 |
1 | # list2拆开添加到mylist |
列表删除
在列表中删除中间的数据,后面的数据会向前移动
根据下标删除
1 | 列表.pop(下标) #删除指定下标对应的数据 |
根据数据值删除
1 | 列表.remove(数据) |
1 | list1=['h','h','h','h','h','h','h','h','h','h','o','l'] |
清空数据(一般不用)
1 | 列表.clear() |
列表的反转和逆置
1 | 1. 列表[::-1] # 得到一个新列表,原列表不会改变 |
1 | list1=list('hello') |
列表的复制
1 | # 使用场景,修改列表中的数据与修改之前的原始数据进行对比 |
1 | list1=['h', 'e', 'l', 'l', 'o'] |
列表的排序
列表的排序一般是针对数字进行排序
1 | 列表.sort() # 按照升序排序 |
列表去重
去除列表中重复的数据
错误示范
1 | my_list = [1, 23, 33, 11, 2, 23, 3, 2, 1, '3', 3] |
当检测到列表中该数据需要删除时,列表后方数据会向前移动,所以紧跟在被删除数据后方的元素会不参与遍历
正确示范
思想:放入新容器
1 | my_list = [1, 23, 33, 11, 2, 23, 3, 2, 1, '3', 3] |
利用集合set特点
缺点:不能保证数据在原列表中的顺序(一般不考虑)
元组tuple
Python 的元组与列表类似,不同之处在于元组的元素不能修改。元组使用小括号 **( )**,列表使用方括号 **[ ]**。
元组一般在函数的传参或者返回值中使用,保证数据不会被修改
1 | mytuple = tuple(list) #将列表转化为元组 |
元组中只包含一个元素时,需要在元素后面添加逗号 , ,否则括号会被当作运算符使用:
1 | tuple1 = (1) |
元组的常用方法
- 没有修改方法
- 元组中可以使用下标和切片获取数据
- 元组中存在index方法
- 元组中存在count方法
- 可以使用in操作
字典
- 字典是另一种可变容器模型,且可存储任意类型对象。
- 字典的每个键值 key=>value 对用冒号 : 分割,每个对之间用逗号(,)分割,整个字典包括在花括号 {} 中 ,格式如下所示:
1 | 变量 = {key1 : value1, key2 : value2, key3 : value3 } |
- 键必须是唯一的,但值则不必。值可以取任何数据类型,但键必须是不可变类型,键一般是字符串,可以是数字,不能是列表。
- 字典不能转化成列表、元组和字符串
1 | # 创建空的字典dict1 |
字典的增加修改
1 | 字典[键]=数据值 |
1 | dict1 = {'name': 'Marry', 'age': 32, 'height': '1.78', 'is married': True, 'hobbies': ['painting', 'music']} |
1 | list1 = [] |
字典的删除
- 删除指定的键值对
1 | del 字典[键] |
1 | dict1 = {'name': 'Marry', 'age': '32', 'height': '1.78', 'is married': True, 'hobbies': ['painting', 'music'], |
- 清空
1 | 字典.clear() |
字典的查询
字典中没有下标,要获取数据需要使用键来获取
使用
字典[键]
1
2
3# 字典[键]
# 如果键存在,返回对应数据值
# 如果键不存在,报错使用
字典.get(键)
1
2
3
4字典.get(键,数据值)
# 数据值一般不写,默认None
# 如果键存在,返回对应数据值
# 如果键不存在,返回括号中书写的数据值(None)
1 | dict1 = {'name': 'Marry', 'age': '32', 'height': '1.78', 'is married': True, 'hobbies': ['painting', 'music']} |
字典的遍历
对字典的键进行遍历
1 | 1. for key in 字典: |
对值进行遍历
1 | for value in 字典.values(): # 字典.values()可以获取字典中所有的值 |
对键值对进行遍历
1 | for k, v in 字典.items(): # 字典.items()可以获取字典中所有键值对 |
1 | dict1 = {'name': 'Marry', 'age': '32', 'height': '1.78'} |
容器总结
- 字符串,列表,元组支持加法运算
- 字符串、列表、元组支持乘上一个整数
- len()在容器中(str、list、dict、tuple)都可以使用
- in 在容器中都可以使用,在字典中判断字典的key是否存在
1 | my_list = [{'id': 1, 'money': 10}, {'id': 2, 'money': 20},{'id': 3, 'money': 30},{'id': 4, 'money': 40}] |
1 | my_dict = {'登录': [{'desc': '正确的用户名密码', 'username': 'admin', 'password': '123456', 'expect': '登录成功'}, |
Python运算符
算术运算符
以下假设变量 a=10,变量 b=21:
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 - 两个对象相加 | a + b 输出结果 31 |
- | 减 - 得到负数或是一个数减去另一个数 | a - b 输出结果 -11 |
* | 乘 - 两个数相乘或是返回一个被重复若干次的字符串 | a * b 输出结果 210 |
/ | 除 - x 除以 y(返回浮点类型) | b / a 输出结果 2.1 |
% | 取模 - 返回除法的余数 | b % a 输出结果 1 |
** | 幂 - 返回x的y次幂 | a**b 为10的21次方 |
// | 取整除 - 往小的方向取整数 | >>> 9//2 4 >>> -9//2 -5 |
优先级:() > ** > * / // % > + -
比较运算符
以下假设变量 a 为 10,变量 b 为20:
运算符 | 描述 | 实例 |
---|---|---|
== | 等于 - 比较对象是否相等 | (a == b) 返回 False。 |
!= | 不等于 - 比较两个对象是否不相等 | (a != b) 返回 True。 |
> | 大于 - 返回x是否大于y | (a > b) 返回 False。 |
< | 小于 - 返回x是否小于y。所有比较运算符返回1表示真,返回0表示假。这分别与特殊的变量True和False等价。 | (a < b) 返回 True。 |
>= | 大于等于 - 返回x是否大于等于y。 | (a >= b) 返回 False。 |
<= | 小于等于 - 返回x是否小于等于y。 | (a <= b) 返回 True。 |
逻辑运算符
Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20:
运算符 | 逻辑表达式 | 描述 | 实例 |
---|---|---|---|
and | x and y | 布尔”与” - 如果 x 为 False,x and y 返回 x 的值,否则返回 y 的计算值。当第一个条件为False时,第二个条件不判断 | (a and b) 返回 20。 |
or | x or y | 布尔”或” - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。当第一个条件为True,第二个条件不再判断 | (a or b) 返回 10。 |
not | not x | 布尔”非” - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 | not(a and b) 返回 False |
当数字不是0时,都表示True
赋值运算符
以下假设变量a为10,变量b为20:
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符 | c = a + b 将 a + b 的运算结果赋值为 c |
+= | 加法赋值运算符 | c += a 等效于 c = c + a |
-= | 减法赋值运算符 | c -= a 等效于 c = c - a |
*= | 乘法赋值运算符 | c *= a 等效于 c = c * a |
/= | 除法赋值运算符 | c /= a 等效于 c = c / a |
%= | 取模赋值运算符 | c %= a 等效于 c = c % a |
**= | 幂赋值运算符 | c * *= a 等效于 c = c ** a |
//= | 取整除赋值运算符 | c //= a 等效于 c = c // a |
:= | 海象运算符,这个运算符的主要目的是在表达式中同时进行赋值和返回赋值的值。Python3.8 版本新增运算符。 | 在这个示例中,赋值表达式可以避免调用 len() 两次:print(n:=10) 输出结果:10 |
成员运算符
运算符 | 描述 | 实例 |
---|---|---|
in | 如果在指定的序列中找到值返回 True,否则返回 False。 | x 在 y 序列中 , 如果 x 在 y 序列中返回 True。 |
not in | 如果在指定的序列中没有找到值返回 True,否则返回 False。 | x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。 |
条件控制
if elif else
1 | if condition_1: |
1 | age = input('输入你的年龄:') |
1 | name = input('输入你的用户名:') |
1 | date = input("输入今天是星期几:") |
if嵌套
1 | num = int(input("请输入一个整数")) |
match…case
1 | match subject: |
1 | def http_error(status): |
猜拳游戏
1 | import random |
循环语句
while循环
1 | while 判断条件(condition): |
死循环和无限循环
- 死循环:bug造成的
- 无限循环:
while true
,故意设计的。应用场景:书写循环时,不确定循环要执行多少次。无限循环的使用一般会在循环中添加if判断,当if条件成立,使用关键字break终止循环
while
1 | # 1到100的数字之和 |
1 | # 1到100的之间的偶数之和 |
while…else
如果 while 后面的条件语句为 false 时,则执行 else 的语句块。如果循环由break中断,不执行else中的语句
语法格式如下:
1 | while <expr>: |
1 | count = 0 |
1 | count = 0 |
for循环
for 循环可以遍历容器中的数据,容器:字符串、列表、元组、字典
for循环的一般格式如下:
1 | for <variable> in <sequence>: |
for … in …
- 容器中有多少个数据,循环执行多少次
- 每次循环会将容器中的数据取出,保存到in前面的变量中
1 | # 遍历列表中的元素 |
1 | word = 'Google' |
for … in range()
整数范围值可以配合 range() 函数使用:
1 | for number in range(1, 6): |
1 | for x in range(6): |
for … else
在 Python 中,for…else 语句用于在循环结束后执行一段代码。
语法格式如下:
1 | for item in iterable: |
当循环执行完毕(即遍历完 iterable 中的所有元素)后,会执行 else 子句中的代码,如果在循环过程中遇到了 break 语句,则会中断循环,此时不会执行 else 子句。
1 | for x in range(6): |
1 | for x in range(6): |
range()函数
- range() 函数会生成数列,range(n),可以生成0到n(不包含n)的数字。循环循环n次
- range(a,b)生成从a到b(不含b)的数列
- rang(a,b,c)c为步长(增量),可以为负数
1 | # 获取列表索引 |
还可以使用 range() 函数来创建一个列表
1 | list(range(5)) |
break和continue
- break:终止循环,循环立即结束
- continue:跳过本次循环,本次循环剩下的代码不再执行,进行下一次循环
- break 语句可以跳出 for 和 while 的循环体。else 子句,它在穷尽列表(以for循环)或条件变为 false (以while循环)导致循环终止时被执行,如果从 for 或 while 循环中被break终止,任何对应的循环 else 块将不执行。
- continue 语句被用来告诉 Python 跳过当前循环块中的剩余语句,然后继续进行下一轮循环。
1 | str = input("输入一个字符串并打印,当输入i的时候i不打印,当输入%的时候结束打印") |
pass语句
pass
是空语句,是为了保持程序结构的完整性。
pass
不做任何事情,一般用做占位语句
函数
函数的定义
- 函数必须先定义后调用
1 | def functname(): |
函数的调用
1 | functname() |
文档注释
本质上是注释,只是书写的位置和作用比较特殊
- 书写位置:在函数名的下方使用,三对双引号进行注释
- 作用:告诉别人函数如何使用
- 查看,在调用的时候,将光标放到函数名上使用ctrl+q
- ctrl+b会转到函数的声明(按住ctrl鼠标左键点击)
函数的嵌套调用
在一个函数定义中调用另一个函数
注意:
- 函数定义不会调用函数体中的代码
- 函数调用会执行函数体中的代码
- 函数体中代码执行结束会回到函数被调用的地方继续向下执行
函数参数
- 在函数定义的时候,使用变量代替具体的数据(进行占位),称为形参,在调用函数的时候,传递具体的数值,称为实参
- 函数的传参传递的是引用
函数传参的方式
位置传参
在函数调用的时候,按照形参的顺序,将实参值传递给形参
关键字传参
在函数调用的时候,指定数据值给到那个形参
混合使用
- 关键字传参必须写在位置传参的后面
- 不能给一个形参传递多个数据
1 | # 求和函数 |
缺省参数
缺省参数即默认参数
1 | # 在函数定义的时候,给形参一个默认的数据值,这个形参就变为缺省参数 |
多值参数(不定长参数)
比如print()函数,可以有多个参数
在书写函数不确定参数个数的时候,可以使用不定长参数(多值参数)
- 不定长位置参数(不定长元组参数)
- 在普通参数的前面加上一个
*
,这个参数就变为不定长位置参数 - 这个形参可以接受任意多个位置的传参
- 参数将被收集到一个元组中,并传递给函数
- 不定长位置参数写在普通参数的后面
- 一般写法,不定长位置参数的名字为
args
,即(*args)
- 在普通参数的前面加上一个
- 不定长关键字参数(不定长字典参数)
- 在普通参数的前面加上两个
**
,这个参数就变为不定长关键字参数 - 这个形参可以接受任意多个关键字的传参
- 这些参数将被收集到一个字典中,并传递给函数。
- 不定长关键字参数写在所有参数的后面
- 一般写法,不定长关键字参数的名字为
kwargs
,即(**kwargs)
- 在普通参数的前面加上两个
1 | def my_sum(*args, **kwargs): |
1 | def my_sum(*args, **kwargs): |
完整的参数顺序
1 | def functname (普通参数, *args, **kwargs): |
1 | def func(*args, **kwargs): |
*在传参的作用
*
的作用是定义位置参数的结束和关键字参数的开始。当在函数定义中的参数列表中出现 *
时,它将标志着其前面的参数只能通过位置传递,而后面的参数只能通过关键字传递。
1 | def func(a, b, *, d): |
函数的返回值
函数整体执行的结果,函数中得到的数据在后续的代码中还要使用,这时需要将数据作为返回值返回
return
之后的代码不会执行
1 | def functname(): # 返回值None |
变量
引用
变量和数据都是保存在内存中的。在 Python 中,变量是用来存储数据的标识符,它们也存储在计算机的内存中。Python 是一种动态类型语言,这意味着变量的类型是根据赋给它们的值来确定的,而不是在声明变量时显式地指定类型。
当我们在 Python 中创建变量并给它们赋值时,Python 解释器会在内存中分配一块空间来存储这些变量的值。每个变量都有一个名称(标识符)和一个关联的内存地址,我们可以使用这个名称来访问和操作变量的值。
变量存储的是对象的引用(对象的内存地址)
在python中函数的参数传递以及返回值都是靠引用传递的
1 | 1. 在定义变量的时候 变量=数据值,Python解释器会在内存中开辟两块空间 |
1 | a = 1 |
可变类型和不可变类型
1 | 数据类型:int float bool str list tuple dict set |
1 | list1=[1,2,3] |
1 | mytuple = (1, 2, [3, 4]) |
如果将可变对象(如列表、字典等)作为函数的参数,并在函数内部修改了这些对象的值(而不是重新赋值给参数),那么这些修改将会影响到传递给函数的实际参数(也称为实参)。这是因为在 Python 中,可变对象是通过引用传递的,而不是通过值传递的。
- 当你传递可变对象(如列表、字典等)给一个函数作为参数时,实际上是将该对象的引用传递给了函数。所谓的引用,实际上是指向存储对象的内存地址的指针。换句话说,函数内部的参数将指向与调用函数时传递的实际对象相同的内存地址,而不是对象的副本。
- 因此,如果在函数内部修改了这个可变对象(例如,向列表中添加元素、修改字典中的键值对等),则实际上是在修改同一个对象。由于函数参数和调用者之间共享对象的引用,因此对对象的修改会在函数外部产生影响。
def modify_list(lst): lst.append(4) lst[0] = 100 my_list = [1, 2, 3] modify_list(my_list) print(my_list) # 输出 [100, 2, 3, 4]
- 需要注意的是,如果在函数内部重新给参数赋值,例如
lst = [4, 5, 6]
,这将会改变参数的引用,但不会影响到原始对象。 def modify_list(lst): lst = [4, 5, 6] my_list = [1, 2, 3] print(my_list) modify_list(my_list) print(my_list)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- ![](image-20240403215607166.png)
- list中+=操作的本质是extend的操作,所以列表的内存地址不发生改变
- 不可变对象(如整数、字符串、元组等)是通过值传递的,而不是通过引用传递的。这意味着当你将不可变对象传递给函数作为参数时,实际上是将该对象的值复制一份,然后将这个复制的值传递给函数。
由于不可变对象的值无法在内存中被修改,因此在函数内部对不可变对象的任何修改都不会影响到原始对象。这意味着函数内部对不可变对象的操作不会影响到函数外部的实际参数。
```python
def modify_string(s):
s = s + " World"
my_string = "Hello"
modify_string(my_string)
print(my_string) # 输出 Hello
交换两个变量的值
1 | # 数学方法 |
组包(pack)和拆包(unpack)
组包:将多个数据值使用逗号连接的过程,组成元组
拆包:将容器中数据值使用多个变量分别保存的过程,注意:变量的个数和容器中数据的个数要保持一致
赋值运算符先执行
=
右边的内容,执行的结果保存到等号左边的变量中注意:对字典进行拆包得到的是字典的键
1
2
3
4
5
6dict1={'name':'Ana','age':18,'height':12}
a,b,c=dict1
print(a,b,c)
print(dict1[a])
print(dict1[b])
print(dict1[c])
1 | a = 10 |
局部变量和全局变量
- 根据变量定义的位置进行区分
局部变量
在函数内部定义的变量,称为局部变量。形参属于局部变量
特点:
- 局部变量只能在当前函数内部使用,不能在其他函数和函数外部使用
- 在不同函数中,可以定义名字相同的局部变量,两者之间没有明显
- 局部变量生存周期(生命周期,作用范围):在函数被调用的时候,局部变量被创建;函数调用结束,局部变量的值被销毁,不能使用
函数局部变量的值想要被函数外部使用,需要使用return
关键字
全局变量
在函数外部定义的变量,称为全局变量
特点:
- 可以在任何函数中读取全局变量的值
- 如果在函数中存在和全局变量名字相同的局部变量,在函数中使用的是局部变量的值(就近原则)
- 在函数内部想要修改全局变量的引用(数据值),需要添加
global
关键字对变量进行声明为全局变量。类似列表的.append()
操作不改变引用不需要globa
的操作(引用传递) - 代码执行的时候全局变量被创建,代码执行结束全局变量被销毁
1 | num=10 |
函数返回值
1 | # 返回多个数据值,将多个数据值组包进行返回 |
匿名函数(lambda表达式)
Python 使用 lambda 来创建匿名函数。
lambda 函数是一种小型、匿名的、内联函数,它可以具有任意数量的参数,但只能有一个表达式。
匿名函数不需要使用 def 关键字定义完整函数。
lambda 函数通常用于编写简单的、单行的函数,通常在需要函数作为参数传递的情况下使用,例如在 map()
、filter()
、reduce()
等函数中。
lambda 函数特点:
- lambda 函数是匿名的,它们没有函数名称,只能通过赋值给变量或作为参数传递给其他函数来使用。返回值不需要
return
,一行代码(表达式)的结果就是返回值 - lambda 函数通常只包含一行代码,这使得它们适用于编写简单的函数。
1 | lambda 参数: 一行代码 |
1 | # 1. 无参无返回值 |
1 | # 定义一个匿名函数,参数为字典,返回字典中键为age的值 |
匿名函数作为参数
列表中的字典排序
sort()
方法会原地修改列表,而不是返回一个新的排序后的列表。该方法有两个可选参数:key
和 reverse
。
key
参数是一个函数,用于指定排序的比较方式。默认情况下,sort()
方法会按照列表元素的大小进行排序。如果指定了key
参数,sort()
方法会使用该函数的返回值来比较元素,从而进行排序。reverse
参数是一个布尔值,用于指定排序的顺序。默认情况下,reverse=False
,即升序排序。如果设置为True
,则会进行降序排序。
对字典来说,比大小需要使用sort()
中的key参数。key参数需要传递一个函数,一般是匿名函数。
字典的排序需要指定依照字典的什么键进行排序,需要使用匿名函数返回字典的键对应的值
1 | user_list = [{'name': 'lisi', 'age': 18}, {'name': 'zhangsan', 'age': 19}, {'name': 'wangwu', 'age': 17}] |
ord()
可以获取字符对应的ASCII码
chr(ASCII)
可以获取ASCII码对应的字符
面向对象
面向对象是一种编程思想
类和对象
- 类
- 多个特征和行为相同或相似事物的统称
- 泛指的(指代多个,而不是一个)
- 对象
- 具体存在的一个事物,看得见摸得着的
- 特指的(指一个)
- 由类创建的
1 | 苹果 ----->类 |
类的组成
- 类名:给多个事物起的名称,在代码中要满足大驼峰命名法
- 属性:事物的特征
- 方法:事物的行为,类中定义的函数。
类的抽象(类的设计):找到类的类名,属性和方法
1 | 1. 小明 今年18岁,身高1.75,每天早上跑完步会去吃东西 |
对象
python中一切都是对象,即用class
定义的类也是对象
- 属性:对对象的特征的描述
- 方法:对象具有的行为
对象的划分
实例对象(实例)
通过类名()
创建的对象即实例对象,简称实例
创建对象的过程称作类的实例化
我们平时说的对象就是实例对象
每个实例都有自己的内存空间,在自己的内存空间中保存自己的属性(实例属性)
类对象(类名)
类对象就是类,可以认为是类名(类名包含了整个类)
在 Python 中,类也是对象。更具体地说,类是一个类型对象,它是由 type
类创建的实例。因此,类对象就是一个实例化了 type
类的对象。
类对象是python解释器在执行代码的过程中创建的
类对象的作用:使用类对象创建实例,类对象也有自己的内存空间,可以保存一些属性值(类属性)信息
在一个代码中,一个类只有一份内存空间
属性的划分
实例属性
概念:是实例对象具有的属性
定义和使用:
在__init__
方法中,使用self.属性名= 属性值
定义
在方法中使用self.属性名
来调用
在每个实例当中都存在一份内存空间
使用时机:基本上99%都是实例属性,通过self
定义。多个对象判断这个值是不是一样的,如果都是一样的,同时变化,一般定义为类属性,否则定义为实例属性
类属性
概念:是类对象具有的属性
定义和使用:
在类内部,方法外部定义的变量
类对象.属性名=属性值
或类名.属性名=属性值
使用类对象.属性名
或类名.属性名
调用
只有类对象中存在一份内存空间
1 | class Dog: |
方法的划分
实例方法(最常用)
定义:在类中直接定义的方法
1
2
3class Demo:
def func(self): # self一般表示实例对象
pass定义时机:如果在方法中需要使用实例属性(需要使用self),则这个方法必须定义为实例方法
调用:
对象.方法名()
类方法
定义:在方法名字的上方书写
@classmethod
装饰器1
2
3
4class Demo:
def func(cls): # cls表示的是类对象(类名)class
pass定义时机:方法中不需要使用实例属性(即self),用到了类属性,可以将这个方法定义为类方法,也可以定义为实例方法
调用:通过类对象调用:
类名.方法名()
,通过实例对象调用:实例.方法名()
静态方法
定义:在方法名字的上方书写@staticmethod 装饰器
1
2
3
4class Demo:
def func(): # 一般没有参数
pass使用时机:方法中不需要使用实例属性,也不使用类属性,可以将这个方法定义为静态方法
调用:通过类对象调用:
类名.方法名()
,通过实例对象调用:实例.方法名()
1 | import random |
1 | import random |
面向对象的代码步骤
定义类,在定义类之前先设计类
先定义简单的类,不包含属性,在python中定义类需要使用关键字
class
方法:在类中定义的函数,第一个参数是
self
(实例方法)1
2
3class 类名:
def 方法名(self):
pass创建对象,使用第一步定义的类创建对象
创建对象使用
类名()
进行创建1
2变量=类名() #变量中保存的是对象的地址,一般直接称为对象
# 一个类可以创建多个对象通过对象调用方法
对象.方法名()
案例实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# 需求:小猫爱吃鱼,小猫要喝水
# 类名:猫类Cat
# 属性:暂无
# 方法:吃鱼eat(),喝水drink()
# 类的定义
class Cat:
def eat(self):
print("小猫爱吃鱼")
def drink(self):
print("小猫要喝水")
# 创建对象
bluecat = Cat()
bluecat.eat()
bluecat.drink()
redcat = Cat()
redcat.eat()
redcat.drink()
self的说明
1 | class Cat: |
- 从函数的语法上讲,
self
是形参,就可以是任意的变量名,只不过我们习惯性将这个形参写成self
self
是普通的形参,但是调用的时候没有传递实参值,原因是python解释器在执行代码的时候,自动**调用这个方法的对象传递给了self
**,即self
的本质是对象- 只需要确定通过哪个对象调用,对象的引用和
self
的引用一样
1 | # 类的定义 |
每次创建对象会开辟新的内存空间
对象的属性操作
添加属性
对象.属性名 = 属性值
类内部添加
1
2self.属性名 = 属性值
# 在类中添加属性一般写在__init__方法中类外部添加
1
对象.属性名 = 属性值 # 一般不使用
魔法方法
python中有一类方法,以两个下划线开头,两个下划线结尾,并且在满足某个条件的情况下会自动调用,这类方法称为魔法方法
__init__方法
- 创建对象后会自动调用
- 应用场景:给对象添加属性的(初始方法,构造方法)。某些代码,在每次创建对象后都要执行,可以写在
__init__
方法中 - 如果
__init__
函数有除了self
之外的参数,要记得传参
1 | class Cat: |
带参数的_init_:
1 | class Cat: |
__str__方法
- 使用
print()
打印对象后会自动调用 - 应用场景:在这个方法中一般书写对象的属性信息,即打印对象的时候想要查看的信息在此方法中定义。如果类中没有定义此方法,
print(对象)
输出对象的引用地址 - 这个方法必须返回一个字符串
1 | class Cat: |
__del__方法
__del__
方法(析构方法),对象被删除销毁时,自动调用。一种是程序运行结束,所有对象被销毁;一种是使用del
删除对象(如果对象有多个名字即多个变量引用同个对象,需要把所有的对象都删除)
1 | class Demo: |
小明跑步
1 | class Person: |
摆放家具
1 | class House: |
登录案例
1 | class LoginPage: |
获取属性
对象.属性名
- 类内部添加:
self.属性名
- 类外部添加:
对象.属性名
,一般很少使用 - 给对象进行属性添加后,对象内存地址不变
1 | class Cat: |
私有和公有
在Python中定义的方法和属性,可以添加访问控制权限(即在什么地方可以使用这个属性和方法)
- 公有权限
- 直接书写的方法和属性都是公有的
- 公有的方法和属性可以在任意地方访问和使用
- 私有权限
- 在类内部,属性名或者方法名前面加上两个下划线
__
,就变为私有 - 私有方法和属性只能在当前类内部使用
- 在类外部操作私有属性,在类内部定义公有方法去操作私有属性
- 在类内部,属性名或者方法名前面加上两个下划线
- 什么时候定义私有?
- 某个属性或者方法不写在类外部被访问和使用,将其定义为私有
- 测试中一般不怎么使用,直接公有
- 开发中根据需求文档去确定什么作为私有
1 | class Person: |
1 | class Person: |
__dict__魔法属性
可以将对象具有的属性组成字典返回
1 | xm = Person('小明', 18) |
继承
继承描述的是类与类之间的关系,可以减少代码的冗余,实现代码的重用。
子类继承父类之后,子类的对象可以直接使用父类中定义的公有属性和方法。
单继承:一个类只继承一个父类
1 | class 类名(父类名): |
1 | class Animal: |
重写
在子类中定义了和父类中名字相同的方法就是重写。
当父类中的方法不能满足子类对象的需求,所以重写。
重写之后,调用子类自己的方法,不调用父类中的方法
重写的方式
覆盖:父类中的功能完全抛弃,重新书写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Dog:
def bark(self):
print('汪汪叫')
class XiaoTianQuan(Dog):
# 覆盖重写
def bark(self):
print('嗷嗷嗷')
dog = Dog()
dog.bark() # 调用自己的方法
xiaotian = XiaoTianQuan()
xiaotian.bark()扩展:父类中的功能还调用,只是添加新的功能(更常见)。直接在子类中定义和父类中名字相同的方法,在合适的地方调用父类中的方法
super().方法()
。然后书写添加的新功能1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Dog:
def bark(self):
print('汪汪叫')
print('汪汪叫')
class XiaoTianQuan(Dog):
# 扩展重写
def bark(self):
print('嗷嗷嗷')
super().bark()
print('嗷嗷嗷')
dog = Dog()
dog.bark() # 调用自己的方法
xiaotian = XiaoTianQuan()
xiaotian.bark()
多态
不同类的对象对相同的方法做出不同的响应。简单来说,多态性使得能够以统一的方式使用不同类型的对象,而不必关心它们具体的类型。
多态性使得我们可以编写更加灵活和通用的代码,因为我们不需要关心对象的具体类型,只需要知道它们共享相同的接口(即方法),就可以以统一的方式对待它们。
文件
文件通常是以字节序列的形式存储在存储设备(如硬盘、固态硬盘、闪存驱动器等)上的。
文本文件:能使用记事本软件打开,能使用记事本转换为文字。txt、md、py
二进制文件:不能使用记事本打开的。exe、mp3、mp4、png、jpg
文件操作的步骤
打开文件
将文件从磁盘中读取到内存中(CPU只能对内存执行操作)
文件流一般写入try中,因为文件流容易报错
1 | open(file,mode='r',encoding=None) |
读写文件
写文件
向文件中写入指定的内容。
前提:文件的打开方式是 w 或者 a
1 | 文件对象.write('写入文件的内容') |
1 | f = open('a.txt', 'w', encoding='utf-8') |
读文件
将文件中的内容读取出来
前提:文件的打开方式是 r
1 | 文件对象.read(n) |
1 | f = open('a.txt', 'r', encoding='utf-8') |
关闭文件
1 | 关闭文件:将文件占用的资源进行清理,同时会保存文件,文件关闭之后这个文件对象就不能使用了 |
with open打开文件
好处:不用自己去书写关闭文件的代码,会自动进行关闭
1 | with open(file, mode, encoding='utf-8') as 变量: |
1 | with open('a.txt', 'a', encoding='utf-8') as f: |
按行读取
一次读取一行内容,文件对象.readline()
1 | with open('a.txt', encoding='utf-8') as f: |
1 | with open('a.txt', encoding='utf-8') as f: |
json文件
json=’java script object notation’,是JavaScript对象表示法,是一种基于文本,独立于语言的轻量级数据交换格式。
- 基于文本:是一个文本文件,不能包含图片,音视频等
- 独立于语言:不是某个语言特有的,每种编程语言都可以使用
- 轻量级:相同的数据和其他格式相比占用的大小比较小
- 数据交换格式:后端程序员给前端的数据(json,html,xml)
json文件也是一种文本文件,可以直接使用read()
和write()
操作,但是不方便。json文件有独特的读取和写入方法
常用在做测试的时候,将测试数据定义为json文件格式,使用代码读取json文件,即读取测试数据,进行传参(参数化)
json语法规则
- json文件的后缀是.json
- json文件中的主要数据类型是对象({}类似Python中的字典)和数组([]类似列表),对象和数组可以互相嵌套
- 一个json文件是一个对象或者数组,即json文件的最外层要么是一个大括号{},要么是一个数组[]
- json中的对象是由键值对组成的,每个数据之间使用,隔开,但是最后一个数据后面不要写逗号
- json中的字符串必须使用””双引号
- json中的其他数据类型:数字类型(int float)、字符串string(str)、布尔类型(true、false)、None(null)
1 | { |
读取json文件
1 | 1. 导包 import jason |
1 | import json |
1 | [{ |
1 | with open('info.json', encoding='utf-8') as f: |
测试读取案例
1 | [ |
1 | # 获取用户名、密码和预期结果,组成[(),(),()]格式(自动化参数化需要的格式) |
json的写入
文件对象.write(字符串)
不能直接将python的列表和字典作为参数传递。想要将python中的数据类型存为json文件,需要使用json提供的方法,不再使用write
1 | 1. 导包 import json |
1 | import json |
1 | def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, |
函数参数列表里,*后面的参数只能以关键字的形式传参,不能使用位置传参
异常
程序运行时,python解释器遇到一个错误,会停止程序的执行,并且提示一些错误信息,这就是异常。
程序停止执行并且提示错误信息,这个动作是抛出异常(raise
)。程序遇到异常,默认动作是终止代码程序的执行。捕获异常可以使代码继续执行,不会终止运行
异常捕获
1 | try: |
1 | while True: |
可以针对不同的异常错误,运行单独的代码处理
1 | try: |
1 | try: |
1 | try: |
1 | while True: |
异常传递
python会将异常进行传递。在函数嵌套调用的过程中,被调用的函数发生了异常,如果没有捕获,会将这个异常向外层传递。如果传到最外层还没有捕获,才报错。
抛出异常raise
让代码报错
模块和包
模块(Module)是一个包含 Python 代码的文件。这些文件通常包含了一些函数、类和变量的定义,可以被其他 Python 程序导入并使用。模块使得代码的组织和管理更加方便,同时也可以提高代码的可重用性。
模块名同样也是一个标识符,需要符合标识符的命名规则
在模块中定义的全局变量、函数、类都是提供给外界直接使用的工具
模块就好比是工具包,要使用这个工具包的工具需要先导入这个模块
自己写的代码想要作为模块使用,代码的名字需要满足标识符的规则
模块的导入
import导入
使用模块中的内容:模块名.工具名
1 | import 模块1名,模块2名 |
1 | from 模块名 import 工具名 |
1 | # 可能存在多个模块之间有名字相同的工具,会造成冲突 |
对于导入的模块和工具可以使用as
起别名,如果起别名,原来的名字就不能使用了
模块的查找顺序
在导入模块的时候,会先在当前目录中找模块,如果找到,就直接使用。如果没有找到,就回去系统的目录中进行查找,找到就直接使用,没有找到会报错
注意:在定义代码文件的时候,代码名字不能和要导入的模块名字相同,会造成混淆产生命名冲突,Python 解释器会优先使用当前目录下的同名文件,而不是标准库或其他模块中的同名模块。
1 | # tool.py |
__name__作用
每个代码文件都是一个模块,在导入模块的时候,会执行模块中的方法。
__name__
变量:
- 是python解释器自动维护的变量
__name__
变量,如果代码直接运行,值是"__main__"
__name__
变量,如果代码是被导入运行,值是模块名(代码名)
包package
包(Package)是一种包含多个模块的目录结构。通常情况下,包是一个目录,包含了一个特殊的 __init__.py
文件和多个模块文件。__init__.py
文件可以为空,也可以包含一些初始化代码。包可以嵌套,即一个包可以包含其他包。
在python使用的时候不需要可以区分是包还是模块,因为使用方式是一样的
random
是一个模块json
是一个包
UnitTest框架
是python中自带的单元测试框架。pytest是第三方框架,需要安装。
对于开发来说,单元测试框架主要用来做单元测试。对测试来说,unittest框架的作用是自动化脚本(用例代码)执行框架,管理运行多个测试用例
- 为什么使用UnitTest框架?
- 能够组织多个用例去执行
- 提供丰富的断言方法(让程序代码替人工自动的判断预期结果和实际结果是否相符)
- 能够生成测试报告
UnitTest核心(组成)
TestCase(最核心的模块)
unittest.TestCase
类是编写测试用例的基础。你可以通过创建继承自TestCase
的子类,并在子类中定义测试方法来编写测试用例。每个测试方法都应该以test_
开头,这样 unittest 才能识别它们并执行。TestSuite
测试套件是一组测试用例的集合。你可以使用
unittest.TestSuite
类来创建一个测试套件,并将多个测试用例添加到这个套件中。这样可以方便地对多个测试用例进行组织和管理。TestRunner
测试运行器负责执行测试套件中的测试用例,并生成测试报告。unittest 提供了命令行接口和 GUI 工具来运行测试,并且可以生成详细的测试报告,包括测试结果、运行时间、覆盖率等信息。
TestLoader
TestLoader(测试加载),对TestSuite(测试套件)功能的补充。自动搜索加载的一种方式,从指定的模块或目录中加载测试用例,并根据一定的规则将它们组织成测试套件。它可以自动发现符合命名规范的测试用例,并将它们添加到测试套件中,以便后续执行。
Fixture
Fixture(测试夹具),书写在TestCase代码中,是一个代码结构,可以在每个方法执行前后都会执行的内容(前置代码)。unittest 提供了
setUp()
和tearDown()
方法,它们分别在每个测试方法执行前后自动调用,用于准备测试环境和清理测试
TestCase(测试用例)
在这个代码文件中书写真正的用例代码,代码文件的名字必须按照标识符的规则来书写
步骤:
- 导包(unittest)
- 自定义测试类,新建测试类必须继承unittest.TestCase。运行的时候主模块是TestCase
- 在测试类中书写测试方法,测试方法名称必须以test_开头。
- 执行用例
1 | """ |
TestSuite&TestRunner
TestSuite(测试套件):管理、打包、组装TestCase(测试用例)文件
TestRunner(测试执行,测试运行):执行TestSuite(测试套件)
步骤:
- 导包(unittest)
- 实例化(创建对象)套件对象
- 使用套件对象添加用例方法
- 实例化运行对象
- 使用运行对象去执行套件对象
1 | """ |
1 | """ |
运行结果中的。表示用例通过,F表示用例不通过(运行结果和预期结果不符合),E代表用例代码有问题。
makeSuite()
要被python官方弃用!!
TestLoader(测试加载)
TestLoader(测试加载)和TestSuite作用是一样的,对TestSuite功能的补充,用来组装测试用例。如果TestCase的代码文件有很多,可以使用TestLoader
步骤
- 导包
- 实例化测试加载对象并添加用例,得到的是Suite对象
- 实例化运行对象
- 运行对象执行套件对象
在一个项目中TestCase的代码一般放在一个单独的目录(case)
1 | # 1. 导包 |
TestLoader与TestSuite区别
- TestSuite需要手动添加测试用例(可以添加测试类,也可以添加测试类中的某个方法
- TestLoader搜索指定目录下指定开头.py文件并添加测试类中的以test开头的所有的测试方法,不能指定添加方法
makeSuite()
方法要被弃用,可以使用TestLoader
添加用例
1 | import unittest |
Fixture(测试夹具)
是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture。在某些特点的情况下会自动去执行
方法级别
写在方法里,在每个测试方法(用例代码)执行前后都会自动调用的结构
1 | # 方法执行之前 |
类级别
在每个测试类中所有方法执行前后都会自动调用的结构(整个类中前后各一次)写在类里面
类级别的Fixture方法是一个类方法
1 | # 类中所有方法之前 |
模块级别
在每个代码文件执行前后执行的代码结构,需要写在类的外面,直接定义函数即可
1 | # 代码文件之前 |
方法级别和类级别的前后方法不需要同时出现,根据用例代码的需要自行的选择使用
登录案例
1 | import unittest |
测试add
1 | """ |
login函数测试
1 | def login(username,password): |
1 | import unittest |
断言
让程序代替人工自动判断预期结果和实际结果是否相符
断言的结果:True->用例通过,False->代码抛出异常,用例不通过
python自带的断言,判断两个字符串是否相等assert "hello" == "hello"
assert "hello"=="hello1" "出错啦"
判断是否包含assert "h" in "hello"
判断是否为True、False assertTrue
assert1
assertFalse
assert0
在unittest中使用断言,都需要通过self.断言方法
来试验
assertEqual
assertEqual(预期结果,实际结果)
,判断预期结果和实际结果是否相等
如果相等,用例通过;如果不相等,用例不通过,抛出异常
1 | import unittest |
失败:
assertIn
assertIn(预期结果,实际结果)
判断预期结果是否包含于实际结果
如果包含,用例通过;如果不不包含,用例不通过,抛出异常
assertTrue
assertTrue(ex)
判断ex是否为True
参数化
解决相同业务逻辑,不同业务数据
在测试方法中,使用变量代替具体的测试数据,使用传参的方法将测试数据传递给方法的变量,可以减少相似代码的书写。测试数据一般放在json文件中,使用代码读取json文件,提取要的数据[(),(),()],[[],[]]
unittest框架本身不支持参数化,需要安装插件完成
1 | 1. 导包 unittest/parameterized |
login_1代码:
1 | def login(username,password): |
1 | # 1. 导包 unittest/parameterized |
通过json导入
1 | // test.json |
1 | # 1. 导包 unittest/parameterized |
跳过
对于一些未完成的或者不满足测试条件的测试函数和测试类可以跳过执行,使用装饰器完成。代码书写在TestCase中
1 | # 直接将测试函数标记为跳过 |
1 | import unittest |
测试报告
自带的测试报告:只有单独运行TestCase的代码才能生成测试报告,组装打包的测试用例不能生成
HTMLTestRunner是一个类库,生成第三方的的测试报告需要用到。
- 获取第三方的测试运行模块,将其放在代码的目录中
- 导包
- 使用套件对象,加载对象添加用例方法
- 实例化第三方的运行对象并运行套件
组织用例文件(TestCase),书写参数化,书写断言,书写Fixture,书写跳过,如果单个测试文件直接运行,得到测试报告,如果有多个测试文件需要使用套件对象组装、加载对象组装运行生成测试报告。接下来要运行对象,使用第三方运行类进行运行。运行对象.run(套件对象)
1 | # 1. 获取第三方的测试运行模块,将其放在代码的目录中 |
unittest自带报告:
html报告: