python

1.字面量

(1)整数

类似数学上面的整数,包括整数和负数

1
print(10)  # 输出 10

(2)浮点数

类似数学上面的小数

1
print(13.14) # 输出 13.14

(3)字符串

定义: 字符串(string),又称文本,是由任意数量的字符如中文、英文、各类符号、数字等组成。所以叫做字符的串。

需要用 双引号” “ 或者 单引号’ ’ 或者 三引号”“” “”” 包围起来。

1
2
3
print("字符串") # 输出 字符串
print('字符串') # 输出 字符串
print("""字符串""") # 输出 字符串

2.注释

定义:在程序代码中对程序进行解释的文字

作用:注释不是程序,不能被执行,只是对代码进行解释说明作用,可以让代码的可对性提高

  • 单行注释:以#开头,右边的所有文字为注释文字#号和注释内容一般建议以一个空格隔开.、
  • 多行注释: 以 一对三个双引号 引起来 (“”” “””)来解释说明一段代码的作用使用方法
1
2
3
4
5
6
# 我是单行注释

"""
我是
多行注释
"""

3.变量

在程序运行的时候,能存储计算结果或者能表示值的抽象概念

(1)变量的定义格式

1
2
3
4
5
6
7
8
"""
变量名 = 变量值
变量名 :每个变量自己的名称,即变量本身
= :表示将等号右侧的值,赋予左侧的变量
变量值 :每个变量储存的值(内容)
"""
name = "断桥圆月" # 示例

(2)变量的赋值

1
2
3
4
5
6
7
8
9
"""
- 每个变量可以重复赋值
- 每次赋值将覆盖原有值
"""
name = "断桥圆月"
print(name) # 输出 断桥圆月
name = "test"
print(name) # 输出 test

4数据类型的查看

变量是没有类型的,但是数据是有类型的

  • 可以使用type(变量名)查看数据的类型
1
2
3
4
5
6
7
8
9
10
11
# 1.使用print直接输出类型信息
print(type(13.14)) # 输出 <class 'float'>

# 2.使用变量存储type()语句的结果(调用type()有返回值)
int_type = type(521)
print(int_type) # 输出 <class 'int'>

# 3.查看变量中存储的数据类型
name = "断桥圆月"
print(type(name)) # 输出 <class 'str'> str为string简写

5.数据类型转换

在一定的条件下,数据的类型可以转换

5.1转为整数

1
2
3
4
5
6
7
8
9
10
# 字符串转为整数
num = "666"
print(int(num)) # 输出 666
print("初始值类型:", type(num), ",转换后类型:", type(int(num)))
# 输出 初始值类型: <class 'str'> ,转换后类型: <class 'int'>

# 将浮点数转为小数
# 会导致精度丢失,即小数点后面的部分
print(int(13.14)) # 输出 13

5.2转为浮点数

使用 float(x) ,将x转换为一个浮点数

1
2
3
4
5
6
7
8
#字符串转浮点数
num = "5.21"
print(float(num)) # 输出 5.21
print("初始值类型:", type(num), ",转换后类型:",type(float(num)))
#输出 初始值类型: <class 'str'> ,转换后类型: <class 'float'>
#整数转浮点数
# 进行补.0
print(float(5)) # 输出 5.0

5.3转为字符串

1
2
3
4
5
6
#使用 str(x) ,将x转换为一个字符串
num = 13.14
print(str(num)) # 输出 13.14
print("初始值类型:", type(num), ",转换后类型:", type(str(num)))

# 输出 初始值类型: <class 'float'> ,转换后类型: <class 'str'>

6.标识符

用户在编程的时候所使用的一系列用于给变量、类、方法等命名的名字

7.运算符

8.字符串的使用

三种定义方式

1
2
3
4
单引号定义法:name = '断桥圆月'
双引号定义法:name = "断桥圆月"
三引号定义法:name = """断桥圆月"""

  • 字符串拼接
1
2
3
4
5
print("断桥圆月" + "study") # 输出 观止study

name = "study"
print("断桥圆月" + name) # 输出 观止study

  • 字符串拼接的格式化
1
2
3
4
5
6
name = "study"
message = "断桥圆月 %s" % name
print(message) # 输出 断桥圆月 study
- % 表示:我要占位
- s 表示:将变量变成字符串放入占位的地方

1
2
3
4
5
hobby = "study"
name = "断桥圆月"
message = "爱好 %s ,姓名 %s" % (hobby, name)
print(message) # 输出 爱好 study ,姓名 断桥圆月

格式符号 意义
%S 将内容转换成字符串,放入占位位置
%d 将内容转换成整数,放入占位位置
%f 将内容转换成浮点型,放入占位位置
1
2
3
4
5
6
name = "study"
age = 19
money = 1.00
message = "姓名:%s,年龄:%d,家当:%f" % (name, age, money)
print(message) # 输出 姓名:study,年龄:19,家当:1.000000

  • 格式化的精度控制
1
2
3
4
5
age = 18  # %5d 表示将整数的宽度控制在5位,用三个空格补足宽度
money = 1.00 # %.2f 将小数点精度设置为2位
message = "断桥圆月%5d,身价:%.2f" % (age, money)
print(message) # 输出 断桥圆月 18,身价:1.00

  • 字符串格式化的另外方式
1
2
3
4
5
age = 18  
money = 1.00
message = f"断桥圆月:{age},身价:{money}"
print(message) # 输出 断桥圆月:18,身价:1.0

缺点:

  • 无法做精度控制
  • 不理会数据类型

9.数据输入

使用imput()语句进行输入

1
2
3
name = input() # 输入 断桥圆月  用name变量来接收输入的数值
print(name) # 输出name储存的数值 断桥圆月

  • 可在input()中输入提示信息,将打印在控制台
1
2
3
4
name = input(”tell me your name?“)
# 会在控制台打印tell me your name? 然后可输入 断桥圆月
print(name) # 输出name储存的数值 断桥圆月

  • 输入的数值都将转为字符串类型,可通过数据类型转换获取需要的数据类型
1
2
3
4
name = input() # 输入 5
print(type(name)) # 输出 <class 'str'>
print(type(int(name))) # 输出 <class 'int'>

10判断

10.1逻辑运算符

  • and逻辑与运算,等价于数学中的“且” a and b 当 a 和 b 两个表达式都为真时,a and b 的结果才为真,否则为假。

  • or逻辑或运算,等价于数学中的“或” a or b 当 a 和 b 两个表达式都为假时,a or b 的结果才是假,否则为真。

  • not逻辑非运算,等价于数学中的“非” not a 如果 a 为真,那么 not a 的结果为假;如果 a 为假,那么 not a 的结果为真。相当于对 a 取反。


  • 在python当中,以下变量都会被当成False:任何数值类型的0、””或’’空字符串、空元组()、空列表[]、空字典{}等。
  • and和or运算符会将其中一个表达式的值作为最终结果,而不是将 True 或者 False 作为最终结果
  • 当遇到一个语句当中有多个逻辑运算符时,按照优先级not>and>or顺序来运算

运算

and运算符

当两边都是表达式时:and两边的表达式都为真时才为真,否则为假

1
2
3
print(15 > 10 and 15 > 6)  # 打印 True
print(15 > 10 and 15 < 6) # 打印 False

不全是表达式

  • 左边表达式的值为,左边表达式的值作为最终结果
  • 左边表达式的值为右边表达式的值作为最终结果
1
2
3
print({} and 15)  # 打印 {}
print(6 and 15) # 打印 15

or运算符

  • 两边都是表达式:or两边的表达式只要有一个真即为真,否则为假
1
2
3
4
print(15 > 10 or 15 > 6)  # 打印 True
print(15 > 10 or 15 < 6) # 打印 True
print(15 < 10 or 15 < 6) # 打印 False

  • 不全是表达式

  • 左边表达式的值为右边表达式的值作为最终结果

  • 左边表达式的值为左边表达式的值作为最终结果

1
2
3
print({} or 15)  # 打印 15
print(6 or 15) # 打印 6

