Python基本语法

Python基本语法

python的版本:3.11.9

image-20240726203457632

Print函数的简单使用

作用:输出括号中的内容

#输入
print("hello word!")

image-20240726203851009

输出:

image-20240726203906301

灰⾊波浪线, 不会影响代码的正常执⾏, 基本上所有的灰⾊波浪线都是 PEP8 造成的

PEP8: 是 Python 代码的书写规范, 如果不按照这个规范书写,会给灰⾊波浪线提示,建议代码的书写按照 PEP8 的规范书写

  1. 可以书写代码的时候注意 PEP8 的代码规范;

  2. 可以在书写完成之后,使⽤快捷键 Ctrl Alt L 来按照PEP8 的规范⾃动格式化代码

变量名的命名规范

  1. 必须由字母、数字和下划线组成,并且不能以数字开头

  2. 不能使用Python中的关键字作为变量名

  3. 区分大小写

  4. 建议性命名

    • 驼峰命名法

      • 大驼峰:每个单词的首字母大写 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

image-20240727063659780

数字类型转换

数据本来的类型不是我们计算使用想要的类型

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 = '_*_')

image-20240729144106347

格式化输出

在字符串指定的位置,输出变量中存储的值。

  1. 在需要使用变量的地方,使用特殊符号占位
  2. 使用变量填充占位的数据

% 格式化输出占位符号

  • %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以上

  1. 需要在字符串的前边加上 f""或者 F""
  2. 占位符号统一变为{}
  3. 需要填充的变量写在{}
# 定义变量 姓名 年龄 身⾼
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() 可以在任意版本中使用

  1. 在需要使用变量的地方使用{}占位
  2. ‘{},{},…’.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没什么关系')

image-20240727073827924

随机数工具包random

  1. 导入随机数工具包

    import random
    
  2. 使用工具包中的工具产生指定范围内的数字

    #产生[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
输出

image-20240727075331162

for循环

for循环也可以让指定的代码重复执⾏ (循环)

for循环可以遍历容器中的数据(

遍历: 从容器中把数据⼀个⼀个取出

容器: 可以简单理解为盒⼦, 盒⼦中可以存放很多的数据

(字符串 str, 列表 list, 元组 tuple, 字典 dict))

for 循环 也可以称为 for 遍历

语法
for 变量名 in 容器:
 重复执⾏的代码
代码
#定义字符串
my_str = 'hello'
#遍历字符串
#字符串中有5个字符,循环就会执行5次
for i in my_str: # 每次循环i的值为字符串中的字符
	# 重复执行代码
	print('我错了',i)
输出

image-20240727083840232

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)

image-20240727085742334

字符串前边加上r""原生字符串,字符串中的 \ 不会作为转义字符,文件操作会用一下

my_str8 = r'I\'m ⼩明'
print(my_str8)
my_str9 = r'I\\\'m ⼩明'
print(my_str9)

image-20240727162741010

len()

获取字符串中的字符个数

str = 'hello'
print(len(str ))

image-20240727162952192

切片

可以获取字符串中多个字符(多个字符的下标是有规律的,等差数列)

语法

字符串[start : end : step]

start 时开始位置的下标,

end 是结束位置的下标(注意,不能取到这个位置的字符)

step 是步长,等差数列的差值,所取的相邻字符下标之间的差值,默认是1,可以不写

find()

字符串.find(sub_str, start, end)

字符串的查找方法,在字符串中查找是否存在sub_str这样的字符串

sub_str:要查找的小字符串;

start:开始位置,从哪个下标位置开始查找,一般不写,默认是0;

end:结束位置,查找到哪个下标结束,一般不写,默认是len().

返回:

  1. 如果找到,返回sub_str第一次出现的正数下标;
  2. 如果没有找到,返回 -1。
str1 = "and itcast and itheima and Python"
# 在字符串中查找 and
num = str1.find('and')
print(num) # 0

image-20240727163904157

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

image-20240727164051570

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']

