【软件测试】Python基础知识

  • 以下是软件测试小白关于python基础知识的学习记录。

一、Python介绍

  • python 主要版本:python 2、python 3(主流)。
  • 语言分类:
  1. 编译型语言(c、c++)
    • 需要编译器,对源代码进行编译最终生成可执行文件。
    • 先编译再执行。
  2. 解释型语言(python)
    • 需要解释器,对源代码逐行解释。
    • 边解释边执行。

二、基础语法

2.1 注释

  • 【说明】注释是对代码进行解释说明的文字,不会被执行,便于理解代码。
  • 【分类】
    • 单行注释:只对单行内容起作用,以#开头,快捷键ctrl+/
    • 多行注释:注释的内容可以换行书写,格式为'''注释内容'''"""注释内容"""

2.2 标识符

  • 【说明】标识符是用户编程时使用的名字,用于给变量、函数、类命名。
  • 【规则】
    1. 由字母、数字和下划线组成。
    2. 不能使用数字开头。
    3. 不能使用python内置关键字。
    4. 严格区分大小写。
    5. 不建议使用中文。

2.3 变量

  • 【说明】变量是用来保存数据的,变量必须先定义再使用。
  • 【语法】变量名=数据值
  • 【使用】直接使用变量名即可。
# 定义变量保存名字和年龄
name = "张三"
age = 18
print(name,age)
  • 变量命名规范
    1. 要遵循标识的规则。
    2. 命名习惯
      • 驼峰命名法:
        • 大驼峰:每个单词的首字母大写,例如:MyName;
        • 小驼峰:第一个单词的首字母小写,其余单词首字母大写,例如:myName;
      • 下划线连接:单词与单词之间使用下划线连接。

2.4 数据类型

  • 数字型
    1. 整型(int)
    2. 浮点型(float)
    3. 布尔型(bool)
  • 非数字型
    1. 字符串(string)
    2. 列表(list[])
    3. 元组(tuple())
    4. 集合(set{})
    5. 字典(dict{键值对})
# int 整型,整数
age = 18
# float 浮点型,小数
num = 2.345
# bool 布尔型,关键字True(真)、False(假)
isRight = True
print(type(age),type(num),type(isRight))
  • 数据类型转换
    • 【语法】变量 = 要转换的数据类型(原数据),例如nums = int(num)
    • 数据类型转换不会改变原数据的类型,是生成一个新数据
num = 2.456
print(type(num))
nums = int(num)
print(nums,type(nums))

int(2.45) # 浮点型转整型,去尾。
int('134') # 字符型(只能是整数数字类型)转整型
float(12) # 整型转浮点型
float('12') # 字符型(只能是数字类型)转浮点型
float('1.234')
str(12) # 整型转字符型
str(1.234) # 浮点型转字符型

2.5 输入

  • 使用函数input(),获取键盘的输入信息。
  • 【语法】变量 = input('提示信息')
    1. 代码从上至下执行,当代码执行遇到 input 时会中断执行,等待键盘输入,直到输入完成再次执行。
    2. 在输入过程中,使用回车键表示此次输入结束。
    3. 使用 input 函数获取的内容,都是字符串。
name = input('请输入您的名字:')
age = input('请输入您的年龄:')
print(name,age)

2.6 输出

  • 格式化输出
  1. 字符串.format()
    • 字符串中需要使用变量的地方,用{}进行占位。
    • 在format()括号内,按占位顺序,将变量填写。
name = '张三'
age = 18
sex = '男'
print('姓名:{},年龄:{},性别:{}'.format(name,age,sex))
  1. F-string
    • python 3.6版本以后才可以使用。
    • 在字符串前面加上fF
    • 字符串中需要使用变量的地方,用{}进行占位。
    • 将变量直接写在{}占位符中。
name = '张三'
age = 18
sex = '男'
print(f'姓名:{name},年龄:{age},性别:{sex}')

2.7 转义字符

  • \n:表示换行
  • \t:表示制表符
  • print 函数中默认有一个 end=‘\n’ ,所有每个print结束后会输出一个换行。
print('张三18')
print('张三18',end=' ')
print('\n张三\n18')
print('张三\t18')

2.8 运算符

  • 算术运算符:
    • 优先级(由高到低):() 高于 **,* 高于 /,//,% 高于 +,-
    • 加、减、乘、除:+ - * /
    • 求商://
    • 取余:%
  • 比较运算符:
    • 只能是相同类型之间进⾏⽐较 (数字和字符串之间不能使⽤)
    • 结果返回的值是bool型,要么为True,要么为False。
    • 相等、不相等:== !=
    • 大于、大于等于、小于、小于等于:> >= < <=
  • 赋值运算符:=
  • 复合赋值运算符:
    • 加后赋值:+=
    • 减后赋值:-=
    • 乘后赋值:*=
    • 除后赋值:/=
    • 取余后赋值:%=
  • 逻辑运算符:
    • 与:and,连接两个条件, 只有都为 True, 结果才为 True, ⼀假为假。
    • 或:or,连接两个条件, 只要⼀个条件 为 True, 结果就为 True, ⼀真为真。
    • 非:not,取反,本来是 True, 加上 not 变为False。

2.9 练习

  • 编写一个Python程序,完成以下任务:
  • 输入:
    • 从用户处获取姓名(name)。
    • 从用户处获取年龄(age),使用input()函数,并转换为整型。
    • 从用户处获取身高(height),使用input()函数,并转换为浮点型。
    • 从用户处获取体重(weight),使用input()函数,并转换为浮点型。
  • 计算:
    • 计算用户的BMI(Body Mass Index,身体质量指数),公式为:BMI = weight / (height ** 2)。
    • 根据BMI值判断用户的健康状态:
      • BMI < 18.5: 体重过轻
      • 18.5 <= BMI < 24.9: 体重正常
      • 25 <= BMI < 29.9: 体重超重
      • BMI >= 30: 肥胖
  • 输出:
    • 使用format()方法格式化输出用户的姓名、年龄、身高、体重、BMI值、健康状态。
    • 使用F-string方法再次输出上述信息。
  • 答:
# 输入
name = input('请输入你的姓名:')
age = int(input('请输入你的年龄:'))
height = float(input('请输入你的身高/米:'))
weight = float(input('请输入你的体重/kg:'))
# 计算
BMI = weight / (height ** 2)
if BMI < 18.5:
	health_status = '体重过轻'
elif 18.5 <= BMI < 24.9:
	health_status = '体重正常'
elif 25 <= BMI < 29.9:
	health_status = '体重超重'
else:
	health_status = '肥胖'
# 输出
print('你的信息如下:\n姓名:{}\n年龄:{}\n身高:{}米\n体重:{}kg\nBMI值:{}\n健康状态:{}'.format(name,age,height,weight,BMI,health_status))
print(f'你的信息如下:\n姓名:{name}\n年龄:{age}\n身高:{height}米\n体重:{weight}kg\nBMI值:{BMI:.2f}\n健康状态:{health_status}')

三、逻辑控制语句

3.1 条件分支语句

  • if…else 结构
if 判断条件:
	判断条件成⽴,执⾏的代码
else:
	判断条件不成⽴,执⾏的代码
  1. 每个条件后面要使用冒号 :,表示接下来是满足条件后要执行的语句块。
  2. 使用缩进来划分语句块,相同缩进数的语句在一起组成一个语句块。
  3. else 不能单独使⽤ 必须配合 if 使⽤, 并且 else 要和 if 对齐。
  • if…elif…else 结构
if 判断条件1:
	判断条件成⽴,执⾏的代码
elif 判断条件2: # 判断条件1不成立,执行判断条件2,依次往下执行
	判断条件成立,执行的代码
......
else:
	判断条件不成⽴,执⾏的代码
  • if 嵌套
  • 把 if…elif…else 结构放在另外一个 if…elif…else 结构中。
if 条件1:
    判断条件成⽴,执⾏的代码
    if 条件2:
        判断条件成⽴,执⾏的代码
    elif 条件3:
        判断条件成⽴,执⾏的代码
    else:
        判断条件不成⽴,执⾏的代码
elif 条件4:
    判断条件成⽴,执⾏的代码
else:
    判断条件不成⽴,执⾏的代码
  • 【练习】
  • 1.定义两个整数变量python_score、c_score,使⽤ input 获取成绩 编写代码判断成绩
  • 2.要求只要有⼀⻔成绩 > 60 分就输出合格
python_score = int(input('请输入你的python成绩:'))
c_score = int(input('请输入你的c语言成绩:'))
if python_score > 60 or c_score > 60:
    print('合格')
else:
    print('不合格')
  • 1.定义 score 变量记录考试分数
  • 2.如果分数是 ⼤于等于 90分 显示 优
  • 3.如果分数是 ⼤于等于 80分 并且 ⼩于 90分 显示 良
  • 4.如果分数是 ⼤于等于 70分 并且 ⼩于 80分 显示 中
  • 5.如果分数是 ⼤于等于 60分 并且 ⼩于 70分 显示 差
  • 6.其它分数显示 不及格
score = int(input('请输入考试成绩:'))
if score>=90:
    print('优')
elif 90 > score >=80:
    print('良')
elif 80 > score >=70:
    print('中')
elif 70 > score >= 60:
    print('差')