not运算符

  • 当表达式为真时,运算结果就为假;当表达式为假时,运算结果为真。not可以理解为取反的意思

10.2 if

条件为True 执行,条件为False跳过

11循环

while循环

1
2
while 条件:
条件为True时重复执行
1
2
3
4
5
i = 0
while i < 100:
print("study")
i += 1 # 等效于 i = i + 1

for循环

1
2
3
4
for 临时变量 in 待处理数据集(可迭代对象): 
循环满足条件时执行的代码
# 从待处理数据集中:逐个取出数据赋值给临时变量

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义字符串name
name = "study"
# for循环处理字符串
for x in name:
print(x)
# 将字符串的内容:依次取出
# 输出
# s
# t
# u
# d
# y

range语句

  • 语法一: range(num)
1
2
# 获取一个从0开始,到num结束的数字序列(不含num本身)
# 如range(5)取得的数据是:[0, 1, 2, 3, 4]
  • 语法二: range(num1,num2)
1
2
# 获得一个从num1开始,到num2结束的数字序列(不含num2本身)
# 如,range(5, 10)取得的数据是:[5, 6, 7, 8, 9]
  • 语法三: range(num1, num2, step)
1
2
3
# 获得一个从num1开始,到num2结束的数字序列(不含num2本身)
# 数字之间的步长,以step为准(step默认为1)
# 如,range(5, 10, 2)取得的数据是:[5, 7, 9]

continue关键字

临时跳过: 暂时跳过本次循环,直接进行下一次

break关键字

直接结束: 提前退出循环,不再继续

12函数

1
2
3
4
5
6
# 定义
def 函数名(传入参数):
函数体
return 返回值
# 使用
函数名(传入参数)
1
2
3
4
5
6
7
# 定义
def say():
print("hello world")
# 使用
say()
# 输出 hello world

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 定义
def add(x, y):
res = x + y
print(res)
# 使用
add(10, 6)
# 输出 16

# 定义
def show(x):
print(x)
# 使用
show(10)
# 输出 10

1
2
3
4
5
6
7
8
9
10
# 定义
def add(x, y):
return x + y
print("end not print") # 在return后不再执行输出

# 使用
res = add(10, 6)
print(res)
# 只输出 16

1.变量的作用域

(1) 局部变量

定义在函数体内部的变量,即只在函数体内部生效

1
2
3
4
5
6
def test():
num = 100
print(num)

test() # 输出 100
print(num) # 报错 name 'num' is not defined

全局变量

在函数体内、外都能生效的变量

1
2
3
4
5
6
num = 100
def test():
print(num)
test() # 输出 100
print(num) # 输出 100

(2) global关键字

  • 一般情况下,在函数内无法修改全局变量的值
1
2
3
4
5
6
7
8
num = 100
def test():
# 声明一个值为200的局部变量num
num = 200
print(num)
test() # 输出 200
print(num) # 输出 100

  • 使用 global关键字可以在函数内部声明变量为全局变量, 如下所示
1
2
3
4
5
6
7
8
9
num = 100
def test():
# 声明num为全局变量
global num
num = 200
print(num)
test() # 输出 200
print(num) # 输出 200

2.函数进阶

2.1多返回值

1
2
3
4
5
6
def test():
return 6, 9, 16
x, y, z= test()
print(f"第一个值为{x},第二个值为{y},第三个值为{z}")
# 输出 第一个值为6,第二个值为9,第三个值为16

  • 支持return不同类型的数据
1
2
3
4
5
def test():
return "观止", True, 16
x, y, z = test()
print(f"第一个值为{x},第二个值为{y},第三个值为{z}")
# 输出 第一个值为观止,第二个值为True,第三个值为16

2.2.关键字参数

函数调用时通过“键=值”形式传递参数.

1
2
3
4
5
6
7
def user_info(name, age, gender):
print(f"您的名字是{name},年龄是{age},性别是{gender}")
user_info(name='TOM', age=20, gender='男')
# 输出 您的名字是TOM,年龄是20,性别是男
user_info(gender='男', age=20, name='TOM')
# 输出 您的名字是TOM,年龄是20,性别是男

2.3.缺省参数

也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值

所有位置参数必须出现在默认参数前,包括函数定义和调用
当调用函数时没有传递参数, 就会使用默认是用缺省参数对应的值.
函数调用时,如果为缺省参数传值则修改默认参数值, 否则使用这个默认值

1
2
3
4
5
6
7
def user_info(name, age, gender='男'):
print(f"您的名字是{name},年龄是{age},性别是{gender}")
user_info('TOM', 20)
# 输出 您的名字是TOM,年龄是20,性别是男
user_info('TOM', 20, '女')
# 输出 您的名字是TOM,年龄是20,性别是女

2.4.不定长参数

也叫可变参数. 用于不确定调用的时候会传递多少个参数(不传参也可以)的场景.

位置传递
  • 以*号标记一个形式参数,以元组的形式接受参数
  • 传进的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元组(tuple),args是元组类型
1
2
3
4
5
6
7
def user_info(*args):
print(args)
user_info('TOM')
# 输出 ('TOM',)
user_info('TOM', 20, '女')
# 输出 ('TOM', 20, '女')

关键字传递
  • 关键字不定长传递以**号标记一个形式参数,以字典的形式接受参数

  • 参数是“键=值”形式的形式的情况下, 所有的“键=值”都会被kwargs接受, 同时会根据“键=值”组成字典.

1
2
3
4
5
def user_info(**kwargs):
print(kwargs)
user_info(name='TOM', age=20)
# 输出 {'name': 'TOM', 'age': 20}

lambda匿名函数(未学习)

13数据容器

根据特点的不同可分为5类:列表(list)、元组(tuple)、字符串(str)、集合(set)、字典(dict)

13.1. list

1
2
3
4
5
6
# 定义列表
变量名称 = [元素1, 元素2, 元素3, 元素4, 元素5]
# 定义空列表
变量名称 = []
变量名称 = list()

列表的下标(索引)

我们可以使用下标索引从列表中取出特定位置的数据

  • 正向索引

从前往后的方向,从0开始,依次递增

  • 反向索引

从-1开始,依次递减

使用方式 作用
列表.append(元素) 向列表中追加一个元素
列表.extend(容器) 将数据容器的内容依次取出,追加到列表尾部
列表.insert(下标, 元素) 在指定下标处,插入指定的元素
del 列表[下标] 删除列表指定下标元素
列表.pop(下标) 删除列表指定下标元素
列表.remove(元素) 从前向后,删除此元素第一个匹配项
列表.clear() 清空列表
列表.count(元素) 统计此元素在列表中出现的次数
列表.index(元素) 查找指定元素在列表的下标 找不到报错ValueError
len(列表) 统计容器内有多少元素

查询元素
查找某元素的下标

查找指定元素在列表的下标,如果找不到,报错ValueError
语法:列表.index(元素)

1
2
3
4
my_list = ["李白", "章北海", "罗辑", "杜甫"]
print(my_list.index("罗辑")) # 打印 2
print(my_list.index("断桥圆月")) # 打印 ValueError: '观止' is not in list

插入元素
在指定的下标位置,插入指定的元素

语法:列表.insert(下标, 元素)

1
2
3
my_list = ["李白", "章北海", "罗辑"]
my_list.insert(1, "断桥圆月")
print(my_list) # 打印 ['李白', '断桥圆月', '章北海', '罗辑']

追加元素
将指定元素,追加到列表的尾部

语法一:列表.append(元素)

1
2
3
4
my_list = ["李白", "章北海", "罗辑"]
my_list.append("断桥圆月")
print(my_list) # 打印 ['李白', '章北海', '罗辑', '断桥圆月']

语法二:列表.extend(其它数据容器)
将其它数据容器的内容取出,依次追加到列表尾部

1
2
3
4
my_list_1 = ["李白", "章北海"]
my_list_2 = ["罗辑", "断桥圆月"]
my_list_1.extend(my_list_2)
print(my_list_1) # 打印 ['李白', '章北海', '罗辑', '断桥圆月']