image-20240727164452723

join()

字符串的链接

字符串.join(列表) 
# 括号中的内容主要是列表,可以是其他容器
# 作⽤: 将字符串插⼊到列表中每相邻的两个数据之间, 组成⼀个新的字符串
- 列表中的数据使⽤使⽤ 逗号隔开的
- 注意点: 列表中的数据必须都是字符串, 否则会报错
list1 = ['good', 'good', 'study']
# 1. 将 列表中的字符串使⽤空格连起来
str1 = ' '.join(list1)
print(str1) # good good study

image-20240727164628985

列表

列表 list ,是使用最多的一种容器(数据类型),列表中可以存放任意类型的数据

定义

定义空列表(没有任何数据的列表)
list1 = list()
print(type(list1),list1)

image-20240727164928815

类型转换 list(容器),将其他容器转换为列表

转换字符串会将字符串中的每个字符作为一个数据存入到列表中

list2 = list('hello')
print(type(list2),list2)

image-20240727165139363

直接使用[]进行定义(常用
直接定义空列表
my_list = []
print(my_list) # []

image-20240727165404653

定义非空列表
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(下标)
  1. 下标不写,默认删除最后一个数据(常用)
  2. 书写存在的下标,删除对应下标位置的数据
  3. 返回的删除的数据。

根据数据值删除

#根据数据值删除
列表.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,元组的特点和列表非常相似

  1. 元组可以存放任意类型的数据
  2. 元组中可以存放任意多个数据

区别:

  1. 元组中的数据内容不能改变,列表中的可以;
  2. 元组使用(),列表使用[]

应用:

在函数中的传参或者返回值中使用,保证数据不会被修改

定义

  1. 使用 类实例化 的方式
  2. 直接使用()方式

常用方法

由于元组中的数据不能修改,所以只有查看的方法

  1. 在元组中,也可以使用下标切片获取数据;
  2. 在元组中存在 index方法,查找下标,如果不存在,会报错;
  3. 在元组中存在 count方法,统计数据出现的次数;
  4. 在元组中可以使用 in操作,判断数据是否存在
  5. 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)

代码

image-20240729083918628

输出

image-20240729083933889

代码

image-20240729083654839

输出

image-20240729083711878

字典

  1. 字典 dict, 字典中的数据是由键(key)值(value)对组成的

(键表示数据的名字, 值就是具体的数据)

  1. 在字典中⼀组键值对是⼀个数据, 多个键值对之间使⽤ 逗号隔开,变量 = {key: value, key:value, …}

  2. ⼀个字典中的键是唯⼀的,不能重复的, 值可以是任意数据

  3. 字典中的键 ⼀般都是 字符串,可以是数字, 不能是列表

定义

1. 使用 类实例化 的方法
my_dict = dict()
print(type(my_dict), my_dict)

dict()不能转列表和元组,字符串

2. 直接使用{}定义
  1. 空字典

    my_dict1 = {}
    print(type(my_dict1), my_dict1) 
    # <class 'dict'> {}
    
  2. 非空字典

    my_dict2 = {"name": "⼩明", "age": 18, "height":
    1.71, "is_men": True, "like": ["抽烟", "喝酒", "烫头"]}
    print(my_dict2)
    print(len(my_dict2))
    

增加和修改操作

语法:字典[键]= 数据值

  1. 如果键已经存在,就是修改数据值;

  2. 如果键不存在,就是添加数据(即添加键值对)。

    # 定义字典 ⼩明 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(键)来获取

使用字典[键]

字典[键]

  1. 如果键存在,返回键对应的数据值;
  2. 如果键不存在,会报错
使用字典.get(键)

字典.get(键,数据值)

  1. 数据值一般不写,默认是None