else:
    print('不及格')
  • 假定某⽹站⽤户名固定为 ‘admin’, 密码固定为’123456’, 验证码 固定为 ‘8888’
  • 1.获取⽤户输⼊的⽤户名,密码和验证码
  • 2.先判断验证码是否正确,如果正确打印输出验证码正确,再判断⽤户名和密码是否正确
  • 3.如果验证吗不正确,直接输出 验证码不正确,请重新输⼊
user_name = input('请输入用户名:')
password = input('请输入密码:')
check_number = input('请输入验证码:')
if check_number == '8888':
    if user_name == 'admin' and password == '123456':
        print(f'验证通过,用户{user_name}')
    else:
        print('用户名错误或密码错误')
else:
    print('验证码不正确,请重新输⼊')
  • 石头剪刀布游戏,玩家从控制台输入石头、剪刀、布,程序随机出拳,判断胜负。
import random
player = int(input('请出拳,石头:1\t剪刀:2\t布:3'))
computer = random.randint(1,3)
# 玩家胜利
if (player == 1 and computer == 2) or (player == 2 and computer == 3) or (player ==3 and computer == 1) :
    print('玩家胜利')
# 平局
elif player == computer:
    print('平局')
else:
    print('电脑胜利')

3.2 循环语句 while、for

  • while 循环
    • 处于 while 缩进中的代码,称为是 while 的循环体
while 判断条件:
	判断条件成立执行的代码
	......
  • 例:
i = 0 # 计数器
while i<5:
    print(i)
    i = i+1 
# 打印5次结果,0,1,2,3,4
  • 死循环和无限循环
    • 死循环和⽆限循环,在程序执⾏层⾯上看起来是⼀样的, 都是代码⼀直执⾏不能停⽌。
    • ⽆限循环中,⼀般会存在⼀个 if 判断语句, 当这个判断语句的条件成⽴, 执⾏ break 语句,来终⽌循环。
    • 关键字 break: 当程序代码执⾏遇到 break, break 所在的循环就会被终⽌执⾏。(终⽌循环)
    • 关键字 continue: 当程序代码执⾏遇到 continue, continue 后续的代码不执⾏,但是会继续下⼀次的循环(结束本次循环,继续下⼀次循环)。
    • break 和 continue 只能⽤在循环中
# 死循环
i = 0
while True:
	print(i,'这是一个死循环')
	i = i+1
# 无限循环版石头剪刀布游戏
import random
while True:
	player = int(input('请出拳,石头:1\t剪刀:2\t布:3'))
	computer = random.randint(1,3)
	# 玩家胜利
	if (player == 1 and computer == 2) or (player == 2 and computer == 3) or (player ==3 and computer == 1) :
		print('玩家胜利')
	elif player == computer:
		print('平局')
	else:
		print('电脑胜利')
	is_again = input('是否继续:yes/no')
	# 如果玩家输入为no退出游戏,否则继续
	if is_again == 'no':
		break
  • 【练习】求 1- 100 之间的累加和
i = 1 
sum = 0
while i<=100:
	sum += i # 把每次i值加
	i = i+1  # 每执行一次i值加1
print(sum)
  • for 循环
    • for 循环也称为是 for 遍历, 也可以做指定次数的循环。
    • 遍历: 是从容器中将数据逐个取出的过程。
    • 容器: 字符串/列表/元组/字典。
# for循环遍历字符串
str = 'python_for_循环'
for i in str:
	print(i)
# for循环遍历列表
num = [0,1,2,3,4,5]
for i in num:
	print(i)
# for循环指定次数的循环
# range(n) 可以⽣成 [0, n) 的整数的序列, 不包含 n
# 指定循环5次
for i in range(5):
	print(f'第{i+1}次循环')

# 使用for循环实现1-100的累加和
sum = 0
for i in range(101):
    sum += i
print(sum)

3.3 练习

  • 按以下需求设计一个程序:
  1. 提示⽤户输⼊登录系统的⽤户名和密码。
  2. 校验⽤户名和密码是否正确(正确的⽤户名:admin、密码:123456)。
  3. 如果⽤户名和密码都正确,打印“登录成功!”,并结束程序。
  4. 如果⽤户名或密码错误,打印“⽤户名或密码错误!”,提示⽤户继续输⼊⽤户名和密码登录
  5. 如果⽤户输⼊的⽤户名为“exit”,则退出程序。
while True:
	user_name = input('请输入用户名,exit退出:')
	if user_name == 'exit':
		break
	password = input('请输入密码:')
	if user_name == 'admin' and password == '123456':
		print('登录成功')
		break
	else:
		print('用户名或密码错误,请重新输入!')

四、容器

4.1 字符串

  • 在Python中使用单引号、双引号或三引号标识的内容,就是字符串。Python 不支持单字符类型,单字符在 Python 中也是作为一个字符串使用。
  • 创建字符串
str1 = '123'
# 如果字符串包含单引号,就不能使用单引号标识
str2 = "'python'"
print(str2)
# 如果字符串包含双引号,就不能使用双引号标识
str3 = '"python"'
print(str3)
  • 转义字符
    • 在需要在字符中使用特殊字符时,python 用反斜杠 \ 转义字符。
    • 续行符:行尾\
    • 反斜杠符:\\
    • 单引号:\'
    • 双引号:\"
    • 换行:\n
    • 横向制表符:\t
    • 取消转义:在字符串的前边加上 r"", 字符串中的 \ 就不会进行转义。
  • 下标(索引)
    • 下标用来标识容器中元素的位置,一般使用的正数标,从0开始。
    • 通过下标来获取容器中具体位置的元素:变量[下标]
    • python 中是支持负数下标, -1 表示最后一个位置的数据。
str = 'python'
# P:0 y:1 t:2 h:3 o:4 n:5
# P:-6 y:-5 t:-4 h:-3 o:-2 n:-1
print(str[0])
print(str[-6])
  • 截取
    • 使用截取操作, 可以一次性获取容器中的多个数据(多个数据之间存在一定的规律,数据的下标是等差数列)。
    • 语法:变量[start:end:step]
      • start:表示开始元素的下标。
      • end:表示结束元素的下标,不包含结束元素。
      • step: 表示相邻元素的间隔长度。
str = 'python'
# P:0 y:1 t:2 h:3 o:4 n:5
# P:-6 y:-5 t:-4 h:-3 o:-2 n:-1
# 1.获取tho字母,start=2,end=5,step=1
print(str[2:5])
print(str[-4:-1])
# 2.获取'pto'字母,start=0,end=5,step=2
print(str[0:5:2])
print(str[-6:-1:2])
# 3.获取thon字母
print(str[2:])
print(str[-4:])
# 逆序输出字符串
print(str[::-1])
  • 字符串查找方法 find()
    • 语法:变量a.find(变量b)
    • 在变量a中查找是否具有匹配变量b的字符串,如果存在,返回第一次出现的下标,不存在则返回-1。
str = 'python'
str1 = 'th'
str_index = str.find(str1)
if str_index == -1:
    print(f'{str1}字符串不存在')
else:
    print(f'{str1}存在,第一次出现的位置{str_index}')
  • 字符串替换 replace()
    • 语法:变量.replace(old,new,count)
    • old:表示要替换的字符串
    • new:表示替换的字符串
    • count:表示替换的次数,不写默认全部替换。
    • 替换并不会改变原来的字符串。
str = '张三今年18,李四今年18'
str1 = '20'
str_replace = str.replace('18',str1)
print(str_replace)
str_replace = str.replace('18',str1,1)
print(str_replace)
  • 字符串拆分 split()
    • 语法:变量.split('分隔字符')
    • 分隔字符:按指定的分隔字符将字符串分隔。
    • 返回结果为列表,列表中的元素就是分隔后的字符串。
str = 'python is\tnot easy to learn at all'
# 默认按空白字符分隔
str_list = str.split()
print(str_list)
# 空格分隔
str_list = str.split(' ')
print(str_list)
# 指定字符分隔
str_list = str.split('easy')
print(str_list)
  • 字符串连接 join()
    • 语法:字符串.join(容器)
    • 容器一般是列表 , 将字符串插入到列表相邻的两个数据之间,组成新的字符串。
    • 注意点: 列表中的数据必须都是字符串才可以。
str_list = ['python', 'is', 'not', 'easy', 'to', 'learn', 'at', 'all']
# 使用空格连接列表元素组成字符串
str = ' '.join(str_list)
print(str)

4.2 列表

  • 列表中的元素类型可以不同,可以是数字、字符串、嵌套列表。
  • 列表写在方括号[]内,元素之间使用逗号隔开。
  • 和字符串一样,列表也可以被索引和截取,列表被截取后返回一个包含所需元素的新列表。
  • 定义列表
    1. 变量 = list()
    2. 变量 = []
  • 列表查询
    • index()方法
      • 索引查找,找到匹配元素,返回其索引。
      • 和find()一样只返回第一次查找到的位置。
    • count()方法
      • 统计指定元素在列表中出现的次数。
str_list1 = ['python',1,2,2,4,2]
# 使用index()查找2的位置
print(str_list1.index(2))
# count()统计2出现的次数
print(str_list1.count(2))
  • 添加数据append()
    • 语法:list.append(数据)
    • 向列表的末尾添加一个新元素。
    • 无返回值。
  • 删除数据方法pop()
    • 语法:list.pop(index)
    • 根据下标删除列表中的数据。
    • 返回删除元素的值。