删除元素
语法一:del 列表[下标]

1
2
3
my_list = ["李白", "章北海", "罗辑"]
del my_list[0]
print(my_list) # 打印 ['章北海', '罗辑']

语法二:列表.pop(下标)

1
2
3
my_list = ["李白", "章北海", "罗辑"]
my_list.pop(0)
print(my_list) # 打印 ['章北海', '罗辑']

语法三:列表.remove(元素)
删除某元素在列表中的第一个匹配项

1
2
3
my_list = ["李白", "章北海", "罗辑", "李白"]
my_list.remove("李白")
print(my_list) # 打印 ['章北海', '罗辑', '李白']

清空列表内容

  • 语法:列表.clear()
1
2
3
my_list = ["李白", "章北海", "罗辑"]
my_list.clear()
print(my_list) # 打印 []
统计某元素在列表内的数量
  • 语法:列表.count(元素)
1
2
3
my_list = ["李白", "章北海", "罗辑", "李白"]
num = my_list.count("李白")
print(num) # 打印 2
统计列表内有多少元素
  • 语法:len(列表)
1
2
my_list = ["李白", "章北海", "罗辑", "李白"]
print(len(my_list)) # 打印 4

列表的遍历

1
2
3
4
5
6
7
8
9
10
11
12
my_list = [1,2,3,4]
index = 0
while index < len(my_list):
num = my_list[index]
print(num)
index += 1
# 输出
# 1
# 2
# 3
# 4

1
2
3
4
5
6
7
8
9
my_list = [1, 2, 3, 4]
for x in my_list:
print(x)
# 输出
# 1
# 2
# 3
# 4

13.2元组(tuple)

元组同列表一样,但一旦定义完成,就不可修改

(1) 基本格式

1
2
3
4
5
6
7
# 定义元组
变量名称 = (元素1, 元素2, 元素3, 元素4, 元素5)
# 定义只有一个元素的元组
变量名称 = (元素1,)
# 定义空元组
变量名称 = ()
变量名称 = tuple()

元组只有一个数据,这个数据后面要添加逗号,否则不是元组

1
2
3
4
my_tuple = ("观止")
print(type(my_tuple)) # 打印 <class 'str'>
my_tuple = ("观止",)
print(type(my_tuple)) # 打印 <class 'tuple'>
方法 作用
元组.index(元素) 查找某个数据,如果数据存在返回对应的下标,否则报错
元组.count(元素) 统计某个数据在当前元组出现的次数
len(元组) 统计元组内的元素个数

13.3 字符串(str)

字符串是字符的容器,一个字符串可以存放任意数量的字符。

1
2
3
4
name = "dreamxiaoshen"
print(name[0]) # 打印 d
print(name[-1]) # 打印 n

字符串的常用操作

操作 说明
字符串[下标] 根据下标索引取出特定位置字符
字符串.index(字符串) 查找给定字符的第一个匹配项的下标
字符串.replace(字符串1, 字符串2) 将字符串内的全部字符串1,替换为字符串2 不会修改原字符串,而是得到一个新的
字符串.split(字符串) 按照给定字符串,对字符串进行分隔 不会修改原字符串,而是得到一个新的列表
字符串.strip() 字符串.strip(字符串) 移除首尾的空格和换行符或指定字符串
字符串.count(字符串) 统计字符串内某字符串的出现次数
len(字符串) 统计字符串的字符个数
1.查找元素

查找特定字符串的下标索引值

语法:字符串.index(字符串)

1
2
3
name = "dreamxiaoshen"
print(name.index("e")) # 打印 2

2.替换元素

将字符串内的全部:字符串1,替换为字符串2

语法:字符串.replace(字符串1,字符串2)

1
2
3
4
5
name = "dreamxiaoshen"
new_name = name.replace("dream", "study")
print(name) # 打印 dreamxiaoshen
print(new_name) # 打印 studyxiaoshen

不是修改字符串本身,而是得到了一个新字符串

3.分割元素

按照指定的分隔符字符串,将字符串划分为多个字符串,并存入列表对象中

语法:字符串.split(分隔符字符串)

1
2
3
4
5
6
7
name = "xiaoshen,study,20"
new_list = name.split(",")
print(name) # 打印 xiaoshen,study,20
print(new_list) # 打印 ['xiaoshen', 'study', '20']
print(type(new_list)) # 打印 <class 'list'>

# 字符串按照给定的,进行了分割,变成多个子字符串,并存入一个列表对象中

字符串本身不变,而是得到了一个列表对象

4. 规整操作

去前后空格以及换行符
语法一:字符串.strip()

1
2
3
4
name = "  xiaoshen  "
new_name = name.strip()
print(new_name) # 打印 xiaoshen

去前后指定字符串
语法二:字符串.strip(字符串)

1
2
3
4
5
name = "20xiaoshen20"
new_name = name.strip("20")
print(name) # 打印 20xiaoshen20
print(new_name) # 打印 xiaoshen

字符串本身不变,而是得到了一个新字符串

5.统计操作

统计字符串中某字符串的出现次数
语法一:字符串.count(字符串)

1
2
3
name = "20xiaoshen20"
print(name.count("20")) # 打印 2

统计字符串的长度
语法二:len(字符串)

1
2
3
4
5
6
name = "20xiaoshen 20"
print(len(name)) # 打印 13

