Python
Elora Rinta!

Python介绍

python常用于回归测试。

python+selenium web自动化(功能测试转换为代码)

python+appium 移动端(手机端app)自动化

python+ requests 接口

Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。python是轻量级语言。

Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。

  • Python 是一种解释型语言: 这意味着开发过程中没有编译这个环节。类似于PHP和Perl语言。
  • Python 是交互式语言: 这意味着,您可以在一个 Python 提示符 >>> 后直接执行代码。
  • Python 是面向对象语言: 这意味着Python支持面向对象的风格或代码封装在对象的编程技术。
  • Python 是初学者的语言:Python 对初级程序员而言,是一种伟大的语言,它支持广泛的应用程序开发,从简单的文字处理到 WWW 浏览器再到游戏。

Python基础代码

标识符

  • 第一个字符必须是字母表中字母或下划线**_** ,不能以数字开头。

    驼峰命名法:

    ​ 大驼峰(又称帕斯卡命名法Pascal Case):通常用于标识符(如变量名、函数名、类名等)的命名。在帕斯卡命名法中,每个单词的首字母都大写,单词之间没有下划线或其他分隔符。:MyName

    ​ 小驼峰(Camel Case):通常用于命名变量、函数、方法等标识符。在小驼峰命名法中,除了第一个单词的首字母小写外,后续单词的首字母都大写,单词之间没有下划线或其他分隔符。,其他大写:myName

    ​ 下划线连接法(Snake Case):每个单词之间用下划线连接my_name

  • 变量一般使用下划线法

  • 标识符的其他的部分由字母、数字和下划线组成。

  • 标识符对大小写敏感。

在 Python 3 中,可以用中文作为变量名,非 ASCII 标识符也是允许的了。

python保留字

1
2
3
4
>['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del',
>'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
>'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with',
>'yield']

多行语句

Python 通常是一行写完一条语句,但如果语句很长,我们可以使用反斜杠 ** 来实现多行语句,例如:

1
2
3
total = item_one + \
item_two + \
item_three

在 [], {}, 或 () 中的多行语句,不需要使用反斜杠 ****,例如:

1
2
total = ['item_one', 'item_two', 'item_three',
'item_four', 'item_five']

数字类型

  • int (整数), 如 1, 只有一种整数类型 int,表示为长整型,没有 python2 中的 Long。
  • bool (布尔), 如 True。
  • float (浮点数), 如 1.23、3E-2
  • complex (复数), 如 1 + 2j、 1.1 + 2.2j

输出print()

1
2
3
4
5
6
7
8
9
10
11
12
# print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end=""
x = "a"
y = "b"
# 换行输出
print(x)
print(y)

print('---------')
# 不换行输出
print(x, end=" ")
print(y, end=" ")
print()

格式化输出

1
2
3
4
5
6
print("%s",%str) #格式化输出字符串,%s可以填充任意类型的数据
print("%d",%a) #有符号的的十进制数
print("%06d",%b) #表示输出的整数显示位数,不足位以0补全,超出位数则原样输出
print("%f",%c) #浮点数
print(".2f",%d) #保留2位小数输出(四舍五入),小数默认显示6位
print("%%") #输出%
1
2
3
4
name = 'ana'
age = 18
height = 1.752
print("her name is %s,her age is %d,her height is %.2fm" % (name, age, height))

1
2
num = 90
print('percentage %d%%' % num)

F-string(f字符串的格式化方法)

1
2
3
1. 需要在字符串前面加上f""或者F""
2. 占位符号统一变为{variable name}
3. 需要填充的变量写在{}
1
2
3
4
5
6
7
8
stu_num = 1
num = 90
name = 'ana'
age = 19
height = 1.821
print(f'her name is {name},age is {age},height is {height:.2f}m,student number is {stu_num:06d},percentage is {num}%')


输入input

1
2
3
a = input('输入你心中想的内容:')
print("你输入的是" + a)
print(type(a))

输入的内容类型是字符串类型

类型转换

1
2
3
4
5
6
7
a = input('输入你的年龄:')
b = int(a)
print("你的年龄是", b)
print("一年后你的年龄是", int(a) + 1)
print(type(a))
print(type(b))

  • int()可以将其他类型转换成int类型

    ​ 可以将float类型转换成整型

    ​ 可以将整数类型字符串转换为整型

  • float()可以将其他类型转换成float类型

    • 可以将int类型转换成浮点型
    • 可以将数字类型字符串(整数、小数)转换为浮点型
  • str()可以将其他类型转换成字符串类型

    • 任何类型都可以使用str()将其转换成字符串,一般都是直接加引号

import与from…import

在 python 用 import或者 from...import来导入相应的模块。

  • 将整个模块(somemodule)导入,格式为:import somemodule
  • 从某个模块中导入某个函数,格式为: from somemodule import somefunction
  • 从某个模块中导入多个函数,格式为: from somemodule import firstfunc, secondfunc, thirdfunc
  • 将某个模块中的全部函数导入,格式为: from somemodule import *
  • import somemodule as alias导入模块指定别名alias

python中的三种波浪线和pep

  • 红色:表示代码的错误,代码没有写完也会出现

  • 灰色:不会影响代码的正常执行,pep8为代码的书写规范。可以在写完后使用ctrl+alt+L格式化

  • 绿色:在引号中,认为书写的内容不是英文单词时会出现

Python基本数据类型

Python 中的变量不需要声明。每个变量在使用前都必须赋值,如果事先不知道赋什么数值,可以先赋值None,variable = None进行初始化。变量赋值以后该变量才会被创建。

在 Python 中,变量就是变量,它没有类型,我们所说的”类型”是变量所指的内存中对象的类型。变量名在赋值时,实际上是将对象的引用(即内存地址)赋值给变量名。因此,变量名实际上是对象的引用,而不是直接对内存地址的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
import ctypes

a = 12 # 变量a指向12的引用(12的内存地址)
b = a # 变量b指向变量a指向的引用,12
print('12的内存地址:', id(a))
print('通过内存地址140714148648072访问值:', ctypes.cast(140714148648072, ctypes.py_object).value) # 12的内存地址
print('12的内存地址:', id(b))
a = 30 # 变量a指向新的引用,30
print('a现在指向30的内存空间,通过a访问', a)
print('30的内存地址:', id(a))
print('b仍然指向之前的引用', b) # b仍然指向之前的引用,12
print('b指向的内存地址', id(b))

Python允许你同时为多个变量赋值,连续赋值从右往左,分别赋值按位置进行赋值。例如:

1
2
3
4
5
6
7
8
9
10
11
a = b = c =1
# 等同于:
c = 1
b = c
a = b
a,b,c=1,2,'hello'
# 等同于:
a = 1
b = 2
c = 'hello'

标准数据类型

Python3 中常见的数据类型有:

  • Number(数字)

    • 整数(int): 表示整数值,例如 42-10
    • 浮点数(float): 表示带有小数点的数值,例如 3.14-0.001
  • String(字符串):表示文本数据,用单引号 ' ' 或双引号 " " 括起来,例如 'hello'"world"

  • bool(布尔类型):表示逻辑值,只有两个值:TrueFalse

  • List(列表):表示有序的可变集合,可以包含任意类型的元素,例如 [1, 2, 'hello']

  • Tuple(元组): 表示有序的不可变集合,可以包含任意类型的元素,例如 (1, 2, 'hello')

  • Set(集合):表示无序的不重复元素的集合,例如 {1, 2, 3}

  • Dictionary(字典):表示键值对的集合,其中每个键都对应一个值,例如 {'name': 'Alice', 'age': 30}

  • NoneType(空值): 表示空值或者缺失值,用 None 表示。

  • bytes(字节串): 表示二进制数据,例如 b'hello'

    -

    • import sys
      
      s = "hello"
      b = b'hello'
      print(sys.getsizeof(s))  # 获取字符串对象 s 占用的内存空间大小
      print(sys.getsizeof(b))  # 获取字符串对象 b 占用的内存空间大小
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32

      ![](image-20240403135732256.png)


      Python3 的六个标准数据类型中:

      - **不可变类型(3 个):**可变类型在创建后其内容无法被修改,每次对不可变类型进行操作时,都会创建一个新的对象

      Number(数字)、String(字符串)、Tuple(元组);

      - **可变类型(3 个):**在 Python 中,可变类型是指可以在创建后修改其内容的数据类型。可变类型的数据结构在内存中的内容可以改变,而不会改变其在内存中的地址。这意味着,对可变类型对象进行操作时,不会创建新的对象,而是直接修改原始对象。

      List(列表)、Dictionary(字典)、Set(集合)。

      -

      - ```python
      list1 = [1, 2, 3]
      print('list1的内存地址:', id(list1))
      print('list1[0]:1的内存地址:', id(list1[0]))
      print('list1[1]:2的内存地址:', id(list1[1]))
      print('list1[2]:3的内存地址:', id(list1[2]))
      list1.append(5)
      print('list1的内存地址:', id(list1))
      list1 = [1, 3, 4] # 指向了一个新的列表对象,原来的列表对象因为没有变量指向它,会被回收
      print('list1的内存地址:', id(list1))
      print('list1[0]:1的内存地址:', id(list1[0]))
      print('list1[1]:3的内存地址:', id(list1[1]))
      print('list1[2]:4的内存地址:', id(list1[2]))
      x = 42
      print('x的内存地址', id(x))
      print('x+1的内存地址', id(x + 1))

number数字

Python3 支持 int、float、bool、complex(复数)

注意:

  • 数值的除法包含两个运算符:**/** 返回一个浮点数,**//** 返回一个整数。
  • 在混合计算时,Python会把整型转换成为浮点数。

查看类型

type()

  • type(obj) 函数返回对象 obj 的类型对象。
  • 例如,type(5) 返回 <class 'int'>,表示整数对象的类型是 int 类型。
1
2
a,b,c,d=2,3.14,True,3+4j
print(type(a),type(b),type(c),type(d))

isinstance()

  • isinstance(obj, classinfo) 函数用于检查对象 obj 是否是指定类型 classinfo 的实例,或者是其子类的实例。
  • classinfo 参数可以是类型对象、类型元组或类型列表。如果 objclassinfo 类型的实例或其子类的实例,则返回 True;否则返回 False
  • 例如,isinstance(5, int) 返回 True,表示整数对象 5int 类型的实例。
1
print(isinstance(a,int))

一定要注意二者的区别和使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A():
pass


class B(A):
pass


a = A()
b = B()
print(type(a))
print(type(b))
print(isinstance(b, A))
print(isinstance(b, B))

String字符串

  • Python 中单引号 和双引号 使用完全相同。

    • 使用三引号**’’’** 或 **”””**可以指定一个多行字符串。
  • 转义符\。

    • 反斜杠可以用来转义,使用 r 可以让反斜杠不发生转义。 如 r”this is a line with \n”\n 会显示,并不是换行。
    • 按字面意义级联字符串,如 “this “ “is “ “string” 会被自动转换为 this is string
    • 字符串可以用 + 运算符连接在一起,用 ***** 运算符重复。
    • print('123\n123')
      print(r'123\n123')
      print('this''is''a''string')
      print('a' + 'bc')
      print('a' * 5)
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14

      - ![](image-20240403153219978.png)

      - Python 中的字符串有两种索引方式,从左往右以 **0** 开始,从右往左以 **-1** 开始。

      - Python 中的字符串不能改变。

      - Python 没有单独的字符类型,一个字符就是长度为 1 的字符串。

      ```python
      word = '字符串'
      sentence = "这是一个句子。"
      paragraph = """这是一个段落,
      可以由多行组成"""

字符串的截取

字符串的截取的语法格式如下:

变量[头下标:尾下标]

索引值以 0 为开始值,-1 为从末尾的开始位置。

尾下标对应的字符不包含