返回:

  1. 如果键存在,返回键对应的数据值;
  2. 如果键不存在,返回的是括号中书写的数据值(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 关键字在容器中都可以使⽤, 注意, 在字典中判断的是
字典的键是否存在

函数

概念

函数,就是把具有独立功能的代码块,组织为一个小模块,在需要的时候调用。

函数定义

  1. 将多行代码放在一起,起名字的过程

  2. 函数必须先定义后调用

语法
def 函数名():
	函数中的代码
	函数中的代码
  1. def是关键字,用来定义函数的 define的缩写
  2. 函数名需要遵守标识符的规则
  3. 处于def缩进中的代码,称为函数体
  4. 函数定义的时候,函数体中的代码不会执行,在调用的时候才会执行

函数的调用

使用多行代码的时候,称为函数调用

  1. 函数调用的时候会执行函数体中的代码
  2. 函数调用的代码,要写在函数体外边

列表去重

列表去重:列表中存在多个数据,需求,去除列表中重复的数据。

方式1

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

遍历:for循环实现

判断是否存在:可以使用 in

存入数据: append()

方式2

在Python中还有一种数据类型(容器),称为是集合(set)

特点:

集合中不能有重复的数据(如果有重复的数据会自动去重),可以使用集合的特点对列表去重

  1. 使用set()类型转换将列表转换为集合类型
  2. 再使用list()类型转换将集合转换为列表

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

变量的引用[理解]

  1. 在定义变量的时候 变量 = 数据值,Python解释器会在内容开辟两块空间;
  2. 变量和数据都有自己的空间
  3. 日常简单理解,将数据保存到变量的内存中,本质是将数据的地址保存到变量对应的内存中;
  4. 变量中存储数据地址的行为,就是引用(变量引用了数据的地址,简单说就是变量中存储数据),存储的地址称为引用地址;
  5. 可以使用id()来获取变量中的引用地址(即数据的地址),如果两个变量的id()获取的引用地址一样,即代表着,两个变量引用了同一个数据,是同一个数据
  6. 只有赋值运算符= ,可以改变变量的引用(等号左边数据的引用)
  7. python中数据的传递,都是传递的引用

可变类型和不可变类型

数据类型: int float bool str list tuple dict sec

可变不可变是指:数据所在的内存是否允许修改,允许修改就是可变类型,不允许修改就是不可变类型(不使用=,变量引用的数据中的内容是否会变化,会变化是可变的,不会变化是不可变的)

可变类型: list dict set

​ 列表.append()

​ 字典.pop(键)

不可变类型: int float bool str tuple

注意:

  1. 只有 = ,可以改变引用
  2. 可变类型做参数,在函数内部,如果不使用 = 直接修改形参的引用,对形参进行的数据修改,会同步到实参中

对于列表来说,+=的本质是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)

局部变量和全局变量

根据变量的定义位置,可以将变量分为局部变量和全局变量

局部变量

局部变量:在函数内部(函数的缩进中)定义的变量,称为是局部变量

特点:

  1. 局部变量只能在当前函数内部使用,不能在其他函数和函数外部使用

  2. 在不同函数中,可以定义名字相同的局部变量,两者之间没有影响

  3. 生存周期(生命周期,作用范围)–>在哪能用

    在函数被调用的时候,局部变量被创建,函数调用结束,局部变量的值被销毁(删除),不能使用

所以函数中的局部变量的值,如果想在函数外部使用,需要使用return关键字,将这个值进行返回

全局变量

定义位置:在函数外部定义的变量,称为是全局变量

特点:

  1. 可以在任何函数中读取(获取)全局变量的值

  2. 如何在函数中存在和全局变量名字相同的局部变量,在函数中使用的是局部变量的值(就近)

  3. 在函数内部想要修改全局变量的引用(数据值),需要添加global关键字,对变量进行声明为全局变量

  4. 生命周期

    代码执行的时候被创建,代码执行结束,被销毁(删除)

函数进阶

返回值-函数返回多个数据值

函数中想要返回一个数据值,使用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)

函数参数