# 定义空列表
list1 = []
# 添加元素ABC
list1.append('A')
list1.append('B')
list1.append('c')
print(list1)
# 删除元素ABC
print(list1.pop(0))
print(list1.pop(0))
print(list1.pop(0))
  • 列表的反转reverse()
    • list[::-1]:得到一个新的列表, 原列表不会改动。
    • list.reverse():直接修改原列表的数据。
str = 'python'
list = []
for i in str:
    list.append(i)
print(list)
# 截取操作逆置元素
print(list[::-1])
# 反转列表
list.reverse()
print(list)
  • 列表排序
    • list.sort():升序,直接在原列表中排序。
    • list.sort(reverse=True):降序,直接在原列表中排序。
  • 列表的嵌套
# 嵌套列表
str_list = [['python','c','c++'],['java','c#','html']]
# 元素python
print(str_list[0][0])
# 输出元素c、c#
for list in str_list:
    print(list[1])

4.3 元组

  • python 的元组与列表类似,不同之处在于元组的元素不能修改。
  • 元组使用小括号 ( ),列表使用方括号 [ ]。
  • 元组中的数据不能修改,因此只能查询, 如 index, count 。
# 定义元组  
my_tuple = ()  
my_tuple1 = (1, 2, 'python', True)  
  
# 类型转换:列表转元组  
my_list = ['A', 'B', 1, 2]  
print(type(my_list))  # 输出: <class 'list'>  
  
my_list_tuple2 = tuple(my_list)  
print(type(my_list_tuple2))  # 输出: <class 'tuple'>

4.4 字典

  • 字典dict,使用花括号{}表示。
  • 由键(key)值(value)对组成,key:value,多个键值对之间使用逗号隔开。
  • 字典的键不能重复,主要使用字符串类型,可以是数字。
  • 字典中没有索引。
# 定义字典
my_dict = {}
my_dict1 = {'name':'张三','age':18,'sex':'男'}

# 修改键值对
my_dict1['age'] = 22
print(my_dict1)

# 添加键值对
my_dict1['id'] = 101
print(my_dict1)
  • 删除数据
    • dict.pop('key')
  • 查询
    • dict['key']:key存在返回值,不存在会报错。
    • dict.get('key'):key存在返回值,不存在返回None
my_dict1 = {'name':'张三','age':18,'sex':'男','id':101}
# 删除键值对
my_dict1.pop('id')
print(my_dict1)

# 查询
print(my_dict1['name'])
print(my_dict1.get('id'))
  • 遍历
my_dict1 = {'name':'张三','age':18,'sex':'男'}
# 遍历
# 遍历字典的键
for i in my_dict1:
    print(i)
# kes()可以获取字典中所有的键
for i in my_dict1.keys():
    print(i)

# 遍历字典的值,values()可以获取字典中所有的值
for i in my_dict1.values():
    print(i)

# 遍历字典的键和值,items() 获取的是字典的键值对
for k,v in my_dict1.items():
    print(k,v)

4.5 练习

  1. 有一个列表,判断列表中的每一个元素是否以s或e结尾,如果是,则将其放入一个新的列表中,最后输出这个新的列表。
    • my_list = [“red”, “apples”, “orange”, “pink”, “bananas”, “blue”, “black”, “white”]
my_list = ["red", "apples", "orange", "pink", "bananas", "blue", "black", "white"]
# 定义空列表存储数据
my_new_list = []
# 遍历列表所有元素
for i in my_list:
    # 取每个元素最后一个字符
    str = i[-1]
    # 判断
    if str == 's' or str == 'e':
        # 匹配则添加到新列表中
        my_new_list.append(i)
print(my_new_list)
  1. 使用 input 输入 5 个学生的名字存入列表,随机的获取一个学生的名字并打印。
import random
my_list = []
for i in range(5):
    i = input("请输入名字:")
    my_list.append(i)
print(my_list)
# 生成一个0~4之间的随机数,包括0和4
name_index = random.randint(0,4)
name = my_list[name_index]
print(name)

五、函数

  • 函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段。
  • python提供了许多内建函数,比如print()、input()、type()、len()等。也可以自己创建函数,这种函数叫做用户自定义函数。
  • 定义函数的好处: 减少代码冗余(重复的代码不需要多次书写),提⾼编程效率。

5.1 函数的定义和调用

  • 定义函数的规则:
    1. 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
    2. 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
    3. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
    4. 函数内容以冒号 : 起始,并且缩进。
    5. return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None。
def 函数名(参数列表):
	函数体(函数中的代码)
	......
  • 函数调用,就是使用函数代码块的过程。
    • 语法:函数名(参数)
# 比较两个数的大小,并返回较大的数
def max(a,b): # 括号内的是形参,用于传递数值
	if a > b:
		return a
	else:
		return b
a = int(input('请输入第一个数:'))
b = int(input('请输入第二个数:'))
print(f'较大数为:{max(a,b)}') # 实参,具有实际值,有多少个形参就要有多少个实参
  • 函数的嵌套调用,就是在一个函数中调用另一个函数
# 计算两数之和与另一个的乘积
# 计算两数之和
def add(a,b):
	return a+b

# 计算两数之和与另一个数的乘积
def mutiply(x,y,z):
	return add(x,y)*z

# 调用函数
print(mutiply(1,2,3))

5.2 函数的返回值

  • 返回值:函数执行的结果。
    1. 在⼀个函数中,想要返回⼀个数据(想要有返回值), 需要使⽤ return 关键字。
    2. 在函数中可能通过各种代码,得到的数据结果,想要在函数外部使⽤,就需要使⽤返回值。
    3. 如果函数有返回值, ⼀般在调⽤的时候会使⽤变量来接收(保存)返回值, 以便后续使⽤。
    4. return 关键字的作⽤:将⼀个数据值返回到调⽤的地⽅; 函数遇到 return 会结束函数的执行;
    5. return 关键字只能⽤在函数中。
    6. 如果⼀个函数没有写 return,可以认为其返回值是 None。
# 实现登录小案例
# 获取用户名
def input_username():
	return input('请输入用户名:')

# 获取密码
def input_password():
	return input('请输入密码:')

# 登录
def login():
	username = input_username()
	password = input_password()
	if username == 'admin' and password == '123456':
		return True
	else:
		return False

# 调用函数
while True:
	option = int(input('请输入选择码:1/登录 2/退出'))
	if option == 1:
		if login():
			print('登录成功')
			break
		else:
			print('登录失败,用户名或密码错误')
	elif option == 2:
		break
	else:
		print('选项错误,请重新选择!')

5.3 模块和包

  • 在Python 中, 每个代码⽂件都可以称为是⼀个模块,在模块中别⼈书写好的功能(变量, 函数, 类),我们可以拿来直接使⽤。
  • 我们⾃⼰写的代码⽂件, 想要作为模块让别⼈使⽤, 代码⽂件名(模块名) 需要满⾜标识符的规则。
  • 想要使⽤别⼈模块中写好的功能,需要先导⼊别⼈写好的模块。
  • as 关键字 , 可以给模块或者功能起别名。
  • 模块的导入方法:
    1. import 模块名:该方式多用于导入系统中常用的模块。
      • 使用模块中的功能:模块名.功能名
    2. from 模块名 import 功能名:导入指定的功能,多⽤于导⼊⾃⼰书写的或者是第三⽅的模块。
# 导入系统中的模块
import random
num = random.randint(1,10)
print(num)

# 导入自己写的模块
# tools.py:
def sum(a,b)
	return a+b
	
# test1.py
from tools import sum

print(sum(1,2)) 
  • 包(package)
    • 将多个模块放在⼀个⽬录中集中管理, 并在这个⽬录中创建⼀个__init__.py⽂件(可以什么都不写), 就是⼀个包。
      在这里插入图片描述
    • 包的导入
# 方式一
import 包名.模块名
# 使用
包名.模块名.工具名

# 方式二
from 包名 import 模块名
# 使用
模块名.工具名

# 方式三
from 包名.模块名 import 工具名
# 使用
工具名

5.4 __name__变量

  • __name__作用: 解决导⼊模块时, 模块中部分代码不想被执⾏的问题。
  • __name__ 变量,是 python 解释器内置的变量(变量的值是⾃动维护的), 每个代码⽂件中,都有这个变量。
  • 在模块中直接右键运⾏代码⽂件, __name__变量的值是'__main__'
  • 如果是被导⼊运⾏代码⽂件, __name__ 变量的值是模块名(⽂件名)。
# tools.py
def sum(a,b):
	return a+b

# __name__变量下的代码只会在当前文件执行,导入的时候不会执行
if __name__ == 'main':
	print(sum(4,5)) # 9

# mytest.py
from tools import sum 
print(sum(1,4)) # 5
  • 【练习】
  • 模拟开发登录功能并测试
  • 需求:
  1. 模拟开发人员实现登录功能,定义一个函数能够接收用户输入的用户名、密码、验证码,根据不同的测试数据返回不同的验证结果。
  2. 已知正确的登录信息(用户名:admin、密码:123456、验证码:8888)。
  3. 要求1:创建login_service.py模块开发登录功能代码,并存放到service包中。
  4. 要求2:创建test_login.py模块开发测试代码,并存放到script包中。
  5. 要求3:至少测试3种测试场景。