image-20240304160659942

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
str = 'abcdefghijk'
print(str)
print("输出字符串第一个字符:" + str[0])
print("输出字符串最后一个字符:" + str[-1])
print("输出字符串第一个到第3个字符:" + str[0:3])
print("输出字符串第一个到倒数第二个个字符:" + str[0:-1])
print("输出字符串第一个到第3个字符:" + str[:3])
print("从字符串倒数第三个字符开始输出:" + str[-3:])
print("字符串的每个字符间隔1个输出(步长为2):" + str[::2])
print("字符串输出2次:" + str * 2)
print("字符串拼接输出:" + '你好' + str)
print("反转字符串:" + str[::-1])
print('hello\npython')
print(r'hello\npython')

相关函数

len()

len()可以用来获取字符串长度

查找方法find()
1
2
3
4
5
6
7
8
字符串.find(sub_str,start,end)
# 在字符串中查找是否存在sub_str这样的字符串
# start默认为0
# end默认是len()
返回结果:
1. 找到了,返回第一次出现的下标(sub_str第一个字符在字符串中的下标)
2. 没有找到,返回-1

1
2
3
str = 'abcdefghijk'
print(str.find('abc'))

1
2
3
4
str = 'ana and luke and lei'
num = str.find('and')
# 查找第2个and出现的位置
print(str.find('and', num + 1))

1
2
3
4
5
6
7
# 过7游戏
for i in range(1, 100):
if i % 7 == 0 or str(i).find('7') != -1:
print("过")
else:
print(i)

字符串替换replace()
1
2
3
4
字符串.replace(old_str,new_str,count)
# 将old_str替换成new_str
# count:替换的次数,默认全部替换
# 返回替换之后的完整字符串,原来的字符串没有发生改变
1
2
3
4
5
6
# 将str1中所有的g改为G
str1 = 'good good study'
str2 = str1.replace('g', 'G')
print("str1=", str1)
print("str2=", str2)

1
2
3
4
5
6
# 将str1中第一个good改成GOOD
str1 = 'good good study'
str2 = str1.replace('good', 'GOOD',1)
print("str1=", str1)
print("str2=", str2)

1
2
3
4
5
6
7
# 将str1中第二个good改成GOOD
str1 = 'good good study'
str2 = str1.replace('good', 'GOOD', )
str3 = str2.replace('GOOD', 'good', 1)
print("str2=", str2)
print("str3=", str3)

字符串的拆分spilt()
1
2
3
4
5
字符串.split.(sep,maxsplit)
# 将字符串按照sep进行拆分,默认为空白字符(空格,换行\n,tab键\t)
# max_spilt:分隔次数,默认全部分隔
# 返回:将一个字符串拆分成多个存在列表中
# 注意:sep不写,想要指定分割次数,写法如:字符串.spilt(maxspilt=n)
1
2
3
4
5
str = "Hello,my name is Elora"
list1 = str.split()
print(list1)
print(str.split(maxsplit=1))

字符串的连接join()
1
2
3
字符串1.join(列表) # 括号中的内容主要是列表,可以是其他容器
# 将字符串插入到列表中每相邻的两个数据之间,组成一个新的字符串
# 注意:列表中的数据必须都是字符串类型,否则会报错
1
2
3
4
5
6
list1 = ['hello', 'world', 'you']
str1 = ' '.join(list1)
print(str1)
str2 = ' and '.join(list1)
print(str2)

字符串格式化

1
2
3
字符串.format()
1. 在需要使用变量的地方使用{}占位
2. '{},{}...'.format(变量,变量)...
1
2
3
4
5
6
7
stu_num = 1
num = 90
name = 'ana'
age = 19
height = 1.821
print('her name is {},age is {},height is {:.2f}m,student number is {:06d},percentage is {}%'.format(name, age, height, stu_num, num))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
name = 'Alice'
age = 30
# 位置参数:
message = 'My name is {0} and I am {1} years old.'.format(name, age)
print(message)
# 关键字参数:
message = 'My name is {name} and I am {age} years old.'.format(name='Alice', age=30)
print(message)
# 格式化输出:
pi = 3.14159
formatted_pi = 'The value of pi is {:.2f}'.format(pi)
print(formatted_pi) # 输出: The value of pi is 3.14
# 索引和命名参数混合使用:
message = 'My name is {0} and I am {age} years old.'.format(name, age=30)
print(message)

bool布尔类型

在 Python 中,True 和 False 都是关键字,表示布尔值。

布尔类型特点:

  • 布尔类型只有两个值:True 和 False。
  • 布尔类型可以和其他数据类型进行比较,比如数字、字符串等。在比较时,Python 会将 True 视为 1,False 视为 0。
  • 布尔类型可以和逻辑运算符一起使用,包括 and、or 和 not。这些运算符可以用来组合多个布尔表达式,生成一个新的布尔值。
  • 布尔类型也可以被转换成其他数据类型,比如整数、浮点数和字符串。在转换时,True 会被转换成 1,False 会被转换成 0。

注意: 在 Python 中,所有非零的数字和非空的字符串、列表、元组等数据类型都被视为 True,只有 0、空字符串、空列表、空元组等被视为 False。因此,在进行布尔类型转换时,需要注意数据类型的真假性。

list列表

  • 列表可以完成大多数集合类的数据结构实现。列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(嵌套)。
  • 列表是写在方括号 [] 之间、用逗号分隔开的元素列表。
  • 和字符串一样,列表同样可以被索引和截取,列表被截取后返回一个包含所需元素的新列表。截取的语法格式:变量[头下标:尾下标]

索引值以 0 为开始值,**-1** 为从末尾的开始位置。

1
2
3
list1 = list()  # 定义一个空的列表
# list(容器)将其他容器转换成列表
print(list('hello'))

1
2
3
list1 = list()  # 定义一个空的列表
# list(容器)将其他容器转换成列表
print(list(' h ello'))

列表的切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
list1 = ['abcd', 78910, 2.233333, 'hello', 351]
tinylist1 = [123, 'abcdefg']
print(list1)
print(tinylist1)
print("打印整个列表:", end=' ')
print(list1)
print("打印列表的第一个元素:", end=' ')
print(list1[0])
print("打印列表第二到第三个元素(包含第三个元素):", end=' ')
print(list1[1:3])
print("打印列表第二到第三个元素(不包含第三个元素):", end=' ')
print(list1[1:2])
print("打印列表第二个元素:", end=' ')
print(list1[1])
print("打印列表第三个元素到末尾:", end=' ')
print(list1[2:])
print("打印tiny列表两次", end=' ')
print(tinylist1 * 2)
print("打印两个列表", end=' ')
print(tinylist1 + list1)
print("打印两个列表合并后第二个到第五个元素", end=' ')
print((tinylist1 + list1)[1:6])
print("逆转list", end=' ')
print(list1[::-1])

列表查询相关操作

数据下标index()
1
2
3
列表.index(el,start,end)
# 使用和find方法一样
# 区别:index()方法,找到返回第一次出现的下标,没有找到直接报错,find()则是返回-1
1
2
3
4
mylist = [1, 3, 5, 7, 2, 3]
num = mylist.index(3)
print("出现3的下标是:", num)
print("出现4的下标是:", mylist.index(4))

判断是否存在in()
1
2
el in 容器
# 存在返回True,不存在返回False

统计出现次数count()
1
2
列表.count(el)
# 返回元素出现的次数,没有返回数字0

列表修改数据

1
2
3
4
5
6
# 直接更改list元素值
list1 = ['abcd', 78910, 2.233333, 'hello', 351]
list1 = [12321] # list1=[12321]
list1[1:3] = ['a', 'b']
list1[3:4] = [444]
print(list1)

1
2
3
4
5
6
list1 = ['abcd', 78910, 2.233333, 'hello', 351]
list1[0] = 12321
list1[1:3] = ['a', 'b']
list1[3:4] = [444]
print(list1)

列表添加数据

尾部添加(最常用)
1
2
3
列表.append(数据)
# 返回:返回的None(关键字,空),一般就不再使用变量保存返回内容
# 查看打印列表,表示返回值
指定下标位置添加
1
2
3
4
列表.insert(下标,数据)
# 在指定的下标位置添加数据,如果指定的下标位置有数据,原数据后移
# 返回:返回的None(关键字,空),一般就不再使用变量保存返回内容
# 查看打印列表,表示返回值
1
2
3
4
5
6
7
8
9
mylist = ['hello', 'world', 'and']
mylist.append('you')
print(mylist)
mylist.insert(1, 'Lily')
print(mylist)
mylist.append(['him', 'and'])
print(mylist)
mylist.insert(1, ['Manny', 'Sue'])
print(mylist)

列表合并extend()
1
2
3
列表1.extend(列表2) # 将列表2中的所有数据逐个添加到列表1的尾部,不会改变列表1的内存地址
# 返回:返回的None(关键字,空),一般就不再使用变量保存返回内容
# 查看打印列表,表示返回值
1
2
3
4
5
6
7
8
9
# list2拆开添加到mylist
mylist = ['我', '爱']
list2 = list('螺蛳粉')
mylist.extend(list2)
print(mylist)
#list2作为整体加入mylist中
mylist.append(list2)
print(mylist)

列表删除

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

根据下标删除
1
2
3
4
5
列表.pop(下标) #删除指定下标对应的数据
# 下标不写,默认删除最后的数据
# 存在的下标,删除对应下标位置的数据,不存在的下标,报错IndexError
# 返回删除的数据
del.列表[下标]
根据数据值删除
1
2
3
4
列表.remove(数据) 
# 返回None
# 若删除的数据不存在,会报错
# 重复的数据删除第一个,想要删除多个进行循环
1
2
3
4
list1=['h','h','h','h','h','h','h','h','h','h','o','l']
while 'h' in list1:
list1.remove('h')
print(list1)

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

列表的反转和逆置

1
2
1. 列表[::-1] # 得到一个新列表,原列表不会改变
2. 列表.reverse() # 直接修改原列表,返回reverse,列表的内存地址不变
1
2
3
4
5
list1=list('hello')
print(id(list1))
list1.reverse()
print(list1)
print(id(list1))

列表的复制

1
2
3
4
5
6
# 使用场景,修改列表中的数据与修改之前的原始数据进行对比
1. 使用切片
变量 = 列表[:]
2. 使用copy方法
变量 = 列表.copy()
列表1 = 列表2 # 会创建一个新的列表对象,并将 list2 中的元素复制到新的列表对象中。这样做会使得 list1 和 list2 指向不同的内存空间,即它们是两个独立的列表对象。list1 和 list2 是两个不同的变量,它们分别指向两个不同的列表对象,这两个列表对象中的元素相同,但是它们在内存中的地址是不同的。
1
2
3
4
5
6
7
list1=['h', 'e', 'l', 'l', 'o']
list2=['h', 'e', 'l', 'l', 'o']
list3=list1.copy()
print(id(list1))
print(id(list2))
print(list3)
print(id(list3))

列表的排序

列表的排序一般是针对数字进行排序

1
2
列表.sort() # 按照升序排序
列表.sort(reverse=True) # 降序排序

列表去重

去除列表中重复的数据

错误示范
1
2
3
4
5
my_list = [1, 23, 33, 11, 2, 23, 3, 2, 1, '3', 3]
for i in my_list:
while (my_list.count(i) > 1):
my_list.remove(i)
print(my_list)

当检测到列表中该数据需要删除时,列表后方数据会向前移动,所以紧跟在被删除数据后方的元素会不参与遍历

正确示范
思想:放入新容器
1
2
3
4
5
6
7
8
my_list = [1, 23, 33, 11, 2, 23, 3, 2, 1, '3', 3]
new_list=[]
for i in my_list:
if i not in new_list:
new_list.append(i)
else:
continue
print(new_list)

利用集合set特点

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

元组tuple

  • Python 的元组与列表类似,不同之处在于元组的元素不能修改。元组使用小括号 **( )**,列表使用方括号 **[ ]**。

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

1
2
mytuple = tuple(list) #将列表转化为元组
mytuple1 = tuple(str) #将字符串转化为元组

元组中只包含一个元素时,需要在元素后面添加逗号 , ,否则括号会被当作运算符使用:

1
2
3
4
5
tuple1 = (1)
tuple2 = (1,)
print(type(tuple1), tuple1)
print(type(tuple2), tuple2)

