Python学习

python + selenium :web自动化测试
python + appium:移动端自动化测试
python + requests:接口测试

IDE(PyCharm):集成开发环境
Ctrl + Alt + L:格式化代码

数据类型转换,不会改变原来数据的类型,会生成一个新的数据类型。
int() 将其他类型转换为整型
1)可以将 float 类型转换为整型
2)将 整数类型的字符串 转换为整型 '3' '124'

float()将其他类型转换为 浮点型
1)可以将int类型转换为 浮点型 float(3) -> 3.0
2)将数字类型的字符串(整数类型和小数类型)转换为 浮点型
float('3.1415926') -> 3.1415926

str()将其他类型转换为 字符串类型
任何类型都可以使用str()将其转换为字符串

%格式化输出

指定显示小数点后几位 %.nf,n需要换成具体的整数数字,即保留小数的位数
整数一共占几位 %0nd,n需要换成具体的整数数字
需要显示%(90%),需要使用两个%%

stu_num = 1
name = '小明'
age = 18
height = 1.71
score = 90
print('我的名字是 %s, 年龄是 %d, 身高是 %.2f' % (name, age, height))  
print('我的学号是 %06d' % stu_num) # 我的学号是000001
print('某次考试的及格率为 %d%%' % score) # 90%

字符串格式化

# f-string
print(f'我的名字{name}, 年龄是{age}, 身高是{height}, 及格率为{num}%' )

'{}, {}, ...'.format(变量1, 变量2, ...)
print('我的名字{}, 年龄是{}, 身高是{:.2f}, 及格率为{}%'.format(name, age, height, num) )

判断语句

if
if... :
else:
if elif else

循环语句

顺序
分支
循环:让指定代码按照指定次数重复执行

死循环和无限循环

死循环:代码一直不停的运行下去,一般是由写代码的人不小心造成的bug
无限循环:写代码的人故意让代码无限制的去执行,代码一直不停的运行下去。
无限循环使用场景:在书写循环时,不确定循环要执行多少次。
无限循环的使用:一般会在循环中添加一个if判断,当if条件成立,使用关键字break来终止循环

while True:
	重复执行的代码
	if 判断条件:
		break # 关键字的作用就是终止循环,当代码执行遇到break时,循环不再执行

break:结束循环(用的最多)
continue:结束本次循环,继续下次循环
range(a,b): 生成[a,b)之间的整数数字,不包含b
range(start, end, step):不包含end

产生随机数

import random
random.randint(a,b) # 产生a-b之间的随机整数,包含a和b

for循环基本格式

for 变量 in 容器:
	重复执行的代码

for 变量 in range(n):
	pass

for 变量 in range(start, end):
	pass

容器

字符串

转义字符:
\' --> ' ; \" --> " ; \\ --> \
字符串前边加上原生字符串r"",字符串中的\不会作为转义字符,文件操作会用

切片:包左不包右

字符串[strat:end:step]

步长为负数,反转(逆置)字符串

str = 'abcdefg'
print(str[::-1])   # gfedcba

字符串的查找方法 find

若找到,返回sub_str第一次出现的下标
若没有找到,返回-1

字符串.find(sub_str, start, end)
str1 = "and itcast and itheima and Python"

# 在字符串中查找 and
num = str1.find('and')   
print(num)  # 0
# 在字符串中查找 第二个 and
num1 = str1.find('and', num + 1)   # 11
# 在字符串中查找 第三个 and
num2 = str1.find('and', num1 + 1)  # 23
# 在字符串中查找 第四个 and
num3 = str1.find('and', num2 + 1)  # -1

字符串的替换方法 replace

返回:替换之后的完整的字符串,原来的字符串没有发生改变

字符串.replace(old_str, new_str, count)

count: 替换的次数,一般不写,默认是全部替换

str1 = "good good study"
# 将str1中的所有g --> G
str2 = str1.replace('g', 'G')
print('str1:',str1) # good good study
print('str2:',str2) # Good Good study

# 将str1中第一个good改为GOOD
str3 = str1.replace('good', 'GOOD', 1)
print('str3:',str3) # GOOD good study

# 将str1中第二个good改为GOOD
# 先将全部的good --> GOOD
str4 = str1.replace('good', 'GOOD')
# 再将第一个GOOD --> good
str4 = str4.replace('GOOD', 'good', 1)
print('str4:',str4) # good GOOD study

字符串的拆分 split

# 将字符串按照sep进行分割 也可以是元组、字典等
字符串.split(sep, max_split())

-sep:字符串按照什么进行拆分,默认是空白字符空格,换行\n,tab键\t
-max_split:分割次数,一般不写,全部分割
返回:将一个字符串拆分为多个,存到列表中
注意:如果sep不写,想要指定分割次数,则需要按照如下方式使用:

字符串.split(max_split=n) # n是次数
str1 = "hello world and itcast and itheima and Python"
# 将str1按照and字符进行拆分
result1 = str1.split('and')
print(result1) # ['hello world ', ' itcast ', ' itheima ', ' Python']
# 将str1按照and字符进行拆分,拆分一次
result2 = str1.split('and', 1)
print(result2) # ['hello world ', ' itcast and itheima and Python']
# 将str1按照空白字符进行拆分
result3 = str1.split()
print(result3) # ['hello','world','and','itcast','and','itheima','and',' Python']
# 将str1按照空白字符进行拆分,拆分一次
result4 = str1.split(maxsplit=1)
print(result4) # ['hello','world and itcast and itheima and Python']

字符串的连接 join

作用:将字符串插入到列表中每相邻的两个数据之间,组成一个新的字符串
列表中的数据使用逗号隔开
注意:列表中的数据必须是字符串

字符串.join(列表) # 括号中的内容主要是列表,可以是其他容器
list1 = ['hello world ', ' itcast ', ' itheima ', ' Python']

# 将 列表 中的字符串使用空格连接
str1 = ' '.join(list1)
print(str1) # hello world itcast itheima Python
# 将 列表 中的字符串使用and连接
str2 = 'and'.join(list1)
print(str2) # hello world and itcast and itheima and Python

列表

list,可以存储任意类型的数据,列表中的数据之间使用逗号隔开
定义一个空列表

list1 = []
list1 = list()

列表支持下标、切片和长度len()
列表的切片得到的是列表

查找数据下标 index

列表.index(数据, start, end)

找到返回第一次出现的下标,没有找到代码直接报错

查找 - 判断是否存在 in

数据 in 容器  # 如果存在返回True,不存在返回False

查找 - 统计出现的次数 count

列表.count()

添加数据

  • 尾部添加列表.append(数据),返回None(空),一般不再使用 变量 来保存返回的内容;想要查看添加后的列表,需要打印列表
  • 指定下标位置添加:列表.insert(下标,数据),如果指定下标位置本来有数据,原数据会后移,返回None(空)
  • 列表合并:列表1.extend(列表2),将列表2中的所有数据逐个添加到列表1的尾部,返回None
my_list = ['郭德纲','郭麒麟','孙越','于谦']
list = ['岳云鹏','烧饼']
my_list.extend(list)
print(my_list)  # ['郭德纲','郭麒麟','孙越','于谦','岳云鹏','烧饼']
my_list.append(list)
print(my_list)  # ['郭德纲','郭麒麟','孙越','于谦',['岳云鹏','烧饼']]

列表推导式:快速生成列表

变量名 = [生成数据的规则 for 变量 in xxx]  # 循环每执行一次,在列表中添加一个数据

list1 = []
for i in range(5):
	list1.append(i)
list1 = ['hello' for i in range(5)]
print(list1) # ['hello','hello','hello','hello','hello']
list2 = [i for i in range(5)]
print(list2) # [0,1,2,3,4]
list3 = [i for i in list1]
print(list3) # ['hello','hello','hello','hello','hello']

修改操作

修改列表中指定下标位置的数据

列表[下标] = 修改后的数据

如果指定的下标不存在,代码会报错
注意:字符串中字符不能使用下标修改

删除操作

在列表中删除中间的数据,后面的数据会向前移动

  • 根据下标删除:列表.pop(下标)