函数传参的方式

  • 位置传参

    在函数调用的时候,按照形参的顺序,将实参值传递给形参

  • 关键字传参

    在函数调用的时候,指定数据值给到那个形参

  • 混合使用

    1. 关键字传参必须写在位置传参的后面
    2. 不要给一个形参传递多个数据值
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)
不定长位置参数(不定长元组参数)
  1. 书写,在普通参数的前边,加上一个*,这个参数就变为不定长位置参数
  2. 特点,这个形参可以接收任意多个位置传参的数据
  3. 数据类型,形参的类型是元组
  4. 注意,不定长位置参数要写在普通的参数后面
  5. 一般写法,不定长位置参数的名字为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)
不定长关键字参数(不定长字典参数)
  1. 书写,在普通参数的前边,加上两个*,这个参数就变为不定长关键字参数
  2. 特点,这个形参可以接收任意多个关键字传参的数据
  3. 数据类型,形参的类型是字典
  4. 注意,不定长关键字参数,要写在所有从三叔的最后边
  5. 一般写法,不定长关键字参数的名字为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. 使用变量进行调用
变量()

image-20240729150105342

image-20240729150115974

练习

# 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)

面向对象

基本的介绍

面向对象是一个编程思想(写代码的套路)

编程思想:

  1. 面向过程
  2. 面向对象

以上两种都属于写代码的套路(方法),最终目的都是为了将代码书写出来,只不过过程和思考方法不太一样

  • 面向过程
    • 关注的是具体步骤的实现,所有功能都自己书写
    • 亲力亲为
    • 定义一个个函数,最终按照顺序调用函数
  • 面向对象
    • 关注的是结果,谁(对象)能帮我做这件事
    • 偷懒
    • 找一个对象(),让对象去做

类和对象

面向对象的核心思想是,找一个对象去帮我们处理事情,在程序代码中,对象是类创建的

类和对象,是面向对象编程思想中非常重要的两个概念

    • 抽象的概念,对多个特征和行为相同或者相似事物的统称
    • 泛指的(指代多个,而不是具体一个)
  • 对象
    • 具体存在的一个事物,看得见摸得着的
    • 特指的,(指代一个)
苹果	--->	类
红苹果	--->	类
张三嘴里正在吃的那个苹果	--->	对象

类的组成

  1. 类名

    给这多个事物起一个名字,在代码中满足大驼峰命名法(每个单词的首字母大写)

  2. 属性

    事物的特征,即有什么,一般文字中的名词

  3. 方法

    事物的行为,即做什么事,一般是动词

类的抽象(类的设计)

其实就是找到类的类名,属性和方法

需求:

  • 小明 今年18岁,身高1.75,每天早上跑完步,会去吃东西
  • 小美 今年17岁,身高1.65,小美不跑步,小美喜欢吃东西
类名: ⼈类(Person, People)
属性: 名字(name), 年龄(age), 身⾼(height)
⽅法: 跑步(run)(eat)

需求:

  • 一只黄颜色的狗狗叫大黄
  • 看见生人汪汪叫
  • 看见家人摇尾巴
类名: 狗类(Dog)
属性: 颜⾊(color) , 名字(name)
⽅法: 汪汪叫 (bark), 摇尾巴(shake)

面向代码的步骤

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

面向对象基本代码的书写

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的说明:

  1. 从函数的语法上讲,self是形参,就可以是任意的变量名,只不过我们习惯性将这个形参写作self
  2. self是最普通的形参,但是在调用的时候没有传递实参值,原因是,Python解释器在执行代码的时候,自动的将调用这个方法的对象传递给self,即self的本质是对象。
  3. 验证,只需要确定通过哪个对象调用,对象的引用和self的引用是一样的
  4. 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中有一类方法,以两个下划线开头,两个下划线结尾,并且在满足某个条件的情况下,会自动调用,这类方法称为 魔法方法

学习:

  1. 什么情况下自动调用
  2. 有什么用,用在哪
  3. 书写的注意事项