元组的常用方法

  • 没有修改方法
  • 元组中可以使用下标和切片获取数据
  • 元组中存在index方法
  • 元组中存在count方法
  • 可以使用in操作

字典

  • 字典是另一种可变容器模型,且可存储任意类型对象。
  • 字典的每个键值 key=>value 对用冒号 : 分割,每个对之间用逗号(,)分割,整个字典包括在花括号 {} 中 ,格式如下所示:
1
变量 = {key1 : value1, key2 : value2, key3 : value3 }
  • 键必须是唯一的,但值则不必。值可以取任何数据类型,但键必须是不可变类型,键一般是字符串,可以是数字,不能是列表
  • 字典不能转化成列表、元组和字符串
1
2
3
4
5
6
7
8
9
10
# 创建空的字典dict1
dict1 = {}
print(type(dict1), dict1)
# 创建空的字典dict2
dict2 = dict()
print(type(dict2), dict2)
dict3 = {'name': 'Marry', 'age': '32', 'height': '1.78', 'is married': True, 'hobbies': ['painting', 'music']}
print(dict3)
print(len(dict3)) # 却决于键的个数

字典的增加修改

1
2
3
字典[键]=数据值
1. 如果键已经存在,就是修改数据值
2. 如果键不存在,就是添加数据(即添加键值对)
1
2
3
4
5
6
7
8
dict1 = {'name': 'Marry', 'age': 32, 'height': '1.78', 'is married': True, 'hobbies': ['painting', 'music']}
print(dict1)
dict1['gender'] = 'female'
print(dict1)
dict1['age'] = 33
print(dict1)
dict1['hobbies'].append('studying')
print(dict1)

1
2
3
4
5
6
7
list1 = []
for i in range(0, 3):
dict1 = {}
dict1['name'] = input("请输入姓名")
dict1['age'] = input("请输入年龄")
list1.append(dict1)
print(list1)

字典的删除

  • 删除指定的键值对
1
2
del 字典[键]
字典.pop(键)
1
2
3
4
5
6
7
8
9
10
11
12
dict1 = {'name': 'Marry', 'age': '32', 'height': '1.78', 'is married': True, 'hobbies': ['painting', 'music'],
'gender': 'female'}
del dict1['gender']
print(dict1)
dict1.pop('age')
print(dict1)
dict1['hobbies'].pop(0)
print(dict1)
dict1['hobbies'].remove('music')
print(dict1)
dict1.clear()
print(dict1)

  • 清空
1
字典.clear()

字典的查询

字典中没有下标,要获取数据需要使用键来获取

  • 使用字典[键]

    1
    2
    3
    # 字典[键]
    # 如果键存在,返回对应数据值
    # 如果键不存在,报错
  • 使用字典.get(键)

    1
    2
    3
    4
    字典.get(键,数据值)
    # 数据值一般不写,默认None
    # 如果键存在,返回对应数据值
    # 如果键不存在,返回括号中书写的数据值(None)
1
2
3
4
5
6
7
8
dict1 = {'name': 'Marry', 'age': '32', 'height': '1.78', 'is married': True, 'hobbies': ['painting', 'music']}
print(dict1['name'])
print(dict1.get('name'))
print(dict1.get('gender'))
print(dict1.get('gender', 'secret'))
print(dict1['hobbies'][0])
print(dict1.get('hobbies')[0])
print(dict1.get('address','no info'))

字典的遍历

对字典的键进行遍历
1
2
3
4
1. for key in 字典:
print(key)
2. for key in 字典.keys(): # 字典.keys()可以获取字典中所有的键
print(key)
对值进行遍历
1
2
for value in 字典.values():  # 字典.values()可以获取字典中所有的值
print(value) # 输出值
对键值对进行遍历
1
2
for k, v in 字典.items(): # 字典.items()可以获取字典中所有键值对
print(k,v) # 输出键值对
1
2
3
4
5
6
7
8
9
10
11
12
13
dict1 = {'name': 'Marry', 'age': '32', 'height': '1.78'}
# 遍历字典的键
for k in dict1:
print(k)
for k in dict1.keys():
print(k)
# 遍历字典的值
print('-' * 30)
for v in dict1.values():
print(v)
print('-' * 30)
for k, v in dict1.items():
print(k, v)

容器总结

  1. 字符串,列表,元组支持加法运算
  2. 字符串、列表、元组支持乘上一个整数
  3. len()在容器中(str、list、dict、tuple)都可以使用
  4. in 在容器中都可以使用,在字典中判断字典的key是否存在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
my_list = [{'id': 1, 'money': 10}, {'id': 2, 'money': 20},{'id': 3, 'money': 30},{'id': 4, 'money': 40}]


def func():
for element in my_list:
if element['id'] % 2 == 0:
element['money'] += 10
else:
element['money'] += 20


func()
print(my_list)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
my_dict = {'登录': [{'desc': '正确的用户名密码', 'username': 'admin', 'password': '123456', 'expect': '登录成功'},
{'desc': '错误的用户名', 'username': 'root', 'password': '123456', 'expect': '登录失败'},
{'desc': '错误的密码', 'username': 'admin', 'password': '123123', 'expect': '登录失败'},
{'desc': '错误的用户名和密码', 'username': 'aaa', 'password': '123123', 'expect': '登录失败'}],
'注册': [{'desc': '注册1', 'username': 'abcd', 'password': '123456'},
{'desc': '注册1', 'username': 'xyz', 'password': '123456'}]}
opt = input("请输入信息:登录/注册") # 获取测试人员的信息
info_list = []
if opt == '登录':
print("获取登录数据")
for i in my_dict.get('登录'):
my_tuple = (i.get('username'), i.get('password'), i.get('expect'))
info_list.append(my_tuple)
print(info_list)
elif opt == '注册':
print("获取注册数据")
for i in my_dict.get('注册'):
my_tuple = (i.get('username'), i.get('password'))
info_list.append(my_tuple)
print(info_list)
else:
print("输入错误")

Python运算符

算术运算符

以下假设变量 a=10,变量 b=21:

运算符 描述 实例
+ 加 - 两个对象相加 a + b 输出结果 31
- 减 - 得到负数或是一个数减去另一个数 a - b 输出结果 -11
* 乘 - 两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 210
/ 除 - x 除以 y(返回浮点类型) b / a 输出结果 2.1
% 取模 - 返回除法的余数 b % a 输出结果 1
** 幂 - 返回x的y次幂 a**b 为10的21次方
// 取整除 - 往小的方向取整数 >>> 9//2 4 >>> -9//2 -5

优先级:() > ** > * / // % > + -

比较运算符

以下假设变量 a 为 10,变量 b 为20:

运算符 描述 实例
== 等于 - 比较对象是否相等 (a == b) 返回 False。
!= 不等于 - 比较两个对象是否不相等 (a != b) 返回 True。
> 大于 - 返回x是否大于y (a > b) 返回 False。
< 小于 - 返回x是否小于y。所有比较运算符返回1表示真,返回0表示假。这分别与特殊的变量True和False等价。 (a < b) 返回 True。
>= 大于等于 - 返回x是否大于等于y。 (a >= b) 返回 False。
<= 小于等于 - 返回x是否小于等于y。 (a <= b) 返回 True。

逻辑运算符

Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20:

运算符 逻辑表达式 描述 实例
and x and y 布尔”与” - 如果 x 为 False,x and y 返回 x 的值,否则返回 y 的计算值。当第一个条件为False时,第二个条件不判断 (a and b) 返回 20。
or x or y 布尔”或” - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。当第一个条件为True,第二个条件不再判断 (a or b) 返回 10。
not not x 布尔”非” - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 not(a and b) 返回 False

当数字不是0时,都表示True

赋值运算符

以下假设变量a为10,变量b为20:

运算符 描述 实例
= 简单的赋值运算符 c = a + b 将 a + b 的运算结果赋值为 c
+= 加法赋值运算符 c += a 等效于 c = c + a
-= 减法赋值运算符 c -= a 等效于 c = c - a
*= 乘法赋值运算符 c *= a 等效于 c = c * a
/= 除法赋值运算符 c /= a 等效于 c = c / a
%= 取模赋值运算符 c %= a 等效于 c = c % a
**= 幂赋值运算符 c * *= a 等效于 c = c ** a
//= 取整除赋值运算符 c //= a 等效于 c = c // a
:= 海象运算符,这个运算符的主要目的是在表达式中同时进行赋值和返回赋值的值。Python3.8 版本新增运算符 在这个示例中,赋值表达式可以避免调用 len() 两次:print(n:=10)输出结果:10

成员运算符

运算符 描述 实例
in 如果在指定的序列中找到值返回 True,否则返回 False。 x 在 y 序列中 , 如果 x 在 y 序列中返回 True。
not in 如果在指定的序列中没有找到值返回 True,否则返回 False。 x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。

条件控制

if elif else

1
2
3
4
5
6
7
if condition_1:
statement_block_1
elif condition_2:
statement_block_2
else:
statement_block_3

1
2
3
4
5
6
age = input('输入你的年龄:')
if int(age) >= 18:
print("满18岁了可以去网吧")
else:
print("未成年不能进入网吧")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
name = input('输入你的用户名:')

if name == 'admin':
print("admin登录成功!")
elif name == 'test':
print("test登录成功!")
else:
print("登陆失败!")

name = input('输入你的用户名:')
if name == 'admin' or name == 'test':
print("%s登陆成功!" % name)
else:
print("登陆失败!")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
date = input("输入今天是星期几:")
if date == '1':
print("今天要去上班")
elif date == '2':
print("才上了一天班呢")
elif date == '3':
print("到周中了")
elif date == '4':
print("今天是疯狂星期四")
elif date == '5':
print("明天就放假了!")
elif date == '6':
print("放假第一天")
elif date == '7':
print("明天就上班了")
else:
print("你输入错误了")

if嵌套

1
2
3
4
5
6
7
8
9
10
11
num = int(input("请输入一个整数"))
if num % 2 == 0:
if num % 3 == 0:
print("数字%d可以整除2和3" % num)
else:
print("数字%d可以整除2,不能整除3" % num)
else:
if num % 3 == 0:
print("数字%d可以整除3,不能整除2" % num)
else:
print("数字%d既不能整除2也不能整除3" % num)

match…case

1
2
3
4
5
6
7
8
9
match subject:
case <pattern_1>:
<action_1>
case <pattern_2>:
<action_2>
case <pattern_3>:
<action_3>
case _:
<action_wildcard>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case 401 | 403 | 404:
return "Not allowed"
case _: # 用于捕获所有未被显式匹配的情况,简单地表示“其他所有情况”
return "Something's wrong with the internet"

mystatus=int(input('输入状态码'))
print(http_error(mystatus))

猜拳游戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import random

while True:
player = input("剪刀石头布,你出什么,想要退出输入0")
if player == '0':
break
randomid = random.randint(0, 2) # 产生0-2之间的随机整数,包含0和2
listcom = ['剪刀', '石头', '布']
computer = listcom[randomid]
match computer:
case '剪刀':
print("电脑出的是%s" % computer)
print("你出的是%s" % player)
match player:
case '剪刀':
print("平局!")
case '石头':
print("你赢了!")
case '布':
print("你输了!")
case _:
print("出错了!")

case '石头':
print("电脑出的是%s" % computer)
print("你出的是%s" % player)
match player:
case '剪刀':
print("你输了!")
case '石头':
print("平局!")
case '布':
print("你赢了!")
case _:
print("出错了!")
case '布':
print("电脑出的是%s" % computer)
print("你出的是%s" % player)
match player:
case '剪刀':
print("你赢了!")
case '石头':
print("你输了!")
case '布':
print("平局!")
case _:
print("出错了!")

循环语句

while循环

1
2
while 判断条件(condition):
执行语句(statements)……

死循环和无限循环

  • 死循环:bug造成的
  • 无限循环:while true,故意设计的。应用场景:书写循环时,不确定循环要执行多少次。无限循环的使用一般会在循环中添加if判断,当if条件成立,使用关键字break终止循环

while