下标不写,默认删除最后一个数据
书写存在的下标,删除对应位置的数据
返回值:返回删除的数据

  • 根据数据值删除:列表.remove(数据值)

返回值:None
注意:如果要删除的数据不存在,会报错
如果列表中有多个该数据值,只能删除第一个,若想全部删除,则使用循环

  • 清空数据(一般不用):列表.clear()

列表的反转(倒置)

1、列表[::-1]:使用切片的方法会得到一个新列表,原列表没有发生改变
2、列表.reverse()直接修改原列表,返回None

列表的复制

将列表中的数据复制一份,给到一个新的列表
使用场景:有一个列表,需要修改操作列表中的数据,修改之后,需要和原数据进行对比,即原数据不能改

1、使用切片:变量 = 列表[:]
2、变量 = 列表.copy()

列表的排序

一般都是对数字进行排序

列表.sort()  # 按照升序排序,从小到大
列表.sort(reverse=True) # 按照降序排序,从大到小

列表的嵌套

列表中的内容还是列表
使用下标来确定获取的是什么类型的数据,然后确定可以继续进行什么操作

列表去重

方式1:遍历原列表中的数据判断在新列表中是否存在,如果存在不管,如果不存在,放入新列表中

my_list = [1,2,3,4,4,3,2,1,2,3,5]
new_list = []
for i in my_list:
	if i not in new_list:
		new_list.append(i)
print(new_list) # [1,2,3,4,5]

方式2:使用集合set)集合中不能有重复的数据,如果有重复的数据会自动去重
①使用set()类型转换 将列表转化为集合类型
②再使用list()类型转换 将集合转换为列表

缺点:不能保证数据在原列表中出现的顺序(一般不考虑这件事)

my_list = [1,2,3,4,4,3,2,1,2,3]
set(my_list)  # {1,2,3,4}
list(set(my_list)) # [1,2,3,4]

元组

元组:tuple,元组的特点和列表非常相似
1)元组中可以存放任意类型的数据
2)元组中可以存放任意多个数据
区别:
1)元组中的数据内容不能改变,列表中的可以改变
2)元组使用(),列表使用[]
应用:在函数的传参或者返回值中使用,保证数据不能被修改
定义:
1)使用类实例化:

# 定义空元组(不会使用)
my_tuple1 = tuple()
# 类型转换:
# (1)可以将列表转换为元组
my_tuple2 = tuple([1,2,3])
print(my_tuple2)  # (1,2,3)
# (2)转换字符串
my_tuple3 = tuple('hello')
print(my_tuple3)  # ('h','e','l','l','o')

2)直接使用()定义

my_tuple4 = (1,"小王",3.14,False)
my_tuple5 = (1,)

注意:定义只有一个数据的元组时,数据后边必须要有一个逗号

由于元组中的数据不能修改,所以只有查看的方法
1、在元组中可以使用下标和切片获取数据
2、存在index方法
3、存在count方法
4、可以使用in操作
以上方法的使用,和列表中一样

字典

字典 dict,由键(key)值(value)对组成(键表示数据的名字,值就是具体的数据)
一组键值对是一个数据,多个键值对之间使用逗号隔开

变量 = {key: value, key: value, ...}

一个字典中的键是唯一的,不能重复,值可以是任意数据
字典中的键一般都是字符串,可以是数字,不能是列表
dict()不能转列表、元组、字符串

定义

1、使用类实例化

my_dict = dict()

2、直接使用{}定义

# 空字典
my_dict = {}
# 非空字典
my_dict = {"name": "小明", "age": 18, "height": 1.71, "is_men":True, "like":["抽烟","喝酒","烫头"]}
len(my_dict)  # 5

增加和修改操作

字典[] = 数据值

若键已经存在,就是修改数据;若键不存在,就是添加数据

my_dict = {"name": "小明", "age": 18, "like":["抽烟","喝酒","烫头"]}
# 添加性别信息
my_dict['sex'] = '男'
print(my_dict) # my_dict = {"name": "小明", "age": 18, "like":["抽烟","喝酒","烫头"], 'sex':'男'}
# 修改年龄为19
my_dict['age'] = 19
print(my_dict) # my_dict = {"name": "小明", "age": 19, "like":["抽烟","喝酒","烫头"], 'sex':'男'}
# 添加一个爱好
my_dict['like'].append('学习')
print(my_dict) # my_dict = {'name': '小明', 'age': 19, 'like':['抽烟','喝酒','烫头','学习'], 'sex':'男'}

删除

删除指定键值对:del 字典[键]字典.pop('键')
清空:字典.clear()

my_dict = {"name": "小明", "age": 18, "like":["抽烟","喝酒","烫头"]}
# 删除抽烟的爱好
my_dict['like'].pop(0)
my_dict['like'].remove('抽烟')

查询-根据键获取对应的值

1、使用 字典['键']
如果键存在,返回键对应的数据值;
如果键不存在,会报错
2、使用 字典.get(键)
完整写法:字典.get(键, 数据值)
数据值一般不写,默认是None
如果键存在,返回键对应的数据值;
如果键不存在,返回括号中书写的数据值None

字典的遍历

  • 字典的键 进行遍历
for 变量 in 字典:
	print(变量) # 变量就是字典的key键

for 变量 in 字典.keys():  # 字典.keys()可以获取字典中所有的键
	print(变量)
  • 字典的值 进行遍历
for 变量 in 字典.values():  # 字典.values()可以获取字典中所有的值
	print(变量)
  • 字典的键值对 进行遍历
# 变量1 就是键, 变量2 是键对应的值
for 变量1, 变量2 in 字典.items(): # 字典.items()获取键值对
	print(变量1, 变量2)

容器总结

  • 字符串、列表、元组 都支持加法运算
  • 字符串、列表、元组 都支持乘一个数字
  • len()在容器中都可以使用
  • in关键字在容器中都可以使用,在字典中判断的是字典的键是否存在

函数

定义:

def 函数名():
	函数中的代码

调用:函数名()

文档注释

书写位置:在函数名的下方使用,三对双引号
作用:告诉别人函数是如何使用的、有什么作用
查看:调用时放到函数名上 使用 Ctrl+q(Windows),Ctrl+j(Mac)
Ctrl(cmd) B转到函数声明中查看(按Ctrl(cmd)鼠标左键点击)

函数的嵌套调用

在一个函数定义中调用另一个函数

  • 函数定义不会执行函数体中的代码
  • 函数调用会执行函数体中的代码
  • 函数体中代码执行结束会回到函数被调用的地方继续向下执行

函数的参数

形参 parameter:函数定义时的参数,起到占位作用,没有具体数据值
实参 argument:函数调用时,括号中的数据会传递给形参,是具体数据值
在定义函数时如果有形参,那么在调用的时候必须要传递实参值,且个数要对应

函数传参的方式

位置传参:在函数调用时,按照形参顺序,将实参值传递给形参
关键字传参:在函数调用时,指定将数据值给哪个形参,关键字必须是 函数的形参名
混合使用:关键字传参必须写在位置传参后面,不要给一个形参传递多个数据值

def func(a,b,c):
	print(f'a:{a}, b:{b}, c:{c}')

# 位置传参
func(1,2,3)  # a:1,b:2,c:3
# 关键字传参
func(a=2,b=3,c=1)  # a:2,b:3,c:1
#混合使用
func(1,3,c=5)   # a:1,b:3,c:5

缺省参数(默认参数)

定义方式:在函数定义时,给形参一个默认数据值,这个形参就变为缺省参数,注意:缺省参数的书写要放在普通参数后边
特点:缺省参数在函数调用时,可以传递实参值,也可以不传实参。如果传参使用的就是传递的实参值,若不传参,使用默认值

多值参数(可变参数、不定长参数)

在书写函数时,若不确定参数的具体个数时,可以使用不定长参数
不定长位置参数(不定长元组参数)

  • 书写:在普通参数前加一个*,这个参数就变为不定长位置参数
  • 特点:这个形参可以接受任意多个位置传参的数据
  • 数据类型:形参的类型是 元组
  • 注意:不定长位置参数要写在普通参数后面
  • 一般写法:不定长位置参数的名字为args,即*args