数字(123…),字母(abcd、ABCD等),符号(空格、!、@、#、$等),中文均算作1个字符
(3) 字符串小结
同列表、元组一样,字符串也支持while循环和for循环进行遍历

13.4.数据容器(序列)的切片

1.基本用法

用法
起始下标可以省略,省略从头开始
结束下标可以省略,省略到尾结束
步长可以省略,省略步长为1(可以为负数,表示倒序执行)

用法一:

1
2
3
4
my_list = [1, 2, 3, 4, 5]
new_list = my_list[1:4] # 下标1开始,下标4(不含)结束,步长1
print(new_list) # 结果:[2, 3, 4]

用法二:

1
2
3
4
my_tuple = (1, 2, 3, 4, 5)
new_tuple = my_tuple[:] # 从头开始,到最后结束,步长1
print(new_tuple) # 结果:(1, 2, 3, 4, 5)

用法三:

1
2
3
4
my_list = [1, 2, 3, 4, 5]
new_list = my_list[::2] # 从头开始,到最后结束,步长2
print(new_list) # 结果:[1, 3, 5]

用法四:

1
2
3
4
my_str = "12345"
new_str = my_str[:4:2] # 从头开始,到下标4(不含)结束,步长2
print(new_str) # 结果:"13"

用法五:

1
2
3
4
my_list = [1, 2, 3, 4, 5]
new_list = my_list[3:1:-1] # 从下标3开始,到下标1(不含)结束,步长-1(倒序)
print(new_list) # 打印 [4, 3]

注:
这个操作对列表、元组、字符串是通用的
此操作不会影响序列本身,而是会得到一个新的序列(列表、元组、字符串)
起始位置,结束位置,步长(正反序)都是可以自行控制的

13.5.集合(set)

不支持元素的重复(自带去重功能)、并且内容无序

(1) 基本格式

1
2
3
4
5
6
7
# 定义集合

变量名称 = {元素1, 元素2, 元素3, 元素4, 元素5}

# 定义空集合

变量名称 = set()

以大括号 {} 作为标识

集合内每一个元素之间用, 逗号隔开

集合可以一次存储多个数据,且可以为不同的数据类型,支持嵌套

使用示例:

1
2
my_set = {"断桥圆月", True, "断桥圆月"}
print(my_set) # 打印 {True, '断桥圆月'}

去重且无序
(2) 集合的遍历
集合不支持下标索引,所以也就不支持使用while。

因为集合是无序的,所以集合不支持下标索引访问

1
2
3
4
5
6
7
8
9
my_set = {"断桥圆月", True, "断桥圆月"}
for i in my_set:
print(i)

# 打印

# True

# 断桥圆月

(3) 集合的常用操作

操作 说明
集合.add(元素) 集合内添加一个元素
集合.remove(元素) 移除集合内指定的元素
集合.pop() 从集合中随机取出一个元素
集合.clear() 将集合清空
集合1.difference(集合2) 得到一个新集合,内含2个集合的差集 原有的2个集合内容不变
集合1.difference_update(集合2) 在集合1中,删除集合2中存在的元素 集合1被修改,集合2不变
集合1.union(集合2) 得到1个新集合,内含2个集合的全部元素 原有的2个集合内容不变
len(集合) 得到一个整数,记录了集合的元素数量

1. 增加元素

集合本身被修改,将指定元素,添加到集合内
语法:集合.add(元素)

1
2
3
my_set = {"断桥圆月", True, "断桥圆月"}
my_set.add("study")
print(my_set) # 打印 {'断桥圆月', True, 'study'}

2. 移除元素

集合本身被修改,将指定元素,从集合内移除

语法一:集合.remove(元素)

1
2
3
4
5
my_set = {"断桥圆月", True, "断桥圆月"}
my_set.remove("断桥圆月")
print(my_set) # 打印 {True}


从集合中随机取出一个元素,同时集合本身被修改,元素被移除

语法二:集合.pop()

1
2
3
4
my_set = {"社区医院", True}
num = my_set.pop()
print(my_set) # 打印 {'断桥圆月'}
print(num) # 打印 True

清空集合,集合本身被清空

语法三:集合.clear()

1
2
3
my_set = {"断桥圆月", True}
my_set.clear()
print(my_set) # 打印 set()

3. 两集合操作

取出集合1和集合2的差集(集合1有而集合2没有的),得到一个新集合,集合1和集合2不变
语法一:集合1.difference(集合2)

1
2
3
4
5
6
7
my_set_1 = {1, 2}
my_set_2 = {1, 3}
my_set_3 = my_set_1.difference(my_set_2)
print(my_set_1) # 打印 {1, 2}
print(my_set_2) # 打印 {1, 3}
print(my_set_3) # 打印 {2}

对比集合1和集合2,在集合1内,删除和集合2相同的元素,集合1被修改,集合2不变

语法二:集合1.difference_update(集合2)

1
2
3
4
5
my_set_1 = {1, 2}
my_set_2 = {1, 3}
my_set_1.difference_update(my_set_2)
print(my_set_1) # 打印 {2}
print(my_set_2) # 打印 {1, 3}

将集合1和集合2组合成新集合(去重),集合1和集合2不变
语法三:集合1.union(集合2)

1
2
3
4
5
6
7
my_set_1 = {1, 2}
my_set_2 = {1, 3}
my_set_3 = my_set_1.union(my_set_2)
print(my_set_1) # 打印 {1, 2}
print(my_set_2) # 打印 {1, 3}
print(my_set_3) # 打印 {1, 2, 3}

4.集合长度

查看集合的元素数量,统计集合内有多少元素

语法四:len(集合)

1
2
3
my_set = {1, 3}
print(len(my_set)) # 打印 2

13.6字典、映射(dict)

Python中字典和生活中字典十分相像

(1) 基本格式

1
2
3
4
5
6
7
# 定义字典

变量名称 = {key:value, key:value, key:value}

# 定义空字典
变量名称 = {}
变量名称 = dict()

使用{}存储原始,每一个元素是一个键值对
每一个键值对包含Key和Value(用冒号分隔)
键值对之间使用逗号分隔
Key和Value可以是任意类型的数据(key不可为字典)
Key不可重复,重复会对原有数据覆盖
使用示例:

1
2
3
4
my_dict = {"观止": 99, "李白": 120, "观止": 110}
print(my_dict) # 打印 {'观止': 110, '李白': 120}
print(type(my_dict)) # 打印 <class 'dict'>

(2) 数据的获取
字典同集合一样,不可以使用下标索引取值
字典可以通过Key值来取得对应的Value

1
2
3
my_dict = {"李白": 120, "观止": 110}
print(my_dict["李白"]) # 打印 120

字典的Key和Value可以是任意数据类型(Key不可为字典),即字典是可以嵌套的

1
2
3
4
5
6
my_dict = {
"李白": {"语文": 110, "数学": 100},
"观止": {"语文": 90, "数学": 120}
}
print(my_dict["李白"]) # 打印 {'语文': 110, '数学': 100}
print(my_dict["李白"]["语文"]) # 打印 110

字典不支持下标索引,同样不可以用while循环遍历

1
2
3
4
5
6
7
8
9
my_dict = {"李白": 120, "断桥圆月": 110}
for name in my_dict:
print(f"key为:{name},value为:{my_dict[name]}")

# 打印

# key为:李白,value为:120

# key为:断桥圆月,value为:110

(3) 字典的常用操作

操作 说明
字典[Key] 获取指定Key对应的Value值
字典[Key] = Value 添加或更新键值对
字典.pop(Key) 取出Key对应的Value并在字典内删除此Key的键值对
字典.clear() 清空字典
字典.keys() 获取字典的全部Key,可用于for循环遍历字典
len(字典) 计算字典内的元素数量

1. 新增元素

字典被修改,新增了元素
如果key不存在字典中执行上述操作,就是新增元素
语法:字典[Key] = Value

1
2
3
4
my_dict = {"李白": 120, "观止": 110}
my_dict["罗辑"] = 115
print(my_dict) # 打印 {'李白': 120, '观止': 110, '罗辑': 115}

2. 更新元素

字典被修改,元素被更新

字典Key不可以重复,所以对已存在的Key执行上述操作,就是更新Value值

语法:字典[Key] = Value

1
2
3
4
my_dict = {"李白": 120, "观止": 110}
my_dict["李白"] = 115
print(my_dict) # 打印 {'李白': 115, '观止': 110}

3. 删除元素

获得指定Key的Value,同时字典被修改,指定Key的数据被删除

语法:字典.pop(Key)

1
2
3
4
my_dict = {"李白": 120, "观止": 110}
name = my_dict.pop("李白")
print(name) # 打印 120
print(my_dict) # 打印 {'观止': 110}

4. 清空字典

字典被修改,元素被清空

语法:字典.clear()

1
2
3
4
my_dict = {"李白": 120, "观止": 110}
my_dict.clear()
print(my_dict) # 打印 {}

5. 获取全部的key

得到字典中的全部Key

语法:字典.keys()

1
2
3
4
my_dict = {"李白": 120, "观止": 110}
my_keys = my_dict.keys()
print(my_keys) # 打印 dict_keys(['李白', '观止'])

6.计算字典内键值对数量

得到一个整数,表示字典内元素(键值对)的数量
语法:len(字典)

1
2
3
my_dict = {"李白": 120, "观止": 110}
print(len(my_dict)) # 打印 2

简单分类

是否支持下标索引

支持:列表、元组、字符串 - 序列类型

不支持:集合、字典 - 非序列类型

是否支持重复元素:

支持:列表、元组、字符串 - 序列类型

不支持:集合、字典 - 非序列类型

是否可以修改

支持:列表、集合、字典

不支持:元组、字符串

(2) 特点对比

列表 元组 字符串 集合 字典
元素数量 支持多个 支持多个 支持多个 支持多个 支持多个
元素类型 任意 任意 仅字符 任意 Key:Value Key:除字典外任意类型 Value:任意类型
下标索引 支持 支持 支持 不支持 不支持
重复元素 支持 支持 支持 不支持 不支持
可修改性 支持 不支持 不支持 支持 支持
数据有序
使用场景 可修改、可重复的一批数据记录场景 可修改、可重复的一批数据记录场景 不可修改一串字符的记录场景 不可重复的数据记录场景 以Key检索Value的数据记录场景

14.文件操作

未学习

15.异常

15.1.什么是异常

程序运行的过程中出现了错误

定义:在程序运行中,检测到一个错误,程序中止运行并且出现了一些错误的提示,也称作BUG
避免程序中止,提前准备处理可能出现的异常

在真实工作中, 我们肯定不能因为一个小的BUG就让整个程序全部奔溃,而是对BUG进行提醒, 整个程序继续运行
三.如何捕获异常
在可能出现异常的地方,做好提前准备,当真的出现异常的时候,可以有后续手段。

(1) 捕获常规异常

基本语法:

1
2
3
4
5
6
7
8
9
10
11
try:
可能发生错误的代码
except:
如果出现异常执行的代码
# 未发生错误try全部代码都会执行

# 未发生错误不会执行except中的代码

# 发生错误try中只会执行到报错行为止的代码

# 发生错误会执行except中的代码
(2) 捕获特定异常

如果尝试执行的代码的异常类型和要捕获的异常类型不一致,则无法捕获异常。
基本语法:

1
2
3
4
try:
可能发生错误的代码
except 待捕获异常名 as 别名:
如果出现异常执行的代码

例如:

捕获未定义变量产生的错误

1
2
3
4
try:
print(name) # 未定义变量,报错
except NameError as e:
print('name变量名称未定义错误')

同样的代码却无法捕获处理找不到文件异常

1
2
3
4
try:
f = open("C:/code/study.txt", "r") # 文件不存在,报错
except NameError as e:
print('文件不存在')
(3) 捕获多个异常

格式一:当待捕获异常名为Exception可以捕获所有类型异常,作用与(1)一致

例如:

1
2
3
4
try:
f = open("C:/code/study.txt", "r")
except Exception as e:
print('文件不存在')

格式二:把要捕获的异常类型的名字,放到except 后,并使用元组的方式进行书写。

基本格式:

1
2
3
4
try:
可能发生错误的代码
except (异常名1,异常名2) as 别名:
如果出现异常执行的代码

使用示例:

1
2
3
4
5
6
# 示例一:

try:
f = open("C:/code/study.txt", "r")
except (FileNotFoundError, NameError) as e:
print('文件不存在')
1
2
3
4
5
6
# 示例二:    

try:
print(name)
except (FileNotFoundError, NameError) as e:
print('名称未定义')

指定的两种异常都能捕获,未指定的无法捕获到

(4) 其他用法

异常描述信息存贮在别名中,可以通过打印别名获取
使用示例

1
2
3
4
try:
print(num) # 未定义,报错
except (NameError, ZeroDivisionError) as e:
print(e) # 打印 name 'num' is not defined

使用示例

出现异常,打印结果与(4.2)一致

1
2
3
4
5
6
try:
print(num) # 未定义,报错
except (NameError, ZeroDivisionError) as e:
print(e) # 打印 name 'num' is not defined
else:#else表示的是如果没有异常要执行的代码。
print("无异常") # 有异常,不执行

无异常

1
2
3
4
5
6
try:
print("正常") # 不报错
except (NameError, ZeroDivisionError) as e:
print(e) # 不执行
else:
print("无异常") # 执行

15.2.异常finally

finally表示的是无论是否异常都要执行的代码

使用示例:

之前提过,如果open文件却一直未close且程序未中止,将一直占用文件无法操作
如果打开文件后发生异常,未close也将导致一直占用,因此可选择在finally中close

1
2
3
4
5
6
7
global f
try:
f = open("C:/code/aaa.txt", "r")
except Exception as e:
print(e)
finally:
f.close() # 一定会执行close操作

15.3.异常的传递

异常是具有传递性的向上一级抛出

当函数调用链中出现异常,如果所有函数都没有捕获异常的时候, 程序就会报错

利用异常具有传递性的特点, 当我们想要保证程序不会因为异常崩溃的时候, 就可以在主函数中设置异常捕获, 由于无论在整个程序哪里发生异常, 最终都会传递到主函数中, 这样就可以确保所有的异常都会被统一捕获

16.包和模块

16.1模块

(1) 什么是模块

一个Python文件,以.py 结尾,能定义函数,类和变量,也能包含可执行的代码

作用:我们可以认为不同的模块就是不同工具包,每一个工具包中都有各种不同的工具(如函数)供我们使用进而实现各种不同的功能.

(2) 模块的导入

模块在使用之前需要先导入正在开发的文件

导入语法:

1
2
3
[from 模块名] import [模块|类|变量|函数|*] [as 别名]

# *表示导入所有

常用的组合形式如:
import 模块名
from 模块名 import 类、变量、方法等
from 模块名 import *
import 模块名 as 别名
from 模块名 import 功能名 as 别名

(2.1) 用法一
1
2
3
4
5
6
7
8
9
10
基本语法:

# 导入

import 模块名
import 模块名1,模块名2

# 使用

模块名.功能名()
1
2
3
4
5
6
7
8
9
10
11
12
# 导入时间模块         

import time
print("开始") # 打印 开始

# 使用time模块中睡眠功能(其中还有众多其他功能)

# 可以让程序睡眠10秒后再继续执行

time.sleep(10)
print("结束") #十秒后打印 结束

(2.2) 用法二

基本语法

1
2
3
4
5
6
# 导入

from 模块名 import 功能名

# 使用
功能名()

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
# 导入时间模块中的sleep方法

# 只能使用time模块中导入的sleep的方法

from time import sleep
print("开始") # 打印 开始

# 让程序睡眠10秒后再继续执行

sleep(10)
print("结束") #十秒后打印 结束

效果图与(2.1)一致

(2.3) 用法三

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 导入一:模块定义别名

import 模块名 as 别名

# 使用一:

别名.功能名()

# 导入二:功能定义别名

from 模块名 import 功能 as 别名

# 使用二:

别名()

使用示例一:

1
2
3
4
5
6
7
8
9
10
11
12
# 本名time将不可用

import time as tt
print("开始") # 打印 开始

# 让程序睡眠10秒后再继续执行

# 通过别名调用

tt.sleep(10)
print("结束") #十秒后打印 结束

使用示例二:

1
2
3
4
5
6
7
8
9
10
# 本名sleep将不可用

from time import sleep as sl
print("开始") # 打印 开始

# 让程序睡眠10秒后再继续执行

sl(10)
print("结束") # 十秒后打印 结束

效果图与(2.1)一致
(2.4) 用法四
基本语法

1
2
3
4
5
6
7
# 导入

from 模块名 import *

# 使用

功能名()

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
# 导入时间模块中的全部功能

# 导入效果与(2.1)一致,使用与(2.2)一致

from time import *
print("开始") # 打印 开始

# 让程序睡眠10秒后再继续执行

sleep(10)
print("结束") #十秒后打印 结束

17.面向对象

一.什么是面向对象

万物皆对象

现实世界的事物都有属性和行为,可在程序中抽离为类来描述现实世界的事物属性和行为。

使用类充当程序内现实事物的“设计图纸”,基于图纸(类)生产实体(对象),由对象做具体的工作,称之为:面向对象编程

二.类与对象

使用类封装属性,基于类创建出一个个的对象来使用

(1) 基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建类

class 类名称:
类的属性(成员变量)


类的行为(成员方法)

# 基于类创建对象

对象名 = 类名称()

# 调用

对象名.成员变量
对象名.成员方法()

class:关键字,表示要定义类了

类的属性:定义在类中的变量(成员变量) -> 事物的属性
类的行为:定义在类中的函数(成员方法) -> 事物的行为

(2) 使用示例

设计表格即设计类(class):

1
2
3
4
5
6
class Student:
name = None # 姓名
sex = None # 性别
country = None # 国籍
native_place = None # 籍贯
age = None # 年龄

打印表格即创建对象:

1
2
3
stu_1 = Student()  # 一张
stu_2 = Student() # 两张
stu_3 = Student() # 三张

填写表格即使用对象(为属性赋值):

1
2
3
stu_1.name = "李白"
stu_2.name = "观止"
stu_3.name = "罗辑"

(3) 成员变量和成员方法

(3.1) 成员变量
  • 定义在类内部的变量称之为成员变量,用法与正常变量一致。
(3.2) 成员方法

定义在类内部的函数称之为方法,与函数存在细微区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#函数
# 形参可以为0-N个

def 函数名(形参1,形参2,..,形参N):
函数体

# 方法

# 形参可以为0-N个

# self关键字必须填写

def 方法名(self,形参1,形参2,..,形参N):
方法体

self关键字在成员方法定义的时候必须填写,表示类对象自身
在方法内部,想要访问类的成员变量,必须使用self

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student:
name = None


# 调用say_hi1时正常打印
def say_hi1(self):
print(f"大家好,我叫{self.name}")

# 调用say_hi2时报错,'name' is not defined
def say_hi2(self):
print(f"大家好,我叫{name}")


当我们使用对象调用方法的时,self会自动被python传入,尽管在参数列表中,传参的时候可以忽略它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#定义
class Student:
name = None

def say_hi(self, msg):
print(f"大家好,我是{msg}")

# 创建

stu_1 = Student()

# 通过对象名调用

stu_1.say_hi("练习两年半的偶像实习生")

# 打印 大家好,我是练习两年半的偶像实习生


(4) 构造方法

通过传参的形式快速对属性赋值

正常情况下,为对象的属性赋值需要依次进行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 定义类

class Student:
name = None # 姓名
sex = None # 性别
age = None # 年龄

# 创建对象

stu_1 = Student()

# 为对象赋值

stu_1.name = "李白"
stu_1.sex = "男"
stu_1.age = 1000

在类可以使用:init()方法,即构造方法,快速为对象赋值。

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
# 定义类

class Student:
name = None # 姓名
sex = None # 性别
age = None # 年龄

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

# 创建对象并赋值

stu_1 = Student("李白", "男", 1000)

# 简化形式:可以省略成员属性定义,但仍可调用

class Student:

def __init__(self, name, sex, age):
self.name = name # 姓名
self.sex = sex # 性别
self.age = age # 年龄

# 创建对象并赋值

stu_1 = Student("李白", "男", 1000)

在创建类对象(构造类)的时候,会自动执行,将传入参数自动传递给__init__方法使用。

构造方法也是成员方法,定义时也需要在参数列表中提供:self

变量定义在构造方法内部,如果要成为成员变量,需要用self来表示,例如self.name

使用了构造方法,创建对象时必须传参否则会报错

(5) 魔术方法

Python类内置的类方法,各自有各自特殊的功能

魔术方法非常多,我们学习几个常用的即可。

方法 功能
init 构造方法,可用于创建类对象的时候设置初始化行为
str 字符串方法,用于实现类对象转字符串的行为
lt 用于2个类对象进行小于(<)或大于(>)比较
le 用于2个类对象进行小于等于(<=)或大于等于(>=)比较
eq 用于2个类对象进行相等(==)比较
(5.1)__str__方法

当直接打印类对象时,打印的是对象的内存地址,用处不大。

1
2
3
4
5
6
7
8
9
10
class Student:

def __init__(self, name, age):
self.name = name # 姓名
self.age = age # 年龄

stu_1 = Student("李白", 1000)
print(stu_1)

# 打印 <__main__.Student object at 0x0000024D8C6195D0>

我们可以通过__str__方法,自定义控制打印类对象时输出的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student:

def __init__(self, name, age):
self.name = name # 姓名
self.age = age # 年龄

# 自定义打印输出内容
def __str__(self):
return f"Student对象,name={self.name},age={self.age}"

stu_1 = Student("李白", 1000)
print(stu_1)

# 打印 Student对象,name=李白,age=1000
(5.2)__lt__方法

直接对2个对象进行比较是不可以的,会报错。

1
2
3
4
5
6
7
8
9
10
class Student:

def __init__(self, name, age):
self.name = name # 姓名
self.age = age # 年龄


stu_1 = Student("李白", 1000)
stu_2 = Student("罗辑", 300)
print(stu_1 > stu_2) # 报错

在类中实现__lt__方法即可完成:小于符号 和 大于符号 2种比较

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student:

def __init__(self, name, age):
self.name = name # 姓名
self.age = age # 年龄

def __lt__(self, other):
return self.age < other.age


stu_1 = Student("李白", 1000)
stu_2 = Student("罗辑", 300)
print(stu_1 > stu_2) # 打印 True
(5.3) __le__方法

在类中实现__le__方法即可完成:小于等于符号 和 大于等于符号 2种比较,否则会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student:

def __init__(self, name, age):
self.name = name # 姓名
self.age = age # 年龄

def __le__(self, other):
return self.age <= other.age


stu_1 = Student("李白", 1000)
stu_2 = Student("罗辑", 1000)
print(stu_1 <= stu_2) # True
print(stu_1 >= stu_2) # True

(5.4) __eq__方法
不实现__eq__方法,对象之间可以比较,但是是比较内存地址,但是不同对象==比较一定是False结果。

1
2
3
4
5
6
7
8
9
10
class Student:

def __init__(self, name, age):
self.name = name # 姓名
self.age = age # 年龄


stu_1 = Student("李白", 1000)
stu_2 = Student("李白", 666)
print(stu_1 == stu_2) # False

实现了__eq__方法,就可以按照自己的想法来决定2个对象是否相等了。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student:

def __init__(self, name, age):
self.name = name # 姓名
self.age = age # 年龄
# 自定义比较规则
def __eq__(self, other):
return self.name == self.name


stu_1 = Student("李白", 1000)
stu_2 = Student("李白", 666)
print(stu_1 == stu_2) # True

三.三大特性

面向对象包含3大主要特性:封装,继承,多态

(1) 封装

将现实世界事物的属性和行为在类中描述为成员变量和成员方法,完成程序对现实世界事物的描述

现实世界中的事物,有属性和行为。但是不代表这些属性和行为都是开放给用户使用的

(1.1) 私有成员
在类中提供仅供内部使用的属性和方法,无法被对象调用

基本语法:
私有成员变量:变量名以__开头(2个下划线)
私有成员方法:方法名以__开头(2个下划线)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student:
name = None # 普通成员变量
__age = None # 私有成员变量


# 普通成员方法
def say_hi(self):
print("你好")

# 私有成员方法
def __DNA(self):
print("DNA数量")


仅在成员内部可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student:
name = None # 普通成员变量
__age = 16 # 私有成员变量

# 普通成员方法
def show(self):
self.__check() # 在类中使用私有成员变量
if self.__age > 18: # 在类中使用私有成员变量
print("成年人")
else:
print("未成年")

# 私有成员方法
def __check(self):
print("自检")


(2) 继承

一个类继承另外一个类的所有成员变量和成员方法(不含私有)

(2.1) 单继承
基本语法:

1
2
class 类名(父类名):
类内容体

基本使用:

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
# 待继承的类

class Phone:
producer = "apple" # 厂商

def call_by_4g(self):
print("4g通话")

# 继承Phone

class Phone2022(Phone):
face_id = True # 面部识别

def call_by_5g(self):
print("2022最新5G通话")

# 创建对象

phone = Phone2022()

# 使用

print(phone.producer) # 可调用 继承自Phone的成员变量
print(phone.face_id) # 可调用 自身的成员变量
phone.call_by_4g() # 可调用 继承自Phone的成员方法
phone.call_by_5g() # 可调用 自身的成员方法

(2.2) 多继承
一个类也可以继承多个类
多个父类中,如果有同名的成员,默认以继承顺序(从左到右)为优先级。即:后继承的被先继承的覆盖
基本语法:

1
2
class 类名(父类1,父类2,...,父类N):
类内容体

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Phone:
producer = "apple" # 厂商

class Camera:
producer = "suoni" # 厂商

class Phone2022(Phone, Camera):
face_id = True # 面部识别

def call_by_5g(self):
print("2022最新5G通话")

phone = Phone2022()

print(phone.producer) # 打印结果为apple而非suoni

(2.3) 复写
子类继承父类的成员属性和成员方法后,如果对其“不满意”,那么可以进行复写。即:在子类中重新定义同名的属性或方法。

一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Phone2021:
producer = "apple" # 厂商

def call_by_5g(self):
print("2021版5G通话")


class Phone2022(Phone2021):
face_id = True # 面部识别

def call_by_5g(self):
print("2022升级版5G通话")


phone = Phone2022()

phone.call_by_5g() # 打印 2022升级版5G通话

如果需要使用被复写的父类的成员,只能在子类内通过如下方式调用父类的同名成员:

方式一:使用父类名调用

1
2
3
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self)