__init__方法

  1. 什么情况下自动调用

    创建对象之后会自动调用

  2. 有什么用,用在哪

    1. 给对象添加属性的,(初始化方法,构造方法)
    2. 某些代码,在每次创建对象之后,都要执行,就可以将这行代码写在 __init__方法
    3. 书写的注意事项
      1. 不要写错了
      2. 如果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__方法

  1. 什么情况下自动调用

    使用print(对象)打印对象的时候会自动调用

  2. 有什么用,用在哪

    在这个方法中一般书写对象的属性信息,即打印对象的时候想要查看什么信息,在这个方法中进行定义的

    如果类中没有定义 __str__方法,print(对象),默认输出对象的引用地址

  3. 书写的注意事项

    这个方法必须返回一个字符串

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('代码运⾏结束')

案例

需求
  1. 小明体重75.0公斤
  2. 小明每次跑步会减肥0.5公斤
  3. 小明每次吃东西体重增加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)

封装

就是定义类的过程

公有和私有

  1. 在Python中定义的方法和属性,可以添加访问控制权限(即在什么地方可以使用这个属性和方法)

  2. 访问控制权限分为两种,公有权限和私有权限

  3. 公有权限

    直接书写的方法和属性,都是公有的

    公有的方法和属性,可以在任意地方访问和使用

  4. 私有权限

    在类内部,属性名或者方法名,前边加上两个下划线,这个属性或者方法就变为私有的

    私有的方法和属性,只能在当前类的内部使用

  5. 什么时候定义私有

    1. 某个属性或者方法,不想在类的外部被访问和使用,就将其定义为私有即可
    2. 测试中,一般不怎么使用,直接公有即可
    3. 开发中,会根据需求文档,确定什么作为私有
  6. 如果想要在类外部操作私有属性,方法是,在类内部定义公有的方法,我们通过这个公有的方法去操作

继承

继承描述的是类与类之间的关系,继承的好处是可以减少代码的冗余

语法

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

class B(A): # 类 B, 继承类 A
	pass
	

案例

  1. 定义一个动物类,吃
  2. 定义一个狗类,继承动物类,吃,叫
  3. 定义一个哮天犬类,继承狗类
# 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中对象,方法()调用方法

  1. 先在自己的类中去找有没有这个方法,如果有,直接调用
  2. 如果没有去父类中查找,如果有,直接调用
  3. 如果没有,去父类的父类中查找,如果有直接调用
  4. 如果Object类中有,直接调用,如果没有,代码报错

重写

在子类中定义了和父类中名字相同的方法,就是重写

重写的原因:父类中的方法,不能满足子类对象的需求,所以重写

重写之后的特点:调用子类字节的方法,不再调用父类中的方法

多态

是一种写代码,调用的一种技巧,同一个方法,传入不同的对象,执行得到不同的结果,这种现象称为是多态,多态可以增加代码的灵活度

哪个对象调用方法,就去自己的类中去查找这个方法,找不到去父类中找

属性和方法

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

对象的划分

实例对象(实例)
  1. 通过类名()创建的对象,我们称为实例对象,简称实例
  2. 创建对象的过程称为是类的实例化
  3. 我们平时所说的对象就是指 实例对象(实例)
  4. 每个实例对象,都有自己的内存空间,在自己的内存空间中保存自己的属性(实例属性)
类对象(类)
  1. 类对象就是类,或者可以认为是类名
  2. 类对象是Python解释器在执行代码的过程中创建的
  3. 类对象的作用:
    1. 使用类对象创建实例 类名()
    2. 类对象也有自己的内存空间,可以保存一些属性(类属性)
  4. 在一个代码中,一个类,只有一份内存空间

属性的划分

实例属性
  • 概念:是实例对象具有的属性

  • 定义和使用(一般定义在 __init__()方法中)

    在 init 方法中, 使用 self.属性名 = 属性值 定义
    在方法中是 使用 self.属性名 来获取(调用)
    
  • 内存

    实例属性,在每个实例中,都存在一份

  • 使用时机

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

  • 定义和使用

    在类内部,方法外部,直接定义的变量 ,就是类属性
    使用: 类对象.属性名 = 属性值 or 类名.属性名 = 属性值
    类对象.属性名 or 类名.属性名
    
  • 内存

    只有类对象中存在一份