# service/login_service.py
'''登录功能'''
def login(username,password,verification_code):
    if username == 'admin':
        if password == '123456':
            if verification_code == '8888':
                print('登录成功')
            else:
                print('登录失败,验证码错误!')
        else:
            print('登录失败,密码错误!')
    else:
        print('登录失败,用户名错误!')

# script/test_login.py
# 导入模块
from service.login_service import login
# 调用登录功能
# 登录成功
login('admin','123456','8888')
# 登录失败
login('张三','123456','8888')
login('admin','123','8888')
login('admin','123456','1234')
  • 代码完成后,执行,这时报了一个错:
    在这里插入图片描述
  • 错误原因是:没有名为‘service’的模块。
  • 检查我的目录结构,确定我的目录文件都没问题。
    在这里插入图片描述
  • 查看python的搜索路径,发现其中没有study目录的路径。
    在这里插入图片描述
  • 解决:把我的项目路径加入系统运行路径
    在这里插入图片描述
  • 编辑settings.json,添加上下面内容,保存退出。
 "terminal.integrated.env.windows": {
        "PYTHONPATH": "/path/to/your/project"
    }

在这里插入图片描述

  • 重启虚拟环境(我这里是venv虚拟环境)
    • 执行:.venv\Scripts\activate
    • 如果遇到PowerShell 的执行策略限制可先执行:Set-ExecutionPolicy RemoteSigned -Scope CurrentUser(允许当前用户运行所有本地 PowerShell 脚本)
  • 配置成功,如下图
    在这里插入图片描述
  • 再次运行test_login.py,发现导入系统模块和自定义模块都无问题。
    在这里插入图片描述

5.5 引用

  • 在定义变量时,变量和数据都会在内存中开辟空间,变量是存储数据的容器,但它们并不直接存储数据本身,而是存储对数据的引用(即内存地址)。
  • 引用实际上指的是,一个变量指向一个对象在内存中的地址。
  • python中所有数据的传递,传递的都是引用。
  • 赋值运算符(=),会改变变量的引用,即只有赋值运算符可以修改变量的引用。
  • 可以使用 id(变量) 函数,查看变量的引用。
# 引用
a = 1 # 本质:将数据1的地址保存到变量a中,通常理解:将1保存到a
b = a # 本质:将变量a中的引用保存到变量b中,通常理解:将a的值赋给b
print(f'a:{a}',{id(a)}) # a:1 {1858864873712}
print(f'b:{b}',{id(b)}) # b:1 {1858864873712}

a = 2 # 本质:将数据2的地址保存到变量a中
# 这里只改变了a的引用(a指向数据2的地址),所以a的值改变,b的引用(b指向数据1的地址)没有改变
print(f'a:{a}',{id(a)})# a:2 {2382150762768} 
print(f'b:{b}',{id(b)})# b:1 {1858864873712}

# 列表的引用
my_list = [1,2,3] # 变量my_list引用列表[1,2,3],即变量my_list指向列表在内存中的地址。
my_list1 = my_list # 将变量my_list1也设置为引用列表[1,2,3]
print(f'my_list:{my_list}',{id(my_list)}) # my_list:[1, 2, 3] {1787426965760}
print(f'my_list1:{my_list1}',{id(my_list1)}) # my_list1:[1, 2, 3] {1787426965760}
# 这里只是改变了列表中下标为1的元素的引用,并没有改变列表的引用
my_list[1] = 5 
print(f'my_list:{my_list}',{id(my_list)}) # my_list:[1, 5, 3] {1787426965760}
print(f'my_list1:{my_list1}',{id(my_list1)}) # my_list1:[1, 5, 3] {1787426965760}
# 由于 my_list 和 my_list1 引用同一个列表对象,所以通过任何一个变量所做的修改都会反映到另一个变量上。

5.6 数据类型的可变与不可变类型

  • 根据内存中的数据是否允许修改,将数据类型分为可变类型与不可变类型。
  • 简单理解: 不使用等号,能不能修改数据值
    • 可变类型: 可以修改
      1. 列表(list)
        • list.append()
      2. 字典(dict)
        • dict.pop()
      3. 集合(set)
    • 不可变类型: 不允许修改
      1. 数字类型(int , float , bool)
      2. 字符串(str)
      3. 元组(tuple)

5.7 局部变量与全局变量

  • 按变量定义的位置将变量分为局部变量和全局变量。
  1. 局部变量:在函数内部定义的变量,称为局部变量。
    • 局部变量只能在当前函数内部使用。
    • 可以在不同函数内部定义名字相同的局部变量。
    • 生命周期(使用范围)
      • 在函数执行(调用)时被创建。
      • 函数执行结束时被销毁。
    • 形参可看作是局部变量,如果想要在函数外部使用局部变量的值,需要使用 return 返回。
  2. 全局变量:在函数外部定义的变量。
    • 全局变量可以在任意函数内访问(读取)。
    • 想要在函数内部修改全局变量的引用,需要使用 global 关键字声明(使用 global 关键字可以声明为全局变量)。
    • 如果在函数内部出现和全局变量名字相同的局部变量,在函数内部使用的是局部变量。
    • 生命周期
      • 代码执行的时候创建, 执行结束销毁。
num = 20 # 全局变量

def sum(a,b):
    i = 10 # 局部变量
    return (a+b)*i

def sum1(a,b):
    return (a+b)*num # 全局变量在函数内部使用
 
def func1():
    global i # 将i声明为全局变量
    i = 20
    a = 10
    return a+i

if __name__ == '__main__':
    print(sum(2,3))
    print(sum1(1,2))
    print(func1())
    print(i+11) # 外部可使用变量i

5.8 函数参数

  • 形参:是函数定义时指定的变量,用于在函数体内部引用传递给函数的值。在函数定义时,形参被放在圆括号()内,并用逗号,分隔。
  • 实参:是调用函数时实际传递给函数的值,可以是常量、变量、表达式的结果等。
  • 参数传递:
    • 传参方式:指的是如何将实参的值传递给形参。
    1. 位置传参:在函数调用的时候按照形参的顺序将实参的值传递给形参。
    2. 关键字传参:在函数调用的时候,指定将实参传递给哪个形参。
    3. 混合使用:
      • 位置参数必须写在关键字参数的前边,关键字参数必须放在位置参数的后边。
      • 同一个形参只能接收一个实参值(不能即使用位置传参和关键字传参给同一个形参传参)。
# 参数传递
def func2(x,y,z):
    print(x,y,z)

if __name__ == '__main__':
    # 位置传参,按形参顺序传递
    func2(1,'python',3)
    # 关键字传递
    func2(y='python',x='java',z='4')
    # 混合使用
    func2(1,y='python',z=5)
  • 缺省参数(默认参数):
    • 定义:在函数定义的时候,给形参一个默认的数据值,这个参数就是缺省参数。
    • 特点:在函数调用时,缺省参数可以不用传递实参值。
      • 如果不传实参值,使用的就是默认值。
      • 如果传递实参值,使用的就是传递的实参值。
    • 缺省参数必须写在普通参数后面。
# 缺省参数
def func3(a,b=1):
    print(a,b)

if __name__ == '__main__':
    # 形参b不传递实参值
    func3(2)
    # 传递实参值
    func3(2,4)
  • 多值参数(可变参数/不定长参数):
    • 使用场景:在函数定义的时候,不确定在调用的时候,实参有多少个,此时可以使用多值参数。
    • 方法:在普通的参数前边加上一个 *,这个参数就变为多值参数。
    • 这个参数可以接收任意多个位置传参的数据,类型:元组。
    • 这个形参一般写作 args(arguments),即 *args。
# 参数顺序 普通参数、缺省参数、可变参数
def func4(普通,缺省,*agrs):
    pass
# 定义一个函数,可以对任意多个数字进行求和计算.
def sum(*agrs):
    num = 0
    # agrs是一个元组
    print(type(agrs))
    for i in agrs:
        num += i
    return num

if __name__ == '__main__':
    print(sum(10,20,30))

5.9 匿名函数

  • 使用 lambda 关键字定义的表达式,称为匿名函数。
  • 语法:lambda 参数,参数:一行代码
    • 只能实现简单功能,只能写一行代码。
    • 匿名函数一般不直接调用,作为函数的参数使用。
# 匿名函数
sum = lambda a,b:a+b 
print(sum(2,3))

# 求两个数字的乘积
num = lambda a,b: a*b
print(num(2,3))

user_list = [{'name':'张三','age':18},{'name':'李四','age':22},{'name':'王五','age':17}]
# 根据字典的 age 键排序,想要对列表中的字典排序,需要 key 形参来指定根据字典中的什么键排序
# 使用函数
def func5(x):
    return x['age'] # 返回这个字典中'age'键对应的值
# sort 方法接受一个 key 参数,用于指定排序的依据。这里,key 参数被设置为 func5 函数
user_list.sort(key=func5) 

# 使用匿名函数
user_list.sort(key = lambda x:x['age'])
print(user_list)

5.10 练习

  • 实现学生管理系统
    • 学生信息录入
    • 学生信息展示
    • 学生信息查询
    • 学生信息删除
# 学生个人信息格式:{'id':'101','name':'张三','age':'18'}
# 学生信息存储格式:[{},{},{}]
 