方式二:使用super()调用

1
2
使用成员变量:super().成员变量
使用成员方法:super().成员方法()

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Phone2021:
producer = "apple" # 厂商

def call_by_5g(self):
print("2021版5G通话")


class Phone2022(Phone2021):
face_id = True # 面部识别

def call_by_5g(self):
# 方式一调用
print(Phone2021.producer) # 打印 apple
Phone2021.call_by_5g(self)# 打印 2021版5G通话
# 方式二调用
print(super().producer)# 打印 apple
super().call_by_5g()# 打印 2021版5G通话


(3) 多态

多种状态,即完成某个行为时,使用不同的对象会得到不同的状态

img

多态常作用在继承关系上,函数(方法)形参声明接收父类对象,实际传入父类的子类对象进行工作,即

  • 以父类做定义声明
  • 以子类做实际工作
  • 用以获得同一行为, 不同状态
(3.1) 抽象类(接口)

抽象类就好比定义一个标准,包含了一些抽象的方法,要求子类必须实现

抽象类:包含抽象方法的类

抽象方法:没有具体实现的方法(只含pass)称之为抽象方法

pass是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思

py

多用于做顶层设计(设计标准),以便子类做具体实现。是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法

并配合多态使用,获得不同的工作状态。

18.类型注解

一.为什么需要类型注解

