Python变量与数据类型:掌握这10个技巧,让你的代码更高效
发布时间: 2024-12-17 12:46:04 阅读量: 3 订阅数: 3 ![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![PDF](https://csdnimg.cn/release/download/static_files/pc/images/minetype/PDF.png)
性能飙升:掌握Python中的代码优化技巧
![Python变量与数据类型:掌握这10个技巧,让你的代码更高效](https://opengraph.githubassets.com/b87ffc3c364de10736897d29c1450fc4133c3efd2fafc7476c86390f8f90d0b0/IT-0824/Python-assignment-1)
参考资源链接:[《Python语言程序设计》课后习题解析与答案](https://wenku.csdn.net/doc/5guzi5pw84?spm=1055.2635.3001.10343)
# 1. Python变量与数据类型基础
Python 作为一种解释型、面向对象的语言,以其简洁易读的语法和强大的功能受到开发者的青睐。变量与数据类型是Python编程的基础,是构建任何程序的基石。本章节将介绍Python变量的基本概念、数据类型及其使用场景,为后续深入探讨Python编程打下坚实的基础。
## 1.1 Python变量的创建与使用
Python中的变量无需显式声明类型,你只需要简单地为变量赋予值即可。例如:
```python
# 变量赋值
number = 10
text = "Hello, Python!"
```
这里,`number`变量存储了一个整数,而`text`存储了一个字符串。Python解释器会自动根据赋值内容推断出变量的数据类型。
## 1.2 基本数据类型
Python中包括但不限于以下基本数据类型:
- 数字:`int`(整数)、`float`(浮点数)、`complex`(复数)
- 布尔值:`bool`(`True` 或 `False`)
- 字符串:`str`
- 列表:`list`
- 元组:`tuple`
- 字典:`dict`
- 集合:`set`
每种数据类型都有其特定的用途和操作方法。例如,列表和元组都可以存储多个值,但列表是可变的,而元组是不可变的:
```python
# 列表和元组
fruits_list = ['apple', 'banana', 'cherry'] # 可变
fruits_tuple = ('apple', 'banana', 'cherry') # 不可变
```
理解每种数据类型的特点以及如何高效地使用它们,将帮助你在编写Python程序时做出更好的选择。
以上内容仅为第一章的概览,后续章节将深入探讨Python变量操作、数据类型的高效利用及优化案例。随着学习的深入,你将获得如何在实际开发中运用这些基础知识的深刻理解。
# 2. 精通Python变量操作
## 2.1 变量的命名规则与最佳实践
### 2.1.1 遵循PEP 8指南的变量命名
在Python编程中,遵循PEP 8风格指南是确保代码可读性和一致性的最佳实践之一。PEP 8指南详细说明了命名规范,这包括变量名的命名约定。通常,变量名应该简洁明了,以小写字母开头,多个单词之间使用下划线分隔。例如,`user_name`是一个比`username`更好的命名,因为它避免了大写混淆。
以下是一些变量命名的具体例子:
```python
# 正确的命名
total_sum = 0
current_position = 10
user_profile = {'name': 'Alice', 'age': 25}
# 错误的命名
TotalSum = 0
current-Position = 10 # 使用下划线而不是破折号
Userprofile = {'name': 'Alice', 'age': 25} # 变量名大小写不一致
```
### 2.1.2 变量命名的常见误区
虽然PEP 8指南为变量命名提供了清晰的指导,但开发者在实践中仍可能犯一些常见错误。这些错误不仅影响代码的可读性,还可能导致运行时错误。一些常见的误区包括:
- 使用Python内置关键字作为变量名。
- 使用缩写,使得变量含义不清。
- 使用单个字符命名,如`i`或`j`,除非在有限的循环中。
- 使用数字开头命名变量。
在避免这些误区时,建议使用有意义的变量名,并检查是否有重复使用Python内置名称。开发团队通常会建立内部代码审查流程,确保代码遵循命名约定。
## 2.2 变量的作用域和生命周期
### 2.2.1 局部变量与全局变量的区别
在Python中,变量的作用域取决于它被声明的位置。变量的作用域决定了它的访问范围和生命周期。局部变量是在函数或代码块内声明的变量,它只能在该函数或代码块内被访问。全局变量则在整个程序范围内都可访问,通常在模块顶层声明。
下面的代码展示了局部变量和全局变量的区别:
```python
x = 10 # 全局变量
def my_function():
y = 5 # 局部变量
print(f"The value of x (global) is {x}")
print(f"The value of y (local) is {y}")
my_function()
# 尝试在函数外部访问局部变量将会导致错误
# print(y) # NameError: name 'y' is not defined
```
### 2.2.2 变量作用域的深入理解
Python使用LEGB规则来解析变量的作用域,这代表了Local、Enclosing、Global和Built-in的查找顺序。当Python需要查找变量值时,会按照这个顺序进行搜索。
- **Local (L)**: 函数内部的局部作用域。
- **Enclosing (E)**: 外部嵌套函数的作用域。
- **Global (G)**: 模块级别的全局作用域。
- **Built-in (B)**: 内置作用域,包含Python的所有内置变量和函数名。
下面是一个涉及嵌套函数的例子:
```python
x = "global"
def outer():
x = "outer_local"
def inner():
x = "inner_local"
print(f"The value of x in inner is {x}")
inner()
print(f"The value of x in outer is {x}")
outer()
print(f"The value of x outside the outer is {x}")
```
此代码中,`inner()`函数中的`print`语句将打印`"inner_local"`,因为该函数有自己的局部变量`x`。`outer()`函数中的`print`语句将打印`"outer_local"`,因为`x`在此函数的局部作用域内被重新赋值。最后,在全局作用域中打印的`x`值为`"global"`。
## 2.3 变量的内存管理
### 2.3.1 引用计数与垃圾回收机制
Python使用引用计数(reference counting)机制来追踪和管理对象的生命周期。每个Python对象都包含一个计数器,记录有多少引用指向该对象。当引用计数降至零时,对象被认为是垃圾,并且会被回收。Python通过垃圾收集器定期执行引用计数的检查和回收操作。
这是一个简单的示例,演示了引用计数如何工作:
```python
import sys
a = "Hello World"
print(f"Initially, the reference count of a is {sys.getrefcount(a)}")
b = a
print(f"After assigning a to b, the reference count is {sys.getrefcount(a)}")
del b # 删除一个引用
print(f"After deleting b, the reference count is {sys.getrefcount(a)}")
```
在上面的代码中,我们看到变量`a`的引用计数随着赋值给`b`和删除`b`而改变。
### 2.3.2 变量内存泄漏的预防与诊断
尽管Python的垃圾收集器对于管理内存非常有用,但在某些情况下,循环引用可能会导致内存泄漏。当两个或更多的对象相互引用,并且没有外部引用时,这些对象可能无法被垃圾收集器回收,导致内存泄漏。
为避免内存泄漏,可以使用`weakref`模块创建弱引用。弱引用不增加对象的引用计数,因此不会阻止对象被回收。
```python
import weakref
class A:
def __init__(self, value):
self.value = value
a = A(10)
# 创建一个弱引用
weak_a = weakref.ref(a)
# a被回收之后,弱引用弱_a的引用将返回None
del a
print(f"Using weakref: {weak_a()}") # 输出将显示为None
```
通过这种方式,如果对象`a`被回收,通过`weakref.ref`得到的弱引用将不再有效。这可以作为诊断和预防循环引用的内存泄漏的一种手段。
在本章节中,我们深入探讨了变量命名、作用域以及内存管理的关键概念。正确地管理变量的作用域和生命周期是编写高效Python代码的基础,而理解Python的内存管理机制则有助于避免内存泄漏等严重问题。在下一章中,我们将继续深入讨论如何高效利用Python的数据类型,进一步提升代码的性能和可维护性。
# 3. 高效利用Python数据类型
数据类型是编程语言中的核心概念,Python作为一门高级编程语言,提供了多种内置数据类型,包括数字、字符串、列表、元组、字典和集合等。高效利用这些数据类型是编写高质量Python代码的关键。本章节将深入解析这些常用数据类型的内部实现,探讨高级操作技巧,并分享数据类型转换的最佳实践。
## 3.1 常用数据类型的深入解析
### 3.1.1 列表、元组、字典和集合的内部实现
Python中的列表(list)、元组(tuple)、字典(dict)和集合(set)是最常用的数据结构,它们各自有独特的内部实现方式。
- **列表(list)**:列表是动态数组,它通过数组存储元素,并且可以动态调整大小。列表的内部实现使用了连续的内存块,因此可以高效地进行索引访问和切片操作。
- **元组(tuple)**:元组与列表类似,不同的是元组是不可变的。这意味着一旦元组被创建,其内容不能被修改。内部实现上,元组通常是一系列指向不可变数据块的引用。
- **字典(dict)**:字典是键值对的集合,在Python中,字典的实现基于哈希表,它提供了非常快速的插入和查找操作。哈希表通过计算键的哈希值来确定元素的存储位置。
- **集合(set)**:集合是无序的、不重复的元素集,在内部实现上,集合和字典非常相似,也是基于哈希表。但是与字典不同,集合不存储键值对,只存储唯一的元素。
```python
# 字典的内部实现基于哈希表
my_dict = {'apple': 1, 'banana': 2}
print(my_dict.keys()) # 输出键集合
print(my_dict.values()) # 输出值集合
```
### 3.1.2 不可变类型与可变类型的性能考量
在Python中,不可变类型(如元组和字符串)与可变类型(如列表和字典)的性能考量是编写高效代码的一个重要方面。
- **内存效率**:不可变对象通常可以被Python的内存管理器优化,因为它们可以在多个地方被重用,而不需要复制。可变对象则通常需要额外的空间来存储其修改状态。
- **性能影响**:可变类型的操作(如列表的append)通常比不可变类型(如元组的拼接)更快,因为可变类型可以直接修改对象本身,而不必创建新对象。
```python
# 创建一个元组并尝试修改它
my_tuple = (1, 2, 3)
# my_tuple[1] = 2 # 这将引发TypeError,因为元组是不可变的
# 创建一个列表并修改它
my_list = [1, 2, 3]
my_list.append(4) # 直接在原地修改列表
```
- **函数参数传递**:当你将可变类型作为参数传递给函数时,实际上传递的是引用。这意味着函数内部对这些对象的修改会影响原始对象。而不可变类型的参数传递则不会出现这种情况。
```python
def mutate_list(input_list):
input_list.append(4)
my_list = [1, 2, 3]
mutate_list(my_list)
print(my_list) # 输出 [1, 2, 3, 4]
```
## 3.2 数据类型的高级操作技巧
### 3.2.1 字符串和字节序列的高效处理
字符串和字节序列是数据处理中的常用类型,高效地处理它们可以显著提高程序性能。
- **字符串操作**:Python的字符串是不可变的,这使得它们在多线程环境下是安全的。使用字符串格式化方法如`str.format()`或f-strings(Python 3.6+)可以提高代码的可读性和性能。
```python
# 使用f-string进行字符串格式化
name = "Alice"
age = 30
print(f"Hello, {name}. You are {age} years old.") # 输出 "Hello, Alice. You are 30 years old."
```
- **字节序列**:在处理二进制数据时,字节序列(bytes)和字节数组(bytearray)类型比字符串更为合适。字节序列是不可变的,而字节数组是可变的。
```python
# 字节序列的创建和操作
my_bytes = b'hello'
my_bytes += b' world'
print(my_bytes) # 输出 b'hello world'
```
### 3.2.2 复合数据类型的操作优化
复合数据类型如列表、字典等,包含多个元素,高效操作它们对于优化程序性能至关重要。
- **列表推导式**:列表推导式提供了一种简洁且高效的方式来创建列表。它们通常比使用循环构造列表要快。
```python
# 列表推导式示例
squares = [x**2 for x in range(10)]
print(squares) # 输出 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
```
- **字典推导式**:类似地,字典推导式也可以用来高效地创建字典。
```python
# 字典推导式示例
squares_dict = {x: x**2 for x in range(10)}
print(squares_dict) # 输出 {0: 0, 1: 1, ..., 9: 81}
```
## 3.3 数据类型转换的最佳实践
### 3.3.1 避免隐式类型转换的陷阱
在Python中,隐式类型转换可能会导致意外的结果和性能下降。了解何时会发生隐式转换,以及如何避免它们,是编写高效代码的一个方面。
- **数字转换**:在进行数学运算时,Python会根据需要将不同类型转换为通用类型。例如,在数字运算中,整数会被自动转换为浮点数。
```python
# 隐式类型转换示例
print(10 / 2) # 输出 5.0,整数被隐式转换为浮点数
```
### 3.3.2 显式类型转换的应用场景与性能影响
显式类型转换提供了对代码行为的精确控制,特别是在需要避免隐式转换可能导致的问题时。
- **性能影响**:显式类型转换可能会带来性能开销,特别是在转换为更复杂的数据类型时。例如,将字符串转换为列表时,需要为每个字符创建一个新对象。
```python
# 显式类型转换示例
my_str = "hello"
my_list = list(my_str) # 将字符串转换为列表
print(my_list) # 输出 ['h', 'e', 'l', 'l', 'o']
```
- **使用场景**:在数据预处理、文件解析和数据验证等场景中,显式类型转换非常有用。
在编写Python程序时,合理地利用数据类型可以显著提高程序的执行效率和可读性。在下一章节中,我们将探讨如何在实际代码中优化这些数据类型的应用,以及如何通过性能测试和分析来进一步提高代码性能。
# 4. Python变量与数据类型优化案例
在本章中,我们将深入探讨Python变量和数据类型的优化实践。我们会通过具体的案例,了解在代码中如何优化变量使用,提升数据处理的效率,并运用性能测试工具来分析代码的执行效率。本章的目标是向读者展示如何在编写代码的过程中运用最佳实践,实现性能提升。
## 4.1 代码中的变量优化技巧
### 4.1.1 变量缓存模式的使用
在Python中,合理利用变量缓存可以减少计算量,提高程序性能。例如,在迭代过程中,避免重复计算相同的结果,可以使用局部变量来存储这些结果。这称为变量缓存模式。
假设我们要计算一系列数字的阶乘,为了避免重复计算已知的阶乘值,我们可以缓存它们:
```python
factorial_cache = {}
def factorial(n):
if n in factorial_cache:
return factorial_cache[n]
if n == 0:
return 1
factorial_cache[n] = n * factorial(n - 1)
return factorial_cache[n]
# 使用缓存模式
for i in range(10):
print(f"{i}! = {factorial(i)}")
```
在上述代码中,我们创建了一个全局字典`factorial_cache`来存储已计算的阶乘值。通过检查这个缓存字典,我们可以避免重复计算,尤其是对于重复调用`factorial`函数时。
### 4.1.2 多线程与变量作用域的协同
多线程编程时,正确地处理共享变量是非常关键的,因为不当的变量共享可能会导致竞争条件和数据不一致。Python中多线程使用`threading`模块,为了避免变量共享问题,我们可以使用线程局部变量:
```python
from threading import Thread, local
data = local()
def thread_function(name):
data.name = name
print(f"Thread {data.name} starting.")
# 假设这里是执行一些线程任务
print(f"Thread {data.name} finishing.")
def main():
for index in range(3):
t = Thread(target=thread_function, args=(f"Thread-{index}"))
t.start()
t.join()
main()
```
在上面的例子中,我们创建了一个`local()`类型的`data`,它是一个线程局部存储对象。每个线程都会拥有`data`的一个独立实例,因此线程间的变量隔离得到了保证。这使得在多线程中对变量的使用变得更加安全。
## 4.2 数据处理的效率提升
### 4.2.1 列表推导式与生成器表达式的对比分析
列表推导式(List Comprehension)和生成器表达式(Generator Expression)是Python中处理集合数据的强大工具。列表推导式会立即创建列表,而生成器表达式会创建一个生成器对象,它按需产生元素,节省内存空间。
以下是一个简单的比较:
```python
import time
# 列表推导式
start_time = time.time()
squares_list = [x * x for x in range(1000000)]
print(time.time() - start_time) # 输出执行时间
# 生成器表达式
start_time = time.time()
squares_gen = (x * x for x in range(1000000))
print(time.time() - start_time) # 输出执行时间
# 遍历生成器表达式
for square in squares_gen:
print(square)
if square > 100:
break
```
在这个例子中,生成器表达式比列表推导式更快地执行,因为它没有立即创建一个大列表,而是创建了一个可以按需产生元素的生成器对象。这对于处理大量数据时,能显著减少内存使用,提升效率。
### 4.2.2 使用集合和字典提升数据操作速度
集合(set)和字典(dict)是Python中两种内置的不可变且无序的容器类型。它们在查找元素时,具有平均时间复杂度为O(1)的性能优势,这在大数据量处理中十分有用。
例如,使用集合来过滤重复元素:
```python
my_list = [1, 2, 2, 3, 4, 5, 5, 6]
unique_elements = set(my_list)
print(unique_elements)
```
在这个例子中,使用集合`set`比手动检查并插入元素到列表中更加高效。集合内部通过哈希表实现了快速查找和添加元素。
字典的使用则可以在数据映射和快速检索中发挥巨大作用。例如,在处理用户信息时:
```python
users = {
"alice": {"age": 25, "country": "USA"},
"bob": {"age": 30, "country": "UK"},
"charlie": {"age": 22, "country": "Canada"}
}
# 快速查找用户
print(users["alice"])
```
这里,字典允许我们快速访问任何用户的详细信息。
## 4.3 性能测试与分析
### 4.3.1 常用性能测试工具的介绍
性能测试是优化代码的重要手段,Python社区提供了多种工具来帮助我们进行性能测试。最常用的工具之一是`timeit`模块,它可以帮助我们准确地测量代码的执行时间:
```python
import timeit
code_to_test = """
def test():
return sum([x * x for x in range(1000)])
execution_time = timeit.timeit(stmt=code_to_test, number=1000)
print(f"Execution time: {execution_time} seconds")
```
上述代码使用`timeit.timeit`函数测试了`test`函数执行1000次所需的总时间。`timeit`模块可以排除启动开销,多次执行代码段,并给出准确的平均执行时间。
### 4.3.2 分析与优化循环和递归中的变量使用
循环和递归是影响程序性能的关键因素。对于循环来说,减少每次迭代中的计算量,避免不必要的数据结构操作是优化的关键。递归则需要注意递归深度和重复计算。
例如,优化一个简单的阶乘函数中的递归:
```python
def optimized_factorial(n, computed={0: 1}):
if n not in computed:
computed[n] = n * optimized_factorial(n - 1, computed)
return computed[n]
```
这个优化版本的阶乘函数使用了一个字典`computed`来存储已经计算过的阶乘值,避免了重复的递归计算。
在循环中,我们可以使用累积变量来存储中间结果,或者使用生成器表达式来减少内存的使用。
性能测试和分析的循环与递归中的变量优化案例,不仅提高了程序的执行效率,也优化了程序的内存使用,提高了程序的运行稳定性。
以上就是第四章的内容,通过对变量优化技巧的案例分析,我们了解了如何在代码中提升数据处理的效率,并通过性能测试来分析和优化程序。在下一章中,我们将探讨面向对象编程中的变量和数据类型运用,以及函数式编程和Python新数据类型的高级应用。
# 5. 变量与数据类型的高级应用
随着Python编程的深入,变量和数据类型的应用不仅限于基础和优化,还会涉及到面向对象编程、函数式编程等高级话题。Python 3.x的更新也带来了新的数据类型,为程序员提供了更多的工具来解决问题。
## 5.1 面向对象中的变量和数据类型
面向对象编程(OOP)是Python编程的核心之一,变量和数据类型在其中扮演着重要角色。
### 5.1.1 封装、继承和多态中的变量处理
**封装**是OOP的核心概念之一,它隐藏了对象的内部实现细节,并提供公共接口与外界交互。变量在封装中扮演着属性的角色,通常与方法一起使用。
```python
class Car:
def __init__(self, model):
self.model = model # 封装在类内部的变量
def display_info(self):
print(f'This car is a {self.model}')
```
**继承**是面向对象的另一个关键概念,它允许子类继承父类的属性和方法。子类可以扩展或修改继承的变量和行为。
```python
class ElectricCar(Car): # 继承自Car类
def __init__(self, model, battery_size):
super().__init__(model) # 调用父类的构造函数
self.battery_size = battery_size # 增加新的变量
def display_info(self):
print(f'This electric car has a {self.battery_size} kWh battery.')
```
**多态**使得不同的对象可以通过相同的接口使用不同的方式来响应消息。变量的类型可以是抽象的基类,具体实现可以由子类提供。
```python
def display_car_info(car):
car.display_info()
my_car = Car('Model S')
my_e_car = ElectricCar('Model 3', 75)
display_car_info(my_car)
display_car_info(my_e_car)
```
### 5.1.2 特殊方法与魔术方法中的数据类型运用
Python中的特殊方法和魔术方法(以双下划线开头和结尾的方法),用于模拟类似Python内置类型的行为。这些方法在数据类型的运用上有着独到之处。
```python
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return f'{self.title} by {self.author}'
def __len__(self):
return len(self.title) + len(self.author)
def __contains__(self, item):
return item in self.title or item in self.author
book = Book('Learning Python', 'Mark Lutz')
print(book) # 调用 __str__
print(len(book)) # 调用 __len__
print('Python' in book) # 调用 __contains__
```
## 5.2 函数式编程与数据类型
Python支持函数式编程范式,这为处理数据类型提供了新的视角和工具。
### 5.2.1 高阶函数与数据类型
高阶函数是指那些可以接受其他函数作为参数或将函数作为结果返回的函数。Python中的内置函数`map()`, `filter()`, 和 `reduce()`是函数式编程中与数据类型紧密相关的例子。
```python
def is_even(x):
return x % 2 == 0
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = list(filter(is_even, numbers)) # 使用 filter() 与 list
squared_numbers = list(map(lambda x: x ** 2, numbers)) # 使用 map() 与 lambda
```
### 5.2.2 递归、迭代器与装饰器在数据处理中的应用
递归函数通过重复调用自身来解决问题,非常适合处理具有自然层级结构的数据类型,如文件系统的目录结构。
迭代器和生成器是处理可迭代数据类型的有效工具,它们提供了一种方式,可以逐个访问序列中的元素,而不是一次性将所有元素加载到内存中。
装饰器是函数增强的工具,它可以让你修改或增强其他函数的行为。这在处理具有特定生命周期和作用域的数据类型时非常有用。
```python
def my_decorator(func):
def wrapper(*args, **kwargs):
print('Something is happening before the function is called.')
result = func(*args, **kwargs)
print('Something is happening after the function is called.')
return result
return wrapper
@my_decorator
def say_hello(name):
print(f'Hello {name}')
say_hello('Alice')
```
## 5.3 Python 3.x版本中的新数据类型
Python 3.x版本引入了一些新数据类型,以支持更复杂的编程需求。
### 5.3.1 Python 3中引入的新数据类型概览
Python 3.6引入了f-string,它是一种格式化字符串的快捷方式,为数据类型提供了更多的格式化选项。
Python 3.7引入了`__future__`模块中的新特性,例如,新引入的类型提示(Type Hints)使得函数和变量的数据类型更加明确,有助于代码维护。
Python 3.8的海象运算符`:=`,允许在表达式中赋值,使得在使用某些数据类型时可以更方便地进行数据操作。
### 5.3.2 迁移与兼容性考虑及最佳实践
当从Python 2迁移到Python 3,需要考虑新数据类型的兼容性问题,特别是在处理字符串、整数和字节序列时。许多第三方库也提供了对Python 3的支持。
最佳实践包括逐步升级代码库,编写可移植的代码,并利用`__future__`模块来兼容旧代码和新特性的使用。
```python
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from __future__ import unicode_literals
# 使用 f-string 进行字符串格式化
name = 'Alice'
print(f'Hello, {name}!')
# 使用类型提示
def greet(name: str) -> str:
return f'Hello, {name}!'
```
以上章节详细介绍了变量与数据类型在面向对象编程、函数式编程以及Python 3.x新特性中的高级应用,展示了Python丰富的编程范式和生态。通过这些高级应用,开发者可以更好地解决复杂问题,提升代码的可读性、可维护性和性能。
0
0
相关推荐
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![pptx](https://img-home.csdnimg.cn/images/20210720083543.png)
![-](https://csdnimg.cn/download_wenku/file_type_column_c1.png)
![-](https://img-home.csdnimg.cn/images/20210720083327.png)
![-](https://img-home.csdnimg.cn/images/20210720083327.png)
![-](https://img-home.csdnimg.cn/images/20210720083327.png)
![-](https://img-home.csdnimg.cn/images/20210720083327.png)
![-](https://csdnimg.cn/download_wenku/file_type_column_c1.png)