方法的划分

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

实例方法(最常用)
  • 定义

    # 在类中直接定义的方法 就是 实例方法
    class Demo:
    	def func(self): # 参数一般写作 self,表示的是实例对象
    		pass
    
  • 定义时机(什么时候用)

    如果在方法中需要使用实例属性(即需要使用self),则这个发方法必须定义为实例方法

  • 调用

    对象.方法名() #不需要给self传参
    
类方法(会用)
  • 定义

    # 在方法名字的上方书写 @classmethod 装饰器(使用 @classmethod 装饰的方法)
    class Demo:
    	@classmethod
    	def func(cls): # 参数一般写作 cls, 表示的是类对象(即类名) class
    		pass
    
  • 定义时机(什么时候用)

    1. 前提,方法中不需要使用实例属性(即self)
    2. 用到了类属性,可以将这个方法定义为类方法(也可以定义为实例方法)
  • 调用

    # 1. 通过类对象调用
    类名.方法名() # 也不需要给 cls 传参, python 解释器自动传递
    # 2. 通过实例对象调用
    实例.方法名() # 也不需要给 cls 传参, python 解释器自动传递
    

静态方法(基本不用)

  • 定义

    # 在方法名字的上方书写 @staticmethod 装饰器(使用 @staticmethod 装饰的方法)
    class Demo:
        @staticmethod
        def func(): # 一般没有参数
    		pass
    
  • 定义时机(什么时候用)

    1. 前提,方法中不需要使用实例属性(即self)
    2. 也不使用类属性,可以将这个方法定义为 静态方法
  • 调用

    # 1. 通过类对象调用
    类名.方法名()
    # 2. 通过实例对象调用
    实例.方法名()
    

哈希(hash)

是一个算法,可以对数据产生一个唯一的值(指纹)

is()

可以用来判断两个对象是不是同一个对象,即两个对象的引用是否相同

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

is 和 == 的区别:

== 只判断数据值是否相同,is 判断引用是否相同

文件

计算机的文件,就是存储在某种长期存储设备上的一段数据。

作用:将数据长期保存下来,在需要的时候使用

  1. 计算机只认识二进制(0,1)
  2. 文件中存储的数据都是以二进制(0 1)的形式去存储的

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

  • 文本文件
    • 能够使用记事本软件打开(能够使用记事本转换为文字)
    • txt,md,py,html,css,js,json
  • 二进制文件
    • 不能使用记事本软件打开的
    • exe,mp3,mp4,jpg,png

文件操作

文件操作的步骤

  1. 打开文件
  2. 读或者写文件
  3. 关闭文件
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文件的语法

  1. json文件的后缀是.json

  2. json中主要数据类型为对象({}类似Python中字典)和数组([],类似Python中的列表),对象和数组可以互相嵌套

  3. 一个json文件是一个对象或者数组(即json文件的最外层要么是一个{},要么是一个数组[])

  4. json中的对象是由键值对组成的,每个数据之间使用逗号隔开,但是最后一个数据后边不要写逗号

  5. json中的字符串必须使用双引号

  6. json中的其他数据类型

    > 数字类型 ----> int float

    > 字符串 string —> str

    > 布尔类型 true, false -----> True, False

    > 空类型 null ----> None

json文件的书写

案例

我叫小明,我今年18岁,性别男,爱好听歌,游戏,购物,吃饭,睡觉,打豆豆,我的居住地址为 国家中国,城市上海

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

读取json文件

  1. 导包 import json
  2. 读打开文件
  3. 读文件

返回的是 字典(文件中是对象)或者列表(文件中是数组)

# 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

步骤

  1. 导包 import json
  2. 写(w)方式打开文件
  3. 写入 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中已经实现好了,我们不需要操作,我们知道异常会进行传递

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