1
2
3
4
5
6
7
8
# 1到100的数字之和
i = 1
sum = 0
while (i <= 100):
sum = sum + i
i = i + 1
print(sum)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1到100的之间的偶数之和
i = 2
sum = 0
while (i <= 100):
sum = sum + i
i = i + 2
print(sum)
# if判断计算1到100的之间的偶数之和
i = 1
sum = 0
while (i <= 100):
i = i + 1
if i % 2 == 0:
sum = sum + i
print(sum)

while…else

如果 while 后面的条件语句为 false 时,则执行 else 的语句块。如果循环由break中断,不执行else中的语句

语法格式如下:

1
2
3
4
while <expr>:
<statement(s)>
else:
<additional_statement(s)>
1
2
3
4
5
6
count = 0
while count < 5:
print(count, " 小于 5")
count = count + 1
else:
print(count, " 大于或等于 5")

1
2
3
4
5
6
7
8
count = 0
while count < 5:
print(count, " 小于 5")
count = count + 1
if count == 3:
break
else:
print(count, " 大于或等于 5")

for循环

for 循环可以遍历容器中的数据,容器:字符串、列表、元组、字典

for循环的一般格式如下:

1
2
3
4
for <variable> in <sequence>:
<statements>
else:
<statements>

for … in …

  • 容器中有多少个数据,循环执行多少次
  • 每次循环会将容器中的数据取出,保存到in前面的变量中
1
2
3
4
5
# 遍历列表中的元素
sites = ["Baidu", "Google", "chrome", "Taobao"]
for site in sites:
print(site)

1
2
3
4
5
word = 'Google'

for letter in word:
print(letter, end=' ')

for … in range()

整数范围值可以配合 range() 函数使用:

1
2
for number in range(1, 6):
print(number,end='')

1
2
3
4
5
6
7
for x in range(6):
if x == 4:
break
print(x, end=' ')

else:
print("Finally finished!")

for … else

在 Python 中,for…else 语句用于在循环结束后执行一段代码。

语法格式如下:

1
2
3
4
for item in iterable:
# 循环主体
else:
# 循环结束后执行的代码

当循环执行完毕(即遍历完 iterable 中的所有元素)后,会执行 else 子句中的代码,如果在循环过程中遇到了 break 语句,则会中断循环,此时不会执行 else 子句。

1
2
3
4
for x in range(6):
print(x,end=' ')
else:
print("Finally finished!")

1
2
3
4
5
6
7
for x in range(6):
if x == 4:
break
print(x, end=' ')

else:
print("Finally finished!")

range()函数

  • range() 函数会生成数列,range(n),可以生成0到n(不包含n)的数字。循环循环n次
  • range(a,b)生成从a到b(不含b)的数列
  • rang(a,b,c)c为步长(增量),可以为负数
1
2
3
4
5
# 获取列表索引
list1 = ['hello', 234, 56, 'wer', 0, 'wqqe']
for i in range(len(list1)):
print(i, list1[i])

image-20240305150210413

还可以使用 range() 函数来创建一个列表

1
2
list(range(5))
[0, 1, 2, 3, 4]

break和continue

  • break:终止循环,循环立即结束
  • continue:跳过本次循环,本次循环剩下的代码不再执行,进行下一次循环
  • break 语句可以跳出 for 和 while 的循环体。else 子句,它在穷尽列表(以for循环)或条件变为 false (以while循环)导致循环终止时被执行,如果从 for 或 while 循环中被break终止,任何对应的循环 else 块将不执行。
  • continue 语句被用来告诉 Python 跳过当前循环块中的剩余语句,然后继续进行下一轮循环。

1
2
3
4
5
6
7
8
9
str = input("输入一个字符串并打印,当输入i的时候i不打印,当输入%的时候结束打印")
for i in str:
if i == 'i':
continue
if i == '%':
break
print(i, end='')
else:
print("打印完成!")

pass语句

pass是空语句,是为了保持程序结构的完整性。

pass不做任何事情,一般用做占位语句

函数

函数的定义

  • 函数必须先定义后调用
1
2
3
4
5
6
7
def functname():
code
...
# 1. def是关键字,用来定义函数的
# 2. 函数名需要遵守标识符的规则
# 3. 处于def缩进中的代码称为函数体
# 4. 函数定义的时候,函数体中的代码不会执行,调用的时候才会执行

函数的调用

1
2
3
functname()
# 1. 函数调用的时候会执行函数体中的代码
# 2. 函数调用的代码要写在函数体外面

文档注释

本质上是注释,只是书写的位置和作用比较特殊

  • 书写位置:在函数名的下方使用,三对双引号进行注释
  • 作用:告诉别人函数如何使用
  • 查看,在调用的时候,将光标放到函数名上使用ctrl+q
  • ctrl+b会转到函数的声明(按住ctrl鼠标左键点击)

函数的嵌套调用

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

注意:

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

函数参数

  • 在函数定义的时候,使用变量代替具体的数据(进行占位),称为形参,在调用函数的时候,传递具体的数值,称为实参
  • 函数的传参传递的是引用

函数传参的方式

位置传参

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

关键字传参

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

混合使用

  • 关键字传参必须写在位置传参的后面
  • 不能给一个形参传递多个数据
1
2
3
4
5
6
7
8
# 求和函数
def my_sum(a, b):
return a + b


num1 = int(input('输入第一个数字'))
num2 = int(input('输入第一个数字'))
print(my_sum(num1, num2))

缺省参数

缺省参数即默认参数

1
2
3
4
5
# 在函数定义的时候,给形参一个默认的数据值,这个形参就变为缺省参数
# 缺省参数的书写要放在普通参数的后边
# 缺省参数在函数调用的时候可以传递实参值,也可以不传递实参值
# 传参即为实参值,不传即为缺省值
def functname(para1,para2='value')

多值参数(不定长参数)

比如print()函数,可以有多个参数

在书写函数不确定参数个数的时候,可以使用不定长参数(多值参数)

  • 不定长位置参数(不定长元组参数)
      1. 在普通参数的前面加上一个*,这个参数就变为不定长位置参数
      2. 这个形参可以接受任意多个位置的传参
      3. 参数将被收集到一个元组中,并传递给函数
      4. 不定长位置参数写在普通参数的后面
      5. 一般写法,不定长位置参数的名字为args,即(*args)
  • 不定长关键字参数(不定长字典参数)
      1. 在普通参数的前面加上两个**,这个参数就变为不定长关键字参数
      2. 这个形参可以接受任意多个关键字的传参
      3. 这些参数将被收集到一个字典中,并传递给函数。
      4. 不定长关键字参数写在所有参数的后面
      5. 一般写法,不定长关键字参数的名字为kwargs,即(**kwargs)
1
2
3
4
5
6
7
8
9
10
11
def my_sum(*args, **kwargs):
num = 0
for i in args:
num += i
for j in kwargs.values():
num += j
print(num)


my_sum(1, 2, a=4, b=5, c=6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def my_sum(*args, **kwargs):
num = 0
for i in args:
num += i
for j in kwargs.values():
num += j
print(num)


mylist = [1, 2, 3, 4]
mydict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# 将列表(元组)中的数据分别作为位置参数进行传参,需要对列表进行解包操作
my_sum(*mylist) # my_sum(1,2,3,4)
# 将字典中的数据作为关键字传参,需要使用**对字典进行解包
my_sum(**mydict) # my_sum(a=1,b=2,c=3,d=4)
my_sum(*mylist, **mydict)

完整的参数顺序

1
2
def functname (普通参数, *args, **kwargs):
pass
1
2
3
4
5
6
7
8
9
10
11
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) # 不定长传参,前面数据都给args,后面kwargs

*在传参的作用

* 的作用是定义位置参数的结束和关键字参数的开始。当在函数定义中的参数列表中出现 * 时,它将标志着其前面的参数只能通过位置传递,而后面的参数只能通过关键字传递。

1
2
3
4
5
6
7
8
def func(a, b, *, d):
print(a, b, d)

func(1, 2, d=3) # 合法的调用方式,指定了参数d的名称
# 输出:1 2 3

func(1, 2, 3) # 错误的调用方式,因为参数d必须通过关键字传递
# 输出:TypeError: func() takes 2 positional arguments but 3 were given

函数的返回值

函数整体执行的结果,函数中得到的数据在后续的代码中还要使用,这时需要将数据作为返回值返回

return之后的代码不会执行

1
2
3
4
5
6
def functname():   # 返回值None
pass
def functname():
return # 返回值None
def functname():
return xx # 返回值xx

变量

引用

  • 变量和数据都是保存在内存中的。在 Python 中,变量是用来存储数据的标识符,它们也存储在计算机的内存中。Python 是一种动态类型语言,这意味着变量的类型是根据赋给它们的值来确定的,而不是在声明变量时显式地指定类型。

    当我们在 Python 中创建变量并给它们赋值时,Python 解释器会在内存中分配一块空间来存储这些变量的值。每个变量都有一个名称(标识符)和一个关联的内存地址,我们可以使用这个名称来访问和操作变量的值。

  • 变量存储的是对象的引用(对象的内存地址)

  • 在python中函数的参数传递以及返回值都是靠引用传递的

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

可变类型和不可变类型

1
2
3
4
5
6
数据类型:int float bool str list tuple dict set
可变类型:数据所在的内存允许修改
不可变类型:数据所在的内存不允许修改
不使用=,变量引用的数据中的内容是否会变化,会变化是可变的,不会变化是不可变的。比方list可以用append()方法进行修改
可变类型:list,dict,set
不可变类型:int,float,bool,str,tuple
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
list1=[1,2,3]
print('list1',id(list1))
list2=[1,2,3]
print('list2',id(list2))
print('list1[0]',id(list1[0]))
print('list1[1]',id(list1[1]))
print('list1[2]',id(list1[2]))
print('list2[0]',id(list2[0]))
print('list2[1]',id(list2[1]))
print('list2[2]',id(list2[2]))
list1[1]=10
print('list1',id(list1))
print('list1[0]',id(list1[0]))
print('list1[1]',id(list1[1]))
print('list1[2]',id(list1[2]))