不定长关键字参数(不定长字典参数)

  • 书写:在普通参数前加两个*,这个参数就变为不定长关键字参数
  • 特点:这个形参可以接受任意多个关键字传参的数据
  • 数据类型:形参的类型是 字典
  • 注意:不定长关键字参数要写在所有参数后面
  • 一般写法:不定长关键字参数的名字为kwargs,即**kwargs

完整的参数顺序

def 函数名(普通函数,*args,缺省参数,**kwargs):
	pass
def func(*args, *kwargs):
	print(type(args),args)
	print(type(kwargs),kwargs)
	
func() # <class 'tuple' ()> <class 'dict' {}>
func(1,2,3) # 位置传参,数据都给args <class 'tuple' (1,2,3)> <class 'dict' {}>
func(a=1,b=2,c=3) # 关键字传参,数据都给kwargs <class 'tuple' ()> <class 'dict' {'a':1,'b':2,'c':3}>
func(1,2,3,a=4,b=5,c=6) # 混合传参 <class 'tuple' (1,2,3)> <class 'dict' {'a':4,'b':5,'c':6}>
my_list = [1,2,3,4]
my_dict = {'a':1, 'b':2, 'c':3, 'd':4}

def my_sum(*args,**kwargs):
	num = 0
	for i in args:
		num += i
	for j in kwargs.values():
		num += j
	print(num)

# 想要将列表中的数据 分别作为位置参数 需要对列表进行拆包操作 ⭐
my_sum(*my_list)   # my_sum(1,2,3,4)
# 想要将字典中的数据 作为关键字参数 需要使用 ** 对字典进行拆包 ⭐
my_sum(**my_dict)  # my_sum(a=1,b=2,c=3,d=4)

Print函数解析

print(self, *agrs, sep = '', end = '\n')
sep = '' # 多个位置参数之间的间隔符
end = '\n' # 每一个print函数结束,都会打印的内容换行 结束符
print(1,end = '\n')
print(2,end = '\n')
print(3)  # 1 2 3
print(1,2,3,4,5,6,sep = '_*_') # 1_*_2_*_3_*_4_*_5_*_6

函数的返回值

函数中得到的数据在后续代码中还要使用,这个时候就应该将这个数据作为返回值返回,以供后续使用
关键字:return(只能在函数中使用)
作用:
① 将数据值作为返回值返回
② 函数代码执行遇到return,会结束函数的执行

返回多个数据值

将多个数据值组成容器进行返回,一般是元组

def calc(a,b):
	num = a + b
	num1 = a - b
	return num, num1

# 写法一
result = calc(10, 5)
print(result,result[0],result[1]) # (15,5) 15 5
# 写法二:拆包
x, y = calc(10,5)
print(x,y)

变量的引用

在定义变量时,变量 = 数据值,python解释器会在内存中开辟两块空间;
变量和数据都有自己的空间;
本质是将数据的地址保存到变量对应的内存中;
变量中存储数据地址的行为就是引用(变量引用了数据的地址),存储的地址为引用地址;
可使用id()来获取变量中的引用地址(数据的地址),如果两个变量的id()获取的引用地址一样,即两个变量引用了同一个数据;
只有 赋值运算符= 可以改变变量的引用
可变类型做参数,在函数内部,如果不使用 = 直接修改形参的引用,对形参进行的数据修改会同步到实参中
python中数据的传递,都是传递的引用
列表的+=操作,本质是extend操作

可变类型和不可变类型

数据类型:int float bool str list tuple dict set
可变不可变指:数据所在的内存是否允许修改,允许修改就是可变类型,不允许修改就是不可变类型
可变类型:list dict set
不可变类型:int float bool str tuple

交换两个变量的值

a = 10
b = 20
# 1、常规方法,引入第三个变量
c = a  # 将变量a的值保存起来 给c
a = b  # 将变量b的值给a
b = c  # 将变量c(10)的值给b

# 2、不使用第三个变量,使用数学中的方法
a = a + b  # 30
b = a - b  # b->10
a = a - b  # a->20

# 3、python特有 ⭐
a, b = b, a
print(a,b)

组包和拆包

组包(pack):将多个数据值使用逗号连接,最终组成元组
拆包(unpack):将容器中的数据值使用多个变量分别保存的过程,注意:变量个数和容器中数据的个数要保持一致,对字典拆包得到字典的键

a = 10
b = 20
# 组包
c = a,b 
print(type(c) , c) # <class 'tuple', (10,20)>

# 拆包
a,b = c
print(a,b) # 10 20

局部变量和全局变量

局部变量

定义位置:在函数内部定义的变量
特点:

  • 只能在当前函数内部使用,不能在其他函数和函数外部使用
  • 在不同函数中,可以定义名字相同的局部变量,两者之间无影响
  • 生存周期(生命周期,作用范围):在函数被调用时,局部变量被创建,函数调用结束,局部变量的值被销毁,不能使用。因此函数中的局部变量的值如果想在函数外部使用,需要使用return将这个值进行返回
  • 形参属于局部变量

全局变量

定义位置:在函数外部定义的变量
特点:

  • 可以在任何函数中读取全局变量的值
  • 如果在函数中存在和全局变量相同名字的局部变量,在函数中使用的是局部变量的值(就近)
  • 在函数内部想要修改全局变量的引用,需要添加global关键字,对变量进行声明为全局变量
  • 生命周期:代码执行时被创建,代码执行结束被销毁
g_num = 10
def func1():
	print(g_num)  

def func2():
	g_num= 20
	print(g_num)  

def func3():
	global g_num# 这个函数中使用的num都是全局变量,写在函数第一行
	g_num= 30  # 修改了全局变量
	print(g_num)  

func1()  # 10
func2()  # 20
func3()  # 30
func1()  # 30

匿名函数

  • lambda关键字能创建小型匿名函数,只能书写一行代码
  • 这种函数省略了用def声明函数的标准步骤
  • 能接收任何数量的参数,返回值不需要return,但只能返回一个表达式的值
  • lambda 参数: 一行代码 # 这一行代码称为表达式
  • 一般不需要调用,作为函数的参数使用

1、在定义时,将匿名函数的引用保存到一个变量中

变量 = lambda 参数: 一行代码

2、使用变量进行调用

变量()
# 1、无参无返回值
def func1():
	print('hello world')

func1()  # hello world
func11 = lambda: print('hello lambda')
func11() # hello lambda

# 2、无参有返回值
def func2():
	return 10

print(func2())  # 10
func22 = lambda:10
print(func22())  # 10

# 3、有参无返回值
def func3(a,b):
	print(a + b)

func3(1,2)  # 3
func33 = lambda a,b:print(a + b)
func33(10,20)  # 30

# 4、有参有返回值
def func4(a,b):
	return a + b

print(func4(1,2))   # 3
func44 = lambda a,b:a+b
print(func44(10,20))  # 30

# 定义一个匿名函数,参数为字典,返回字典中键为age的值
# 参数只是一个占位的作用,定义时没有具体数据值,形参的值在调用的时候进行传递,此时形参才有数据值。形参的类型由实参决定,在定义函数时,参数(x)只是一个符号,写什么都可以,想让其是字典类型,只需保证实参是字典即可
func1 = lambda x: x.get('age')
func2 = lambda x: x['age']

my_dict = {'name':'张三','age':18}
print(func1(my_dict))  # 18
print(func2(my_dict))  # 18

匿名函数作为函数的参数-列表中的字典排序

列表的排序,默认是对列表中的数据进行比大小,可以对数字类型和字符串进行比大小
对字典来说,需要使用sort()中的key参数,来指定字典比大小的方法
key参数:需要传递一个函数,一般是匿名函数,字典的排序,要指定根据字典的什么键进行排序,只需要使用匿名函数返回字典的这个键对应的值即可

列表.sort(key = lambda x: x['键'])

