Python基本语法
python的版本:3.11.9
Print函数的简单使用
作用:输出括号中的内容
#输入
print("hello word!")
输出:
灰⾊波浪线, 不会影响代码的正常执⾏, 基本上所有的灰⾊波浪线都是 PEP8 造成的
PEP8: 是 Python 代码的书写规范, 如果不按照这个规范书写,会给灰⾊波浪线提示,建议代码的书写按照 PEP8 的规范书写
-
可以书写代码的时候注意
PEP8
的代码规范; -
可以在书写完成之后,使⽤
快捷键 Ctrl Alt L
来按照PEP8 的规范⾃动格式化代码
变量名的命名规范
-
必须由字母、数字和下划线组成,并且不能以数字开头
-
不能使用Python中的关键字作为变量名
-
区分大小写
-
建议性命名
-
驼峰命名法
- 大驼峰:每个单词的首字母大写
MyName
- 小驼峰:第一个单词的首字母小写,其余单词的首字母大写
myName
- 大驼峰:每个单词的首字母大写
-
下划线连接法
每个单词之间使用下划线my_name
Python中,一般类的命名使用大驼峰,方法的命名使用小驼峰,变量的命名使用下划线连接
-
见名知意
name 姓名 age 年龄 height 身高
-
数据类型
将生活中常见的数据划分为不同的类型,因为不同的类型可以进行的操作是不一样的,数字需要加减乘除,文字不需要。
数字类型
-
整型(int):就是整数,即不带小数点的数
-
浮点型(float):小数
-
布尔类型(bool):只有两个值 True & False
-
复数类型
3+4i
,很少用
非数字类型
- 字符串(str):使用引号引起来的就是字符串
- 列表(list):[1,2,3,4,]
- 元组(tuplr):(1,2,3,4)
- 字典(dict):{‘name’: ‘小明’,‘age’: 18}
type()函数
可以获取变量的数据类型
#输出hello world
print("hello word!")
#输入赋值给遍历name
name = input()
#输出name的数据类型
print(type(name))
可见name变量的数据类型是 str
数字类型转换
数据本来的类型不是我们计算使用想要的类型
int()
:将其他类型转换为 int
类型(小数,整数类型的字符串)
float()
:将其他类型转换为 float
类型(整数,数字类型的字符串)
str()
:将其他类型转换为 str()
类型(任意类型
输入input()
获取用户使用键盘录入的内容
#输入格式
变量 = input('提示的信息')
变量的数据类型一定是 str
输出print()
将程序中的数据或者结果打印到控制台
print('hello world')
name = '⼩明'
print(name)
age = 18
print(name, age) # 可以使⽤逗号输出多个内容
print函数的解析
#print()
#sep = '',多个位置参数之间的间隔
#end = '\n',每一个print函数结束,都会打印的内容 结束符
print(1,end = ' ')
print(2,end = ' ')
print(3)
print(1,2,3,4,5,6,sep = '_')
print(1,2,3,4,5,6,sep = '_*_')
格式化输出
在字符串指定的位置,输出变量中存储的值。
- 在需要使用变量的地方,使用特殊符号占位
- 使用变量填充占位的数据
% 格式化输出占位符号
%d
占位,填充整型数据digit
%f
占位,填充浮点型数据float
%s
占位,填充字符型数据string
其实 %s 的占位符,可以填充任意类型的数据
# 定义变量 姓名 年龄 身⾼
name = '⼩明' # 可以使⽤ input 输⼊
age = 18 # 可以使⽤ input 输⼊
height = 1.71 # 可以使⽤ input 输⼊
# 要求按照以下个数输出个⼈信息
# 我的名字是 xx, 年龄是 xx, 身⾼是 xx m
# 使⽤格式化输出实现
# print('我的名字是 name, 年龄是 age, 身⾼是 heightm')
print('我的名字是 %s, 年龄是 %d, 身⾼是 %f m' % (name, age, height))
# ⼩数默认显示 6 位, 如果想要指定显示⼩数点后⼏位, %.nf , n 需要换成具体的整数数字,即保留⼩数的位置
print('我的名字是 %s, 年龄是 %d, 身⾼是 %.2f m' %(name, age, height)) # 两位⼩数
# 补充
stu_num = 1 # 学号
# 我的学号是 000001
print('我的学号是%d' % stu_num)
# %0nd n 需要换成具体的整数数字, 表示整数⼀共占⼏位
print('我的学号是%06d' % stu_num)
F-string(f字符串的格式化方法)
前提:python版本为3.6以上
- 需要在字符串的前边加上
f""
或者F""
- 占位符号统一变为{}
- 需要填充的变量写在{}
# 定义变量 姓名 年龄 身⾼
name = '⼩明' # 可以使⽤ input 输⼊
age = 18 # 可以使⽤ input 输⼊
height = 1.71 # 可以使⽤ input 输⼊
stu_num = 1 # 学号
num = 90 # 及格率
# print('我的名字是 xx, 年龄是 xx, 身⾼是 xx m, 学号xx, 本次考试的及格率为 xx%')
print(f'我的名字是 {name}, 年龄是 {age}, 身⾼是{height} m, 学号 {stu_num}, 本次考试的及格率为{num}%')
# ⼀般不会有这样的需求
print(f'我的名字是 {name}, 年龄是 {age}, 身⾼是{height:.3f} m, 学号 {stu_num:06d}, 本次考试的及格率为 {num}%')
# 在字符串中想要输出换⾏ \n (转义字符)
print(f'我的名字是 {name}, 年龄是 {age}, 身⾼是
{height:.3f} m, 学号 {stu_num:06d},\n本次考试的及格
率为 {num}%')
.format()
字符串.format() 可以在任意版本中使用
- 在需要使用变量的地方使用{}占位
- ‘{},{},…’.format(变量,变量, …)
print(f'我的名字是 {name}, 年龄是 {age}, 身高是 {height:.3f} m, 学号 {stu_num:06d}, 本次考试的及格率为 {num}%')
# 字符串.format()
print('我的名字是 {}, 年龄是 {}, 身高是 {} m, 学号 {}, 本次考试的及格率为 {}%'.format(name,age, height, stu_num, num))
print('我的名字是 {}, 年龄是 {}, 身高是 {:.3f} m, 学号 {:06d}, 本次考试的及格率为{}%'.format(name, age, height, stu_num, num))
快捷键小操作
添加引号括号: 可以直接选中要添加引号或者括号的内容, 书写即可
撤销 : Ctrl Z
删除⼀⾏: Ctrl x
复制粘贴⼀⾏: Ctrl d
快速 在代码下⽅, 新建⼀⾏: shift 回⻋
算数预算符
优先级:先算谁,再算谁 (不确定优先级,就使⽤ ())
() > ** > * / // % > + -
判断
日常生活中的说的 如果…否则…,这个就是判断,在程序代码中需要使用if(如果) elif(如果) else(否则) 三个关键词来实现
if的基本语法
if 判断条件:
书写条件成立(真),执行的代码
书写条件成立(真),执行的代码
顶格书写,没有缩进的代码,和 if无关, 不管条件是否成立,都会执行
# 1. if 是一个关键字, 和后续的判断条件之间需要一个空格
# 2. 判断条件后边需要一个冒号,不要少了
# 3. 冒号之后,回车,代码需要缩进, 在 pycharm 中会自动进行缩进, 一般是 4 个空格 或者 一个 tab键
# 4. 所有在 if 代码下方的缩进中书写的代码,属于 if 语句的代码块, 判断条件为 True 的时候会执行
# 5. if 代码块中的代码,要么都执行,要么都不执行
# 6. if 代码块结束之后, 代码要顶格书写(不再有缩进), 表示是和 if 无关的代码
代码案例
#1. 使用input获取用户的年龄,类型是str
age = int(input("Enter your age: "))
if age < 18:
print("You are a teenager")
print('我和if没什么关系')
随机数工具包random
-
导入随机数工具包
import random
-
使用工具包中的工具产生指定范围内的数字
#产生[a,b]之间的随机整数,包含a b的 random.randint(a,b)
具体代码实施
import random
num = random.randint(1,6)
print(num)
循环
让指定的代码重复执行
while循环
1. 设置循环的初始条件(计数器)
2. 书写循环的判断条件
while 判断条件:
# 3. 需要重复执行的代码
# 4. 改变循环的初始条件(计数器)
代码
#1. 设置循环的初始条件(记录说了几遍 我错了)
i = 0
#2. 书写判断条件
while i < 5:
# 3. 重复执行的代码
print('媳妇,我错了')
# 4. 改变初始条件
#i = i + 1
i += 1
输出
for循环
for循环也可以让指定的代码重复执⾏ (循环)
for循环可以遍历容器中的数据(
遍历: 从容器中把数据⼀个⼀个取出
容器: 可以简单理解为盒⼦, 盒⼦中可以存放很多的数据
(字符串 str, 列表 list, 元组 tuple, 字典 dict))
for 循环 也可以称为 for 遍历
语法
for 变量名 in 容器:
重复执⾏的代码
代码
#定义字符串
my_str = 'hello'
#遍历字符串
#字符串中有5个字符,循环就会执行5次
for i in my_str: # 每次循环i的值为字符串中的字符
# 重复执行代码
print('我错了',i)
输出
for做指定次数的循环
for 变量 in range(n):
重复的执行代码
range() 是python中的函数,作用:
使用可以生成[0,n)之间的整数,不包含n的,一个有n个数字,所以这个循环循环n次
变量的值也是每次循环从[0,n)取出一个值,第一次取得是0,最后一次取得是n-1。
range()变形
需求:使用for循环获取到5到10之间的数字
for 变量 in range(a,b):
重复的代码
range(a,b) 作用是生成[a,b)之间的整数数字,不包含b
break和continue
break:中止循环,即代码执行遇到break,循环不再执行,立即结束,break只跳出一重循环
continue:跳过本次循环,即代码执行遇到continue,本次循环剩下的代码不再执行,继续下一次循环
容器
容器:也可以称为是数据序列,或者高级数据类型,也是Python中的数据类型
容器中可以存放多个数据
字符串
字符串是容器,因为字符串中可以包含多个字符
定义: 使用引号(单引号、双引号、三引号)引起来的内容就是字符串
字符串本身包含单引号,在定义时,想使用单引号时,可以使用
\
转义字符,将字符串本身的引号进行转义 。\'
–>'
my_str6 = 'I\'m ⼩明'
print(my_str6)
#字符串 I\'m ⼩明 \\ --> \
my_str7 = 'I\\\'m ⼩明'
print(my_str7)
字符串前边加上
r""
原生字符串,字符串中的 \ 不会作为转义字符,文件操作会用一下
my_str8 = r'I\'m ⼩明'
print(my_str8)
my_str9 = r'I\\\'m ⼩明'
print(my_str9)
len()
获取字符串中的字符个数
str = 'hello'
print(len(str ))
切片
可以获取字符串中多个字符(多个字符的下标是有规律的,等差数列)
语法
字符串[start : end : step]
start 时开始位置的下标,
end 是结束位置的下标(注意,不能取到这个位置的字符)
step 是步长,等差数列的差值,所取的相邻字符下标之间的差值,默认是1,可以不写
find()
字符串.find(sub_str, start, end)
字符串的查找方法,在字符串中查找是否存在sub_str这样的字符串
sub_str:要查找的小字符串;
start:开始位置,从哪个下标位置开始查找,一般不写,默认是0;
end:结束位置,查找到哪个下标结束,一般不写,默认是len()
.
返回:
- 如果找到,返回sub_str第一次出现的正数下标;
- 如果没有找到,返回 -1。
str1 = "and itcast and itheima and Python"
# 在字符串中查找 and
num = str1.find('and')
print(num) # 0
replace
字符串的替换方法
字符串.replace(old_str, new_str, count) # 将字符串中
old_str 替换为 new_str
- old_str: 被替换的内容
- new_str: 替换为的内容
- count: 替换的次数, ⼀般不写,默认是全部替换
- 返回: 替换之后的完整的字符串, 注意: 原来的字符串没有发
⽣改变
str1 = 'good good study'
# 1, 将 str1 中 所有的 g 改为 G
str2 = str1.replace('g', 'G')
print('str1:', str1) # str1: good good study
print('str2:', str2) # str2: Good Good study
split()
字符串的拆分
字符串.split(sep, maxsplit)
# 将字符串按照 sep 进⾏分割(拆分)
- sep, 字符串按照什么进⾏拆分, 默认是空⽩字符(空格, 换
⾏\n, tab键\t)
- max_split, 分割次数,⼀般不写, 全部分割
- 返回: 将⼀个字符串拆分为多个,存到列表中
- 注意: 如果 sep 不写, 想要指定分割次数 则需要按照如下⽅
式使⽤
字符串.split(maxsplit=n) # n 是次数
str1 = "hello world and itcast and itheima and
Python"
# 1. 将 str1 按照 and 字符进⾏拆分
result1 = str1.split('and')
print(result1) # ['hello world ', ' itcast ', '
itheima ', ' Python']
join()
字符串的链接
字符串.join(列表)
# 括号中的内容主要是列表,可以是其他容器
# 作⽤: 将字符串插⼊到列表中每相邻的两个数据之间, 组成⼀个新的字符串
- 列表中的数据使⽤使⽤ 逗号隔开的
- 注意点: 列表中的数据必须都是字符串, 否则会报错
list1 = ['good', 'good', 'study']
# 1. 将 列表中的字符串使⽤空格连起来
str1 = ' '.join(list1)
print(str1) # good good study
列表
列表 list ,是使用最多的一种容器(数据类型),列表中可以存放任意类型的数据
定义
定义空列表(没有任何数据的列表)
list1 = list()
print(type(list1),list1)
类型转换 list(容器),将其他容器转换为列表
转换字符串会将字符串中的每个字符作为一个数据存入到列表中
list2 = list('hello')
print(type(list2),list2)
直接使用[]进行定义(常用)
直接定义空列表
my_list = []
print(my_list) # []
定义非空列表
my_list1 = [1, '⼩明', 3.14, False]
print(my_list1) # [1, '⼩明', 3.14, False]
列表推导式
快速的生成列表
list1 = []
for i in range(5):
list1.append(i)
列表查找
查找-查找列表中数据下标的方法–index()
在字符串中使⽤的 find ⽅法查找下标的,不存在返回的是 -1.
在列表中没有 find ⽅法, 想要查找数据的下标,使⽤的index() ⽅法
列表.index(数据, start, end) 使⽤和 find ⽅法⼀样, 同时在字符串中也有 index ⽅法
区别: 返回, index() ⽅法,找到返回第⼀次出现的下标, 没有找到代码直接报错
查找-判断是否存在
判断容器中某个数据是否存在可以使⽤ in 关键字
数据 in 容器 # 如果存在返回 True ,如果不存在,返回False
查找-统计出现的次数
统计出现的次数,使⽤的是 count() ⽅法
列表.count(数据) # 返回 数据出现的次数
列表添加数据的方法
尾部添加(最常用)–apend()
列表.append(数据) # 将数据添加到列表的尾部
返回: 返回的 None(关键字,空), ⼀般就不再使⽤ 变量 来
保存返回的内容,想要查看添加后的列表,需要打印的是列表
指定下标位置添加–insert()
列表.insert(下标, 数据) # 在指定的下标位置添加数据,
如果指定的下标位置本来有数据, 原数据会后移
返回: 返回的 None(关键字,空), ⼀般就不再使⽤ 变量 来
保存返回的内容,想要查看添加后的列表,需要打印的是列表
列表合并–extend()
列表1.extend(列表2) # 将列表 2 中的所有数据逐个添加
的列表1 的尾部
返回: 返回的 None(关键字,空), ⼀般就不再使⽤ 变量 来
保存返回的内容
想要查看添加后的列表,需要打印的是列表
列表删除操作
在列表中删除中间的数据,那么后面的数据会向前移动
根据下标删除
#删除指定下标位置对应的数据
列表.pop(下标)
- 下标不写,默认删除最后一个数据(常用)
- 书写存在的下标,删除对应下标位置的数据
- 返回的删除的数据。
根据数据值删除
#根据数据值删除
列表.remove(数据值)
返回None
注意:如果要删除的数据不存在,会报错
清空数据(一般不用)
列表.clear()
列表中的反转(倒置)
字符串
字符串[::-1]
列表
#使用切片的方法,会得到一个新列表,原列表不会发生改变
列表[::-1]
#直接修改原列表,返回None
列表.reverse()
列表的复制
#使用场景:有一个列表,需要修改操作列表中的数据,修改之后,需要和原数据进行对比,即原数据不能改
1. 使用切片
变量 = 列表[:]
2.使用copy方法
变量 = 列表.copy()
列表的排序
列表的排序,一般来说都是对数字进行排序的
#按照升序排序,从小到大
列表.sort()
#按照降序排序,从大到小
列表.sort(reverse=True)
列表嵌套
列表嵌套,列表中的内容还是列表
使用下标来确定获取的是什么类型的数据,然后确定可以继续进行什么操作
person_info = [["张三", "18", "功能测试"], ["李四",
"20", "⾃动化测试"]]
print(len(person_info)) # 2
print(person_info[0]) # ['张三', '18', '功能测试']
print(person_info[0][0]) # '张三'
print(person_info[0][0][0]) # 张
# 将 18 改为 19
person_info[0][1] = '19'
print(person_info) # [['张三', '19', '功能测试'],
['李四', '20', '⾃动化测试']]
# 给 李四 所在的列表添加⼀个性别 信息
person_info[1].append('男')
print(person_info) # [['张三', '19', '功能测试'],
['李四', '20', '⾃动化测试', '男']]
# 将张三的年龄信息删除
# person_info[0].pop(1)
person_info[0].remove('19')
print(person_info) # [['张三', '功能测试'], ['李四',
'20', '⾃动化测试', '男']]
元组
元组:tuple,元组的特点和列表非常相似
- 元组可以存放任意类型的数据
- 元组中可以存放任意多个数据
区别:
- 元组中的数据内容不能改变,列表中的可以;
- 元组使用(),列表使用[]
应用:
在函数中的传参或者返回值中使用,保证数据不会被修改
定义
- 使用 类实例化 的方式
- 直接使用()方式
常用方法
由于元组中的数据不能修改,所以只有查看的方法
- 在元组中,也可以使用下标和切片获取数据;
- 在元组中存在
index
方法,查找下标,如果不存在,会报错; - 在元组中存在
count
方法,统计数据出现的次数; - 在元组中可以使用
in
操作,判断数据是否存在 len()
统计个数
类实例化的方式
定义空元组(基本不会使用)
my_tuple1 = tuple()
print(type(my_tuple1), my_tuple1) # <class 'tuple'>
()
类型转换
可以将列表转换为元组,只需要将[],变为(),同时可以将元组转换为列表
my_tuple2 = tuple([1, 2, 3])
print(my_tuple2) # (1, 2, 3)
转换字符串,和列表中一样,只是将列表的[]变为()
my_tuple3 = tuple('hello')
print(my_tuple3) # ('h', 'e', 'l', 'l', 'o')
my_tuple4)
直接使用()定义
my_tuple4 = (1, "⼩王", 3.14, False)
print(my_tuple4)
特殊点
定义只有一个数据的元组时,数据后边必须有一个逗号
my_tuple5 = (1,)
print(my_tuple5)
代码
输出
代码
输出
字典
- 字典 dict, 字典中的数据是由键(key)值(value)对组成的
(键表示数据的名字, 值就是具体的数据)
在字典中⼀组键值对是⼀个数据, 多个键值对之间使⽤ 逗号隔开,变量 = {key: value, key:value, …}
⼀个字典中的键是唯⼀的,不能重复的, 值可以是任意数据
字典中的键 ⼀般都是 字符串,可以是数字, 不能是列表
定义
1. 使用 类实例化 的方法
my_dict = dict()
print(type(my_dict), my_dict)
dict()
不能转列表和元组,字符串
2. 直接使用{}定义
-
空字典
my_dict1 = {} print(type(my_dict1), my_dict1) # <class 'dict'> {}
-
非空字典
my_dict2 = {"name": "⼩明", "age": 18, "height": 1.71, "is_men": True, "like": ["抽烟", "喝酒", "烫头"]} print(my_dict2) print(len(my_dict2))
增加和修改操作
语法:字典[键]
= 数据值
-
如果键已经存在,就是修改数据值;
-
如果键不存在,就是添加数据(即添加键值对)。
# 定义字典 ⼩明 18 爱好 my_dict = {"name": "⼩明", "age": 18, "like": ['抽烟', '喝酒', '烫头']} print(my_dict) # {'name': '⼩明', 'age': 18, 'like': ['抽烟', '喝酒', '烫头']} # 1. 添加性别信息 sex my_dict['sex'] = '男' print(my_dict) # {'name': '⼩明', 'age': 18, 'like': ['抽烟', '喝酒', '烫头'], 'sex': '男'} # 2. 修改年龄为 19 my_dict['age'] = 19 print(my_dict) # {'name': '⼩明', 'age': 19, 'like': ['抽烟', '喝酒', '烫头'], 'sex': '男'} # 3. 添加⼀个爱好, 学习--> 本质是向列表中添加⼀个数据 my_dict['like'].append('学习') print(my_dict) # {'name': '⼩明', 'age': 19, 'like': ['抽烟', '喝酒', '烫头', '学习'], 'sex': '男'}
删除
删除指定键值对
del 字典[键]
or
字典.pop(键)
#键必须书写
清空
字典.clear()
my_dict = {'name': '⼩明', 'age': 19, 'like': ['抽
烟', '喝酒', '烫头', '学习'], 'sex': '男'}
# 删除 sex 键值对
del my_dict['sex']
print(my_dict) # {'name': '⼩明', 'age': 19,'like': ['抽烟', '喝酒', '烫头', '学习']}
# 字典.pop('键')
my_dict.pop('age')
print(my_dict) # {'name': '⼩明', 'like': ['抽烟',
'喝酒', '烫头', '学习']}
# 删除抽烟的爱好 ---> 本质操作是在列表中删除 数据值
# my_dict['like'].pop(0)
my_dict['like'].remove('抽烟')
print(my_dict) # {'name': '⼩明', 'like': ['喝酒',
'烫头', '学习']}
# 清空键值对
my_dict.clear()
print(my_dict) # {}
查询-根据键获取对应的值
字典中没有下标的概念,想要获取数据值,要使用key(键)来获取
使用字典[键]
字典[键]
- 如果键存在,返回键对应的数据值;
- 如果键不存在,会报错
使用字典.get(键)
字典.get(键,数据值)
- 数据值一般不写,默认是None
返回:
- 如果键存在,返回键对应的数据值;
- 如果键不存在,返回的是括号中书写的数据值(None)
一般建议使用get方法
my_dict = {'name': '⼩明', 'age': 19, 'like': ['抽
烟', '喝酒', '烫头', '学习']}
# 1. 获取 名字
print(my_dict['name']) # ⼩明
print(my_dict.get('name')) # ⼩明
print(my_dict.get('name', 'zzz')) # ⼩明
# 2. 获取 sex 性别
# print(my_dict['sex']) # 代码会报错, 原因 key 不存在
print(my_dict.get('sex')) # None
print(my_dict.get('sex', '保密')) # 保密
# 3. 获取 第⼆个爱好
print(my_dict['like'][1]) # 喝酒
字典的遍历
对字典的键进行遍历
for 变量 in 字典:
print(变量)
#变量就是字典的key,键
for 变量 in 字典.keys()
#字典.keys()可以获取字典中所有的键
print(变量)
对字典的值进行遍历
for 变量 in 字典.values():
#字典.values()可以获取字典中所有的值
print( )
对字典的键值对进行遍历
字典.item()
获取键值对
for 变量1,变量2 in 字典.items()
print(变量1,变量2)
# 变量1 就是 键, 变量2 就是键对应的值
for 变量1, 变量2 in 字典.items(): # 字典.items() 获取
键值对
print(变量1, 变量2)
# 定义字典
my_dict = {'name': '⼩明', 'age': 18, 'sex': '男'}
# 1. 遍历字典的键
for k in my_dict:
print(k)
for k in my_dict.keys():
print(k)
print('-' * 30)
# 2. 遍历字典的值
for v in my_dict.values():
print(v)
print('*' * 30)
# 3. 遍历键值
for k, v in my_dict.items():
print(k, v)
容器部分总结
# 1. 字符串, 列表, 元组 ⽀持加法运算
str1 = 'hello' + ' world' # 'hello world'
list1 = [1, 2] + [3, 4] # [1, 2, 3, 4]
tuple1 = (1, 2) + (3, 4) # (1, 2, 3, 4)
# 2. 字符串 列表 元组 ⽀持 乘⼀个数字
'hello ' * 3 # ===> 'hello hello hello '
[1, 2] * 3 # ===> [1, 2, 1, 2, 1, 2]
(1, 2) * 3 # ===> (1, 2, 1, 2, 1, 2)
# 3. len() 在 容器中都可以使⽤
# 4. in 关键字在容器中都可以使⽤, 注意, 在字典中判断的是
字典的键是否存在
函数
概念
函数,就是把具有独立功能的代码块,组织为一个小模块,在需要的时候调用。
函数定义
将多行代码放在一起,起名字的过程
函数必须先定义后调用
语法
def 函数名():
函数中的代码
函数中的代码
- def是关键字,用来定义函数的 define的缩写
- 函数名需要遵守标识符的规则
- 处于def缩进中的代码,称为函数体
- 函数定义的时候,函数体中的代码不会执行,在调用的时候才会执行
函数的调用
使用多行代码的时候,称为函数调用
- 函数调用的时候会执行函数体中的代码
- 函数调用的代码,要写在函数体外边
列表去重
列表去重:列表中存在多个数据,需求,去除列表中重复的数据。
方式1
遍历原列表中的数据,判断在新列表中是否存在,如果存在,不管,如果不存在,则放入新的列表中
遍历:for循环实现
判断是否存在:可以使用 in
存入数据: append()
方式2
在Python中还有一种数据类型(容器),称为是集合(set)
特点:
集合中不能有重复的数据(如果有重复的数据会自动去重),可以使用集合的特点对列表去重
- 使用set()类型转换将列表转换为集合类型
- 再使用list()类型转换将集合转换为列表
缺点:不能保证数据在原列表中出现的顺序(一般来说,也不考虑这件事)
变量的引用[理解]
- 在定义变量的时候 变量 = 数据值,Python解释器会在内容开辟两块空间;
- 变量和数据都有自己的空间
- 日常简单理解,将数据保存到变量的内存中,本质是将数据的地址保存到变量对应的内存中;
- 变量中存储数据地址的行为,就是引用(变量引用了数据的地址,简单说就是变量中存储数据),存储的地址称为引用地址;
- 可以使用id()来获取变量中的引用地址(即数据的地址),如果两个变量的id()获取的引用地址一样,即代表着,两个变量引用了同一个数据,是同一个数据
- 只有赋值运算符= ,可以改变变量的引用(等号左边数据的引用)
- python中数据的传递,都是传递的引用
可变类型和不可变类型
数据类型: int
float
bool
str
list
tuple
dict
sec
可变不可变是指:数据所在的内存是否允许修改,允许修改就是可变类型,不允许修改就是不可变类型(不使用=,变量引用的数据中的内容是否会变化,会变化是可变的,不会变化是不可变的)
可变类型: list
dict
set
列表.append()
字典.pop(键)
不可变类型: int
float
bool
str
tuple
注意:
- 只有 = ,可以改变引用
- 可变类型做参数,在函数内部,如果不使用 = 直接修改形参的引用,对形参进行的数据修改,会同步到实参中
对于列表来说,+=的本质是extend操作
def func(list1):
list1 += [1, 2]
my_list = ['a', 'b']
func(my_list)
print(my_list) ===> ?
① ['a', 'b'] ② ['a', 'b', 1, 2] 选②
组包和拆包
组包(pack):将多个数据值使用逗号连接,组成元组
拆包(unpack):将容器中的数据值使用多个变量分别保存的过程,注意:变量的个数和容器中数据的个数要保持一致。
赋值运算符,都是先执行等号右边的代码,执行的结果,保存到等号左边的代码
a = 10,b = 20
# 组包
c = b, a # 组包
print(type(c), c) # <class 'tuple'> (10, 20)
# 拆包
a, b = c
print(a, b)
x, y, z = [1, 2, 3]
print(x, y, z)
局部变量和全局变量
根据变量的定义位置,可以将变量分为局部变量和全局变量
局部变量
局部变量:在函数内部(函数的缩进中)定义的变量,称为是局部变量
特点:
-
局部变量只能在当前函数内部使用,不能在其他函数和函数外部使用
-
在不同函数中,可以定义名字相同的局部变量,两者之间没有影响
-
生存周期(生命周期,作用范围)–>在哪能用
在函数被调用的时候,局部变量被创建,函数调用结束,局部变量的值被销毁(删除),不能使用
所以函数中的局部变量的值,如果想在函数外部使用,需要使用return关键字,将这个值进行返回
全局变量
定义位置:在函数外部定义的变量,称为是全局变量
特点:
-
可以在任何函数中读取(获取)全局变量的值
-
如何在函数中存在和全局变量名字相同的局部变量,在函数中使用的是局部变量的值(就近)
-
在函数内部想要修改全局变量的引用(数据值),需要添加global关键字,对变量进行声明为全局变量
-
生命周期
代码执行的时候被创建,代码执行结束,被销毁(删除)
函数进阶
返回值-函数返回多个数据值
函数中想要返回一个数据值,使用return关键字
将多个数据值组成容器进行返回,一般是元组(组包)
def calc(a,b):
num = a + b
num1 = a - b
return num, num1
# 写法⼀
result = calc(10, 5)
print(result, result[0], result[1])
# 写法⼆, 直接拆包
x, y = calc(20, 10)
print(x, y)
函数参数
函数传参的方式
-
位置传参
在函数调用的时候,按照形参的顺序,将实参值传递给形参
-
关键字传参
在函数调用的时候,指定数据值给到那个形参
-
混合使用
- 关键字传参必须写在位置传参的后面
- 不要给一个形参传递多个数据值
def func(a, b, c):
print(f'a: {a}, b: {b}, c: {c}')
# 位置传参
func(1, 2, 3)
# 关键字传参
func(a=2, b=3, c=1)
# 混合使⽤
func(1, 3, c=5)
缺省参数
缺省参数:默认参数
#不写参数,删除最后一个
列表.pop()
列表.sort(reverse=True)
定义方式
在函数定义的时候,给形参一个默认的数据值,这个形参就变为缺省参数,注意,缺省参数的书写要放在普通参数的后边
特点(好处)
缺省参数,在函数调用的时候,可以传递实参值,也可以不传递实参值
如果传参,使用的就是传递的实参值,如果不传参,使用的就是默认值
def show_info(name, sex='保密'):
print(name, sex)
show_info('⼩王')
show_info('⼩王', '男')
多值参数[可变参数/不定长参数]
当我们书写函数的时候,不确定参数的具体个数时,可以使用不定长参数
print(1)
print(1, 2)
print(1, 2, 3)
print(1, 2, 3, 4)
不定长位置参数(不定长元组参数)
- 书写,在普通参数的前边,加上一个*,这个参数就变为不定长位置参数
- 特点,这个形参可以接收任意多个位置传参的数据
- 数据类型,形参的类型是元组
- 注意,不定长位置参数要写在普通的参数后面
- 一般写法,不定长位置参数的名字为args,即(*args)
函数调用时的拆包
def my_sum(*args,**kwargs):
num = 0 #定义变量,保存求和的结果
for i in args:
num += i
for j in kwargs.values():
num += j
print(num)
#需求,my_list = [1,2,3,4] 字典 my_dict = {'a':1,'b':2,'c':3,'d':4}
my_list = [1,2,3,4]
my_dict = {'a':1,'b':2,'c':3,'d':4}
#将字典和列表中的数据使用my_sum函数进行求和,改如何传参的问题
#my_sum(1,2,3,4)
#my_sum(a=1,b=2,c=3,d=4)
#想要将列表(元组中的数据 分别作为位置参数,进行传参,需要对列表进行拆包操作)
my_sum(*my_list) #my_sum(1,2,3,4)
my_sum(**my_dict) #my_susm(a=1,b=2,c=3,d=4)
不定长关键字参数(不定长字典参数)
- 书写,在普通参数的前边,加上两个*,这个参数就变为不定长关键字参数
- 特点,这个形参可以接收任意多个关键字传参的数据
- 数据类型,形参的类型是字典
- 注意,不定长关键字参数,要写在所有从三叔的最后边
- 一般写法,不定长关键字参数的名字为kwargs,即(**kwargs)
完整的参数顺序
def 函数名(普通函数, *args, 缺省参数, **kwargs):
pass
# ⼀般在使⽤的时候, 使⽤ 1-2种, 按照这个顺序挑选书写即可
def func(*args,
**kwargs):
print(type(args), args)
print(type(kwargs), kwargs)
print('-' * 30)
func()
func(1, 2, 3) # 位置传参, 数据都给 args
func(a=1, b=2, c=3) # 关键字传参, 数据都给 kwargs
func(1, 2, 3, a=4, b=5, c=6)
匿名函数
匿名函数就是使用lambda关键字定义的函数,一般称为使用def关键字定义的函数为,标准函数
匿名函数只能书写一行代码
匿名函数的返回值不需要return,一行代码(表达式)的结果就是返回值
使用场景:作为函数的参数,这个函数比较简单,值使用一次,没有必要使用def定义
语法
lambda 参数:一行代码 #这行代码称为是表达式
#匿名函数一般不需要我们主动的调用,一般作为函数的参数使用
#我们在学习阶段为了查看匿名函数定义的是否正确,可以调用
#1. 定义的时候,将匿名函数的引用保存到变量中
变量 = lambda 参数:一行代码
#2. 使用变量进行调用
变量()
练习
# 1. 定义⼀个匿名函数可以求两个数的乘积 (参数需要两个)
func1 = lambda a, b: a * b
# 2. 定义⼀个匿名函数, 参数为字典,返回字典中键为 age的值
# 参数只是⼀个占位的作⽤,定义的时候没有具体的数据值,形参的值是在调⽤的时候进⾏传递,此时,形参才有数据值形参的类型就是由实参来决定的, 在函数定义的时候,参数只是⼀个符号,写什么都可以, 想让其是字典类型,只需要保证
# 实参是字典即可
func2 = lambda x: x.get('age')
字典的排序
# user_list.sort()
# 列表的排序, 默认是对列表中的数据进⾏⽐⼤⼩的, 可以对数字类型和字符串进⾏⽐⼤⼩,
# 但是对于字典来说,就不知道该怎么⽐⼤⼩, 此时,我们需要使⽤ sort 函数中的 key 这个参数, 来指定字典⽐⼤⼩的⽅法
# key 这个参数, 需要传递⼀个函数,⼀般是匿名函数, 字典的排序,其实要指定根据字典的什么 键进⾏排序, 我们只需要使⽤
# 匿名函数返回字典的这个键对应的值即可
# 列表.sort(key=lambda x: x['键'])
# 根据年龄排序
# user_list.sort(key=lambda x: x['age'])
user_list.sort(key=lambda x: x['age'],reverse=True)
print(user_list)
# user_list.sort(key=lambda x: x['age'])
# 说明: 匿名函数中的参数是 列表中的数据, 在 sort 函数内部,会调⽤ key 这个函数(将列表中每个数据作为实参传递给形参),
# 从列表中的获取函数的返回值, 对返回值进⾏⽐⼤⼩操作(<)
def get_value(x):
return x['age']
user_list.sort(key=get_value)
面向对象
基本的介绍
面向对象是一个编程思想(写代码的套路)
编程思想:
- 面向过程
- 面向对象
以上两种都属于写代码的套路(方法),最终目的都是为了将代码书写出来,只不过过程和思考方法不太一样
- 面向过程
- 关注的是具体步骤的实现,所有功能都自己书写
- 亲力亲为
- 定义一个个函数,最终按照顺序调用函数
- 面向对象
- 关注的是结果,谁(对象)能帮我做这件事
- 偷懒
- 找一个对象(),让对象去做
类和对象
面向对象的核心思想是,找一个对象去帮我们处理事情,在程序代码中,对象是类创建的
类和对象,是面向对象编程思想中非常重要的两个概念
- 类
- 抽象的概念,对多个特征和行为相同或者相似事物的统称
- 泛指的(指代多个,而不是具体一个)
- 对象
- 具体存在的一个事物,看得见摸得着的
- 特指的,(指代一个)
苹果 ---> 类
红苹果 ---> 类
张三嘴里正在吃的那个苹果 ---> 对象
类的组成
-
类名
给这多个事物起一个名字,在代码中满足大驼峰命名法(每个单词的首字母大写)
-
属性
事物的特征,即有什么,一般文字中的名词
-
方法
事物的行为,即做什么事,一般是动词
类的抽象(类的设计)
其实就是找到类的类名,属性和方法
需求:
- 小明 今年18岁,身高1.75,每天早上跑完步,会去吃东西
- 小美 今年17岁,身高1.65,小美不跑步,小美喜欢吃东西
类名: ⼈类(Person, People)
属性: 名字(name), 年龄(age), 身⾼(height)
⽅法: 跑步(run) 吃(eat)
需求:
- 一只黄颜色的狗狗叫大黄
- 看见生人汪汪叫
- 看见家人摇尾巴
类名: 狗类(Dog)
属性: 颜⾊(color) , 名字(name)
⽅法: 汪汪叫 (bark), 摇尾巴(shake)
面向代码的步骤
- 定义类,在定义类之前先设计类
- 创建对象,使用第一步定义的类创建对象
- 通过对象调用方法
面向对象基本代码的书写
1. 定义类
先定义简单的类,不包含属性,在python中定义类需要使用关键字class
方法:方法的本质是在类中定义的函数,只不过,第一个参数是self
class 类名:
# 在缩进中书写的内容,都是类中的代码
def ⽅法名(self): # 就是⼀个⽅法
pass
2. 创建对象
创建对象是使用类名()进行创建,即
类名() # 创建⼀个对象, 这个对象在后续不能使⽤
# 创建的对象想要在后续的代码中继续使⽤, 需要使⽤⼀个变量,
将这个对象保存起来
变量 = 类名() # 这个变量中保存的是对象的地址, ⼀般可以
成为这个变量为对象
# ⼀个类可以创建多个对象, 只要出现 类名() 就是创建⼀个对
象,每个对象的地址是不⼀样的
3. 调用方法
对象.方法名()
列表.sort()
列表.append()
"""
需求:⼩猫爱吃⻥,⼩猫要喝⽔, 定义不带属性的类
"""
class Cat:
# 在缩进中书写 ⽅法
def eat(self): # self 会⾃动出现,暂不管
print('⼩猫爱吃⻥...')
def drink(self):
print('⼩猫要喝⽔----')
# 2. 创建对象
blue_cat = Cat()
# 3. 通过对象调⽤类中的⽅法
blue_cat.eat()
blue_cat.drink()
# 创建对象
black_cat = Cat()
black_cat.eat()
black_cat.drink()
Cat() # 是
a = black_cat # 不是
b = Cat # 不是
self的说明:
- 从函数的语法上讲,self是形参,就可以是任意的变量名,只不过我们习惯性将这个形参写作self
- self是最普通的形参,但是在调用的时候没有传递实参值,原因是,Python解释器在执行代码的时候,自动的将调用这个方法的对象传递给self,即self的本质是对象。
- 验证,只需要确定通过哪个对象调用,对象的引用和self的引用是一样的
- self是函数中的局部变量,直接创建的对象是全局变量
class Cat:
# 在缩进中书写 ⽅法
def eat(self): # self 会⾃动出现,暂不管
print(f'{id(self)}, self')
print('⼩猫爱吃⻥...')
# 2. 创建对象
blue_cat = Cat()
print(f'{id(blue_cat)}, blue_cat')
# 3. 通过对象调⽤类中的⽅法
blue_cat.eat() # blue_cat 对象调⽤ eat ⽅法, 解释器就
print('_*_' * 30)
# 创建对象
black_cat = Cat()
print(f"{id(black_cat)}, black_cat")
black_cat.eat() # black_cat 对象调⽤ eat ⽅法, 解释器
对象的属性操作
添加属性
对象.属性名 = 属性值
-
类内部添加
在内部方法中,self是对象,
self.属性名 = 属性值
在类中,添加属性一般写在
_init_
方法中 -
类外部添加
对象.属性名 = 属性值 #一般不使用
获取属性
对象.属性名
-
类内部
在内部方法中,self是对象,self.属性名
-
类外部
对象.属性名 #一般很少使用
class Cat:
# 在缩进中书写 ⽅法
def eat(self): # self 会⾃动出现,暂不管
print(f'{id(self)}, self')
print(f'⼩猫{self.name} 爱吃⻥...')
# 2. 创建对象
blue_cat = Cat()
print(f'{id(blue_cat)}, blue_cat')
# 给 蓝猫添加 name 属性
blue_cat.name = '蓝猫'
# 3. 通过对象调⽤类中的⽅法
blue_cat.eat() # blue_cat 对象调⽤ eat ⽅法, 解释器就会将 blue_cat 对象传给 self
print('_*_' * 30)
# # 创建对象
black_cat = Cat()
black_cat.name = '⿊猫'
print(f"{id(black_cat)}, black_cat")
black_cat.eat() # black_cat 对象调⽤ eat ⽅法, 解释器
就会将 black_cat 对象传给 self
魔法方法
python中有一类方法,以两个下划线开头,两个下划线结尾,并且在满足某个条件的情况下,会自动调用,这类方法称为 魔法方法
学习:
- 什么情况下自动调用
- 有什么用,用在哪
- 书写的注意事项
__init__
方法
-
什么情况下自动调用
创建对象之后会自动调用
-
有什么用,用在哪
- 给对象添加属性的,(初始化方法,构造方法)
- 某些代码,在每次创建对象之后,都要执行,就可以将这行代码写在
__init__
方法 - 书写的注意事项
- 不要写错了
- 如果init方法中,存在出了self之外的参数,在创建对象的时候必须传参
class Cat:
# 定义添加属性的⽅法
def __init__(self, name, age): # 这个⽅法是创建对象之后调⽤
self.name = name # 给对象添加 name 属性
self.age = age # 给对象添加 age 属性
# 输出属性信息
def show_info(self):
print(f'⼩猫的名字是: {self.name}, 年龄是:
{self.age}')
# 创建对象,不要在⾃⼰类缩进中创建
# Cat() # 创建对象 ,会输出
blue_cat = Cat('蓝猫', 2)
blue = blue_cat
blue.show_info()
# 创建⿊猫
black_cat = Cat('⿊猫', 3)
black_cat.show_info()
__str__
方法
-
什么情况下自动调用
使用print(对象)打印对象的时候会自动调用
-
有什么用,用在哪
在这个方法中一般书写对象的属性信息,即打印对象的时候想要查看什么信息,在这个方法中进行定义的
如果类中没有定义
__str__
方法,print(对象),默认输出对象的引用地址 -
书写的注意事项
这个方法必须返回一个字符串
class Cat:
# 定义添加属性的⽅法
def __init__(self, n, age): # 这个⽅法是创建对象之后调⽤
self.name = n # 给对象添加 name 属性
self.age = age # 给对象添加 age 属性
def __str__(self):
# ⽅法必须返回⼀个字符串, 只要是字符串就⾏,
return f'⼩猫的名字是: {self.name}, 年龄是:
{self.age}'
# 创建对象,不要在⾃⼰类缩进中创建
# Cat() # 创建对象 ,会输出
blue_cat = Cat('蓝猫', 2)
print(blue_cat)
# 创建⿊猫
black_cat = Cat('⿊猫', 3)
print(black_cat)
__del__
方法【了解】
__init__ ⽅法, 创建对象之后,会⾃动调⽤ (构造⽅法)
__del__ ⽅法, 对象被删除销毁时, ⾃动调⽤的(遗⾔, 处理后
事) (析构⽅法)
1. 调⽤场景, 程序代码运⾏结束, 所有对象都被销毁
2. 调⽤场景, 直接使⽤ del 删除对象(如果对象有多个名字(多
个对象引⽤⼀个对象),需要吧所有的对象都删除才⾏ )
class Demo:
def __init__(self, name):
print('我是 __init__, 我被调⽤了 ')
self.name = name
def __del__(self):
print(f'{self.name} 没了, 给他处理后事...')
# Demo('a')
a = Demo('a')
b = Demo('b')
del a # 删除销毁 对象,
print('代码运⾏结束')
案例
需求
- 小明体重75.0公斤
- 小明每次跑步会减肥0.5公斤
- 小明每次吃东西体重增加1公斤
类名: ⼈类 Person
属性: 姓名 name, 体重 weight
⽅法: 跑步 run
吃东⻄ eat
添加属性 __init__
属性信息 __str__
class Person:
def __init__(self, name, weight):
self.name = name
self.weight = weight
def __str__(self):
return f"姓名: {self.name}, 体重 {self.weight} kg"
def run(self):
print(f'{self.name} 跑步 5 km, 体重减少了')
# 减体重, 即修改属性
self.weight -= 0.5
def eat(self):
print(f'{self.name} ⼤餐⼀顿, 体重增加了')
# 修改体重
self.weight += 1
xm = Person('⼩明', 75.0)
print(xm)
xm.run()
print(xm)
xm.eat()
print(xm)
封装
就是定义类的过程
公有和私有
-
在Python中定义的方法和属性,可以添加访问控制权限(即在什么地方可以使用这个属性和方法)
-
访问控制权限分为两种,公有权限和私有权限
-
公有权限
直接书写的方法和属性,都是公有的
公有的方法和属性,可以在任意地方访问和使用
-
私有权限
在类内部,属性名或者方法名,前边加上两个下划线,这个属性或者方法就变为私有的
私有的方法和属性,只能在当前类的内部使用
-
什么时候定义私有
- 某个属性或者方法,不想在类的外部被访问和使用,就将其定义为私有即可
- 测试中,一般不怎么使用,直接公有即可
- 开发中,会根据需求文档,确定什么作为私有
-
如果想要在类外部操作私有属性,方法是,在类内部定义公有的方法,我们通过这个公有的方法去操作
继承
继承描述的是类与类之间的关系,继承的好处是可以减少代码的冗余
语法
# class A(object):
class A: # 没有写父类,但也有父类, object, object 类是 Python 中最顶级(原始)的类
pass
class B(A): # 类 B, 继承类 A
pass
案例
- 定义一个动物类,吃
- 定义一个狗类,继承动物类,吃,叫
- 定义一个哮天犬类,继承狗类
# 1. 定义一个 动物类, 吃
class Animal:
def eat(self):
print('要吃东西')
# 2. 定义一个 狗类, 继承动物类, 吃, 叫
class Dog(Animal):
def bark(self):
print('汪汪汪叫....')
# 3. 定义一个 哮天犬类, 继承 狗类
class XTQ(Dog):
pass
# 创建 动物类的对象
# ani = Animal()
# ani.eat()
# 创建狗类对象
# dog = Dog()
# dog.eat() # 调用父类中的方法
# dog.bark() # 调用自己类中方法
# 创建哮天犬类对象
xtq = XTQ()
xtq.bark() # 调用 父类 Dog 类的方法
xtq.eat() # 可以调用 父类的父类中的方法
结论
python中对象,方法()调用方法
- 先在自己的类中去找有没有这个方法,如果有,直接调用
- 如果没有去父类中查找,如果有,直接调用
- 如果没有,去父类的父类中查找,如果有直接调用
- 如果Object类中有,直接调用,如果没有,代码报错
重写
在子类中定义了和父类中名字相同的方法,就是重写
重写的原因:父类中的方法,不能满足子类对象的需求,所以重写
重写之后的特点:调用子类字节的方法,不再调用父类中的方法
多态
是一种写代码,调用的一种技巧,同一个方法,传入不同的对象,执行得到不同的结果,这种现象称为是多态,多态可以增加代码的灵活度
哪个对象调用方法,就去自己的类中去查找这个方法,找不到去父类中找
属性和方法
Python中一切皆对象,即使用class定义的类,也是一个对象
对象的划分
实例对象(实例)
- 通过类名()创建的对象,我们称为实例对象,简称实例
- 创建对象的过程称为是类的实例化
- 我们平时所说的对象就是指 实例对象(实例)
- 每个实例对象,都有自己的内存空间,在自己的内存空间中保存自己的属性(实例属性)
类对象(类)
- 类对象就是类,或者可以认为是类名
- 类对象是Python解释器在执行代码的过程中创建的
- 类对象的作用:
- 使用类对象创建实例 类名()
- 类对象也有自己的内存空间,可以保存一些属性(类属性)
- 在一个代码中,一个类,只有一份内存空间
属性的划分
实例属性
-
概念:是实例对象具有的属性
-
定义和使用(一般定义在
__init__()
方法中)在 init 方法中, 使用 self.属性名 = 属性值 定义 在方法中是 使用 self.属性名 来获取(调用)
-
内存
实例属性,在每个实例中,都存在一份
-
使用时机
- 基本上99%都是实例属性,即通过self去定义
- 找多个对象,来判断这个值是不是都是一样的,如果都是一样的,同时变化,则一般定义为类属性为实例属性
类属性
-
概念:是类对象具有的属性
-
定义和使用
在类内部,方法外部,直接定义的变量 ,就是类属性 使用: 类对象.属性名 = 属性值 or 类名.属性名 = 属性值 类对象.属性名 or 类名.属性名
-
内存
只有类对象中存在一份
方法的划分
方法,使用def关键字定义在类中的函数就是方法
实例方法(最常用)
-
定义
# 在类中直接定义的方法 就是 实例方法 class Demo: def func(self): # 参数一般写作 self,表示的是实例对象 pass
-
定义时机(什么时候用)
如果在方法中需要使用实例属性(即需要使用self),则这个发方法必须定义为实例方法
-
调用
对象.方法名() #不需要给self传参
类方法(会用)
-
定义
# 在方法名字的上方书写 @classmethod 装饰器(使用 @classmethod 装饰的方法) class Demo: @classmethod def func(cls): # 参数一般写作 cls, 表示的是类对象(即类名) class pass
-
定义时机(什么时候用)
- 前提,方法中不需要使用实例属性(即self)
- 用到了类属性,可以将这个方法定义为类方法(也可以定义为实例方法)
-
调用
# 1. 通过类对象调用 类名.方法名() # 也不需要给 cls 传参, python 解释器自动传递 # 2. 通过实例对象调用 实例.方法名() # 也不需要给 cls 传参, python 解释器自动传递
静态方法(基本不用)
-
定义
# 在方法名字的上方书写 @staticmethod 装饰器(使用 @staticmethod 装饰的方法) class Demo: @staticmethod def func(): # 一般没有参数 pass
-
定义时机(什么时候用)
- 前提,方法中不需要使用实例属性(即self)
- 也不使用类属性,可以将这个方法定义为 静态方法
-
调用
# 1. 通过类对象调用 类名.方法名() # 2. 通过实例对象调用 实例.方法名()
哈希(hash)
是一个算法,可以对数据产生一个唯一的值(指纹)
is()
可以用来判断两个对象是不是同一个对象,即两个对象的引用是否相同
a is b ===> id(a) == id(b)
is 和 == 的区别:
== 只判断数据值是否相同,is 判断引用是否相同
文件
计算机的文件,就是存储在某种长期存储设备上的一段数据。
作用:将数据长期保存下来,在需要的时候使用
- 计算机只认识二进制(0,1)
- 文件中存储的数据都是以二进制(0 1)的形式去存储的
可以根据文件中的二进制内容,能否使用记事本软件,将其转换为文字,将文件分为两种:文本文件和二进制文件
- 文本文件
- 能够使用记事本软件打开(能够使用记事本转换为文字)
- txt,md,py,html,css,js,json
- 二进制文件
- 不能使用记事本软件打开的
- exe,mp3,mp4,jpg,png
文件操作
文件操作的步骤
- 打开文件
- 读或者写文件
- 关闭文件
1. 打开文件
将文件从磁盘中读取到内存中
语法:
open(file, mode = 'r', encoding = None)
- file:是要打开的文件,类型是字符串,文件的路径可以是相对路径,也可以是绝对路径,建议使用相对路径(相对于当前代码文件所在的路径,./…/)
- mode: 默认参数(缺省参数),表示的是打开文件的方式
- r:read 只读打开
- w: write 只写打开
- a: append 追加打开,在文件的末尾写入内容
- 参数encoding: 编码方式,(文字和二进制如何进行转换的)
- gbk:将汉字转换为2个字节二进制
- utf-8:(常用),将一个汉字转换为3个字节的二进制
- 返回值:返回的是文件对象,后续对文件的操作,都需要这个对象
2. 读或者写文件
写文件
向文件中写入指定的内容
前提:文件的打开方式是w或者a
⽂件对象.write('写⼊⽂件的内容')
# 返回值: 写⼊⽂件的字符数,⼀般不关注
# 注意 w ⽅式打开⽂件:
1. ⽂件不存在,会直接创建⽂件
2. ⽂件存在,会覆盖原⽂件(将原⽂件中的内容清空)
# 1, 打开⽂件
f = open('a.txt', 'w', encoding='utf-8')
# 2, 写⽂件
f.write('好好学习\n')
f.write('天天向上')
# 3, 关闭⽂件
f.close()
读文件
将文件中的内容读取出来
前提:文件的打开方式需要是r
⽂件对象.read(n)
# 参数 n 表示读取多少个字符, ⼀般不写,表示读取全部内容
# 返回值: 读取到的⽂件内容, 类型 字符串
# 1, 打开⽂件
f = open('a.txt', 'r', encoding='utf-8')
# 2, 读⽂件
buf = f.read()
print(buf) # ⽬前只是打印读取的内容,可以做其它的事
# 3. 关闭⽂件
f.close()
# r ⽅式打开⽂件 ,如果⽂件不存在,代码会报错
3. 关闭文件
关闭文件:将文件占用的资源进行清理,同时会保存文件,文件关闭之后,这个文件对象就不能使用了
文件对象.close()
使用with open打开文件
with open()打开文件的好处:不用自己去书写关闭文件的代码,会自动进行关闭
with open(file, mode, encoding='utf-8') as 变量:
# 在缩进中去读取或者写⼊⽂件
# 缩进中的代码执⾏结束, 出缩进之后, ⽂件会⾃动关闭
with open('a.txt', 'a', encoding='utf-8') as f:
f.write('good good study ')
# a ⽅式打开⽂件, ⽂件不存在会创建⽂件, ⽂件存在, 在⽂件
的末尾写⼊内容
按行读取文件内容
按行读取文件:一次读取一行内容
文件对象.readline()
# read() 和 readline() 读到⽂件末尾, 返回⼀个空字符串,
即⻓度为 0
with open('b.txt', encoding='utf-8') as f:
while True:
buf = f.readline()
if len(buf) == 0:
break
else:
print(buf, end='')
# 在容器中 , 容器为空,即容器中的数据的个数为 0 ,表示False, 其余情况都是 True
with open('b.txt', encoding='utf-8') as f:
while True:
buf = f.readline()
if buf: # if len(buf) != 0
print(buf)
else:
break
json文件的处理
json文件也是一个文本文件,就可以直接使用read()和write()方法去操作文件,只是使用这两个方法,不方便,所以对json文件有自己独特的读取和写入方式
常用在 在做测试的时候,将测试数据定义为json文件格式,使用代码读取json文件,即读取测试数据,进行传参(参数化)
json的介绍
json基于文本,独立于语言的轻量级的数据交换格式
- 基于文本,是一个文本文件,不能包含图片,音视频等
- 独立于语言,不是某个语言特有的,每种编程语言都可以使用
- 轻量级,相同的数据,和其他格式相比,占用的大小比较小
- 数据交换格式,后端程序员给前端的数据(json,html,xml)
json文件的语法
-
json文件的后缀是.json
-
json中主要数据类型为对象({}类似Python中字典)和数组([],类似Python中的列表),对象和数组可以互相嵌套
-
一个json文件是一个对象或者数组(即json文件的最外层要么是一个{},要么是一个数组[])
-
json中的对象是由键值对组成的,每个数据之间使用逗号隔开,但是最后一个数据后边不要写逗号
-
json中的字符串必须使用双引号
-
json中的其他数据类型
> 数字类型 ----> int float
> 字符串 string —> str
> 布尔类型 true, false -----> True, False
> 空类型 null ----> None
json文件的书写
案例
我叫小明,我今年18岁,性别男,爱好听歌,游戏,购物,吃饭,睡觉,打豆豆,我的居住地址为 国家中国,城市上海
{
"name": "小明",
"age": 18,
"isMen": true,
"like": [
"听歌",
"游戏",
"购物",
"吃饭",
"睡觉",
"打豆豆"
],
"address":{
"country":"中国",
"city":"上海"
}
}
读取json文件
- 导包
import json
- 读打开文件
- 读文件
返回的是 字典(文件中是对象)或者列表(文件中是数组)
# 1.导入json
import json
# 2.读打开文件
with open('info.json',encoding = 'utf-8') as f:
# 3. 读取文件
# buf = f.read()
# print(type(buf),buf)
result = json.load(f)
print(type(result)) #<class 'dict'>
#姓名
print(result.get('name'))
#年龄
print(result.get('age'))
#城市
print(refult.get('address').get('city'))
练习1
我叫小明,我今年18岁,性别男,爱好 听歌,游戏,吃饭,睡觉,打豆豆
我的居住地址为 国家中国,城市上海,
我叫小红,我今年17岁,性别女,爱好 听歌,学习,购物,
我的居住地址是 国家中国,城市北京
{
"people": [
{
"name": "小明",
"age": 18,
"gender": "男",
"hobbies": [
"听歌",
"游戏",
"吃饭",
"睡觉",
"打豆豆"
],
"address": {
"country": "中国",
"city": "上海"
}
},
{
"name": "小红",
"age": 17,
"gender": "女",
"hobbies": [
"听歌",
"学习",
"购物"
],
"address": {
"country": "中国",
"city": "北京"
}
}
]
}
import json
with open('info.json', encoding='utf-8') as f:
data = json.load(f)
print(type(data)) # This will print <class 'dict'>
for person in data['people']:
print(person['name'])
print(person['age'])
# To access address city, you can do the following
# print(person['address']['city'])
练习2
某⽹站的测试数据如下 data.json, 需求,提取 json ⽂件中的
⽤户名,密码和预期结果, 组成如下格式: [(), (), ()] (⾃
动化参数化需要的数据格式)
[
{
"desc": "正确的⽤户名密码",
"username": "admin",
"password": "123456",
"expect": "登录成功"
},
{
"desc": "错误的⽤户名",
"username": "root",
"password": "123456",
"expect": "登录失败"
},
{
"desc": "错误的密码",
"username": "admin",
"password": "123123",
"expect": "登录失败"
}
]
import json
def read_data():
new_list = []
with open('info.json',encoding = 'utf-8') as f:
data = json.load(f)
for i in data:
new_list.append((i.get('username'),i.get('password'),i.get('expext')))
return new_list
list = read_data()
print(list)
json的写入
文件对象.write(字符串) 不能直接将Python的列表和字典作文参数传递,想要将Python中的数据类型存为json文件,需要使用json提供的方法,不再使用write
步骤:
- 导包
import json
- 写(w)方式打开文件
- 写入
json.dump(Python中的数据类型,文件对象)
import json
def write_data(data):
with open('info.json', encoding='utf-8', mode='w') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 要写入的数据
my_list = [('admin', '123456', '登录成功'), ('root','123456', '登录失败'), ('admin', '123123', '登录失败')]
write_data(my_list)
# 读取数据的函数
def read_data():
with open('info.json', encoding='utf-8') as f:
data = json.load(f)
return data
# 读取并打印数据
data = read_data()
print(data)
写入后,原本json的数据会被清除
异常
程序在运行时,如果python解释器遇到一个错误,会停止程序的执行,并且提示一些错误信息,这就是异常
程序停止执行并且提示错误信息这个动作,抛出异常(raise关键字)
捕获异常:程序遇到异常,默认动作是终止代码程序的执行,遇见异常之后,可以使用异常捕获,让程序代码继续运行,不会终止运行(重点)
异常捕获(重点)
基本语法
try:
书写可能发生异常的代码
except: #任何类型的异常都能捕获
发生了异常执行的代码
try:
书写可能发生异常的代码
except 异常类型 # 只能捕获指定类型的异常,如果不是这个异常,还是会报错
try:
#1. 获取用户从键盘输入的数据
num = input('请输入数字:')
#2. 转换数据类型为整数
num = int(num)
#3. 输出转换之后的数据内容
print(num)
except:
print('请输入正确的数字')
print('后续其他的代码,可以继续执行')
try:
#1. 获取用户从键盘输入的数据
num = input('请输入数字:')
#2. 转换数据类型为整数
num = int(num)
#3. 输出转换之后的数据内容
print(num)
excexpt ValueError: #只能捕获ValueError类型及其子类的异常
print('发生了异常,请输入正确的数字...')
捕获多个指定类型的异常
好处:可以针对不同的异常错误,进行单独的代码处理
try:
书写可能发生异常的代码
except 异常类型1: #只能捕获特定类型的异常,如果不是这个异常,还是会报错
发生了异常1执行的代码
except 异常类型2:
发生了异常2执行的代码
except 异常类型...:
发生了异常...执行的代码
try:
# 1. 获取⽤户从键盘输⼊的数据
num = input('请输⼊数字:')
# 2. 转换数据类型为整数
num = int(num)
# 3. 输出转换之后的数据内容
print(num)
a = 10 / num # 10 / 0
print(f'a: {a}')
except ValueError: # 只能捕获 ValueError 类型及其⼦类的异常
print('发⽣了异常, 请输⼊正确的数字...')
except ZeroDivisionError:
print('除数不能为 0')
异常捕获的完整版本
完整版本中的内容,不是说每一次都要全部书写,根据自己的需要选择其中的进行使用
try:
可能发⽣异常的代码
except 异常类型1:
发⽣异常类型1执⾏的代码
# Exception 是常⻅异常类的⽗类, 这⾥书写 Exception,可以捕获常⻅的所有⼀会, as 变量, 这个变量是⼀个异常类的对象, print(变量) 可以打印异常信息
except Exception as 变量:
发⽣其他类型的异常,执⾏的代码
else:
没有发⽣异常会执⾏的代码
finally:
不管有没有发⽣异常,都会执⾏的代码
try:
可能发⽣异常的代码
except Exception as e:
发⽣异常执⾏的代码
try:
# 1. 获取⽤户从键盘输⼊的数据
num = input('请输⼊数字:')
# 2. 转换数据类型为整数
num = int(num)
# 3. 输出转换之后的数据内容
print(num)
a = 10 / num # 10 / 0
print(f'a: {a}')
except Exception as e:
print(f"错误信息为: {e}")
else:
print('没有发⽣异常我会执⾏')
finally:
print('不管有没有发⽣异常,我都会执⾏')
# print('不管有没有发⽣异常,我都会执⾏')
异常传递[了解]
异常传递是python中已经实现好了,我们不需要操作,我们知道异常会进行传递
异常传递:在函数嵌套调用的过程中,被调用的函数,发生了异常,如果没有捕获,会将这个异常向外层传递…如果传到最外层还没有捕获,才报错
模块和包
- Python源代码文件就是一个模块
- 模块中定义的变量 函数 类,都可以让别人使用,同样,可以使用别人定义的(好处:别人定义好的不需要我们再次书写,直接使用即可)
- 想要使用别人的模块中的内容工具(变量、类、函数),必须先导入模块才可以
- 我们自己写的代码,想要作为模块使用,代码的名字需要满足标识符的规则(由数字,字母,下划线组成,不能以数字开头)
导入模块的语法
方式一
import 模块名
# 使用模块中的内容
模块名.工具名
#举例
import random
import json
random.randint(a,b)
json.load()
json.dump()
方式二
from random import randint
from json import load,dump
randint(a,b)
load()
dump()
方式三[了解]基本不用
from 模块名 import * # 将模块中所有的内容都导⼊
from random import *
from json import *
randint(a, b)
load()
dump()
对于导入的模块和工具可以使用as关键字给其起别名,如果起别名,原来的名字就不能用了,只能使用别名
模块查找的顺序
在导入模块的时候,会先在当前目录中找模块,如果找到,就直接使用
如果没有找到,会去系统的目录中进行查找,找到,直接使用,没有找到,报错
注意:
定义代码文件的时候,代码名字不能和要导入的模块名字相同
__name__
的作用
-
每个代码文件都是一个模块;
-
在导入模块的时候,会执行模块中的代码(三种方法都会);
-
__name__
变量是pyhon解释器自动维护的变量; -
__name__
变量,如果代码是直接运行,值是"__main__"
; -
__name__
变量,如果代码是被导入执行,值是模块名(即代码文件名); -
在代码文件中,在被导入时不想被执行的代码,可以写在
if__name__=="__main__":
代码的缩进中;
代码练习
- 定义一个模块tools.py;
- 在模块中定义一个函数,func,输出’我是tools模块中的funn函数’;
- 在模块中定义一个类,Dog,具有属性name,age,方法play,输出’xx在快乐的玩耍’;
- 新建一个代码文件,调用tools模块中的func函数,并创建Dog类的对象,调用play方法。
包(package)
在python中,包是一个目录,只不过这个目录存在一个文件
__init__.py
(可以是空的)将功能相近或者相似的代码放在一起的。
在python中使用的时候,不需要区分是包还是模块,使用方式是一样的。
random 模块(单个代码文件)
json 包(目录)
unittest 包(目录)
1. import 包名
2. alt 回车 快捷导入
UnitTest框架
介绍
-
框架
说明:
- 框架英文单词framework;
- 为解决一类事情的功能集合;
- 需要按照框架的规定(套路)去书写代码。
-
什么是UnitTest框架?
概念:UnitTest是python自带的一个单元测试框架,用它来做单元测试。
自带的框架(官方):不需要单外安装,只要安装了Python,就可以使用 random,json,os,time
第三方框架:想要使用 需要先安装后使用(pytest)
selenium,appium,requests
单元测试框架:主要用来做单元测试,一般单元测试是开发做的。
对于测试来说,unittest框架的作用是自动化脚本(用例代码)执行框架(使用unittest框架来管理运行多个测试用例的)
-
为什么使用UnitTest框架?
- 能够组织多个用例去执行;
- 提供丰富的断言方法(让程序代码代替人工自动的判断预期结果和实际结果是否相符);
- 能够生成测试报告。
-
UnitTest核心要素(unitest的组成部分)
-
TestCase(最核心的模块)
TestCase(测试用例),注意这个测试用例是unittest框架的组成部分,不是手工和自动化中我们所说的用例(TestCase)。
主要作用:每个TestCase(测试用例)都是一个代码文件,在这个代码文件中来书写真正的用例代码。
-
TestSuite
TestSuite(测试套件),用来管理组装(打包)多个TestCase(测试用例)的。
-
TestRunner
TestRunner(测试执行,测试运行),用来执行TestSuite(测试套件)的。
-
TestLoader
TestLoader(测试加载),功能是对 TestSuite(测试套件)功能的补充,管理组装(打包)多个TestCase(测试用例)的。
-
Fixture
Fixture(测试夹具),书写在TestCase(测试用例)代码中,是一个代码结构,可以在每个方法执行前后都会执行的内容。
举例:
登录的测试用例,每个用例中重复的代码就可以写在Fixture代码结构中,只写一遍,但每次用例方法的执行,都会执行Fixture中的代码。
- 打开浏览器;
- 输入网址。
-
TestCase(测试用例)
- 是一个代码文件,在代码文件中来书写真正的用例代码;
- 代码文件的名字必须按照标识符的规则来书写(可以将代码的作用在文件的开头使用注释说明)
步骤
- 导包(unittest)
- 自定义测试类
- 在测试类中书写 测试方法
- 执行用例
代码
#1. 导包
import unittest
#2. 自定义测试类,需要继承unittest模块中的TestCase类即可
class TestDemo(unittest.TestCase):
#3. 书写测试方法,即用例代码。目前没有真正的用例代码,使用print代替
#书写要求,测试方法必须以test_开头(本质是以test开头)
def test_method1(self):
print('测试方法 1')
def test_method2(self):
print('测试方法 2')
#4. 执行用例(方法)
#将光标放在 类名的后边 运行,会执行类中所有的测试方法
#将光标放在方法名的后边 运行,只执行当前的方法
问题1 代码文件的命名不规范
- 代码文件的命名以数字开头;
- 代码问件名字中有空格;
- 文件名字有中文;
- 其他的特殊符号(数字,字母,下划线组成,不能以数字开头)
问题2 代码运行没有结果
右键运行没有unittests for的提示,出现的问题
解决方案:
- 重新新建一个代码文件,将写好的代码复制进去;
- 删除已有的运行方式。
问题3 没有找到用例
测试方法中不是以test_开头的,或者单词写错了
TestSuite & TestRunner
TestSuite(测试套件):管理 打包 组装 TestCase(测试用例)文件的
TestRunner(测试执行):执行TestSuite(套件)
-
步骤
- 导包(unittest)
- 实例化(创建对象)套件对象
- 使用套件对象添加用例方法
- 实例化运行对象
- 使用运行对象去执行套件对象
-
代码
TestSuite(测试套件):时用来管理多个(测试用例的),先创建多个TestCase(测试用例文件)
#学习TestSuite和TestRunner的使用 #1.导包 import unittest from hm_07_testcase1 import TestDemo1 from hm_07_testcase2 import TestDemo2 #2. 实例化(创建对象)套件对象 suite = unittest.TestSuite() #3. 使用套件对象添加用例方法 #方式一:套件对象.addTest(测试类名(方法名)) #建议测试类名和方法名直接去复制,不要手写 suite.addTest(TestDemo1('test_method1')) suite.addTest(TestDemo1('test_method2')) suite.addTest(TestDemo2('test_method1')) suite.addTest(TestDemo2('test_method2')) #4. 实例化运行对象 runner = unittest.TextTestRunner() #5. 使用运行对象去执行套件对象 #运行对象.run(套件对象) runner.run(suite)
#学习TestSuite和TestRunner的使用 #1. 导包(unittest) import unittest #2. 实例化(创建对象)套件对象 from hm_07_testcase1 import TestDemo1 from hm_07_testcase2 import TestDemo2 #3. 使用套件对象添加用例方法 #方式二 将一个测试类中的所有方法进行添加 #套件对象.addTest(unittest.makeSuite(测试类名)) #缺点:maleSuite()不会提示 suite.addTest(unittest.makeSuite(TestDemo1)) suite.addTest(unittest.makeSuite(TestDemo2)) #4. 实例化运行对象 runner = unittest.TextTestRunner() #5. 使用运行对象去执行套件对象 #运行对象.run(suite) runner.run(suite)
练习
在tools模块中定义add函数,对于两个数字进行求和计算;
书写TestCase代码对add()进行测试
用例1:1,2,3
用例2:10,20,30
用例3:2,3,5
用例代码:
#1. 导包
import unittest
from tools import add
#2. 自定义测试类
class TestAdd(unittest.TestCase):
#3. 书写测试方法,就是测试用例代码
def test_method1(self):
# 1,2,3 判断实际结果和预期结果是否相符
if add(1,2) == 3:
print('测试通过')
else:
print('测试不通过')
def test_method2(self):
if add(10,20) == 30:
print('测试通过')
else:
print('测试不通过')
def test_methods3(self):
#1,2,3 判断实际结果和预期结果是否相符
if add(2,3) == 5:
print('测试通过')
else:
print('测试不通过')
套件和执行的代码:
import unittest
#实例化套件对象
from hm_08_test import TestAdd
suite = unittest.TestSuite()
#添加测试方法
suite.addTest(unittest.makeSuite(TestAdd))
#实例化执行对象
runner = unittest.TextTestRunner()
runner.run(suite)
unittest组成
TestLoader(测试加载)
TestLoader(测试加载),作用和TestSuite的作用是一样的,对TestSuite功能的补充,用来组装测试用例的。
比如:如果TestCase的代码文件有很多,(10,20,30)
使用步骤:
- 导包
- 实例化测试加载对象并添加用例,得到的是suite对象
- 实例化运行对象
- 运行对象执行套件对象
代码实现
在一个项目中,TestCase(测试用例)的代码,一般放在一个单独的目录(case)
#TestLoader的使用
#1. 导包
import unittest
#2. 实例化加载对象并添加用例
#unittest.TestLoader().discover('用例的代码文件名')
#用例所在的路径,建议使用相对路径,用例的代码文件名可以使用(任意多个任意字符)通配符
#suite = unittest.TestLoader().discover('./case','hm*.py')
#suite = unitest.TestLoader().discover('./case','*test*.py')
#suite = unittest.TestLoader().discover('./case','*case1.py')
suite = unittest.TestLoader().discover('./case','*case1.py')
#3. 实例化运行对象
#runner = unittest.TextTestRunner()
#runner = unittest.TextTestRunner()
##4. 执行
#runner.run(suite)
#可以将3 4 步变为一步
unittest.TextTestRunner().run(suite)
#1. 导包
import unittest
#2. 使用默认的加载对象并加载用例
suite = unittest.defaultLoadTestLoader.discover('case','hm_*.py')
#可以将3 4 步变为一步
unittest.TextTestRunner().run(suite)
Fixture(测试夹具)
Fixture(测试夹具)是一种代码结构,在某些特定的情况下,会自动执行。
方法级别[掌握]
在每个测试方法(用例代码)执行前后都会自定调用的结构
#方法执行之前
def setup(self):
#每个测试方法执行之前都会执行
pass
#方法执行之后
def tearDown(self):
#每个测试方法之后都会执行
pass
类级别[掌握]
在每个测试类中所有方法执行前后,都会自动调用的结构(在整个类中,执行之前个一次)
#类级别的Fixture方法,是一个类方法
#类中所有方法之前
@classmethod
def setUpclass(cls):
pass
#类中所有方法之后
@classmethod
def tearDownClass(cls):
pass
模块级别[了解]
模块:代码文件
在每个代码文件执行前后执行的代码结构
#模块级别的需要写在类的外边直接定义函数即可
#代码文件之前
def setUpModule():
pass
#代码文件之后
def tearDownModule():
pass
方法级别和类级别的 前后的方法,不需要同时出现,根据用例diamagnetic的需要自行的选择使用
案例
打开浏览器(整个测试过程就打开一次浏览器)类级别
输入网址(每个测试方法都需要一次)方法级别
输入用户名密码验证码点击登录(不同的测试数据)测试方法
关闭当前页面(每个测试方法都需要一次)方法级别
关闭浏览器(震哥哥测试过程就关闭一次浏览器)类级别
打开浏览器(整个测试过程就打开一次浏览器)类级别
输入网址(每个测试方法都需要一次)方法级别
输入用户名密码验证码点击登录(不同的测试数据)测试方法
关闭当前页面(每个测试方法都需要一次)方法级别
输入网址(每个测试方法都需要一次)方法级别
输入用户名密码验证码点击登录(不同的测试数据)测试方法
关闭当前页面(每个测试方法都需要一次)方法级别
关闭浏览器(整个测试过程中就关闭一次浏览器)类级别
import unittest
class TestLogin(unittest.TestCase):
def setUp(self):
#每个测试方法执行之前都会先调用的方法
print('输入网址')
def tearDown(self) -> None:
#每个测试方法执行之前都会先调用的方法
print('关闭当前页面')
@classmethod
def setUpClass(cls) -> None:
print('-----1. 打开浏览器')
@classmethod
def tearDownClass(cls) -> None:
print('-----5. 关闭浏览器')
def test_1(self):
print('输入正确用户名密码验证码,点击登录 1')
def test_2(self):
print('输入错误用户名密码验证码,点击登录 2')
断言
让程序代替人工自动的判断预期结果和实际结果是否相符
断言结果有两种:
- True: 用例通过;
- False: 代码抛出异常,用例不通过
在unittest中使用断言,都需要通过self.断言方法来试验。
assertEqual
#判断预期结果和实际结果是否相等
self.assertEqual(预期结果,实际结果)
1. 如果相等,用例通过;
2. 如果不相等,用例不通过,抛出异常
asserIn
#判断预期结果是否包含在实际结果中
self.assertIn(预期结果,实际结果)
1. 包含,用例通过;
2. 不包含,用例不通过,抛出异常。
#包含
assertIn('admin','admin')
#包含
assertIn('admin','adminnnnnnnnn')
#包含
assertIn('admin','aaaadmin')
#包含
assertIn('admin','aaaaadminnnnn')
#不包含
assertIn('admin','addddddmin')
import unittest
from tools import login
class TestLogin(unittest.TestCase):
def test_username_password_ok(self):
#正确的用户名和密码:admin.123456,登录成功
self.assertEqual('登录成功',login('admin','123456'))
def test_username_error(self):
#错误的用户名:root,123456,登录失败
self.assertEqual('登录失败',login('root','123456'))
def test_password_error(self):
#错误的密码:admin,123123,登录失败
self.asserEqual('登录失败',login('admin'))
def test_username_password_error(self):
#错误的用户名和错误的密码:aaa,123123,登录失败
#self.asserEqual('登录失败',login('aaa','123123'))
self.asserIn('失败',login('aaa','123123'))
参数化
参数化在测试方法中,使用变量来代替具体的测试数据,然后使用传参的方法将测试数据传递给方法的变量
好处:相似的代码不需要多次书写
工作中的场景:
- 测试数据一般放在json文件中
- 使用代码读取json文件,提取我们想要的数据 —> [(),()] or[[],[]]
安装插件
unittest框架本身是不支持参数化,想要使用参数化,需要安装插件来完成
#联网安装(在cmd窗口安装 或者)
pip install parameterized
pip是Python中包(插件)的管理工具,使用这个工具下载安装插件
验证
pip list #查看到 parameterized
新建一个python代码文件,导包验证
from parameteried import parameterized
参数化代码
- 导包unittest/pa
- 定义测试类
- 书写测试方法(用到的测试数据使用变量代替)
- 组织测试数据并传参
[
{
"desc":"正确的用户名和密码",
"username":"admin",
"password":"123456",
"expect":"登录成功"
},
{
"desc":"错误的用户名",
"username":"root",
"password":"123456",
"expect":"登录失败"
},
{
"desc":"错误的密码",
"username":"admin",
"password":"123123",
"expect":"登录失败"
}
]
#1. 导包unittest/pa
import json
import unittest
from parameterized import parameterized
from tools import login
#1. 组织测试数据 [(),(),()] or [[],[],[]]
def build_data():
with open('data.json',encoding = 'utf-8') as f:
# [{},{},{}]
result = json.load(f)
data = []
#i{}
for i in result:
data.append((i.get('username'),i.get('password'),i.get('expect')))
return data
#2. 定义测试类
class TestLogin(unittest.TestCase):
#3. 书写测试方法(用到的测试数据使用变量代替)
@parameterized.expand(build_data())
def test_login(self,username,password,expect):
self.assertEqual(expect,login(username,password))
#4. 组织测试数据并传参(装饰器@)
跳过
对于一些未完成的或者不满足测试条件的测试函数和测试类,不想执行,可以使用跳过
使用方法,装饰器完成
代码书写在TestCase文件
#直接将测试函数标记成跳过
@unittest.skip('跳过的原因')
#根据条件判断测试函数是否跳过,判断条件成立,跳过
@unittest.skipIf(判断条件,'跳过原因')
import unittest
#version = 30
version = 29
class TestDemo(unittest.TestCase):
@unittest.skip('没有原因,就是不想执行')
def test_1(self):
print('测试方法 1')
@unittest.skipIf(version >=30,'版本大于等于30,不用测试')
def test_2(self):
print('测试方法 2')
def test_3(slef):
print('测试方法 3')
测试报告
自带的测试报告
只有单独运行TestCase的代码,才会生成测试报告
生成第三方的测试报告
- 获取第三方的测试运行类模块,将其放在代码的目录中
- 导包unittest
- 使用套件对象,加载对象去添加用例方法
- 实例化第三方的运行对象并运行套件对象
#1. 获取第三方的测试运行类模块,将其放在代码的目录中
#2. 导包unittest
import unittest
from HTMLTestRunner import HTMLTestRunner
#3. 使用套件对象,加载对象去添加用例方法
suite = unittest.defaultTestLoader.discover('.','hm_05_pal.py')
#4. 实例化第三方的运行对象并运行套件对象
#HTMLTestRunner()
#stream = sys.stdout,必须,测试报告的文件对象(open),注意点,要使用wb打开
#verbosity = 1,可选,报告的详细程度,默认1简略,2详细
#title = None ,可选,描述信息,python的版本,pycharm版本
#file = 'report.html' #报告的后缀是.html
file = 'report1.html' #报告的后缀是.html
with open(file,'wb') as f:
#runner = HTMLTestRunner(f) #运行对象
runner = HTMLTestRunner(f,2,'测试报告','python 3.6.8') #运行对象
#运行对象执行套件,要写在with的缩进中
runner.run(suite)
组织用例文件(TestCase里边),书写参数化,书写断言,书写Fixture,书写跳过,如果单个测试测试文件,直接运行,得到测试报告,如果有多个测试文件,需要组装生成测试报告
使用套件对象组装,或者使用加载对象组装
运行对象运行
3.1 运行对象 = 第三方的运行类(文件对象(打开文件需要使用wb方式))
3.2 运行对象.run(套件对象)
import unittest
from HTMLTestRunnerCN import HTMLTestReportCN
#组装用例方法
suite = unittest.defaultTestLoader.discover('.','*pal.py')
#实例化运行对象
with open('report_cn.html','wb') as f:
runner = HTMLTeestReportCN(f)
runner.run(suite)