模块和包

  1. Python源代码文件就是一个模块
  2. 模块中定义的变量 函数 类,都可以让别人使用,同样,可以使用别人定义的(好处:别人定义好的不需要我们再次书写,直接使用即可)
  3. 想要使用别人的模块中的内容工具(变量、类、函数),必须先导入模块才可以
  4. 我们自己写的代码,想要作为模块使用,代码的名字需要满足标识符的规则(由数字,字母,下划线组成,不能以数字开头)

导入模块的语法

方式一
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__的作用

  1. 每个代码文件都是一个模块;

  2. 在导入模块的时候,会执行模块中的代码(三种方法都会);

  3. __name__ 变量是pyhon解释器自动维护的变量;

  4. __name__变量,如果代码是直接运行,值是 "__main__";

  5. __name__变量,如果代码是被导入执行,值是模块名(即代码文件名);

  6. 在代码文件中,在被导入时不想被执行的代码,可以写在 if__name__=="__main__":代码的缩进中;

    image-20240731080358977

代码练习

  1. 定义一个模块tools.py;
  2. 在模块中定义一个函数,func,输出’我是tools模块中的funn函数’;
  3. 在模块中定义一个类,Dog,具有属性name,age,方法play,输出’xx在快乐的玩耍’;
  4. 新建一个代码文件,调用tools模块中的func函数,并创建Dog类的对象,调用play方法。

image-20240731081229170

image-20240731081245154

image-20240731081313602

包(package)

在python中,包是一个目录,只不过这个目录存在一个文件 __init__.py(可以是空的)

将功能相近或者相似的代码放在一起的。

在python中使用的时候,不需要区分是包还是模块,使用方式是一样的。

random 模块(单个代码文件)
json 包(目录)
unittest 包(目录)

1. import 包名
2. alt 回车 快捷导入

UnitTest框架

介绍

  • 框架

    说明:

    1. 框架英文单词framework;
    2. 为解决一类事情的功能集合;
    3. 需要按照框架的规定(套路)去书写代码。
  • 什么是UnitTest框架?

    概念:UnitTest是python自带的一个单元测试框架,用它来做单元测试。

    自带的框架(官方):不需要单外安装,只要安装了Python,就可以使用 random,json,os,time

    第三方框架:想要使用 需要先安装后使用(pytest)

    selenium,appium,requests


    单元测试框架:主要用来做单元测试,一般单元测试是开发做的。

    对于测试来说,unittest框架的作用是自动化脚本(用例代码)执行框架(使用unittest框架来管理运行多个测试用例的)

  • 为什么使用UnitTest框架?

    1. 能够组织多个用例去执行;
    2. 提供丰富的断言方法(让程序代码代替人工自动的判断预期结果和实际结果是否相符);
    3. 能够生成测试报告。
  • UnitTest核心要素(unitest的组成部分)

    1. TestCase(最核心的模块)

      TestCase(测试用例),注意这个测试用例是unittest框架的组成部分,不是手工和自动化中我们所说的用例(TestCase)。

      主要作用:每个TestCase(测试用例)都是一个代码文件,在这个代码文件中来书写真正的用例代码。

    2. TestSuite

      TestSuite(测试套件),用来管理组装(打包)多个TestCase(测试用例)的。

    3. TestRunner

      TestRunner(测试执行,测试运行),用来执行TestSuite(测试套件)的。

    4. TestLoader

      TestLoader(测试加载),功能是对 TestSuite(测试套件)功能的补充,管理组装(打包)多个TestCase(测试用例)的。

    5. Fixture

      Fixture(测试夹具),书写在TestCase(测试用例)代码中,是一个代码结构,可以在每个方法执行前后都会执行的内容。

      举例:

      登录的测试用例,每个用例中重复的代码就可以写在Fixture代码结构中,只写一遍,但每次用例方法的执行,都会执行Fixture中的代码。

      1. 打开浏览器;
      2. 输入网址。