匿名函数中的参数是列表中的数据,在sort函数内部,会调用key这个函数(将列表中每个数据作为实参传递给形参),从列表中获取函数的返回值,对返回值比大小

user_list = [
{'name':'zhangsan','age':18},
{'name':'lisi','age':17},
{'name':'wangwu','age':19}
]
 
user_list.sort(key = lambda x: x['age']) # 升序
user_list.sort(key = lambda x: x['age'], reverse = True) # 降序

字符串比大小

字符比大小:字符对应的ASCII 码值
A < Z < z < a
ord(字符) 获取字符对应的ASCII 码值
chr(ASCII 值) 获取对应的字符
字符串比大小:对应下标位置字符的大小,直到比出大小,若全部比完还没有比出大小,就是相等

面向对象

面向过程:关注的是 具体步骤的实现,所有功能都自己书写;定义一个函数,最终按照顺序调用函数
面向对象:关注的是 结果,对象能做这件事;找一个对象,让其去做

三大特征

  • 封装:根据职责将 属性和方法 封装到一个抽象的 类 中
  • 继承:实现代码的重用,相同的代码不需要重复编写
  • 多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码灵活性

类和对象

类:抽象的概念,对多个特征(属性)和行为(方法)相同或相似事物的统称,泛指(指代多个),不能直接使用。负责创建对象
对象:具体存在的一个事物,特指(指代一个)

组成:

  • 类名:大驼峰命名法-每个单词的首字母大写
  • 属性:事物的特征,即有什么,一般是名词
  • 方法:事物的行为,即做什么,一般是动词

类的抽象(设计):找到类的类名、属性和方法
面向对象代码步骤:

  • 定义类,在定义类之前先设计类
  • 创建对象,使用第一步定义的类创建对象
  • 通过对象调用方法

面向对象基本代码的书写

1、定义类
关键字:class
方法的本质是在类中定义的函数,只不过第一个参数是self

class 类名:
	def 方法名(self):
		pass

2、创建对象

一个类可以创建多个对象,只要出现 类名() 就是创建一个对象,每个对象的地址不一样

类名()  # 创建一个对象,这个对象不能再后续使用
# 创建的对象想要在后续代码继续使用,需要使用一个变量,将这个对象保存起来
变量 = 类名() # 这个变量中保存的是对象的地址,一般称这个变量为对象

3、调用方法

对象.方法名()

简单案例

# 1、定义类
class Cat:
	def eat(self):
		print("小猫爱吃鱼...")
	def drink(self):
		print("小猫要喝水...")

# 2、创建对象
blue_cat = Cat()

# 3、通过对象调用类中的方法
blue_cat.eat()
blue_cat.drink()

self的说明

1、从函数语法上讲,self是形参,可以是任意的变量名
2、self是普通的形参,但是在调用的时候没有传递实参值,原因是python解释器在执行代码时,自动将调用这个方法的对象 传递给了self,即self的本质是对象
3、self是函数中的局部变量,直接创建的对象是全局变量

对象的属性操作

  • 添加属性
对象.属性名 = 属性值

类内部添加:在内部方法中,self是对象,在类中添加属性一般写在__init__

self.属性名 = 属性值

类外部添加:一般不使用

对象.属性名 = 属性值
  • 获取属性
对象.属性名

类内部:self.属性名
类外部:对象.属性名 一般很少使用

# 1、定义类
class Cat:
	def eat(self):
		print(f"小猫 {self.name} 爱吃鱼...")

# 2、创建对象
blue_cat = Cat()
# 类外部添加属性
blue_cat.name = '蓝猫'

# 3、通过对象调用类中的方法
blue_cat.eat()

魔法方法

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}")

# 创建对象
blue_cat = Cat('蓝猫',2)
blue_cat.show_info()  # 小猫的姓名是:蓝猫,年龄是:2
black_cat = Cat('黑猫',3)
black_cat.show_info()  # 小猫的姓名是:黑猫,年龄是:3

__str__方法⭐

  • 使用 print(对象) 打印对象时 会自动调用
  • 一般书写对象的 属性信息,即打印对象时想要查看什么信息,在这个方法中进行定义
  • 若类中没有定义__str__方法,print(对象) ,默认输出对象的引用地址
  • 必须返回 一个字符串
class Cat:
	# 定义添加属性的方法
	def __init__(self,name,age):
		self.name = name  # 给对象添加 name 属性
		self.age = age        # 给对象添加 age 属性
	
	# 输出属性信息
	def __str__(self):
		return f"小猫的姓名是:{self.name},年龄是:{self.age}"

# 创建对象
blue_cat = Cat('蓝猫',2)
print(blue_cat) # 小猫的姓名是:蓝猫,年龄是:2
black_cat = Cat('黑猫',3)
print(black_cat) # 小猫的姓名是:黑猫,年龄是:3

__del__方法 [了解]
对象被删除销毁时,自动调用
调用场景:

  • 程序代码运行结束,所有对象都被销毁
  • 直接使用 del 删除对象(如果对象有多个名字,即多个对象引用一个对象,需要把所有的对象都删除才行)
class Demo:
	def __init__(self, name):
		print(f'我是__init__方法,我被调用了')
	
	def __del__(self):
		print(f'{self.name} 没了')

a = Demo('a')
b = Demo('b')
del a # 删除销毁对象
print('代码运行结束')

# 我是__init__方法,我被调用了
# 我是__init__方法,我被调用了
# a 没了
# 代码运行结束
# b 没了

案例1

在这里插入图片描述

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} 跑步5km,体重减少了')
		# 修改属性
		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)

案例2

在这里插入图片描述

class Student:
	# 添加属性
	def __init__(self, name, age):
		self.name = name
		self.age = age
	
	def eat(self):
		print(f'{self.name} 要吃饭')
	
	def sleep(self):
		print(f'{self.name} 要睡觉')
	
	def year(self):
		self.age += 1
	
	def __str__(self):
		return f'姓名:{self.name},年龄:{self.age}岁'

# 创建对象
xiaoming = Student('小明',18)
xiaohong = Student('小红',17)
print(xiaoming)
print(xiaohong)
xiaoming.eat()
xiaoming.sleep()
xiaoming.year()

案例3

在这里插入图片描述
类名:电脑类Computer
属性:品牌 brand,价格 price
方法:播放电影 play_movie() __init__()

class Computer:
	def __init__(self, brand, price):
		self.brand = brand
		self.price = price
	
	def play_movie(self,movie):
		print(f'{self.brand} 播放电影 {movie}')

# 创建对象
mi = Computer('小米', 4999)
mac = Computer('Mac', 16999)
mi.play_movie('葫芦娃')
mac.play_movie('变形金刚')

案例4

需求:
在这里插入图片描述
在这里插入图片描述

设计类

类名:房子类 House
属性:户型 name,面积 total_area,家具名称列表 item_list = [],剩余面积 free_area = total_area
方法:__init__()__str__()
添加家具方法 add_item()

def add_item(self, item)#  item家具对象
	先判断房子的剩余面积和总面积的关系
			修改房子的剩余面积
			修改房子的家具名称列表

类名:家具类 HouseItem
属性:名字 name,占地面积 area
方法:__init__()__str__()

class HouseItem:
	"""添加属性名的方法"""
	def __init__(self,name,area):
		self.name = name
		self.area = area
		
	def __str__(self):
		return f'家具名:{self.name},占地面积:{self.area}平米'

class House:
	def __init__(self, name, area):
		self.name = name  # 户型
		self.total_area = area  # 总面积
		self.free_area = area   # 剩余面积
		self.item_list = []     # 家具名称列表
	
	def __str__(self):
		return f'户型{self.name},总面积:{self.total_area},剩余面积:{self.free_area},家具名称列表:{self.item_list}'
	
	def add_item(self, item):
		# self表示 房子对象, item表示 家具对象
		if self.free_area > item.area:
			# 添加家具 -- > 向列表中添加数据
			self.item_list.append(item.name)
			# 修改剩余面积
			self.free_area -= item.area
			print(f'{item.name}添加成功')
		else:
			print('剩余面积不足')
	