# 录入学生信息
def Enter_students_information():
    # 学生信息列表
    students = []
    # 录入人数
    num = int(input('请输入学生信息录入人数:'))
    for i in range(num):
        id = input(f'请输入{i+1}个学生的学号:')
        name = input(f'请输入{i+1}个学生的姓名:')
        age = input(f'请输入{i+1}个学生的年龄:')
        # 字典存储学生个人信息
        students_dict = {"id":id,"name":name,"age":age}
        # 单个人信息添加到学生信息列表
        students.append(students_dict)
    return students


# 展示学生信息列表
def display_students_information(students):
    print('-----------学生信息列表------------')
    print('学号            姓名           年龄')
    # students 是一个列表,student是列表中的字典元素,变量students中的student。
    for student in students:
        # 使用dict.get("key")获取学生信息的值(value),可添加一个默认值,当key不存在时可返回默认值
        print(f'{student.get("id","id")}\t\t{student.get("name","name")}\t\t{student.get("age","age")}')
    print('----------------------------------')


# 查询学生信息
def search_student_information(students):
    # 通过学生姓名查找
    name = input('请输入要查询学生的姓名:\n')
    # 标志
    found = False
    for student in students:
        if name == student.get("name"):
            print(f'{name}的信息如下:')
            print(f'学号:{student.get("id","ID")}\t姓名:{student.get("name","name")}\t年龄:{student.get("age","age")}')
            # 存在该学生设为True
            found = True
            break
    if not found:
        print(f'对不起,名字叫{name}的学生不存在!')
    

# 删除学生信息
def delete_student_information(students):
    # 通过学生ID删除
    id = input('请输入要删除学生的学号:\n')
    # students列表使用pop()删除需要一个索引
    # 索引列表  
    index_list = []
    # 计数
    index = 0
    # 遍历students中的student  
    for student in students:  
        if id == student.get("id"):
            # 匹配的索引添加到索引列表
            index_list.append(index)
        index += 1  

    # 从后往前删除,以避免修改尚未处理的索引  
    for i in reversed(index_list):  
        students.pop(i) 
    print(f'学号为{id}的学生信息删除成功!')
        

if __name__ == '__main__':
    students = []
    while True:
        option = input('请输入功能码:\n1录入学生信息\t2展示学生信息\t3查询学生信息\t4删除学生信息\t5退出\n')
        if option == '1':
          students = Enter_students_information()
        elif option == '2':
            display_students_information(students)
        elif option == '3':
            search_student_information(students)
        elif option == '4':
            delete_student_information(students)
        elif option == '5':
            break
        else:
            print('选择码错误,请重新输入!')

六、面向对象

  • 编程方法(编程思想)
    1. 面向过程
      • 把程序看作是一系列需要按照特定顺序执行的过程(函数),每个过程都执行特定的任务。这些过程可以调用其他过程,也可以被其他过程调用,从而形成了一个过程调用网络。
      • 特点:结构化、线性执行、数据与操作分离、可重用性低。
      • 面向过程编程需要详细规划每个过程的执行步骤,以及过程之间的数据传递方式,它更多地考虑的是程序实现的细节。
    2. 面向对象
      • 把程序看作是一组相互协作的对象,每个对象都有自己的属性和方法,并且知道如何与其他对象进行交互。
      • 特点:封装、继承、多态、抽象。
      • 面向对象编程主要关注每个对象的职责和能力,以及它们之间的交互方式,相对而言,它更侧重于程序的整体结构和实现结果,而不过于拘泥于具体的执行细节。

6.1 类和对象

  • 类:是对具有相同特征或者⾏为的事物的⼀个统称,它是抽象的,不能直接使⽤。
    • 指代多个事物
    • 代码中的类是由关键字class定义
  • 对象:是由类创建出来的一个具体存在的事物,可以直接使用。
    • 指代具体的一个事物
    • 代码中用类去创建(实例化)
  • 类的构成(类的三要素):
    1. 类名:指代一类事物的名称,大驼峰命名法。
      • 如:Person、Car
    2. 属性:事物的特征。
      • 如:Person类的属性可能有:name、age、sex等。
    3. 方法:事物的行为。
      • 如:Person类的方法可能有:walk、talk等
  • 使用面向对象编程的步骤:
    1. 设计类(找类的三要素)
    2. 定义类
    3. 创建对象(实例化对象)
    4. 由对象调用类中的方法

6.2 面向对象基本语法

  • 类的基本使用
    1. 定义类
      • 在python中定义类使用关键字class
    2. 创建对象
      • 类名()
      • 一般会使用一个变量引用对象变量 = 类名()
    3. 调用方法
      • 对象.方法名()
# 类的定义
class Person:
    # 在类的缩进中定义类属性和方法,这里没有定义类属性,只定义了方法
    def walk(self): # 方法的本质是函数
        print("行走")
# 创建对象,可将person看作是一个对象,本质是变量保存对象Person()在内存中的地址
person = Person()
# 调用方法
person.walk()
  • self 参数
    • self 是形参,名字可以是任意的变量名,只是习惯性叫self。
    • self 是⼀个普通的参数,按照函数的语法,在调⽤的时候,必须传递实参值。 但是上面并没有传递实参值,是因为 Python 解释器自动将调⽤这个⽅法的对象作为参数传递给 self。
class Person:  
    # 奔跑方法
    def run(self):  
        print("奔跑")
        print(f"self:{id(self)}") # self:2358807954816
  
# 创建一个Person对象  
person = Person()  
# 调用run方法,Python解释器会自动将person对象传递给self  
person.run()
print(f"person:{id(person)}") # person:2358807954816
  • 属性
    • 给对象添加属性:对象.属性名 = 属性值
    • 获取对象的属性值:对象.属性名
    • 在方法中操作属性
      • 给对象添加属性:self.属性名 = 属性值
      • 获取对象属性值:self.属性名
class Person:  
    # 奔跑方法
    def run(self):
        print("奔跑")
        # 添加属性
        self.move_speed = "7"
        # 获取属性值
        print(f"奔跑的速度为:{self.move_speed}米/秒")  
  
# 创建一个Person对象  
person = Person()
# 添加属性  
person.name = '张三'
# 调用run方法,将对象person传递给self,对象person也具有奔跑的属性
person.run()
# 获取属性
print(f"{person.name}奔跑的速度为:{person.move_speed}米/秒")

6.3 魔法方法

  • 在python中,方法名以两个下划线开头并以两个下划线结尾,在满足某个条件的情况下,解释器会自动调用的特殊方法称为魔法方法。
  • 基本魔法方法:
  • 初始化方法__init__
    • 构造器,当一个实例被创建的时候调用的初始化方法。用于初始化新创建的对象的状态或属性。
    • 调用时机:在创建对象之后,解释器会自动调用该方法。
    • 应用场景:初始化对象,为对象添加属性。
    • 注意事项:如果属性是会变化的,可以将属性的值作为参数传递,但在创建对象的时候,必须传递实参值。
class Cat:
    # 初始化方法
    # 属性姓名、颜色、年龄
    def __init__(self,name,color,age): # 
        self.name = name
        self.color = color
        self.age = age
    def eat(self):
        print(f"{self.name}是只{self.color}的猫,爱吃小鱼干。") # 小白是只黑色的猫,爱吃小鱼干。
# 创建对象,自动调用初始化方法,传递姓名、颜色、年龄实参值
cat = Cat('小白','黑色',4)
# 调用方法
cat.eat()
print(f"{cat.name}今年{cat.age}岁了") # 小白今年4岁了
  • __str__方法
    • 调用时机:使⽤print(对象)打印对象的时候,解释器会⾃动调⽤该方法。
      • 如果没有定义__str__⽅法,默认打印的是对象的引⽤地址。
      • 如果定义__str__⽅法,打印的是⽅法的返回值。
    • 应用场景:使⽤print(对象)打印输出对象的属性信息。
    • 注意事项:返回值必须是一个字符串类型。
class Pig:
    # 初始化方法
    def __init__(self,name,color,age):
        self.name = name
        self.color =color
        self.age = age
    def __str__(self):
        return f"{self.name}是只{self.color}的小猪,它今年{self.age}岁了"
    def talk(self):
        print(f"{self.name}说要去踩水坑")

pig = Pig('佩奇','粉色',3)
print(pig) 
# 定义了__str__方法输出:佩奇是只粉色的小猪,它今年3岁了
# 没定义__str__方法输出:<__main__.Pig object at 0x00000213EACBB790> 
pig.talk() # 佩奇说要去踩水坑

6.4 封装

  • 封装指的是将对象的属性和方法组合在一起,并对外界隐藏对象的具体细节,仅通过公开的方法与外界进行交互。
  • 在python中,封装主要通过类属性和类方法来实现。
  • 封装的好处
    • 灵活性:通过封装,可以轻松地修改类的内部实现,而不会影响使用该类的代码。
    • 安全性:可以防止外部代码直接访问或修改对象的敏感属性。
    • 可维护性:封装使得代码更加模块化,易于理解和维护。
  • 封装练习
# 进⼊某 Web 项⽬登录⻚⾯,输⼊⽤户名、密码、验证码之后登录系统
# 设计类
# 类名:LonginPage
# 属性:用户名、密码、验证码
# 方法:登录
class LoginPage:
    def __init__(self,userName,passWord,verificationCode):
        self.userName = userName
        self.passWord = passWord
        self.verificationCode =verificationCode

    def login(self):
        if self.userName == 'admin' and self.passWord == '123456':
            if self.verificationCode == '8888':
                print(f"{self.userName}登录成功!")
            else:
                print("验证码错误!")
        else:
            print("登录失败,用户名或密码错误,请重新登录!")