在代码中提供数据类型的注解(显式的说明),使用时能获得相关提示

帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示

显示声明时,pycharm确定这个对象是list类型,使用时能有对应提示

没有声明具体类型时,使用不会有任何相关提示

帮助开发者自身对变量进行类型注释(备注),后面调用不易出错

(1) 语法格式

变量名: 数据类型 = 数值

Python中类型注解仅仅起到提示作用,没有其他语言那么严格
Python解释器不会根据类型注解对数值做验证和判断,无法对应上也不会导致错误

(2) 基础类型

整数类型注解

1
var_1: int = 1314 

浮点数类型注解

1
var_2: float = 5.21 

布尔类型注解

1
var_3: bool = True  

字符串类型注解

1
var_4: str = "hhybd"  
(3) 类对象
1
2
3
4
5
6
# 定义学生类

class Student:
pass

stu: Student = Student() # 学生类类型注解
(4) 数据容器

列表类型注解

方式一:

1
my_list: list = [1, 2, 3]

方式二,list[基础类型]:

1
my_list: list[int] = [1, 2, 3]

元组类型注解

方式一:

1
my_tuple: tuple = (1, 2, 3)

方式二,元组类型需要将每一个元素都标记出来:

1
my_tuple: tuple[str, int, bool] = ("bd", 521, True)