1
2
3
4
5
6
7
mytuple = (1, 2, [3, 4])
# 元组中的数据不能改变,指的是这三个地址不能改变
print(id(mytuple[-1]))
mytuple[-1][0] = 10 # 修改的是列表中下标为0的位置的引用地址,列表的地址没有变,元组中的内容没有变化(元组的内容是引用地址)
print(mytuple)
print(id(mytuple[-1]))

  • 如果将可变对象(如列表、字典等)作为函数的参数,并在函数内部修改了这些对象的值(而不是重新赋值给参数),那么这些修改将会影响到传递给函数的实际参数(也称为实参)。这是因为在 Python 中,可变对象是通过引用传递的,而不是通过值传递的。

    • 当你传递可变对象(如列表、字典等)给一个函数作为参数时,实际上是将该对象的引用传递给了函数。所谓的引用,实际上是指向存储对象的内存地址的指针。换句话说,函数内部的参数将指向与调用函数时传递的实际对象相同的内存地址,而不是对象的副本。
    • 因此,如果在函数内部修改了这个可变对象(例如,向列表中添加元素、修改字典中的键值对等),则实际上是在修改同一个对象。由于函数参数和调用者之间共享对象的引用,因此对对象的修改会在函数外部产生影响。
    • def modify_list(lst):
          lst.append(4)
          lst[0] = 100
      
      my_list = [1, 2, 3]
      modify_list(my_list)
      print(my_list)  # 输出 [100, 2, 3, 4]
      
    • 需要注意的是,如果在函数内部重新给参数赋值,例如 lst = [4, 5, 6],这将会改变参数的引用,但不会影响到原始对象。
    • def modify_list(lst):
          lst = [4, 5, 6]
      
      
      my_list = [1, 2, 3]
      print(my_list)
      modify_list(my_list)
      print(my_list)
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16

      - ![](image-20240403215607166.png)

      - list中+=操作的本质是extend的操作,所以列表的内存地址不发生改变
      - 不可变对象(如整数、字符串、元组等)是通过值传递的,而不是通过引用传递的。这意味着当你将不可变对象传递给函数作为参数时,实际上是将该对象的值复制一份,然后将这个复制的值传递给函数。

      由于不可变对象的值无法在内存中被修改,因此在函数内部对不可变对象的任何修改都不会影响到原始对象。这意味着函数内部对不可变对象的操作不会影响到函数外部的实际参数。

      ```python
      def modify_string(s):
      s = s + " World"

      my_string = "Hello"
      modify_string(my_string)
      print(my_string) # 输出 Hello

交换两个变量的值

1
2
3
4
5
6
7
8
# 数学方法
a = 10
b = 20
a = a + b
b = a - b
a = a - b
# Python特有
a, b = b, a #先组包,后拆包

组包(pack)和拆包(unpack)

  • 组包:将多个数据值使用逗号连接的过程,组成元组

  • 拆包:将容器中数据值使用多个变量分别保存的过程,注意:变量的个数和容器中数据的个数要保持一致

  • 赋值运算符先执行=右边的内容,执行的结果保存到等号左边的变量中

  • 注意:对字典进行拆包得到的是字典的键

    1
    2
    3
    4
    5
    6
    dict1={'name':'Ana','age':18,'height':12}
    a,b,c=dict1
    print(a,b,c)
    print(dict1[a])
    print(dict1[b])
    print(dict1[c])

1
2
3
4
5
6
7
8
9
10
11
12
a = 10
b = 20
c = a, b
# 组包
print('组包结果:', type(c), c)
# 拆包
a, b = c # a,b等于c容器中的两个数据
print('拆包结果是', a, b)
# 先组包后拆包
x, y, z = [1, 2, 3] #将1,2,3组起来,然后存入x,y,z
print(x, y, z)

局部变量和全局变量

  • 根据变量定义的位置进行区分

局部变量

在函数内部定义的变量,称为局部变量。形参属于局部变量

特点:

  1. 局部变量只能在当前函数内部使用,不能在其他函数和函数外部使用
  2. 在不同函数中,可以定义名字相同的局部变量,两者之间没有明显
  3. 局部变量生存周期(生命周期,作用范围):在函数被调用的时候,局部变量被创建;函数调用结束,局部变量的值被销毁,不能使用

函数局部变量的值想要被函数外部使用,需要使用return关键字

全局变量

在函数外部定义的变量,称为全局变量

特点:

  1. 可以在任何函数中读取全局变量的值
  2. 如果在函数中存在和全局变量名字相同的局部变量,在函数中使用的是局部变量的值(就近原则)
  3. 在函数内部想要修改全局变量的引用(数据值),需要添加global关键字对变量进行声明为全局变量。类似列表的.append()操作不改变引用不需要globa的操作(引用传递)
  4. 代码执行的时候全局变量被创建,代码执行结束全局变量被销毁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
num=10
def func1():
print(num)
def func2():
num=20
print(num)
def func3():
global num
num=30
print(num)
func1()
func2()
func3()
func1()

函数返回值

1
2
# 返回多个数据值,将多个数据值组包进行返回
return a,b

匿名函数(lambda表达式)

Python 使用 lambda 来创建匿名函数。

lambda 函数是一种小型、匿名的、内联函数,它可以具有任意数量的参数,但只能有一个表达式。

匿名函数不需要使用 def 关键字定义完整函数。

lambda 函数通常用于编写简单的、单行的函数,通常在需要函数作为参数传递的情况下使用,例如在 map()filter()reduce() 等函数中。

lambda 函数特点:

  • lambda 函数是匿名的,它们没有函数名称,只能通过赋值给变量或作为参数传递给其他函数来使用。返回值不需要return,一行代码(表达式)的结果就是返回值
  • lambda 函数通常只包含一行代码,这使得它们适用于编写简单的函数。
1
2
lambda 参数: 一行代码
# 匿名函数一般作为函数的参数使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 1. 无参无返回值
def func1():
print('hello world')


func1()
a = lambda: print('hello lambda')
a()


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


print(func2())
b = lambda: 22
print(b())


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


mysum(1, 2)
c = lambda a, b: print(a + b)
c(10, 22)


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


print(sum1(2, 4))
d = lambda a, b: a + b
print(d(5, 6))

1
2
3
4
5
6
7
# 定义一个匿名函数,参数为字典,返回字典中键为age的值
# 参数只是占位作用,定义的时候没有具体值,形参的值是在调用的时候进行传递,此时形参才具有数据值
# 形参的类型就是由实参决定,在函数定义的时候形参只是一个函数符号。想要形参是字典类型,实参是字典类型就可

b = lambda x: x.get('age')
print(b({'name': 'ann', 'age': 18}))

匿名函数作为参数

列表中的字典排序

sort()方法会原地修改列表,而不是返回一个新的排序后的列表。该方法有两个可选参数:keyreverse

  • key 参数是一个函数,用于指定排序的比较方式。默认情况下,sort() 方法会按照列表元素的大小进行排序。如果指定了 key 参数,sort() 方法会使用该函数的返回值来比较元素,从而进行排序。
  • reverse 参数是一个布尔值,用于指定排序的顺序。默认情况下,reverse=False,即升序排序。如果设置为 True,则会进行降序排序。

对字典来说,比大小需要使用sort()中的key参数。key参数需要传递一个函数,一般是匿名函数。

字典的排序需要指定依照字典的什么键进行排序,需要使用匿名函数返回字典的键对应的值

1
2
3
4
5
6
user_list = [{'name': 'lisi', 'age': 18}, {'name': 'zhangsan', 'age': 19}, {'name': 'wangwu', 'age': 17}]
user_list.sort(key=lambda x: x.get('name'), reverse=True) # 降序排序
# 匿名函数的参数是列表中的数据。在sort()函数内部,会调用key函数,将列表中的每个元素作为实参传递给形参,从列表中获取函数的返回值,对返回值进行比大小
print(user_list)
user_list.sort(key=lambda x: x.get('age'), reverse=True)
print(user_list)

ord()可以获取字符对应的ASCII码

chr(ASCII)可以获取ASCII码对应的字符

面向对象

面向对象是一种编程思想

类和对象

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

类的组成

  1. 类名:给多个事物起的名称,在代码中要满足大驼峰命名法
  2. 属性:事物的特征
  3. 方法:事物的行为,类中定义的函数。

类的抽象(类的设计):找到类的类名,属性和方法

1
2
3
4
5
6
7
8
9
10
1. 小明 今年18岁,身高1.75,每天早上跑完步会去吃东西
2. 小红 今年17岁,身高1.72,不跑步,喜欢吃吃东西
类名:人类(Person)
属性:名字(name)、年龄(age)、身高(height)
方法:跑步(run)、吃(eat)

一只黄颜色的狗狗叫大黄,看见生人汪汪叫,看见熟人摇尾巴
类名:狗类(dog)
属性:颜色(color)、名字(name)
方法:汪汪叫(bark),摇尾巴(shake)

对象

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

  1. 属性:对对象的特征的描述
  2. 方法:对象具有的行为

对象的划分

实例对象(实例)

通过类名()创建的对象即实例对象,简称实例

创建对象的过程称作类的实例化

我们平时说的对象就是实例对象

每个实例都有自己的内存空间,在自己的内存空间中保存自己的属性(实例属性)

类对象(类名)

类对象就是类,可以认为是类名(类名包含了整个类)

在 Python 中,类也是对象。更具体地说,类是一个类型对象,它是由 type 类创建的实例。因此,类对象就是一个实例化了 type 类的对象。

类对象是python解释器在执行代码的过程中创建的

类对象的作用:使用类对象创建实例,类对象也有自己的内存空间,可以保存一些属性值(类属性)信息

在一个代码中,一个类只有一份内存空间

属性的划分

实例属性

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

定义和使用:

__init__方法中,使用self.属性名= 属性值定义

在方法中使用self.属性名来调用

在每个实例当中都存在一份内存空间

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

类属性

概念:是类对象具有的属性

定义和使用:

在类内部,方法外部定义的变量

类对象.属性名=属性值类名.属性名=属性值

使用类对象.属性名类名.属性名调用

只有类对象中存在一份内存空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Dog:
# 定义类属性count,记录创建对象的个数
count = 0

def __init__(self, name):
# 定义实例属性,实例属性在init方法中定义
self.name = name;
# 每创建一个对象就会调用init方法
Dog.count += 1


dog1 = Dog('小花')
print(Dog.count)
dog2 = Dog('大黄')
print(Dog.count)
dog2 = Dog('小白')
print(Dog.count)
# 可以用 实例对象.类属性名 来获取类属性的值(原因:实例对象属性的查找现在实例属性中找,找到直接使用,没有找到会去类属性里寻找)
print(dog1.count)
print(dog2.count)
print(dog3.count)

方法的划分

实例方法(最常用)
  • 定义:在类中直接定义的方法

    1
    2
    3
    class Demo:
    def func(self): # self一般表示实例对象
    pass
  • 定义时机:如果在方法中需要使用实例属性(需要使用self),则这个方法必须定义为实例方法

  • 调用:对象.方法名()

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

    1
    2
    3
    4
    class Demo:
    @classmethod
    def func(cls): # cls表示的是类对象(类名)class
    pass
  • 定义时机:方法中不需要使用实例属性(即self),用到了类属性,可以将这个方法定义为类方法,也可以定义为实例方法

  • 调用:通过类对象调用:类名.方法名(),通过实例对象调用:实例.方法名()

静态方法
  • 定义:在方法名字的上方书写@staticmethod 装饰器

    1
    2
    3
    4
    class Demo:
    @staticmethod
    def func(): # 一般没有参数
    pass
  • 使用时机:方法中不需要使用实例属性,也不使用类属性,可以将这个方法定义为静态方法

  • 调用:通过类对象调用:类名.方法名(),通过实例对象调用:实例.方法名()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import random


class Game:
top_score = 0

def __init__(self, name):
self.name = name

@classmethod
def start_game(cls):
cls.score = random.randint(10, 100)
print(f'本次游戏得分{cls.score}')

@classmethod
def show_top_score(cls):
if cls.score > Game.top_score:
Game.top_score = cls.score
print(f'现在的最高分是{Game.top_score}')
else:
print(f'现在的最高分是{Game.top_score}')

@staticmethod
def show_help():
print("这场游戏的得分都是随机的")


player1 = Game('小王')
player1.start_game()
Game.show_top_score()
player1.start_game()
Game.show_top_score()
Game.show_help()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import random


class Game:
top_score = 0
top_score_player=''

def __init__(self, name):
self.name = name
self.score = 0

def start_game(self):
self.score = random.randint(10, 100)
print(f'本次{self.name}游戏得分{self.score}')

def show_top_score(self):
if self.score > Game.top_score:
Game.top_score = self.score
Game.top_score_player = self.name
print(f'现在的最高分是{Game.top_score},得主是{Game.top_score_player}')
else:
print(f'现在的最高分是{Game.top_score},得主是{Game.top_score_player}')

@staticmethod
def show_help():
print("这场游戏的得分都是随机的")


player1 = Game('小王')
player1.start_game()
player1.show_top_score()
player2 = Game('小李')
player2.start_game()
player2.show_top_score()
player3 = Game('小张')
player3.start_game()
player3.show_top_score()
player4 = Game('小红')
player4.start_game()
player4.show_top_score()
Game.show_help()

面向对象的代码步骤

  1. 定义类,在定义类之前先设计类

    先定义简单的类,不包含属性,在python中定义类需要使用关键字class

    方法:在类中定义的函数,第一个参数是self(实例方法)

    1
    2
    3
    class 类名:
    def 方法名(self):
    pass
  2. 创建对象,使用第一步定义的类创建对象

    创建对象使用类名()进行创建

    1
    2
    变量=类名() #变量中保存的是对象的地址,一般直接称为对象
    # 一个类可以创建多个对象
  3. 通过对象调用方法

    对象.方法名()

  4. 案例实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 需求:小猫爱吃鱼,小猫要喝水
    # 类名:猫类Cat
    # 属性:暂无
    # 方法:吃鱼eat(),喝水drink()
    # 类的定义
    class Cat:
    def eat(self):
    print("小猫爱吃鱼")

    def drink(self):
    print("小猫要喝水")


    # 创建对象
    bluecat = Cat()
    bluecat.eat()
    bluecat.drink()
    redcat = Cat()
    redcat.eat()
    redcat.drink()

self的说明

1
2
3
class Cat:
def eat(self):
print("小猫爱吃鱼")
  1. 从函数的语法上讲,self是形参,就可以是任意的变量名,只不过我们习惯性将这个形参写成self
  2. self是普通的形参,但是调用的时候没有传递实参值,原因是python解释器在执行代码的时候,自动**调用这个方法的对象传递给了self**,即self的本质是对象
  3. 只需要确定通过哪个对象调用,对象的引用和self的引用一样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 类的定义
class Cat:
def eat(self):
print(f'{id(self)},self')
print("小猫爱吃鱼")


# 创建对象
bluecat = Cat()
bluecat.eat() # bluecat调用eat()方法,解释器就会将bluecat对象传给self
print(id(bluecat), 'bluecat')
a = bluecat # 不是创建对象
a.eat()
print(id(a), 'a')
redcat = Cat()
redcat.eat()
print(id(redcat), 'redcat')
# self是函数局部变量,创建的对象是全局变量

每次创建对象会开辟新的内存空间

对象的属性操作

添加属性

对象.属性名 = 属性值

  • 类内部添加

    1
    2
    self.属性名 = 属性值
    # 在类中添加属性一般写在__init__方法中
  • 类外部添加

    1
    对象.属性名 = 属性值 # 一般不使用

魔法方法

python中有一类方法,以两个下划线开头,两个下划线结尾,并且在满足某个条件的情况下会自动调用,这类方法称为魔法方法

__init__方法
  1. 创建对象后会自动调用
  2. 应用场景:给对象添加属性的(初始方法,构造方法)。某些代码,在每次创建对象后都要执行,可以写在__init__方法中
  3. 如果__init__函数有除了self之外的参数,要记得传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Cat:
def __init__(self): # 创建对象才调用
self.name = '蓝猫'
self.age = 2
# 下方代码只是为了验证该方法被调用,实际代码中不要书写
print("我是__init__,我被调用了")

def show_info(self):
print(f'小猫的名字是{self.name},年龄是{self.age}')


# 创建对象
Cat() # 创建了对象,但是没有保存
bluecat = Cat()
a = bluecat # 不是在创建对象,不调用__init__()
bluecat.show_info()

带参数的_init_:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Cat:
# 定义添加书属性的方法
def __init__(self, name, age): # 创建对象才调用
self.name = name
self.age = age
# 下方代码只是为了验证该方法被调用,实际代码中不要书写
print("我是__init__,我被调用了")

def show_info(self):
print(f'小猫的名字是{self.name},年龄是{self.age}')


# 创建对象
bluecat = Cat('蓝猫', 2)
bluecat.show_info()
redcat = Cat('红猫', 3)
redcat.show_info()

__str__方法
  1. 使用print()打印对象后会自动调用
  2. 应用场景:在这个方法中一般书写对象的属性信息,即打印对象的时候想要查看的信息在此方法中定义。如果类中没有定义此方法,print(对象)输出对象的引用地址
  3. 这个方法必须返回一个字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Cat:
# 定义添加书属性的方法
def __init__(self, name, age): # 创建对象才调用
self.name = name
self.age = age

def __str__(self):
return f'小猫的名字是{self.name},年龄是{self.age}'


# 创建对象
bluecat = Cat('蓝猫', 2)
print(bluecat)
redcat = Cat('红猫', 3)
print(redcat)

__del__方法

__del__方法(析构方法),对象被删除销毁时,自动调用。一种是程序运行结束,所有对象被销毁;一种是使用del删除对象(如果对象有多个名字即多个变量引用同个对象,需要把所有的对象都删除)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Demo:
def __init__(self, name):
print('__init__被调用了')
self.name = name

def __del__(self):
print(f'{self.name}没了')


a = Demo('a')
b = Demo('b')
c = Demo('c')
del a
print('代码运行结束')

小明跑步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person:
def __init__(self, name, weight):
self.name = name
self.weight = weight

def __str__(self):
return f'姓名:{self.name},体重是{self.weight}公斤'

def run(self):
# 跑步减少0.5公斤
print(f'{self.name}跑了5km,体重减少了')
self.weight -= 0.5

def eat(self):
print(f'{self.name}大餐一顿,体重增加了')
self.weight += 1.0


xm = Person('小明', 75.0) # 调用__init__
print(xm) #调用__str__
xm.run()
print(xm) #调用__str__
xm.eat()
print(xm) #调用__str__

摆放家具
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class House:
def __init__(self, housetype, square):
self.housetype = housetype # 房子的户型
self.square = square # 房子的总面积
self.leftsquare = square # 房子剩余的面积,初始值等于总面积
self.furniture = [] # 房子有的家具

def __str__(self):
return (f'房子的户型是{self.housetype},房子的总面积是{self.square}平方米,'
f'房子的剩余面积是{self.leftsquare}平方米,家具有{self.furniture}')

def add_item(self, item): # 添加家具
if self.leftsquare > item.square:
self.furniture.append(item.name) # 将家具加入列表
self.leftsquare -= item.square # 剩余面积计算
else:
print('剩余面积不够了!添加失败!')


class HouseItem:
def __init__(self, name, square):
self.name = name # 家具的名字
self.square = square # 家具的占据面积

def __str__(self):
return f'家具是{self.name},占据面积是{self.square}平方米'


bed = HouseItem('席梦思', 4)
print(bed)
chest = HouseItem('衣柜', 2)
print(chest)
table = HouseItem('餐桌', 1.5)
print(table)
newhouse = House('一室一厅', 98)
newhouse.add_item(bed)
print(newhouse)
newhouse.add_item(bed)
print(newhouse)
newhouse.add_item(chest)
print(newhouse)
newhouse.add_item(table)
print(newhouse)

登录案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class LoginPage:
def __init__(self, username, password, code):
self.name = username
self.password = password
self.code = code
self.btn = '登录'

def login(self):
print(f'1. 输入用户名{self.name}')
print(f'2. 输入密码{self.password}')
print(f'3. 输入验证码{self.code}')
print(f'4. 点击按钮{self.btn}')


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

获取属性

对象.属性名

  • 类内部添加:self.属性名
  • 类外部添加:对象.属性名,一般很少使用
  • 给对象进行属性添加后,对象内存地址不变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Cat:
def eat(self):
print(f'{id(self)},self')
print(f"小猫{self.name}爱吃鱼")


# 创建对象
bluecat = Cat()
# 给蓝猫添加 name属性
print(f'{id(bluecat)},bluecat')
bluecat.name = '蓝猫'
bluecat.eat()
redcat = Cat()
print(f'{id(redcat)},redcat')
redcat.name = '红猫'
redcat.eat()

私有和公有

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

  • 公有权限
    • 直接书写的方法和属性都是公有的
    • 公有的方法和属性可以在任意地方访问和使用
  • 私有权限
    • 在类内部,属性名或者方法名前面加上两个下划线__,就变为私有
    • 私有方法和属性只能在当前类内部使用
    • 在类外部操作私有属性,在类内部定义公有方法去操作私有属性
  • 什么时候定义私有?
      1. 某个属性或者方法不写在类外部被访问和使用,将其定义为私有
      2. 测试中一般不怎么使用,直接公有
      3. 开发中根据需求文档去确定什么作为私有
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age

def __str__(self):
return f'名字:{self.name},年龄{self.__age}'


xm = Person('小明', 18)
# 类内部可以使用__age
print(xm)
# 类外部不能直接使用私有属性
print(xm.__age)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person:
def __init__(self, name, age):
self.name = name
# 私有的本质:解释器执行代码,发现属性名或者方法前有两个__,会将这个名字重命名
# 会在这个名字前面加上_类名前缀,即self.__age=self._Person__age
self.__age = age

def __str__(self):
return f'名字:{self.name},年龄{self.__age}'


xm = Person('小明', 18)
# 类内部可以使用__age
print(xm)
xm.__age = 20 #不是修改私有属性,是添加了一个名叫__age的公有属性
print(xm)
print(xm._Person__age)
xm._Person__age=19 #这样才从外部访问修改了私有属性
print(xm._Person__age)

__dict__魔法属性

可以将对象具有的属性组成字典返回

1
2
3
4
5
xm = Person('小明', 18)
# 类内部可以使用__age
print(xm.__dict__)
xm.__age = 20 #不是修改私有属性,是添加了一个公有属性__age
print(xm.__dict__)

image-20240307213405447

继承

继承描述的是类与类之间的关系,可以减少代码的冗余,实现代码的重用。

子类继承父类之后,子类的对象可以直接使用父类中定义的公有属性和方法。

单继承:一个类只继承一个父类

1
2
3
4
5
6
class 类名(父类名):
pass
class A: #没有写父类,但是有父类object,object类是python中最原始的类
pass
class B(A): #类B继承类A,A类是父类(基类),B类为子类(派生类)
pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Animal:
def eat(self):
print('哐哐吃')


class Dog(Animal):
def bark(self):
print('汪汪叫')


class XiaoTianQuan(Dog):
pass


ani = Animal()
ani.eat()
dog = Dog()
dog.eat() # 调用父类的方法
dog.bark() # 调用自己的方法
xiaotian = XiaoTianQuan()
xiaotian.eat()
xiaotian.bark()

重写

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

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

重写之后,调用子类自己的方法,不调用父类中的方法

重写的方式

  • 覆盖:父类中的功能完全抛弃,重新书写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Dog:
    def bark(self):
    print('汪汪叫')


    class XiaoTianQuan(Dog):
    # 覆盖重写
    def bark(self):
    print('嗷嗷嗷')


    dog = Dog()
    dog.bark() # 调用自己的方法
    xiaotian = XiaoTianQuan()
    xiaotian.bark()

  • 扩展:父类中的功能还调用,只是添加新的功能(更常见)。直接在子类中定义和父类中名字相同的方法,在合适的地方调用父类中的方法 super().方法()。然后书写添加的新功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Dog:
    def bark(self):
    print('汪汪叫')
    print('汪汪叫')


    class XiaoTianQuan(Dog):
    # 扩展重写
    def bark(self):
    print('嗷嗷嗷')
    super().bark()
    print('嗷嗷嗷')


    dog = Dog()
    dog.bark() # 调用自己的方法
    xiaotian = XiaoTianQuan()
    xiaotian.bark()

多态

不同类的对象对相同的方法做出不同的响应。简单来说,多态性使得能够以统一的方式使用不同类型的对象,而不必关心它们具体的类型。

多态性使得我们可以编写更加灵活和通用的代码,因为我们不需要关心对象的具体类型,只需要知道它们共享相同的接口(即方法),就可以以统一的方式对待它们。

文件

文件通常是以字节序列的形式存储在存储设备(如硬盘、固态硬盘、闪存驱动器等)上的。

文本文件:能使用记事本软件打开,能使用记事本转换为文字。txt、md、py

二进制文件:不能使用记事本打开的。exe、mp3、mp4、png、jpg

文件操作的步骤

打开文件

将文件从磁盘中读取到内存中(CPU只能对内存执行操作)

文件流一般写入try中,因为文件流容易报错

1
2
3
4
5
6
7
8
9
10
open(file,mode='r',encoding=None)
> 参数file:打开的文件,类型是字符串,文件路径可以是相对路径(推荐),也可以是绝对路径
> 参数mode:默认参数(缺省参数),表示打开文件的方式
> r:read 只读打开
> w:write 只写打开
> a:append 追加打开,在文件的末尾写入内容
> 参数encoding:编码方式,文字和二进制如何转换
>gbk:将一个汉字转换为两个字节二进制
>utf-8:将一个汉字转换为三个字节二进制
> 返回值:返回的是文件对象,后续对文件的操作都需要这个对象

读写文件

写文件

向文件中写入指定的内容。

前提:文件的打开方式是 w 或者 a

1
2
3
4
5
6
7
8
文件对象.write('写入文件的内容')
# 返回值:写入文件的字符数,一般不关注
# 注意w方式打开文件:
1. 文件不存在会直接创建
2. 文件存在会覆盖原文件
# 注意a方式打开文件:
1. 文件不存在会直接创建
2. 文件存在会在末尾写入内容
1
2
3
f = open('a.txt', 'w', encoding='utf-8')
f.write('good good')
f.close()

image-20240308123816685

读文件

将文件中的内容读取出来

前提:文件的打开方式是 r

1
2
3
4
文件对象.read(n)
# 参数n:表示读取多少个字符,一般不写表示读取全部内容
# 返回值:读取到的文件内容,类型:字符串
# 注意r方式打开文件,文件不存在会报错
1
2
3
4
f = open('a.txt', 'r', encoding='utf-8')
buf = f.read()
print(buf)
f.close()

image-20240308141004764

关闭文件

1
2
关闭文件:将文件占用的资源进行清理,同时会保存文件,文件关闭之后这个文件对象就不能使用了
文件对象.close()

with open打开文件

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

1
2
3
with open(file, mode, encoding='utf-8') as 变量:
# 缩进中读取/写入文件
# 缩进中的代执行结束,出缩进之后文件会自动关闭
1
2
with open('a.txt', 'a', encoding='utf-8') as f:
f.write('day day up')

按行读取

一次读取一行内容,文件对象.readline()

1
2
3
4
with open('a.txt', encoding='utf-8') as f:
buf=f.readline()
print(buf)
print(f.readline())

1
2
3
4
5
6
7
8
9
10
11
with open('a.txt', encoding='utf-8') as f:
for i in f: # 按行遍历
print(i, end='')
# read()和readline()读到文件末尾会返回空的字符串
with open('a.txt', encoding='utf-8') as f:
while True:
buf = f.readline()
if len(buf) == 0: #if buf:容器中的数据个数为0为空
break
else:
print(buf,end='')

json文件

json=’java script object notation’,是JavaScript对象表示法,是一种基于文本,独立于语言的轻量级数据交换格式

  • 基于文本:是一个文本文件,不能包含图片,音视频等
  • 独立于语言:不是某个语言特有的,每种编程语言都可以使用
  • 轻量级:相同的数据和其他格式相比占用的大小比较小
  • 数据交换格式:后端程序员给前端的数据(json,html,xml)

json文件也是一种文本文件,可以直接使用read()write()操作,但是不方便。json文件有独特的读取和写入方法

常用在做测试的时候,将测试数据定义为json文件格式,使用代码读取json文件,即读取测试数据,进行传参(参数化)

json语法规则

  • json文件的后缀是.json
  • json文件中的主要数据类型是对象({}类似Python中的字典)和数组([]类似列表),对象和数组可以互相嵌套
  • 一个json文件是一个对象或者数组,即json文件的最外层要么是一个大括号{},要么是一个数组[]
  • json中的对象是由键值对组成的,每个数据之间使用,隔开,但是最后一个数据后面不要写逗号
  • json中的字符串必须使用””双引号
  • json中的其他数据类型:数字类型(int float)、字符串string(str)、布尔类型(true、false)、None(null)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"name": "小明",
"age": 18,
"gender": 1,
"isFan": true,
"hobbies": [
"听歌",
"游戏",
"购物",
"睡觉"
],
"address": {
"country": "China",
"city": "Shanghai"
}
}

读取json文件

1
2
3
4
5
1. 导包 import jason
2. 读打开文件
3. 读文件
json.load(文件对象)
# 返回的是字典(对象)或者列表(数组)
1
2
3
4
5
6
7
8
9
10
11
import json

with open('info.json', encoding='utf-8') as f:
# buf = f.read()
# print(type(buf), buf)
result = json.load(f)
print(type(result))
print(result.get('name'))
print(result.get('age'))
print(result.get('address').get('city'))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[{
"name": "小明",
"age": 18,
"gender": 1,
"isFan": true,
"hobbies": [
"听歌",
"游戏",
"购物",
"睡觉"
],
"address": {
"country": "China",
"city": "Shanghai"
}
},
{"name": "小红",
"age": 17,
"gender": 0,
"isFan": false,
"hobbies": [
"听歌",
"学习",
"购物",
"睡觉"
],
"address": {
"country": "China",
"city": "Beijing"
}
}
]

1
2
3
4
5
6
7
8
with open('info.json', encoding='utf-8') as f:
# buf = f.read()
# print(type(buf), buf)
result = json.load(f)
print(type(result))
for info in result:
print(info.get('name'), info.get('age'), info.get('address').get('city'))

测试读取案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[
{
"desc": "正确的用户名密码",
"username": "admin",
"password": "123456",
"expect": "登录成功"
},
{
"desc": "错误的用户名",
"username": "root",
"password": "123456",
"expect": "登录失败"
},
{
"desc": "错误的密码",
"username": "admin",
"password": "123123",
"expect": "登录失败"
},
{
"desc": "错误的用户名和密码",
"username": "aaa",
"password": "123123",
"expect": "登录失败"
}
]
1
2
3
4
5
6
7
8
9
10
# 获取用户名、密码和预期结果,组成[(),(),()]格式(自动化参数化需要的格式)
with open('test.json', encoding='utf-8') as f:
# buf = f.read()
# print(type(buf), buf)
result = json.load(f)
test_list = []
for info in result:
test_tuple = (info.get('username'), info.get('password'), info.get('expect'))
test_list.append(test_tuple)
print(test_list)

json的写入

文件对象.write(字符串)不能直接将python的列表和字典作为参数传递。想要将python中的数据类型存为json文件,需要使用json提供的方法,不再使用write

1
2
3
4
1. 导包 import json 
2. 写(w)方式打开文件
3. 写入
json.dump(python中的数据类型,文件对象)
1
2
3
4
5
6
7
8
9
import json

my_list = [('admin', '123456', '登录成功'), ('root', '123456', '登录失败'), ('admin', '123123', '登录失败'),
('aaa', '123123', '登录失败')]
with open('info1.json', 'w', encoding='utf-8') as f:
# json.dump(my_list, f, ensure_ascii=False) #单行显示
# 显示缩进
json.dump(my_list, f, ensure_ascii=False,indent=2) # indent是缩进的意思

1
2
3
def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw)

函数参数列表里,*后面的参数只能以关键字的形式传参,不能使用位置传参

异常

程序运行时,python解释器遇到一个错误,会停止程序的执行,并且提示一些错误信息,这就是异常。

程序停止执行并且提示错误信息,这个动作是抛出异常(raise)。程序遇到异常,默认动作是终止代码程序的执行。捕获异常可以使代码继续执行,不会终止运行

异常捕获

1
2
3
4
5
6
7
8
9
try:
可能发生异常的代码
except: # 任何类型的异常都能去捕获
发生了异常执行的代码

try:
可能发生异常的代码
except 异常类型: # 只能捕获特定类型及其子类的异常,如果不是这个异常还是会报错
发生了异常执行的代码
1
2
3
4
5
6
7
8
while True:
try:
num=input('请输入数字')
num=int(num)
print(num)
except:
print('请输入正确数字')
print('后续代码仍然运行')

可以针对不同的异常错误,运行单独的代码处理

1
2
3
4
5
6
7
8
9
10
11
try:
可能发生的异常的代码
except 异常类型1:
发生异常类型1执行的代码
# Exception是常见异常类的父类,这里书写Exception可以捕获常见的所有异常,as 变量,变量是一个异常类的对象,print(变量)可以打印异常信息
except Exception as 变量:
发生其他类型的异常,执行的代码
else:
没有发生异常执行的代码
finally:
不论如何都会执行的代码
1
2
3
4
try:
可能发生的异常的代码
except Exception as e:
发生异常执行的代码
1
2
3
4
5
6
7
8
9
10
try:
num=input('请输入数字')
num=int(num)
num=20/num
print(num)
except ValueError:
print('发生了异常,请输入正确数字')
except ZeroDivisionError:
print('不要输入0')

1
2
3
4
5
6
7
8
9
10
11
12
13
while True:
try:
num=input('请输入数字')
num=int(num)
num=20/num
print(num)

except Exception as e:
print(f'错误信息为:{e}')
else:
print("没有发生异常")
finally:
print('我都会执行')

异常传递

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

抛出异常raise

让代码报错

模块和包

模块(Module)是一个包含 Python 代码的文件。这些文件通常包含了一些函数、类和变量的定义,可以被其他 Python 程序导入并使用。模块使得代码的组织和管理更加方便,同时也可以提高代码的可重用性。

模块名同样也是一个标识符,需要符合标识符的命名规则

在模块中定义的全局变量、函数、类都是提供给外界直接使用的工具

模块就好比是工具包,要使用这个工具包的工具需要先导入这个模块

自己写的代码想要作为模块使用,代码的名字需要满足标识符的规则

模块的导入

import导入

使用模块中的内容:模块名.工具名

1
2
3
4
5
6
7
8
9
import 模块1名,模块2
# 使用模块中的内容:
模块名.工具名
# 举例
import random
import json
random.randint(a,b)
json.load
json.dump
1
2
3
4
5
6
7
8
9
from 模块名 import 工具名
# 使用
工具名
# 举例
from random import randint
from json import load,dump
randint(a, b)
load()
dump()
1
2
3
4
5
6
7
8
# 可能存在多个模块之间有名字相同的工具,会造成冲突
from 模块名 import * # 将模块中的所有内容都导入
# 举例
from random import *
from json import *
randint(a, b)
load()
dump()

对于导入的模块和工具可以使用as起别名,如果起别名,原来的名字就不能使用了

模块的查找顺序

在导入模块的时候,会先在当前目录中找模块,如果找到,就直接使用。如果没有找到,就回去系统的目录中进行查找,找到就直接使用,没有找到会报错

注意:在定义代码文件的时候,代码名字不能和要导入的模块名字相同,会造成混淆产生命名冲突,Python 解释器会优先使用当前目录下的同名文件,而不是标准库或其他模块中的同名模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# tool.py
def func():
print('我是tools模块中的func方法')


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

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

tools.func()
dog1 = tools.Dog('大黄', 3)
dog1.play()

__name__作用

每个代码文件都是一个模块,在导入模块的时候,会执行模块中的方法。

__name__变量:

  1. 是python解释器自动维护的变量
  2. __name__ 变量,如果代码直接运行,值是"__main__"
  3. __name__ 变量,如果代码是被导入运行,值是模块名(代码名)

包package

包(Package)是一种包含多个模块的目录结构。通常情况下,包是一个目录,包含了一个特殊的 __init__.py 文件和多个模块文件。__init__.py 文件可以为空,也可以包含一些初始化代码。包可以嵌套,即一个包可以包含其他包。

在python使用的时候不需要可以区分是包还是模块,因为使用方式是一样的

random是一个模块json是一个包

UnitTest框架

是python中自带的单元测试框架。pytest是第三方框架,需要安装。

对于开发来说,单元测试框架主要用来做单元测试。对测试来说,unittest框架的作用是自动化脚本(用例代码)执行框架,管理运行多个测试用例

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

UnitTest核心(组成)

  1. TestCase(最核心的模块)

    unittest.TestCase 类是编写测试用例的基础。你可以通过创建继承自 TestCase 的子类,并在子类中定义测试方法来编写测试用例。每个测试方法都应该以 test_ 开头,这样 unittest 才能识别它们并执行。

  2. TestSuite

    测试套件是一组测试用例的集合。你可以使用 unittest.TestSuite 类来创建一个测试套件,并将多个测试用例添加到这个套件中。这样可以方便地对多个测试用例进行组织和管理。

  3. TestRunner

    测试运行器负责执行测试套件中的测试用例,并生成测试报告。unittest 提供了命令行接口和 GUI 工具来运行测试,并且可以生成详细的测试报告,包括测试结果、运行时间、覆盖率等信息。

  4. TestLoader

    TestLoader(测试加载),对TestSuite(测试套件)功能的补充。自动搜索加载的一种方式,从指定的模块或目录中加载测试用例,并根据一定的规则将它们组织成测试套件。它可以自动发现符合命名规范的测试用例,并将它们添加到测试套件中,以便后续执行。

  5. Fixture

    Fixture(测试夹具),书写在TestCase代码中,是一个代码结构,可以在每个方法执行前后都会执行的内容(前置代码)。unittest 提供了 setUp()tearDown() 方法,它们分别在每个测试方法执行前后自动调用,用于准备测试环境和清理测试

TestCase(测试用例)

在这个代码文件中书写真正的用例代码,代码文件的名字必须按照标识符的规则来书写

步骤:

  1. 导包(unittest)
  2. 自定义测试类,新建测试类必须继承unittest.TestCase。运行的时候主模块是TestCase
  3. 在测试类中书写测试方法,测试方法名称必须以test_开头
  4. 执行用例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""
学习TestCase模块的书写方法
"""
# 1. 导包(unittest)
import unittest
# 2. 自定义测试类,需要继承UnitTest模块中的TestCase类
class TestDemo(unittest.TestCase):
# 3. 在测试类中书写测试方法,即用例代码。目前用print代替
# 测试方法必须以test 开头,_连接
def test_method1(self):
print('测试方法1')
def test_method2(self):
print('测试方法2')
# 4. 执行用例
# 4.1 将光标放在类名后边运行,会执行类中的所有测试方法
# 4.2 将光标放在方法名后边运行,只执行当前的方法

TestSuite&TestRunner

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

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

步骤:

  1. 导包(unittest)
  2. 实例化(创建对象)套件对象
  3. 使用套件对象添加用例方法
  4. 实例化运行对象
  5. 使用运行对象去执行套件对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"""
用来学习testsuite和testrunner
"""
# 1. 导包(unittest)
import unittest

from day1.testcase1 import TestDemo1
from day1.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'))
# 方法二:添加一个类里所有以test开头的方法
suite.addTest(unittest.makeSuite(ClassName))
# 4. 实例化运行对象
runner=unittest.TextTestRunner()
# 5. 使用运行对象去执行套件对象
# 运行对象.run(套件对象)
runner.run(suite)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"""
用来学习testsuite和testrunner
"""
# 1. 导包(unittest)
import unittest

from day1.testcase1 import TestDemo1
from day1.testcase2 import TestDemo2

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

运行结果中的。表示用例通过,F表示用例不通过(运行结果和预期结果不符合),E代表用例代码有问题。

makeSuite()要被python官方弃用!!

image-20240309175553547

TestLoader(测试加载)

TestLoader(测试加载)和TestSuite作用是一样的,对TestSuite功能的补充,用来组装测试用例。如果TestCase的代码文件有很多,可以使用TestLoader

步骤

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

在一个项目中TestCase的代码一般放在一个单独的目录(case)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 导包
import unittest

# 2. 实例化加载对象并添加用例
# unittest.TestLoader().discover('用例所在的路径','用例的代码文件名')
# 用例所在的路径建议使用相对路径,用例的代码文件名可以使用*(任意多个任意字符)通配符。defaultTestLoader = unittest.TestLoader()
suite = unittest.TestLoader().discover('./case', '*.py')
# 3. 实例化运行对象
runner = unittest.TextTestRunner()
# 4. 执行
runner.run(suite)
# 可以将3、4步变为1步
# 即跳过创建runner对象,unittest.TextTestRunner().run(suite)

TestLoader与TestSuite区别

  1. TestSuite需要手动添加测试用例(可以添加测试类,也可以添加测试类中的某个方法
  2. TestLoader搜索指定目录下指定开头.py文件并添加测试类中的以test开头的所有的测试方法,不能指定添加方法

makeSuite()方法要被弃用,可以使用TestLoader添加用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import unittest

class MyTestCase(unittest.TestCase):
def test_case1(self):
self.assertEqual(1 + 1, 2)

def test_case2(self):
self.assertTrue(True)

def test_case3(self):
self.assertFalse(False)

# 创建一个测试套件
suite = unittest.TestLoader().loadTestsFromTestCase(MyTestCase)

# 使用 TextTestRunner 运行测试套件,并输出测试结果
runner = unittest.TextTestRunner()
runner.run(suite)

Fixture(测试夹具)

是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture。在某些特点的情况下会自动去执行

方法级别

写在方法里,在每个测试方法(用例代码)执行前后都会自动调用的结构

1
2
3
4
5
6
7
8
# 方法执行之前
def setup(self):
# 每个测试方法执行之前都会执行
pass
# 方法执行之后
def teardown(self):
# 每个测试方法执行之后都会执行
pass

类级别

在每个测试类中所有方法执行前后都会自动调用的结构(整个类中前后各一次)写在类里面

类级别的Fixture方法是一个类方法

1
2
3
4
5
6
7
8
# 类中所有方法之前
@classmethod
def setupClass(cls):
pass
# 类中所有方法之后
@classmethod
def teardownClass(cls):
pass

模块级别

在每个代码文件执行前后执行的代码结构,需要写在类的外面,直接定义函数即可

1
2
3
4
5
6
# 代码文件之前
def setupModule():
pass
# 代码文件之后
def teardownModule():
pass

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

登录案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import unittest

from day1.login_1 import login


class LoginTest(unittest.TestCase):
def setUp(self):# 方法级
"""每个测试方法执行之前都会先调用的方法"""
print('输入网址...')

def tearDown(self):
"""每个测试方法执行之后都会先调用的方法"""
print('关闭网站...')

def test_username_password_ok(self):
print('输入正确用户名和密码点击登录1')
if login('admin', '123456') == '登录成功':
print('pass')
else:
print('fail')

@classmethod
def setUpClass(cls): #类级别
print('1. 打开浏览器')

@classmethod
def tearDownClass(cls):
print('5. 关闭浏览器')

def test_username_error(self):
print('输入错误用户名和密码点击登录2')
if login('root', '123456') == '登录失败':
print('pass')
else:
print('fail')

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

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

测试add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"""
学习TestCase模块的书写方法
"""
# 1. 导包(unittest)
import unittest
import tools


# 2. 自定义测试类,需要继承UnitTest模块中的TestCase类
class TestAdd(unittest.TestCase):
def test_method1(self):
if tools.add(1, 2)==3:
print('测试通过')
else:
print('测试不通过')
def test_method2(self):
if tools.add(10,20)==30:
print('测试通过')
else:
print('测试不通过')
def test_method3(self):
if tools.add(2, 3)==5:
print('测试通过')
else:
print('测试不通过')

login函数测试

1
2
3
4
5
def login(username,password):
if username =='admin' and password=='123456':
return '登录成功'
else:
return '登录失败'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import unittest

from day1.login_1 import login


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

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

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

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

断言

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

断言的结果:True->用例通过,False->代码抛出异常,用例不通过

python自带的断言,判断两个字符串是否相等assert "hello" == "hello" assert "hello"=="hello1" "出错啦"

判断是否包含assert "h" in "hello"

判断是否为True、False assertTrue assert1 assertFalse assert0

在unittest中使用断言,都需要通过self.断言方法来试验

assertEqual

assertEqual(预期结果,实际结果),判断预期结果和实际结果是否相等

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import unittest

from day1.login_1 import login


class LoginTest(unittest.TestCase):
def test_username_password_ok(self):
self.assertEqual('登录成功',login('admin', '123456'))

def test_username_error(self):
self.assertEqual('登录失败',login('root', '123456') )

def test_password_error(self):
self.assertEqual('登录失败', login('admin', '123123'))

def test_username_password_error(self):
self.assertEqual('登录失败', login('aaa', '123123'))

失败:

assertIn

assertIn(预期结果,实际结果)判断预期结果是否包含于实际结果

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

assertTrue

assertTrue(ex)判断ex是否为True

参数化

解决相同业务逻辑,不同业务数据

在测试方法中,使用变量代替具体的测试数据,使用传参的方法将测试数据传递给方法的变量,可以减少相似代码的书写。测试数据一般放在json文件中,使用代码读取json文件,提取要的数据[(),(),()],[[],[]]

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

1
2
3
4
1. 导包 unittest/parameterized
2. 定义测试类
3. 书写测试方法(用到的测试数据使用变量来代替)
4. 组织测试数据并传参

login_1代码:

1
2
3
4
5
def login(username,password):
if username =='admin' and password=='123456':
return '登录成功'
else:
return '登录失败'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1. 导包 unittest/parameterized


import unittest
from parameterized import parameterized

from day1.login_1 import login

# 组织测试数据,按位置传参
data = [
('admin', '123456', '登录成功'),
('root', '123456', '登录失败'),
('admin', '123123', '登录失败'),
('root', '123123', '登录失败')

] # 几组数据就有几组用例


# 2. 定义测试类
class TestLogin(unittest.TestCase):
# 4. (装饰器@修饰方法)组织测试数据并传参(列表类型,多个参数为列表嵌套元组)
@parameterized.expand(data)
# 3. 书写测试方法(用到的测试数据使用变量来代替)按位置传参
def test_login(self, username, password, expect):
self.assertEqual(expect, login(username, password))

通过json导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// test.json
[
{
"desc": "正确的用户名密码",
"username": "admin",
"password": "123456",
"expect": "登录成功"
},
{
"desc": "错误的用户名",
"username": "root",
"password": "123456",
"expect": "登录失败"
},
{
"desc": "错误的密码",
"username": "admin",
"password": "123123",
"expect": "登录失败"
},
{
"desc": "错误的用户名和密码",
"username": "aaa",
"password": "123123",
"expect": "登录失败"
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1. 导包 unittest/parameterized


import unittest
from parameterized import parameterized
import json

from day1.login_1 import login

# 组织测试数据,按位置传参
def build_data():
with open('test.json', encoding='utf-8') as f:
res = json.load(f)
data = []
for i in res: #每个i是一个字典元素
testtuple = (i.get('username'), i.get('password'), i.get('expect'))
data.append(testtuple)
return data



# 2. 定义测试类
class TestLogin(unittest.TestCase, ):
# 4. 组织测试数据并传参(装饰器@)
@parameterized.expand(build_data())
# 3. 书写测试方法(用到的测试数据使用变量来代替)
def test_login(self, username, password, expect):
self.assertEqual(expect, login(username, password))

跳过

对于一些未完成的或者不满足测试条件的测试函数和测试类可以跳过执行,使用装饰器完成。代码书写在TestCase中

1
2
3
4
5
# 直接将测试函数标记为跳过
@unittest.skip('跳过原因')
# 根据条件判断测试函数是否跳过,判断条件成立跳过
@unittest.skipIf(condition, reason)
# 跳过可以用来修饰类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import unittest

version = 30


class TestDemo(unittest.TestCase):
@unittest.skip('没有什么原因就是不想执行')
def test_1(self):
print('method 1')

@unittest.skipIf(version >= 30, '版本号新,不用测试')
def test_2(self):
print('method 2')

def test_3(self):
print('method 3')

测试报告

自带的测试报告:只有单独运行TestCase的代码才能生成测试报告,组装打包的测试用例不能生成

HTMLTestRunner是一个类库,生成第三方的的测试报告需要用到。

  1. 获取第三方的测试运行模块,将其放在代码的目录中
  2. 导包
  3. 使用套件对象,加载对象添加用例方法
  4. 实例化第三方的运行对象并运行套件

组织用例文件(TestCase),书写参数化,书写断言,书写Fixture,书写跳过,如果单个测试文件直接运行,得到测试报告,如果有多个测试文件需要使用套件对象组装、加载对象组装运行生成测试报告。接下来要运行对象,使用第三方运行类进行运行。运行对象.run(套件对象)

1
2
3
4
5
6
7
8
9
10
# 1. 获取第三方的测试运行模块,将其放在代码的目录中
# 2. 导包
import unittest
from HtmlTestRunner import HTMLTestRunner

# 3. 使用套件对象,加载对象添加用例方法

suite = unittest.defaultTestLoader.discover('.', 'parametertest.py')
# 4. 实例化第三方的运行对象并运行套件 # 测试报告的文件对象(open)要使用wb打开(二进制方法书写)
HTMLTestRunner().run(suite) # 运行对象

unittest自带报告:

html报告:

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
This site is deployed on
Total words 75.1k Unique Visitor Page View