loginPage = LoginPage('admin','123456','8888')
loginPage.login()

6.5 继承

  • 继承是面向对象编程中的一种机制,它允许一个类(子类)继承另一个类(父类)的属性和方法。通过继承,子类可以重用父类的代码,并添加或覆盖自己的属性和方法。
    • 单继承:一个子类只能继承一个父类。
    • 多继承:python中支持多继承,一个子类可以继承多个父类。
  • 继承的好处:减少代码冗余,重复代码不要多次书写,提高编程效率。
  • 继承的传递性: C 继承 B,B 继承 A,C 可以使用 A 类中的属性和方法。
# 继承
# 动物类,父类/基类
class Animal:
    # 动物具有姓名、年龄、颜色共同属性
    def __init__(self,name,age,color):
        self.name = name
        self.age = age
        self.color = color
    # 动物具有吃和睡的共同行为
    def eat(self):
        print(f"{self.name}正在吃东西")
    def sleep(self):
        print(f"{self.name}正在睡觉")

# 狗类,子类继承父类
class Dog(Animal):
    # 狗类具有动物类的共同属性和共同行为
    # 狗类的特殊行为,看家
    def look_after_the_door(self):
        print(f"{self.name}正在看家")
        
# 猫类,子类继承父类
class Cat(Animal):
    # 猫类具有动物类的共同属性和共同行为
    # 猫类的特殊行为,抓老鼠
    def catch_the_mouse(self):
        print(f"{self.name}正在抓老鼠")

# 黑猫警长类,继承父类猫类
class BlackCat(Cat):
    # 继承的传递性:黑猫警长类具有猫类的特殊行为也具有动物类的共同属性和行为
    # 黑猫警长的特殊行为,说话
    def talk(self):
        print(f"我是{self.name}")


if __name__ == '__main__':
    # 创建对象小黄
    dog = Dog("小黄",3,"灰色")
    print(f"{dog.name}是一只{dog.color}的小狗,它今年{dog.age}岁了!") # 小黄是一只灰色的小狗,它今年3岁了!
    # 调用动物吃的方法
    dog.eat() # 小黄正在吃东西
    # 调用狗类看门方法
    dog.look_after_the_door() # 小黄正在看家

    # 创建对象小花
    cat = Cat("小花",4,"花色")
    print(f"{cat.name}是一只{cat.color}的小猫,它今年{cat.age}岁了!") # 小花是一只花色的小猫,它今年4岁了!
    # 调用动物类睡觉方法
    cat.sleep() # 小花正在睡觉

    # 创建对象黑猫警长
    black_cat = BlackCat("黑猫警长",5,"黑色")
    print(f"{black_cat.name}是一只{black_cat.color}的猫,它今年{black_cat.age}岁了!") # 黑猫警长是一只黑色的猫,它今年5岁了!
    # 调用动物类吃的方法
    black_cat.eat() # 黑猫警长正在吃东西 父类的父类方法
    # 调用猫类抓老鼠的方法
    black_cat.catch_the_mouse() # 黑猫警长正在抓老鼠 父类方法
    # 调用黑猫警长类说话的方法
    black_cat.talk() # 我是黑猫警长 子类方法
  • 重写(override)
    • 重写是在子类中定义了和父类中同名的方法。
    • 重写的原因:父类中该方法不能满足子类对象的需求。
    • 重写的方式:
      • 覆盖式重写:父类中的功能全部不要,直接在子类中定义和父类中方法名字一样的方法接口,书写新的代码。
      • 扩展式重新:父类中的功能还需要,只是添加了新的功能。先在子类中定义和父类中名字相同的方法,在子类的代码中使用 super().方法名() 调用父类中的功能,接着书写新的功能。
# 重写
class Animal:
    def __init__(self,name):
        self.name = name
    # 动物的共同行为,叫
    def bark(self):
        print(f"{self.name}正在叫!")

class Dog(Animal):
    # 覆盖式重写
    def bark(self):
        print(f"{self.name}正在汪汪叫!")

class Cat(Animal):
    # 扩展式重写
    def bark(self):
        super().bark() # 调用父类叫的方法
        print(f"{self.name}正在喵喵叫!")
        
if __name__ == '__main__':
	dog = Dog("小黄")
	dog.bark() # 小黄正在汪汪叫!
	cat = Cat('小白')
	cat.bark() # 小白正在叫!小白正在喵喵叫!

6.6 多态

  • 多态是指不同的对象可以通过相同的接口调用方法,而这些方法在不同对象上的具体实现可以不同。多态允许使用统一的接口来处理不同类型的对象。
# 多态 不同子类对象调用相同方法产生不同结果
# 求三角形和矩形的面积
# 定义一个父类(形状)
class Shape:
    def __init__(self,shapeName,width,height):
        self.shapeName = shapeName
        self.width = width
        self.height = height
    # 形状都有求面积的方法,方法因形状而异,因此父类方法无法满足子类求面积的需求
    def area(self):
        print("求形状的面积")

# 定义一个子类(矩形)继承Shape
class Rectangle(Shape):
    # 重写求面积方法
    def area(self):
        return self.width * self.height
    
# 定义子类(三角形)继承Shape
class Triangle(Shape):
    # 重写求面积方法
    def area(self):
        return (self.width * self.height) * 1/2
    
# 定义一个函数,它接受一个Shape对象并调用其area方法
def print_area(shape):
    print(f"{shape.shapeName}的面积为:{shape.area()}")
    
if __name__ == '__main__':
    # 创建矩形对象
    rectangle = Rectangle("矩形",2,3)
    # 创建三角形对象
    triangle = Triangle("三角形",2,3)

    # 调用输出方法
    print_area(rectangle) # 矩形的面积为:6
    print_area(triangle) # 三角形的面积为:3.0

6.7 属性和方法的访问权限

  • 在python 中,定义类的时候,可以给属性和方法设置访问权限,即规定在什么地方可以使用。权限一般分为两种: 公有权限和私有权限。
  • 公有权限:
    • 公有属性和方法是在类定义中不加任何特殊前缀直接定义的。
    • 可以在任何地方访问和使用,只要有对象就可以访问和使用。
  • 私有权限:
    • 只能在类内部定义(class 关键字的缩进中)。
    • 只需要在属性名或方法名前面加上两个下划线__方法名或__属性名, 这个方法或者属性就变为私有的。
    • 私有的属性和方法,只能在当前类的内部使用,不能在类外部和子类直接使用。
class Person:
    def __init__(self,name,length_of_service):
        self.name = name # 公有属性
        self.__length_of_service = length_of_service # 私有属性
    
    # 定义外部获取工龄属性值方法
    def get_los(self):
        return self.__length_of_service
    # 定义外部修改工龄属性值的方法
    def set_los(self,length_of_service):
        self.__length_of_service = length_of_service

class Worker(Person):
    def working(self):
        print(f"{self.name}正在工作,他的工龄为{self.get_los()}年") # 通过调用公有方法获取私有属性值
        # print(f"{self.name}正在工作,他的工龄为{self.__length_of_service}年") # 私有属性只有在当前类可以访问
        # 子类无法访问工龄这个私有属性
        # AttributeError: 'Worker' object has no attribute '_Worker__length_of_service'

tom = Worker("汤姆",10)
tom.working() # 汤姆正在工作,他的工龄为10年

6.8 对象、属性、方法的划分

  • 对象的划分
    • 类对象:类,使用class定义的类,在代码执行时,解释器会自动创建的对象。
    • 实例对象:由类对象创建的对象称为实例对象,实例对象可以保存实例的特征值(属性值)。
  • 属性的划分
    • 类属性:类对象具有的特征,是整个类的特征,在类的内部(class 缩进中),方法的外部(def 的缩进外部) 定义的变量。
      • 修改:类对象.属性 = 属性值
      • 访问:类对象.属性
    • 实例属性:是每个实例对象具有的特征, 每个实例对象的特征。一般都是在 __init__ 方法中,使用 self.属性名 = 属性值 来定义。
      • 修改:实例对象.属性 = 属性值
      • 访问:实例对象.属性
  • 方法的划分
    • 类方法:使用 @classmethod 装饰器定义的方法,它们需要类本身作为第一个参数(通常是 cls),而不是实例。类方法通常用于创建或修改类级别的数据。
    • 实例方法:定义在类中的方法,它们需要通过类的实例来调用。实例方法至少有一个参数,通常是 self,它引用调用该方法的对象。
    • 静态方法:使用 @staticmethod 装饰器定义的方法,它们不需要类或实例作为参数。静态方法通常用于执行与类或实例无关的功能。
# Dog类,类对象
import random
class Game:
    top_score = 0 # 类属性,记录游戏最高分,所有实例共享
    # 初始化方法,特殊的实例方法
    def __init__(self,player_name):
        self.player_name = player_name
    # 静态方法,不需要类或实例作为参数
    @staticmethod
    def show_help():
        print("游戏帮助信息")

    # 类方法,参数Cls代表类本身
    @classmethod
    def show_top_score(cls):
        print(f"游戏最高分:{cls.top_score}")
    
    # 实例方法
    def start_game(self):
        self.score = random.randint(10,100)
        print(f"本次得分:{self.score}")
        if self.score > Game.top_score:
            Game.top_score = self.score
    