集合类型注解

方式一:

1
my_set: set = {1, 2, 3}

方式二,set[基础类型]:

1
my_set: set[int] = {1, 2, 3}

字典类型注解

方式一:

1
my_dict: dict = {"hhbdy": 250}

方式二,dict[键类型,值类型]:

1
my_dict: dict[str, int] = {"hhbdy": 250}

字符串类型注解

1
my_str: str = "hhybd"
(5) 其他语法格式

在注释中进行类型注解
语法格式:

1
type:类型

使用示例:

1
2
3
4
stu = Student()  # type:Student
var_1 = 123 # type:int
my_list = [1, 2, 3] # type:list
my_set = {1, 2, 3} # type:set[int]

三.函数(方法)的类型注解
标注形参和返回值数据类型

类型注解仅仅起到提示作用
(1) 形参注解

语法格式:

1
2
def 函数方法名(形参名1:类型,形参名2:类型):
函数体

(2) 返回值注解
语法格式:

1
2
def 函数方法名(形参名1:类型,形参名2:类型) -> 返回值类型:
函数体

使用示例:

1
2
def add(x: int, y: int) -> int:
return x + y

四.Union类型
联合类型注解,在变量注解、函数(方法)形参和返回值注解中均可使用

需要导包使用

当数据类型不唯一时基本格式无法满足要求,此时便可使用Union类型

使用示例,Union[类型,类型]:

在变量中:

1
2
3
4
5
6
7
8
9
from typing import Union

# 数据为字符串和整数

my_list: list[Union[str, int]] = [2, "hhy", 5, "bd", 0]

# 键为字符串,值为字符串和整数

my_dict: dict[str, Union[str, int]] = {"name": "hhy", "QS": 250}

在函数中:

1
2
3
4
5
6
from typing import Union

# 接收字符串或整数,返回字符串或整数

def func(data: Union[int, str]) -> Union[int, str]:
pass

19.高级

一.闭包
可以保存函数内变量,不会随着函数调用完而销毁

(1) 基本定义
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1.在函数嵌套(函数中定义函数)的前提下
def func_out(num1):
def func_inner(num2):
# 2.内部函数使用了外部函数的变量
num = num1 + num2
print(f"num的值为:{num}")

# 3.外部函数返回了内部函数
return func_inner

# 创建闭包实例

f = func_out(10)

# 执行闭包

f(6) # 打印 num的值为:16

(2) 修改外部函数变量的值
在闭包函数(内部函数中)想要修改外部函数的变量值,必须用nonlocal声明这个外部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1.在函数嵌套(函数中定义函数)的前提下

def func_out(num1):
def func_inner(num2):
# 声明外部变量
nonlocal num1
# 2.内部函数使用了外部函数的变量
num1 += num2
print(f"num1的值为:{num1}")

# 3.外部函数返回了内部函数
return func_inner


# 创建闭包实例

f = func_out(10)

# 执行闭包

f(8) # 打印 num的值为:18

(3) 小结
优点:

无需定义全局变量即可实现通过函数,持续的访问、修改某个值
闭包使用的变量于所在的函数内,难以被错误的调用修改,可使变量更安全不易被恶意行为修改
缺点:

由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存(额外的内存占用)
二.装饰器
也是一种闭包,可在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能

(1) 基本使用
装饰器就是把一个函数当做参数传递给闭包中的外部函数,同时在内部函数中使用这个函数,并给他添加新的功能。
外部函数只能有一个参数,往往是被装饰的函数
内部函数可以根据被装饰的函数提供多个参数以及返回值

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
# 定义一个装饰器

def remind(func):
# 为目标函数增加新功能
def inner():
print("我睡觉了")
func()
print("我起床了")

return inner


# 需要被装饰的函数

def sleep():
import random
import time
print("睡眠中...")
time.sleep(random.randint(1, 5))


# 未装饰

sleep()

# 打印

# 睡眠中...

# 使用装饰器装饰函数(增加睡前起床提醒)

# 返回增强后的inner函数

fn = remind(sleep)
fn()

# 打印

# 我睡觉了

# 睡眠中...

# 我起床了

(2) 语法糖使用
可直接在需要被装饰的函数上加@装饰器名字,解释器碰到时会自动执行装饰过程,简化使用流程

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
# 定义一个装饰器

def remind(func):
def inner():
print("我睡觉了")
func()
print("我起床了")

return inner


# 需要被装饰的函数

# 解释器遇到@remind 会立即执行 sleep = remind(sleep)

@remind
def sleep():
import random
import time
print("睡眠中...")
time.sleep(random.randint(1, 5))

# 通过语法糖注解,直接调用即可达到效果

sleep()

# 打印

# 我睡觉了

# 睡眠中...

# 我起床了

(3) 多个装饰器使用
将装饰器都写在需要被装饰的函数上面即可
谁离被装饰的函数最近,谁就先去装饰函数

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
# 定义装饰器1

def remind(func):
def inner():
print("我睡觉了")
func()
print("我起床了")

return inner


# 定义装饰器2

def study(func):
def inner():
func()
print("我要敲代码啦")

return inner