TestCase(测试用例)

  1. 是一个代码文件,在代码文件中来书写真正的用例代码;
  2. 代码文件的名字必须按照标识符的规则来书写(可以将代码的作用在文件的开头使用注释说明)
步骤
  1. 导包(unittest)
  2. 自定义测试类
  3. 在测试类中书写 测试方法
  4. 执行用例
代码
#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 代码文件的命名不规范

  1. 代码文件的命名以数字开头;
  2. 代码问件名字中有空格;
  3. 文件名字有中文;
  4. 其他的特殊符号(数字,字母,下划线组成,不能以数字开头)

问题2 代码运行没有结果

右键运行没有unittests for的提示,出现的问题

解决方案:

  1. 重新新建一个代码文件,将写好的代码复制进去;
  2. 删除已有的运行方式。

问题3 没有找到用例

测试方法中不是以test_开头的,或者单词写错了

TestSuite & TestRunner

TestSuite(测试套件):管理 打包 组装 TestCase(测试用例)文件的

TestRunner(测试执行):执行TestSuite(套件)

  • 步骤

    1. 导包(unittest)
    2. 实例化(创建对象)套件对象
    3. 使用套件对象添加用例方法
    4. 实例化运行对象
    5. 使用运行对象去执行套件对象
  • 代码

    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)
    

练习

  1. 在tools模块中定义add函数,对于两个数字进行求和计算;

  2. 书写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)

使用步骤:

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

代码实现

在一个项目中,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的需要自行的选择使用

案例
  1. 打开浏览器(整个测试过程就打开一次浏览器)类级别

  2. 输入网址(每个测试方法都需要一次)方法级别

  3. 输入用户名密码验证码点击登录(不同的测试数据)测试方法

  4. 关闭当前页面(每个测试方法都需要一次)方法级别

  5. 关闭浏览器(震哥哥测试过程就关闭一次浏览器)类级别


  6. 打开浏览器(整个测试过程就打开一次浏览器)类级别

  7. 输入网址(每个测试方法都需要一次)方法级别

  8. 输入用户名密码验证码点击登录(不同的测试数据)测试方法

  9. 关闭当前页面(每个测试方法都需要一次)方法级别

  10. 输入网址(每个测试方法都需要一次)方法级别

  11. 输入用户名密码验证码点击登录(不同的测试数据)测试方法

  12. 关闭当前页面(每个测试方法都需要一次)方法级别

  13. 关闭浏览器(整个测试过程中就关闭一次浏览器)类级别

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')

断言

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

断言结果有两种:

  1. True: 用例通过;
  2. 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'))

参数化

参数化在测试方法中,使用变量来代替具体的测试数据,然后使用传参的方法将测试数据传递给方法的变量

好处:相似的代码不需要多次书写

工作中的场景:

  1. 测试数据一般放在json文件中
  2. 使用代码读取json文件,提取我们想要的数据 —> [(),()] or[[],[]]

安装插件

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

#联网安装(在cmd窗口安装 或者)
pip install parameterized

pip是Python中包(插件)的管理工具,使用这个工具下载安装插件

验证

pip list #查看到 parameterized

新建一个python代码文件,导包验证
from parameteried import parameterized

参数化代码

  1. 导包unittest/pa
  2. 定义测试类
  3. 书写测试方法(用到的测试数据使用变量代替)
  4. 组织测试数据并传参
[
    {
        "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的代码,才会生成测试报告

生成第三方的测试报告

  1. 获取第三方的测试运行类模块,将其放在代码的目录中
  2. 导包unittest
  3. 使用套件对象,加载对象去添加用例方法
  4. 实例化第三方的运行对象并运行套件对象
#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)
  1. 组织用例文件(TestCase里边),书写参数化,书写断言,书写Fixture,书写跳过,如果单个测试测试文件,直接运行,得到测试报告,如果有多个测试文件,需要组装生成测试报告

  2. 使用套件对象组装,或者使用加载对象组装

  3. 运行对象运行

    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)
  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值