# 创建对象
bed = HouseItem('席梦思',4)
chest = HouseItem('衣柜',2)
table = HouseItem('餐桌',1.5)
print(bed)
print(chest)
print(table)

# 创建房子对象
house = House('三室一厅', 100)
print(house)
# 添加家具
house.add_item(bed)
print(house)

案例5

在这里插入图片描述
类名:登陆页面LoginPage
属性:用户名 username,密码 password,验证码 code,登录按钮 button
方法:登录 login()__init__()

class LoginPage:
	def __init__(self, username, password, code):
		self.username = username
		self.password = password
		self.code = code
		self.btn = '登录'
	
	def login(self):
		print(f'1、输入用户名:{self.username}')
		print(f'2、输入密码:{self.password}')
		print(f'3、输入验证码:{self.code}')
		print(f'4、点击按钮:{self.btn}')
	
user = LoginPage('小明','123456','8888')
user.login()

私有和公有

在python中定义的属性和方法,可以添加访问控制权限
访问控制权限分为两种:公有权限和私有权限
公有权限:

  • 直接书写的属性和方法
  • 共有的属性和方法,可以在任意地方访问和使用

私有权限:

  • 在类内部,属性名或方法名,前边加两个 下划线 ,这个属性或方法就变为私有的
  • 只能在当前类内部使用
  • 本质:当python解释器执行代码时,发现属性名或方法名前有两个 下划线,会将这个名字重命名,会在这个名字前边加上 _类名 前缀,即self._age ⇒ self._Person_age

什么时候定义私有

  • 某个属性或方法,不想在类外部被访问和使用
  • 测试中,一般不怎么使用,直接公有即可
  • 开发中,会根据需求文档,确定什么作为私有

若想在类外部操作私有属性,方法是:在类内部定义公有的方法,通过这个方法去操作

class Person:
	def __init__(self,name,age):
		self.name = name
		# 私有的本质:当python解释器执行代码时,发现属性名或方法名前有两个 下划线,会将这个名字重命名,会在这个名字前边加上 _类名前缀,即`self._age ⇒ self._Person_age`
		self.__age = age # 年龄,将其定义为私有属性
	
	def __str__(self):
		return f'名字:{self.name},年龄:{self.age}'

xm = Person('小明',18)
print(xm) # 名字:小明,年龄:18
# 在类外部直接访问 age属性 
print(xm.__age) # 会报错,在类外部不能直接使用私有属性
print(xm.__dict__) # {'name':'小明','age':18}
# 直接修改 age 属性
xm.__age = 20 # 这个不是修改私有属性,是添加了一个共有属性__age
print(xm.__dict__) # {'name':'小明','age':18,'__age':20}
print(xm) # 名字:小明,年龄:18
print(xm._Person__age) # 18 能用但是不要用 
xm._Person__age = 19
print(xm) # 名字:小明,年龄:19

# 对象.__dict__ 魔法属性,可以将对象具有的属性组成字典返回

继承

子类可以直接使用父类的公有属性和方法
语法:

class 子类名(父类名):
	pass

class A: # 没有写父类,但也有父类 object,object类是python中最顶级的类
	pass
	
class B(A):
	pass

A类 称为父类(基类)
B类 称为子类(派生类)
单继承:一个类只继承一个父类

# 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() # 调用父类中的方法
xtq.eat()  # 调用父类的父类中的方法

重写

在子类中定义了和父类中名字相同的方法。
原因:父类中的方法,不能满足子类对象的需求
特点:调用子类自己的方法,不再调用父类中的方法
方式:

  • 覆盖:父类中功能完全抛弃–直接在子类中 定义和父类中名字相同的方法
class Dog:
	def bark(self):
		print('汪汪汪叫...')

class XTQ(Dog):
	def bark(self):
		print('嗷嗷嗷叫...')

xtq = XTQ()
xtq.bark()  # 嗷嗷嗷叫...
  • 扩展:父类中功能还调用,只是添加一些新的功能(使用较多)----直接在子类中 定义和父类中名字相同的方法;在合适的地方调用 父类中的方法 super().方法();书写添加新的功能
class Dog:
	def bark(self):
		print('汪汪汪叫...')

class XTQ(Dog):
	def bark(self):
		print('嗷嗷嗷叫...')
		# 调用父类中的方法
		super().bark()

xtq = XTQ()
xtq.bark()  # 嗷嗷嗷叫...

多态[了解]

不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
可以增加代码的灵活度
哪个对象调用方法,就去自己的类中去查找这个方法,找不到去父类中找

属性和方法

python中一切皆对象,即使用class定义的类 也是一个对象

对象的划分

实例对象(实例)

通过 类名() 创建的对象,称为实例对象,简称实例
创建对象的过程 叫做 类的实例化
我们平时所说的对象就是指 实例对象(实例)
每个实例对象都有自己的内存空间,在自己的内存空间中保存自己的属性

创建对象的动作叫做 实例化
对象的属性 叫做 实例属性
对象调用的方法 叫做 实例方法

类对象(类)

类对象 就是 类,或可以认为是 类名
类对象是python解释器在执行代码过程中创建的
作用:①使用类对象创建实例 类名()
②有自己的内存空间,可以保存一些属性值信息(类属性)
在一个代码中,一个类只有一份内存空间

属性的划分

实例属性

概念:实例对象 具有的属性
定义:在__init__方法中,使用self.属性名 = 属性值
使用:在方法中使用 self.属性名 来获取(调用)
内存:在每个实例中都存在一份
使用场景:

  • 基本上99%都是实例属性,即通过self去定义
  • 找多个对象来判断这个值是不是都是一样的,如果是一样的,同时变化,则一般定义为 类属性,否则定义为 实例属性
类属性

概念:类对象 具有的属性
定义:在类内部,方法外部,直接定义的变量
使用:类对象.属性名 = 属性值 or 类名.属性名 = 属性值
获取:类对象.属性名 类名.属性名
内存:只有类对象中存在一份

可以使用 实例对象.类属性名 获取类属性的值:实例对象属性的查找顺序是先在实例属性中找,找到直接使用;没有找到会去类属性中找,找到了可以使用,没有找到会报错

练习

在这里插入图片描述

class Dog:
	# 定义类属性
	count = 0
	# 定义实例属性
	def __init__(self, name):
		self.name = name  # 实例属性
		# 每创建一个对象 就会调用init方法,就将个数加1的操作写在init方法
		Dog.count += 1

# 在类外部
# 打印输出目前创建几个对象
print(Dog.count)  # 0
# 创建一个对象
dog1 = Dog('小花')
# 打印输出目前创建几个对象
print(Dog.count)  # 1
dog2 = Dog   # 1 不是创建对象,个数不变
dog3 = dog1  # 1 不是创建对象,个数不变

dog4 = = Dog('小黄')
print(Dog.count)  # 2 创建了一个对象,个数+1

dog5 = = Dog('小白')
print(Dog.count)  # 3 创建了一个对象,个数+1

# 使用 实例对象.类属性名 获取类属性值
print(dog1.count)  # 3
print(dog4.count)  # 3
print(dog5.count)  # 3

方法的划分

方法:使用 def 关键字定义在类中的函数

实例方法(最常用)

定义:在类中直接定义的方法

class Demo:
	def func(self):  # 参数一般写作self 表示的是实例对象
		pass

定义时机(什么时候用):如果在方法中需要使用实例属性,即需要使用self
调用:对象.方法名() 不需要给self传参

类方法(会用)

定义:在方法名字的上方书写 @classmethod装饰器

class Demo:
	@classmethod
	def func(cls):  # 参数一般写作cls 表示的是类对象(类名)class
		pass

定义时机(什么时候用):前提–方法中不需要使用实例属性(即self);用到了类属性,可以将这个方法定义为类方法,也可以定义为实例方法
调用:
① 通过类对象调用 类名.方法名() 不需要给cls传参,python解释器自动传递
② 通过实例对象调用 实例.方法名() 不需要给cls传参,python解释器自动传递

静态方法(基本不用)

定义:在方法名上方书写 @staticmethod装饰器