# 谁近谁先装饰

@study # 2.执行 sleep = study(remind(sleep))
@remind # 1.执行 sleep = remind(sleep)
def sleep():
import random
import time
print("睡眠中...")
time.sleep(random.randint(1, 5))


sleep()

# 打印

# 我睡觉了

# 睡眠中...

# 我起床了

# 我要敲代码啦

(4) 带参数的装饰器
需要再增加一层函数嵌套来接收传递的参数

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
# 第一层:用于接收装饰器传递的参数

def logging(flag):
# 第二层:外部函数用于接收待装饰函数
def decorator(fn):
# 第三层:内部函数用于装饰接收的函数
def inner(num1, num2):
# 使用参数
if flag == "+":
print(">正在进行加法运算<")
elif flag == "-":
print(">正在进行减法运算<")
result = fn(num1, num2)
return result


return inner

# 返回装饰器
return decorator


# 被带有参数的装饰器装饰的函数

@logging('+')
def add(a, b):
result = a + b
return result


result = add(1, 3)
print(result)

(5) 类装饰器(了解即可)
一个类里面一旦实现了__call__方法,那么这个类创建的对象就是一个可调用对象,可以像调用函数一样进行调用

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义类

class Login:
def __call__(self, *args, **kwargs):
print("登录中。。。")

# 创建实例

login = Login()

# 如函数般调用

login() # 打印 登录中。。。

类装饰器装饰函数的功能通过call方法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 定义类装饰器

class Check:
# 接收待装饰的函数
def __init__(self, fn): # fn = comment
self.__fn = fn


def __call__(self, *args: object, **kwargs: object) -> object:
print("登录")
self.__fn() # comment()


# 被装饰的函数

@Check # comment = Check(comment)
def comment():
print("发表评论")


comment()

三.property属性
把类中的一个方法当作属性进行使用,简化开发

例如我们如果想获取和修改私有属性必须通过类方法修改,示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person:
def __init__(self):
self.__age = 18

def age(self):
return self.__age

def set_age(self, new_age):
self.__age = new_age


p = Person()
age = p.age()
print(f"修改前年龄是:{age}") # 打印 修改前年龄是:18
p.set_age(66)
age = p.age()
print(f"修改后年龄是:{age}") # 打印 修改后年龄是:66

通过使用如下两种方式可简化上述代码的使用

(1) 装饰器方式使用
@property表示把方法当作属性使用,表示当获取属性时执行下面修饰的方法
property修饰的方法名要与属性名一样
@方法名.setter表示把方法当作属性使用,表示当设置属性值时会执行下面修饰的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person:
def __init__(self):
self.__age = 18

@property
def age(self):
return self.__age

@age.setter
def age(self, new_age):
self.__age = new_age


p = Person()

# 可直接通过对象.属性使用

print(f"修改前年龄是:{p.age}") # 打印 修改前年龄是:18
p.age = 66
print(f"修改后年龄是:{p.age}") # 打印 修改后年龄是:66

(2) 类属性方式使用
property的参数说明:
属性名 = property(获取值方法,设置值方法)
第一个参数:获取属性时要执行的方法
第二个参数:设置属性时要执行的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person:
def __init__(self):
self.__age = 18

def get_age(self):
return self.__age

def set_age(self, new_age):
self.__age = new_age

# 类属性方式的property属性
age = property(get_age, set_age)


p = Person()
print(f"修改前年龄是:{p.age}") # 打印 修改前年龄是:18
p.age = 66
print(f"修改后年龄是:{p.age}") # 打印 修改后年龄是:66

四.上下文管理器
由实现了__enter__()和__exit__()方法的类创建的对象

在文件操作篇提到过使用with语句可以自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作。

1
2
with open("guanzhi.txt", "w") as f:
f.write("hello world")

使用with语句简化操作是建立在上下文管理器上的,open函数创建的f文件对象就是一个上下文管理器对象

__enter表示上文方法,需要返回一个操作文件对象

__exit__表示下文方法,with语句执行完成会自动执行,即使出现异常也会执行该方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 定义一个File类

class File:
def __init__(self, file_name, file_model):
self.file_name = file_name
self.file_model = file_model

# 实现__enter__()和__exit__()方法
def __enter__(self):
print("这是上文")
self.file = open(self.file_name, self.file_model)
return self.file

def __exit__(self, exc_type, exc_val, exc_tb):
print("这是下文")
self.file.close()


# 使用with语句来完成文件操作

with File("1.txt", "w") as f:
f.write("hello world")

五.深拷贝浅拷贝
开辟新的内存空间接收变量

调用id()可获得变量的内存地址
(1) 浅拷贝
(1.1) 可变类型浅拷贝

使用copy函数进行浅拷贝,只对可变类型的第一层对象进行拷贝
对拷贝的对象开辟新的内存空间进行存储
不会拷贝对象内部的子对象

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
import copy

a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]

# 普通赋值,指向同一空间

d = c
print(f"c内存地址:{id(c)}") # 打印 c内存地址:2265505547072
print(f"d内存地址:{id(d)}") # 打印 d内存地址:2265505547072


a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]

# 浅拷贝,指向不同空间

d = copy.copy(c)
print(f"c内存地址:{id(c)}") # 打印 c内存地址:2265505547648
print(f"d内存地址:{id(d)}") # 打印 d内存地址:2265505548608

# 不会拷贝对象内部的子对象

print(id(a)) # 打印 2135734964288
print(id(c[0])) # 打印 2135734964288
print(id(d[0])) # 打印 2135734964288

(1.2) 不可变类型浅拷贝
不可变类型进行浅拷贝不会给拷贝的对象开辟新的内存空间,只是拷贝了这个对象的引用

1
2
3
4
5
6
7
8
9
10
11
a = (1, 2, 3)
b = (11, 22, 33)
c = (a, b)

# 浅拷贝效果与普通赋值一样

d = c
e = copy.copy(c)
print(f"c内存地址:{id(c)}") # c内存地址:1536064302016
print(f"d内存地址:{id(d)}") # d内存地址:1536064302016
print(f"e内存地址:{id(e)}") # e内存地址:1536064302016

(2) 深拷贝
保障数据的独立性

(2.1) 可变类型深拷贝
使用deepcopy函数进行深拷贝,会对可变类型内每一层可变类型对象进行拷贝,开辟新的内存空间进行存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import copy

a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]

d = copy.deepcopy(c)
print(f"c内存地址:{id(c)}") # 打印 c内存地址:2603978212160
print(f"d内存地址:{id(d)}") # 打印 d内存地址:2603978215488

# 内部的可变类型也会拷贝

print(id(a)) # 打印 2603978215104
print(id(c[0])) # 打印 2603978215104
print(id(d[0])) # 打印 2603978212992

(2.2) 不可变类型深拷贝
不可变类型进行深拷贝不会给拷贝的对象开辟新的内存空间,只是拷贝了这个对象的引用

1
2
3
4
5
6
7
a = (1, 2, 3)
b = (11, 22, 33)
c = (a, b)

d = copy.deepcopy(c)
print(f"c内存地址:{id(c)}") # 打印 c内存地址:1312354282432
print(f"e内存地址:{id(d)}") # 打印 e内存地址:1312354282432

六.eval函数
eval()函数可将字符串当成有效的表达式来求值并返回计算结果

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
# 基本的数学运算

res = eval("(1+9)*5")
print(res)

# 打印 50

# 字符串重复

res = eval("'*'*10")
print(res)

# 打印 **********

# 字符串转换成列表

print(type(eval("[1,2,3,4]")))

# 打印 <class 'list'>

# 字符串转成字典

print(type(eval("{'name':'guanzhi','age':20}")))

# 打印 <class 'dict'>

注意事项:开发时千万不要使用eval直接转换input的结果

用户可能恶意输入有危害的终端指令

1
2
3
4
5
6
7
8
input_str = input() # 输入 __import__('os').system('rm -rf /*')
eval(input_str) # 直接运行可能导致主机崩溃

# 等价于

import os
os.system("终端命令")