if __name__ == '__main__':
    # 调用静态方法
    Game.show_help()
    # 调用类方法
    Game.show_top_score()
    # 实例化对象
    game = Game("小李")
    game.start_game()
    Game.show_top_score()
    game.start_game()
    Game.show_top_score()

七、文件和异常

7.1 文件

  • 文件介绍:
    • 可以存储在长期存储设备(硬盘, U盘)上的一段数据即为文件。
    • 计算机只认识 二进制数据(0 和 1),所有的文件在计算机中存储的形式都是二进制,即 0 和 1 ,打开文件看到的是文字而不是 0 和 1 ,是因为打开文件的软件会自动的将二进制转换为文字。
  • 文件的分类:
    • 文本文件
      • 后缀为:.txt、.py、.md、.json
    • 二进制文件
      • 后缀为:.mp3、.mp4、.png、.jpg、.gif、.exe
  • 文件操作
    • 文件操作,使用代码来读写文件。
    • 自动化测试,测试数据是在文件中保存的,需要从文件中读取测试数据,进行自动化代码的执行。
  • 文件操作的步骤:
  • 打开文件:open(file,mode='r',encoding=None)
    • file:表示要打开文件的名字,可使用相对路径和绝对路径。
    • mode = 'r':表示打开文件的方法,参数有r、w、a
      • r:(read)只读打开,如果文件不存在会报错。
      • w:(write)只写打开,如果文件存在,会覆盖原文件。
      • a:(append)追加打开,在文件末尾写入新的内容。
    • encoding = None:表示打开文件的编码格式,即二进制数据和汉字转换的规则。参数有utf-8、gbk
      • utf-8:将一个汉字转换为3个字节(B)的二进制数据。
      • gbk:将一个汉字转换为2个字节的二进制数据。
    • 返回值:文件对象,后续对文件的操作都需要这个对象。
  • 读文件:变量 = 文件对象.read()
    • 返回值:返回读取到的文件内容,类型为字符串。
  • 写文件:文件对象.write()
    • 参数:写入文件的内容,类型字符串。
    • 返回值:写入文件中的字符数。字符串的长度,一般不关注。
  • 关闭文件:文件对象.close()
    • 如果是写文件,会自动保存(即将内存中的数据同步到硬盘中)。
# 文件操作
# test.txt "这是一个文本文件"
# 只读方式打开文件,data变量引用文件对象
data = open('./test.txt',mode='r',encoding='utf-8')
print(data) # <_io.TextIOWrapper name='./test.txt' mode='r' encoding='utf-8'>
# 读取test.txt文件内容
test_content = data.read()
print(test_content) # 这是一个文本文件
# 关闭文件
data.close()

# 另一种文件打开方式,会自动关闭文件
# 以只写的方式打开文件,如果文件存在会覆盖原文件
with open('./test.txt',mode='w',encoding='utf-8') as f:
    print(f)
    # 写入内容
    f.write('这是写入内容')
with open('./test.txt',mode='r',encoding='utf-8') as f:
    print(f.read()) # 这是写入内容

# 以追加方式打开文件,在文件末尾写入新的内容。
with open('./test.txt',mode='a',encoding='utf-8') as f:
    f.write('这是追加的内容')
with open('./test.txt',mode='r',encoding='utf-8') as f:
    print(f.read()) # 这是写入内容这是追加的内容

# 其他打开方式 r+(读写,文件必须存在)、w+(读写,文件不存在则创建,存在则清空)、a+(读写,文件不存在则创建,存在则在末尾追加)
with open('./test.txt',mode='r+',encoding='utf-8') as f:
    data = f.read()
    print(data)
    f.write('\nhello python')
  • 按行读取文件:文件对象.readline()
    • 一次性读取一行文件,返回到读取的内容。
# 读写方式打开文件,清空test.txt文件内容
with open('./test.txt','w+',encoding='utf-8') as f:
    print(f.read())
    f.write('hello python\n文件操作练习')

with open('./test.txt',mode='r',encoding='utf-8') as f:
    print(f.read()) 
    # hello python
    # 文件操作练习

with open('./test.txt',mode='r',encoding='utf-8') as f:
    data = f.readline()
    print(data) # hello python
    data1 = f.readline()
    print(data1) # 文件操作练习
  • 读取大文件
# 读取大文件
with open('./test.txt',encoding='utf-8') as f:
    while True:
        data = f.readline() # 按行读取,读完会返回空字符
        if data == '': # 判断是否读完,是则结束循环
            break
        else :
            print(data)
  • JSON 文件
    • json 文件本质上是文本文件,但由于其结构类似于Python中的字典和列表,因此使用专门的库来处理它们会更为方便。在Python中,json模块提供了处理JSON数据的方法,可以很方便地将JSON数据转换为Python的字典或列表。
    • json 文件介绍:
      1. json ⽂件,是⼀种基于⽂本,独⽴于语⾔的轻量级数据交换格式。基于⽂本,⽂本⽂件,不包含图⽚,视频等。
      2. 独⽴于语⾔,不是某⼀种语⾔特有的。
      3. 轻量级,相同的数据量,json ⽂件的占⽤的⽂件大小相对较小。
      4. 数据交换格式,后端服务器和前端页面交换数据使⽤的格式。
      5. 在⾃动化测试中经常⽤来存放测试数据,⽂件后缀名为: .json
    • json 语法:
      1. json中的数据类型:
        • 对象(object):{} 对应python中的字典。
        • 数组(array):[] 对应python中的列表。
        • 字符串(string):"",必须使用双引号,对应python中的str。
        • 数字类型(number):对应python中的int、float
        • 布尔类型:true、false 对应python中的True、False。
        • 空值:null 对应python中的None。
      2. json ⽂件,是⼀个对象或者是数组,对象和数组可以相互嵌套。
      3. json 中的对象,是由键值对组成的,键必须是字符串类型。
      4. json 中的数据直接使⽤逗号隔开,最后⼀个数据后边不能加逗号。
      5. json 文件的后缀是 .json
[
    {
        "name": "张三",
        "age": 23,
        "address": "北京",
        "isMan": true,
        "skills": [
            "python",
            "c++"
        ]
    },
    {
        "name": "李四",
        "age": 25,
        "address": "北京",
        "isMan": true,
        "skills": [
            "java",
            "c#"
        ]
    }
]
  • 读取json 文件
import json # 导入json库
# 打开test.json 文件,该文件是由两个对象组成的数组
with open('./test.json','r',encoding='utf-8') as f:
    data = json.load(f)
print(data)
print(type(data)) # <class 'list'> json数组对应python列表
# 遍历字典
for json_dict in data:
    print(json_dict)
    # 获取张三的年龄信息
    if json_dict.get("name") == "张三":
        zs_age = json_dict.get("age")
        break
print(type(json_dict)) # <class 'dict'>
print(zs_age)
  • 写入json文件
    • 将python中列表或字典转换为python文件。
    • 使用json.dump()将内容写入json文件。
# 导入json库
import json
# 定义写入内容
user_list =[{"name":"Tom","age":22},{"name":"Jack","age":25}]
# 使用w+方式打开文件
with open('./users.json','w+',encoding='utf-8') as f:
    data = json.dump(user_list,f) # dump(写入数据,文件对象)

# 读取users.json 文件
with open('./users.json','r',encoding='utf-8') as f:
    data = json.load(f)
print(data) # [{'name': 'Tom', 'age': 22}, {'name': 'Jack', 'age': 25}]

7.2 异常

  • 程序在运⾏时,如果Python解释器遇到到⼀个错误,则会停⽌程序的执⾏,并且提示⼀些错误信息,这就是异常。
  • 程序停⽌执⾏并且提示错误信息这个动作,通常称之为: 抛出 (raise) 异常。
  • 捕获异常:
    • 通过捕获异常,程序在遇到错误或异常情况时不会立即终止,而是可以处理这些异常,并在处理完毕后继续执行后续的代码。
  • 捕获异常的基本语法
    • 在捕获异常的过程中,程序会检查某个代码块(通常称为try块)是否在执行时发生了异常。如果发生了异常,程序会立即跳转到与该异常类型相匹配的except块,并执行该块中的代码来处理该异常。
# 捕获任意类型的异常
try:
	# 可能发生异常的代码
except:
	# 发生异常执行的代码
  • 常见异常类型:
    1. Exception:标准异常的父类,用来捕获不清楚异常类型的异常。
    2. SyntaxError:语法错误,通常发生在代码不符合python的语法规则时。比如缺少冒号,括号没有正确闭合等。
    3. IndentationError:缩进错误,python使用缩进来区分不同的代码块,缩进错误通常发生在不小心改变了缩进级别时。
    4. TypeError:类型错误,当尝试执行的操作不支持该类型的数据时,就会发生类型错误。比如将整数和字符串相加。
    5. AttributeError:属性错误,通常发生在尝试访问对象不存在的属性或方法时。
    6. NameError:名称错误,当程序试图访问一个未被定义的变量时,就会抛出名称错误。
    7. IndexError:索引错误,通常发生在尝试访问列表或其他序列类型的索引超出范围时。
    8. ValueError:值错误,操作或函数接收到一个具有正确类型但不适合该操作的参数时引发。比如将字符串转换为整数时遇到非数字字符。
    9. ZeroDivisionError:除零错误,发生在尝试将一个数除以零时。
  • 捕获指定类型的异常:
# 捕获指定类型的异常
try:
    num = int(input('请输⼊⼀个整数数字:'))
    num1 = 8 / num
    print(num1)
except ValueError:
    print('输⼊的内容⾮数字,请重新输⼊')
except ZeroDivisionError:
    print('不能输出数字 0, 请重新输⼊')

# 捕获未知类型异常
try:
    num = int(input('请输⼊⼀个整数数字:'))
    num1 = 8 / num
    print(num1)
except Exception as e:
    print(f'发⽣了异常, {e}')

八、Unittest 框架

  • Unittest 框架是python自带的单元测试框架,它支持自动化测试,可以帮助开发人员确保代码按预期工作,并在代码更改时快速检测到任何引入的错误。
  • 使用的原因:
    1. 能够组织多个用例去执行。
    2. 提供丰富的断言方法。
    3. 能够生成测试报告。
  • 框架组成部分(核心要素):
    1. TestCase:测试用例,是所有测试用例的父类。它提供了一系列的方法来定义测试逻辑,并包含断言来验证测试结果。
    2. TestSuite:测试套件,是一个容器,用于组织多个测试用例。
    3. TestLoader:测试加载,用于加载测试用例,并将它们组织成测试套件。
    4. TestRunner:测试运行,用于运行测试套件,并输出测试结果。
    5. Fixture :测试夹具,指的是在测试用例执行前后自动执行的代码结构,用于设置和清理测试环境。
    6. TestResult:测试结果,用于存储测试结果。它跟踪测试的执行情况,包括哪些测试通过了,哪些失败了,以及失败的原因。

8.1 Unittest 基本使用

  • TestCase 测试用例
    • 对于简单测试用例,我们可以直接通过继承unittest.TestCase类来定义测试类。
    • 步骤:
      1. 导包:import unittest
      2. 定义测试类:新建测试类必须继承unittest.TestCase。
      3. 定义测试方法:测试方法命名必须以test开头。
# 加法函数
def add(a, b):  
    return a + b  
# 导包
import unittest  
# 定义测试类  
class TestMathFunctions(unittest.TestCase):
	# 暂时不使用断言方法  
    # 定义测试方法
    def test_add_01(self):
        result = add(1, 1)
        print("result1=", result)
    def test_add_02(self):
        result = add(1, 0)
        print("result2=", result)
if __name__ == '__main__':  
    unittest.main()
  • TestSuite和TestRunner
    • 如果需要批量执行测试用例,我们就需要使用测试套件TestSuite(将多条测试用例集合在一起)。
    • 步骤:
      1. 导包: import unittest
      2. 实例化套件对象:变量 = unittest.TestSuite()
      3. 添加⽤例⽅法:套件对象.addTest(测试类('测试方法')) 一次添加一条测试用例。
      4. 实例化执行对象:变量 = unittest.TextTestRunner()
      5. 执行对象执行套件对象:执行对象.run(套件对象)
  • TestLoader 测试加载
    • 用来加载TestCase到TestSuite中,即加载满足条件的测试用例,并把测试用例封装成测试套件。
    • 使用unittest.TestLoader,通过该类下面的discover()方法自动搜索指定目录下指定开头的.py文件,并将查找到的测试用例组装到测试套件。
    • 步骤:
      1. 导包: import unittest
      2. 实例化加载对象并加载用例:变量 = unittest.TestLoader().discover('用例所在的目录','用例文件*.py')(变量引用的是套件对象)
      3. 实例化执行对象:变量 = unittest.TextTestRunner()
      4. 执行对象执行套件对象:执行对象.run(套件对象)
  • Fixture 测试夹具
    • 某个模块的测试用例的测试步骤可能会涉及到多个操作,比如,测试wed项目的登录功能,测试步骤可能是1.打开浏览器 -》2.进入登录页面 -》3.输入用户名、密码、验证码 -》4.关闭登录页面 -》5.关闭浏览器。
      • 这里操作步骤1和5,执行完所有测试用例可能只需要执行一次。
      • 操作步骤2和4,每条测试用例也只需要执行一次。
      • 因此需要一个能够在测试用例执行前后自动执行的代码结构——Fixtrue。
  • Fixture控制级别
    1. 方法级别:运行一次测试方法就会自动执行一次。
      • 前置:def setUp(self),每个用例执行前都会调用一次。
      • 后置:def tearDown(self),每个用例执行后都会调用一次。
    2. 类级别:在类中所有的测试⽅法执⾏前后会⾃动执⾏的代码,只执⾏⼀次。
      • 前置:@classmethod def setUpClass(cls)
      • 后置:@classmethod def tearDownClass(cls)
import unittest

class TestLogin(unittest.TestCase):
    # 方法级别
    def setUp(self):
        print('2. 打开登录页面')
    def tearDown(self):
        print('4. 关闭登录页面')
    
    # 类级别
    @classmethod
    def setUpClass(cls):
        print('1. 打开浏览器')
    @classmethod
    def tearDownClass(cls):
        print('5. 关闭浏览器')
     
    # 测试方法
    def test_1(self):
        print('3. 输⼊⽤户名密码验证码,点击登录 ')
    def test_2(self):
        print('3. 输⼊⽤户名密码验证码,点击登录 ')
    def test_3(self):
        print('3. 输⼊⽤户名密码验证码,点击登录 ')

if __name__ == '__main__':
    unittest.main()

8.2 断言

  • 什么是断言?使用代码自动判断预期结果和实际结果是否相符。
  • 常见断言方法:
    1. assertTrue(x):验证x是否为true,如果为false,测试将失败。
    2. assertFalse(x):验证x是否为false,如果为true,测试将失败。
    3. assertEqual(a,b):验证a是否等于b,如果不相等,测试将失败,并显示两个值的差异。
    4. assertNotEqual(a,b):验证a和b是否不相等,如果相等,测试将失败。
    5. assertIsNone(x):验证x是否为None,如果不为空,测试将失败。
    6. assertIsNotNone(x):验证x是否不为None,如果为空,测试将失败。
    7. assertIn(a,b):验证a是否为b的子集,如果a不在b中,测试将失败。
    8. assertNotIn(a,b):验证a是否不是b的子集,如果a在b中,测试将失败。
  • 使用断言方法测试求和函数
# 求和函数
def add(a,b):
    return a+b

# 导包
import unittest
from common.tools import add
# 定义测试类
class TestAdd(unittest.TestCase):
    def test_add_case1(self):
        self.assertEqual(1,add(1,0))
    def test_add_case2(self):
        self.assertEqual(2,add(1,1))
    def test_add_case3(self):
        self.assertEqual(3,add(1,2))
    def test_add_case4(self):
        self.assertEqual(4,add(1,3))

if __name__ == '__main__':
    unittest.main()

8.3 参数化

  • 通过参数的方式来传递数据,从而实现数据和脚本分离。并且可以实现用例的重复执行。(在书写用例方法的时候,测试数据使用变量代替,在执行的时候进行据说传递)
  • unittest 测试框架,本身不支持参数化,但是可以通过安装unittest扩展插件 parameterized 来实现。
  • 安装parameterized插件:pip install parameterized
  • 查看安装插件:pip list
    在这里插入图片描述
  • 使用parameterized插件
# 导包
import unittest
# 导入求和函数模块
from common.tools import add
# 导入参数化模块
from parameterized import parameterized
# 测试数据
data = [(1,2,3),(2,3,5),(2,2,4),(3,5,8)]
# 定义测试类
class TestAdd(unittest.TestCase):
    # parameterized装饰器将测试数据作为参数传递给测试方法
    @parameterized.expand(data)
    def test_add_case(self,a,b,expect):
        self.assertEqual(expect,add(a,b))

if __name__ == '__main__':
    unittest.main()

8.4 测试报告

  • 使用第三方的报告模版,生成报告HTMLTestReport,本质是TestRunner。
  • 安装HTMLTestReport插件:pip install HTMLTestReport
    在这里插入图片描述
  • 使用:
# 导包
import unittest
# 导入HTMLTestReport模块
from htmltestreport import HTMLTestReport
# 导入测试类模块
from case.test_add_case import TestAdd

if __name__ == '__main__':
    # 实例化套件对象
    suite = unittest.TestSuite()
    # 实例化加载对象
    loader = unittest.TestLoader()
    # 添加测试方法
    suite.addTest(loader.loadTestsFromTestCase(TestAdd))
    # 实例化执行对象
    runner = HTMLTestReport('add_test_report.html','求和函数测试报告') # 参数:file_path(测试报告路径)、title(标题)、description(描述内容)
    # 执行测试套件对象
    runner.run(suite)

8.5 跳过

  • 对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执行(简单来说,不想执行的测试方法,可以设置为跳过)。

  • 直接将测试函数标记成跳过:@unittest.skip('跳过的原因')

  • 根据条件判断测试函数是否跳过:@unittest.skipIf(判断条件, reason='原因') # 判断条件为 True, 执行跳过

  • 以上就是关于python基础知识学习记录的所有内容了,如有错误还望指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值