class Demo:
	@staticmethod
	def func():  # 一般没有参数
		pass

定义时机(什么时候用):前提–方法中不需要使用实例属性(即self);也不使用类属性,可以将这个方法定义为静态方法
调用:
① 通过类对象调用 类名.方法名()
② 通过实例对象调用 实例.方法名()

练习

在这里插入图片描述

import random

class Game:
	# 定义类属性
	top_score = 0
	# 获取最高分玩家的名字
	top_score_player = ''
	
	def __init__(self, name):
		self.name = name

	@staticmethod
	def show_help(self):
		print('这是游戏的帮助信息')
	
	@classmethod
	def show_top_score(cls):
		print(f'游戏的最高分为:玩家:{Game.top_score_player} 得分:{cls.top_score}')

	def start_game(self):
		print(f'{self.name}开始一局游戏,游戏中...',end=' ')
		# 本次游戏得分
		score = random.randint(10,100)
		# 判断本次得分与最高分之间的关系
		if score > Game.top_score:
			# 修改最高分
			Game.top_score = score
			# 修改最高分玩家名字
			Game.top_score_player = self.name
		print(f'本次游戏得分为:{score}')

xw = Game('小王')
xw.start_game()
xw.show_top_score()
xw.start_game()
xw.show_top_score()
xl = Game('小李')
xl.start_game()
xl.show_top_score()
xh = Game('小红')
xh.start_game()
xh.show_top_score()

哈希(hash):是一个算法,可以对数据产生一个唯一的值(指纹)
is 关键字 用来判断两个对象是不是同一对象,即两个对象的引用是否相同

a is b  ===> id(a) == id(b)

is== 的区别?
== 只判断数据值是否相同
is 判断引用是否相同

文件

文件的概念

  • 计算机的 文件,就是存储在某种 长期存储设备 上的一段 数据
  • 长期存储设备:硬盘、U盘、移动硬盘、光盘…
  • 作用:将数据长期保存下来,在需要的时候使用
  • 存储方式:二进制

可根据 文件中的二进制内容,能否使用记事本软件 将其转换为文字,将文件分为两种:文本文件和二进制文件
文本文件:能够使用记事本软件打开
txtmdpyhtmlcssjsjson
二进制文件:不能使用记事本软件打开
exeMP3MP4jpgpng

文件操作

步骤:

  • 1、打开文件
  • 2、读或写文件
  • 3、关闭文件

打开文件

将文件从磁盘(硬盘)中 读取到内存
语法:

open(file, mode = 'r', encoding = None)

参数:

  • file要打开的文件,类型是字符串,文件的路径可以是相对路径,也可以是绝对路径(从根目录开始),建议使用相对路径(相对于当前代码文件所在的路径,./ …/)
  • mode:默认参数(缺省参数),表示打开文件的方式r(read)只读打开;w(write)只写打开;a(append)追加打开,在文件的末尾写入内容
  • encoding编码方式gbk:将一个汉字转换为2个字节二进制;utf-8:将一个汉字转换为3个字节二进制(常用)
  • 返回值:返回 文件对象

在这里插入图片描述

读或写文件

写文件

向文件中写入指定内容
前提:文件的打开方式modew 或者 a

文件对象.write('写入文件的内容')

返回值:写入文件的字符数,一般不关注
注意 w 方式打开文件:

  • 文件不存在,会直接创建文件
  • 文件存在,会覆盖原文件(将原文件内容清空)
f = open('a.txt', 'w', enconding = 'utf-8')
f.write('好好学习\n')
f.write('天天向上')
f.close()
读文件

将文件中的内容读取出来
前提:文件的打开方式moder
如果文件不存在,代码会报错

文件对象.read(n)  # n表示读取多个个字符,一般不写 表示读取全部内容

返回值:读取到的文件内容,类型为字符串

f = open('a.txt', 'r', enconding = 'utf-8')
buf = f.read()
print(buf) # 目前只是打印读取的内容
f.close()

关闭文件

将文件占用的资源进行清理,同时会保存文件,文件关闭之后,这个文件对象就不能再使用了

文件对象.close()
使用with open打开文件

好处:不用自己书写关闭文件的代码,会自动进行关闭

with open(file, mode, enconding = 'utf-8') as 变量:
	# 在缩进中去读或写文件
with open('a.txt', 'a', enconding = 'utf-8') as f:
	f.write('good good study')

a 方式打开文件:
文件不存在会创建文件;
文件存在,会在文件的末尾写入内容

按行读取文件内容

一次读取一行内容

文件对象.readline()

read()readline() 读到文件末尾,返回一个空字符串,即长度为0

with open('a.txt', enconding = 'utf-8') as f:
	for i in f:   # 按行读取,读到文件末尾结束
		print(i, end = '')

with open('a.txt', enconding = 'utf-8') as f:
	while True:
		buf = f.readline()
		if len(buf) == 0:
			break
		else:
			print(buf, end = '')

# 在容器中,容器为空,即容器中数据个数为0,表示False,其余情况都是True
with open('a.txt', enconding = '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的全称是 JavaScript Object Notation,是JavaScript对象表示法,它是一种基于文本,独立于语言(不是某个语言特有的,每种编程语言都可以使用)的轻量级(相同数据 相较于其他格式,占用大小 小)数据交换格式(后端程序员给前端的数据:html xml json)。
特点:

  • 是纯文本
  • 具有良好的自我描述性,便于阅读和编写
  • 具有清晰的层级结构
  • 有效提升网络传输效率
json文件的语法

后缀 .json
主要数据类型:对象{} 类似python种的字典)和数组[],类似python种的列表),对象和数组可以互相嵌套
一个json文件是一个 对象或数组,即json文件的最外层要么是一个 对象{},要么是一个 数组[]
json中的对象键值对组成,每个数据之间使用 逗号隔开,但是最后一个数据后边不要写逗号
json中的字符串必须使用双引号
json中的其他数据类型:

  • 数字类型 ------>int float
  • 字符串 string ------>str
  • 布尔类型 true, fasle ------>True,False
  • 空类型 null ------>None
json文件的书写

在这里插入图片描述
info.json

{
 "name": "小明",
 "age": 18,
 "isMen": true,
 "like": [
     "听歌",
     "游戏",
     "购物",
     "吃饭",
     "睡觉",
     "打豆豆"
 ],
 "adress": {
   "country": "中国",
   "city": "上海"
 }
}
读取json文件 ⭐⭐

1、导包 import json
2、读(默认)r 打开文件
3、读文件 json.load(文件对象)
返回:字典(文件中是对象)或列表(文件中是数组)

import json

# 读打开文件
with open('info.json', encoding = 'utf-8') as f:
	# 读文件
	result = json.load(f)
	print(type(result))  # <class 'dict'>
	# 姓名
	print(result.get('name'))  
	# 年龄
	print(result.get('age')) 
	# 城市
	print(result.get('address').get('city'))  

在这里插入图片描述

[
  {
 "name": "小明",
 "age": 18,
 "isMen": true,
 "like": [
     "听歌",
     "游戏",
     "购物",
     "吃饭",
     "睡觉",
     "打豆豆"
 ],
 "adress": {
   "country": "中国",
   "city": "上海"
 }
},
{
 "name": "小红",
 "age": 17,
 "isMen": false,
 "like": [
     "听歌",
     "购物",
     "学习"
 ],
 "adress": {
   "country": "中国",
   "city": "北京"
 }
}
]

import json

# 读打开文件
with open('info.json', encoding = 'utf-8') as f:
	# 读文件
	info_list = json.load(f)  # [{},{}]
	for info in info_list:  # info {}
		print(info.get('name'),info.get('age'),info.get('address').get('city'))   
json的写入
文件对象.write(字符串) 

不能直接将python的列表和字典作为参数传递
步骤:

  • 1、导包 import json
  • 2、写w 方式打开文件
  • 3、写入 json.dump(python中的数据类型, 文件对象)
import json

my_list = [
	('admin','123456','登录成功'),
	('root','123456','登录失败'),
	('admin','123321','登录失败')
]

with open('info.json','w',encoding='utf-8') as f:
	json.dump(my_list, f)  # 中文以ASCII显示
	json.dump(my_list, f, ensure_ascii = False)  # 直接显示中文
	# 显示缩进indent = 2/4
	json.dump(my_list, f, ensure_ascii = False, indent = 2)

在这里插入图片描述

异常

程序在运行时,如果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)
except 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
	print(f'a:{}')
except ValueError:  # 只能捕获ValueError类型及其子类异常
	print('发生了异常,请输入正确的数字...')
except ZeroDivisionError:
	print('除数不能为0')

异常捕获的完整版本

完整版本中的内容,不是说每一次都要全部书写,根据自己的需要,选择其中的进行使用
Exception是常见异常类的父类,这里书写Exception可以捕获常见的所有异常,as 变量,这个变量是一个异常类的对象,print(变量)可以打印异常信息

try:
	可能发生异常的代码
except 异常类型1:   
	发生了异常1执行的代码
except Exception as 变量:
	发生其他类型的异常,执行的代码
else:
	没有发生异常会执行的代码
finally:
	不管有没有发生异常,都会执行的代码

常用

try:
	可能发生异常的代码
except Exception as 变量:
	发生异常执行的代码
try:
	# 1.获取用户从键盘输入的数据
	num = input('请输入数字:')
	# 2.转换数据类型为整型
	num = int(num)
	# 3.输出转换之后的数据内容
	print(num)
	a = 10 / num
	print(f'a:{}')
except Exception as e:  
	print(f'错误信息为:{e}')
else:
	print('没有发生异常,执行的代码...')
finally:
	print('不管有没有发生异常,都会执行的代码')

异常的传递[了解]

异常传递:在函数嵌套调用过程中,被调用的函数发生了异常,如果没有捕获,会将这个异常向外层传递;如果传到最外层还没有捕获,会报错

模块和包

语法:

# 方式一:
import 模块名
# 使用模块中的内容
模块名.工具名

import random
random.randint(a,b)

# 方式二 快捷方式导包:
from 模块名 import 工具名
# 使用
工具名

from random import randint
randint(a,b)


# 方式三 基本不用:
from 模块名 import *
# 使用
工具名

from random import *
randint(a,b)

对于导入的模块和工具可以使用 as 关键字给其起别名
注意:如果起别名,原来的名字就不能用了,只能使用别名

模块的查找顺序

在导入模块时,会先在当前目录中找模块,如果找到直接使用;如果没找到会去系统的目录中找,若找到直接使用,没有找到会报错
注意:定义代码文件时,文件名不能和导入的模块名相同

__name__的作用

每个代码文件都是一个模块
在导入模块时,会执行模块中的代码
__name__变量,是python解释器自动维护的变量
__name__变量,如果代码直接运行,值是__main__
__name__变量,如果代码是被导入运行,值是 模块名(代码文件名)
在代码文件中,在被导入时不想执行的代码,可以写在
if __name__ == ' __main__':代码缩进中
在这里插入图片描述
在这里插入图片描述
代码练习
在这里插入图片描述
tools.py

def func():
	print('我是 tools 模块中的 func 函数')

class Dog:
	def __init__(self, name, age):
		self.name = name
		self.age = age

	def play(self):
		print(f'{self.name}在快乐的玩耍')

test.py

import tools

# 调用函数
tools.func()

# 创建对象
dog = tools.Dog('小白',2)
dog.play()

包(package)

在python中,包是一个目录,存在一个文件__init__.py中,可以是空的
将功能相近或相似的代码放在一起
使用时,无需刻意区分是包还是模块,使用方式相同
random 模块(单个代码文件)
json 包(目录)
使用:
1、import 包名
2、alt+回车 快捷导入

UnitTest框架

框架(framework):为解决一类事情的功能集合

什么是UnitTest框架?

UnitTest框架:是python自带的一个单元测试框架,用它来做单元测试。
自带的框架(官方):不需要额外安装,只要安装了python就可以使用 randomjsonostime
第三方框架:想要使用 需先安装 seleniumappiumrequests
单元测试框架:主要用来做单元测试,一般单元测试由开发做
作用:自动化脚本(用例代码)执行框架,即 使用UnitTest框架来管理 运行 多个测试用例

为什么使用UnitTest框架?

  • ① 能够组织多个用例去执行
  • ② 提供丰富的断言方法(让程序代码代替人工自动的判断预期结果和实际结果是否相符)
  • ③ 能够生成测试报告

UnitTest框架核心要素

  • 1、TestCase(测试用例):最核心模块 是UnitTest框架的组成部分,不是手工和自动化中所说的用例(Test Case)。主要作用:每个TestCase(测试用例)都是一个代码文件,在这个代码文件中书写真正的用例代码
  • 2、TestSuite(测试套件):用来管理 组装 多个 TestCase(测试用例)
  • 3、TestRunner(测试执行):执行 TestSuite(测试套件)
  • 4、TestLoader(测试加载):对 TestSuite(测试套件)功能的补充,管理 组装 多个 TestCase(测试用例)
  • 5、Fixture(测试夹具):书写在 TestCase(测试用例)代码中,是一种代码结构,在每个方法执行前后都会执行的内容。每个用例中重复的代码就可以写于此,只写一遍,但每次用例方法的执行,都会执行 Fixture 中的代码

TestCase(测试用例)

是一个代码文件,在这个代码文件中书写真正的用例代码
代码文件名必须按照标识符规则(字母、数字、下划线,不能以数字开头)来写(可以将代码的作用在文件的开头使用注释说明)
步骤:

  • 1、导包 import unittest
  • 2、自定义测试类,需要继承UnitTest模块中的TestCase类:class 类名(unittest.TestCase)
  • 3、在测试类中书写测试方法,即用例代码,必须test_开头
  • 4、执行用例(方法):①将光标放在 类名的后边 运行,会执行类中的所有测试方法 ②将光标放在 方法名的后边 运行,只执行当前的方法
    在这里插入图片描述
    问题1:代码文件的命名不规范
    问题2:代码运行没有结果
    右键运行没有unittest for的提示,出现的问题
    解决方案:
    方案1、重新新建一个代码文件,将写好的代码复制进去
    方案2、删除已有的运行方式
    在这里插入图片描述
    问题3:没有找到用例
    测试方法名不是以test_开头,或者单词写错

TestSuite & TestRunner

步骤:

  • 1、导包 import unittest
  • 2、实例化(创建对象)套件对象suite = unittest.TestSuite()
  • 3、使用 套件对象 添加 用例方法 suite.addTest(unittest.makeSuite(测试类名))
  • 4、实例化 运行对象 runner = unittest.TextTestRunner()
  • 5、使用运行对象 去执行 套件对象 runner.run(suite)
import unittest 
from testcase1 import TestDemo1
from 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)
import unittest 
from testcase1 import TestDemo1
from testcase2 import TestDemo2

# 2、实例化(创建对象)套件对象
suite = unittest.TestSuite()
# 3、使用套件对象添加用例方法
# 方式二:将一个测试类中的所有方法进行添加
# 套件对象.addTest(unittest.makeSuite(测试类名))
suite.addTest(unittest.makeSuite(TestDemo1))
suite.addTest(unittest.makeSuite(TestDemo2))
# 4、实例化运行对象
runner = unittest.TextTestRunner()
# 5、使用运行对象去执行套件对象
# 运行对象.run(套件对象)
runner.run(suite)

在这里插入图片描述
练习

import unittest
from tools import add

# 定义测试类
class TestAdd(unittest.TestCase):
	# 书写测试方法,即测试用例代码
	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_method3(self):
		if add(2,3) == 5:
			print('测试通过')
		else:
			print('测试不通过')

套件和执行

import unittest
from testcase import TestAdd
# 实例化套件对象
suite = unittest.TestSuite()
# 添加测试方法
suite.addTest(unittest.makeSuite(TestAdd))
# 实例化执行对象
runner = unittest.TextTestRunner()
# 执行套件对象
runner.run(suite)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
tools.py

def login(username,password):
	if username == 'admin' and password == '123456':
		return '登录成功'
	else:
		return '登录失败'

testlogin.py

import unittest
from tools import login

class TestLogin(unittest.TestCase):
	def test_username_password_ok(self):
		if login('admin','123456') == '登录成功':
			print('pass')
		else:
			print(fail)

	def test_username_error(self):
		if login('root','123456') == '登录失败':
			print('pass')
		else:
			print(fail)

	def test_password_error(self):
		if login('admin','123123') == '登录失败':
			print('pass')
		else:
			print(fail)

TestLoader测试加载

作用和TestSuite一样,是对TestSuite功能的补充,用来组装 管理 测试用例
使用步骤:

  • 1、导包 import unittest
  • 2、实例化 测试加载对象 并添加用例,得到的是 suite 对象
  • 3、实例化 运行对象
  • 4、运行对象执行套件对象

在一个项目中 TestCase(测试用例)的代码,一般放在一个单独的目录中

# 1、导包
import unittest
# 2、实例化测试加载对象并添加用例
# unittest.TestLoader().discover('用例所在的路径','用例代码文件名')
# 用例所在的路径 建议使用相对路径,用例代码文件名 可以使用*通配符
suite = unittest.TestLoader().discover('./case','test*.py')
# 3、实例化 运行对象
runner = unittest.TextTestRunner()
# 4、运行对象执行套件对象
runner.run(suite)

# 将3 4 变为一步
unittest.TextTestRunner().run(suite)
# 1、导包
import unittest

# 2、使用默认的测试加载对象并添加用例
suite = unittest.defaultTestLoader.discover('case','test*.py')

# 将3 4 变为一步
unittest.TextTestRunner().run(suite)

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

方法级别和类级别 前后的方法,不需要同时出现,根据用例代码的需要自行选择使用

案例

  • 1、打开浏览器(整个测试过程中只打开一次浏览器) 类级别
  • 2、输入网址(每个测试方法都需要一次)方法级别
  • 3、输入用户名密码验证码点击登录(不同测试数据) 测试方法
  • 4、关闭当前页面(每个测试方法都需要一次)方法级别
  • 5、关闭浏览器(整个测试过程关闭一次浏览器)类级别
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_login1(self):
		print('输入正确的用户名密码验证码,点击登录1')
	
	def test_login2(self):
		print('输入正确的用户名密码验证码,点击登录2')

断言

让程序代替人工自动判断 预期结果和实际结果是否相符
断言结果:

  • True 用例通过
  • False 代码抛出异常,用例不通过

unittest中使用断言,都需要通过self.断言方法 来使用
在这里插入图片描述
在这里插入图片描述

assertEqual方法

判断预期结果和实际结果是否相等

self.assertEqual(预期结果,实际结果)

如果相等,用例通过
如果不相等,用例不通过,抛出异常

assertIn方法

判断预期结果是否包含在实际结果中

self.assertIn(预期结果,实际结果)

包含,用例通过
不包含,用例不通过,抛出异常

assertIn('admin', 'admin') # 包含
assertIn('admin', 'adminnnnn') # 包含
assertIn('admin', 'aaaaadmin') # 包含
assertIn('admin', 'aaaaadminnnn') # 包含
assertIn('admin', 'adddddmin') # 不包含

参数化

在测试方法中,使用 变量 来代替具体的测试数据,然后使用传参的方法将测试数据传递给方法的变量
优点:相似的代码无需多次书写
工作中场景:
1、测试数据一般放在 json 文件中
2、使用代码读取 json 文件,提取我们想要的数据 —> [(),()] or [[],[]]格式

安装插件

unittest 框架本身不支持参数化,想要使用参数化,需要安装插件来完成
联网安装

pip install parameterized

验证

pip list  # 查看到 parameterized

新建一个python代码文件,导包验证

from parameterized import parameterized

参数化代码

步骤:

  • 1、导包 import unittest/parameterized
  • 2、定义测试类
  • 3、书写测试方法(用到的测试数据使用变量代替)
  • 4、组织测试数据并传参
import unittest
form parameterized import parameterized
from tools import login

# 组织测试数据 格式: [(),(),()] or [[], [], []] 数据顺序要与参数保持一致 列表中有几组数据,就有几个用例
data = [
	('admin', '123456','登录成功'),
	('root', '123456','登录失败'),
	('admin', '123123','登录失败')
]

# 2.定义测试类
class TestLogin(unittest.TestCase):
	# 3.书写测试方法(用到的测试数据使用变量代替)
	@parameterized.expand(data)
	def test_login(self,username,password,expect):
		self.assertEqual(expect, login(username,password))

# 4、组织测试数据并传参(装饰器@)

data.json

[
 {
 	"desc":"正确的用户名和密码",
 	"username": "admin",
 	"password": "123456",
 	"expect": "登录成功"
 },
 {
 	"desc":"错误的用户名",
 	"username": "root",
 	"password": "123456",
 	"expect": "登录失败"
 },
 {
 	"desc":"错误的密码",
 	"username": "admin",
 	"password": "123123",
 	"expect": "登录失败"
 }
]

参数化代码

import unittest
form parameterized import parameterized
from tools import login
import json

def build_data():
	with open('data.json', encoding = 'utf-8') as f:
		result = json.load(f)  # [{},{},{}] --> [(),(),()]
		data = []
		for i in result:  # i {}
			data.append((i.get('username'),i.get('password'),i.get('expect')))
	
	return data
	
class TestLogin(unittest.TestCase):
	@parameterized.expand(build_data())
	def test_login(self,username,password)
		self.assertEqual(expect, login(username,password))

跳过

对于一些未完成或不满足测试条件的测试函数和测试类,不想执行,可以使用跳过
使用方法:装饰器完成
代码书写在 TestCase 文件中
① 直接将测试函数标记成跳过

@unittest.skip('跳过的原因')

② 根据条件判断测试函数是否跳过,判断条件成立则跳过

@unittest.skipIf(判断条件, '跳过的原因')
import unittest

version = 30

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(self):
		print('测试方法 3')

测试报告

自带的测试报告

只有单独运行TestCase的代码,才会生成测试报告

生成第三方的测试报告

HTMLTestRunner是一个第三方的类库,用来执行测试用例并生成HTML格式的测试报告
步骤:

  • 1、获取第三方的测试运行类模块,将其放在代码的目录下
  • 2、导包 import unittest
  • 3、使用 套件对象TestSuite or 加载对象TestLoader 去添加用例方法
  • 4、实例化 第三方运行对象HTMLTestRunner 并运行套件对象

英文报告

import unittest
from HTMLTestRunner import HTMLTestRunner

suite = unittest.defaultTestLoader.discover('.','pa1.py')
# HTMLTestRunner ()
# stream=sys.stdout:必填,测试报告的文件对象(open),使用wb打开
# verbosity=1:可选,报告的详细程度 默认1 简略,2 详细
# title=None:可选,测试报告的标题
# description=None:可选,描述信息,python的版本,pycharm的版本

file = 'report.html'  # 报告的后缀是.html
with open(file, 'wb') as f:
	# runner = HTMLTestRunner(f)  # 运行对象
	runner = HTMLTestRunner(f,2,'测试报告','python 2.6.8')  # 运行对象
	# 运行套件对象
	runner.run(suite)

流程:
1、组织用例文件(TestCase里),书写参数化、断言、Fixture、跳过,如果单个测试文件直接运行,得到测试报告,如果有多个测试文件,需要组装运行生成测试报告
2、使用 套件对象组装 or 加载对象 组装
3、运行对象 运行:运行对象=第三方运行类(文件对象(打开文件需要使用wb方式));运行对象.run(套件对象)

中文测试报告

import unittest
from HTMLTestRunnerCN import HTMLTestRunnerCN

# 组装用例方法
suite = unittest.defaultTestLoader.discover('.','pa1.py')

# 实例化运行对象
with open('report_cn.html', 'wb') as f:
	runner = HTMLTestRunner(f)  # 运行对象
	# 运行套件对象
	runner.run(suite)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值