Python语言与系统设计(1)
第一章:Python基础与快速入门
Python 是一门跨平台、开源、免费的解释型高级动态编程语言。
python 同时支持伪编译将 Python 源程序转换为字节码来优化程序和提高运行速度。
支持使用 py2exe、pyinstaller 或 cx_Freeze 工具将 Python 程序转换为二进制可执行文件。
Python 支持命令式编程(How to do)、函数式编程(What to do),完全支持面向对象程序设计
注:这里所谓的命令式编程和函数式编程其实很好理解,下面的例子:
命令式编程:
函数式编程:
胶水语言:可以把多种不同语言编写的程序融合到一起实现无缝拼接,更好地发挥不同语言和工具的优势,满足不同应用领域的需求。
Python 几个重要的网站:
http://stackoverflow.com/questions/tagged/python
这里忽略了 python 的安装与环境的配置,我使用的 Jupyter Notebook 进行 python 程序的编写。
基础知识
Python 对象模型
对象是 python 中最基本的概念,在 python 中处理的一切都是对象。
包含许多内置对象可直接使用,如数字、字符串、列表、del 等;
非内置对象需要导入模块才能使用,如正弦函数 sin (x),随机数产生函数 random ( ) 等。
对象类型 | 类型名称 | 示例 | 简要说明 |
---|---|---|---|
数字 | int, float, complex | 1234, 3.14, 1.3e5, 3+4j | 数字大小没有限制,内置支持复数及其运算 |
字符串 | str | ‘swfu’, “I’m student”, ”’Python ”’, r’abc’, R’bcd’ | 使用单引号、双引号、三引号作为定界符,以字母 r 或 R 引导的表示原始字符串 |
字节串 | bytes | b’hello world’ | 以字母 b 引导,可以使用单引号、双引号、三引号作为定界符 |
列表 | list | [1, 2, 3],[‘a’, ‘b’, [‘c’, 2]] | 所有元素放在一对方括号中,元素之间使用逗号分隔,其中的元素可以是任意类型 |
字典 | dict | {1:’food’ ,2:’taste’, 3:’import’} | 所有元素放在一对大括号中,元素之间使用逗号分隔,元素形式为 “键: 值” |
元组 | tuple | (2, -5, 6), (3,) | 不可变,所有元素放在一对圆括号中,元素之间使用逗号分隔,如果元组中只有一个元素的话,后面的逗号不能省略 |
集合 | set frozenset | {‘a’, ‘b’, ‘c’} | 所有元素放在一对大括号中,元素之间使用逗号分隔,元素不允许重复 ; 另外,set 是可变的,而 frozenset 是不可变的 |
字符串(string)
定义:字符串是由 Unicode 字符组成的序列,用于处理文本数据。
特性
- 可以包含各种语言的字符。
- 不可变,即创建后不能更改其中的元素。
- 在 Python 3 中,所有的字符串默认都是 Unicode 字符串,所以前缀
u
或U
不再需要。不过,仍然可以通过前缀u
或U
来表示一个 Unicode 字符串(例如u"Hello"
),但这在 Python 3 中并不是必需的。
用途:字符串通常用于表示和处理人类可读的文本信息,如文字、数字、符号等。
字节串(bytes)
定义:字节串是由 0~255 范围内的整数(即 8 位字节)构成的序列,用于在程序中处理二进制数据。
特性
- 可以包含 ASCII 字符或其他编码的字节。
- 不可变,即一旦创建了一个字节串,就不能修改它。
- 在 Python 中,可以通过前缀
b
或B
来表示一个字节串(例如b"Hello"
)。
用途:字节串通常用于处理非文本数据,如文件内容、网络数据、图像、音频等二进制数据。
主要区别
- 编码方式:字符串使用 Unicode 编码,可以包含任何语言的字符;而字节串使用
bytes
编码,通常包含 ASCII 字符或其他特定编码的字节。 - 存储与表示:字符串以文本形式存储和表示,便于人类阅读和编辑;字节串则以二进制形式存储和表示,更适合计算机处理和传输。
- 用途差异:字符串主要用于处理文本信息;而字节串则主要用于处理二进制数据,如文件读写、网络通信等场景。
转换方法
- 字符串转字节串:可以使用字符串的
.encode()
方法将其编码为字节串。例如,string.encode('utf-8')
会将字符串string
编码为 UTF-8 格式的字节串。 - 字节串转字符串:可以使用字节串的
.decode()
方法将其解码为字符串。例如,bytes_data.decode('utf-8')
会将 UTF-8 格式的字节串bytes_data
解码为字符串。
对象类型 | 类型名称 | 示例 | 简要说明 |
---|---|---|---|
布尔型 | bool | True, False | 逻辑值,关系运算符、成员测试运算符、同一性测试运算符组成的表达式的值一般为 True 或 False |
空类型 | NoneType | None | 空值 |
异常 | Exception、ValueError、TypeError | Python 内置大量异常类,分别对应不同类型的异常 | |
文件 | f = open(‘data.dat’, ‘rb’) | open 是 Python 内置函数,使用指定的模式打开文件,返回文件对象 | |
其他可迭代对象 | 生成器对象、range 对象、zip 对象、enumerate 对象、map 对象、filter 对象等等 | 具有惰性求值的特点,除 range 对象之外,其他对象中的元素只能看一次 | |
编程单元 | 函数(使用 def 定义) 类(使用 class 定义) 模块(类型为 module) | 类和函数都属于可调用对象,模块用来集中存放函数、类、常量或其他对象 |
Python 变量
在 Python 中,不需事先声明变量名及其类型,直接赋值即可创建各种类型的对象变量。这一点适用于 Python 任意类型的对象。
在 cpp 中,变量在声明时就需要声明变量的类型,而在 python 中则是直接给变量赋值。
Python 属于强类型编程语言,Python 解释器会根据赋值或运算来自动推断变量类型。Python 还是一种动态类型语言,变量的类型也是可以随时变化的。
如果变量出现在赋值运算符或复合赋值运算符(例如 +=、*= 等等)的左边则表示创建变量或修改变量的值,否则表示引用该变量的值
字符串和元组属于不可变序列,不能通过下标的方式来修改其中的元素值,试图修改元组中元素的值时会抛出异常。
python 中允许多个变量指向同一个值
当修改其中一个变量值后,其内存地址将会变化,但并不影响另一变量
Python 采用的是基于值的内存管理方式,如果为不同变量赋值为相同值,这个值在内存中只有一份,多个变量指向同一块内存地址。
变量名必须以字母或下划线开头,但以下划线开头的变量在 Python 中有特殊含义;
变量名中不能有空格以及标点符号(括号、引号、逗号、斜线、反斜线、冒号、句号、问号等);
不能使用关键字作变量名,可以导入 keyword 模块后使用 print (keyword.kwlist) 查看所有 Python 关键字 (33 个);
|
|
注(下划线的含义):
单下划线 _
:
- 在交互式环境中,
_
表示上一个表达式的结果。 - 在循环或迭代中,
_
通常用作一个占位符,表示某个值会被忽略。
双下划线 __
:
- 类的属性名如果以双下划线开头且不以双下划线结尾(例如,
__foo
),这会触发名称改写(name mangling)。这意味着该属性会在类被继承时通过特定的方式被重命名,以避免子类意外覆盖父类的属性。
双下划线前缀和后缀 __xxx__
:
- 这种命名方式通常用于特殊方法或魔术方法(magic methods),这些方法具有特定的含义,并由 Python 解释器在特定情况下调用。例如,
__init__
用于初始化对象,__str__
用于定义对象的字符串表示等。
单下划线前缀 _xxx
:
- 通常用于表示变量或方法是 “受保护的” 或 “内部使用的”,这是一种约定俗成的命名习惯,用于指示这些变量或方法不应该被类的外部直接访问。然而,这并不会引起 Python 解释器的任何强制限制,只是表明了一种使用意图。
不建议****使用系统内置的模块名、类型名或函数名以及已导入的模块名及其成员名作变量名,这将会改变其类型和含义,可以通过 dir (builtins) 查看所有内置模块、类型和函数;
变量名对英文字母的****大小写敏感,例如 student 和 Student 是不同的变量。
Python 中的变量并不直接存储值,而是存储了值的内存地址或者引用,这也是变量类型随时可以改变的原因。
赋值语句的执行过程是:首先把等号右侧表达式的值计算出来,然后在内存中寻找一个位置把值存放进去,最后创建变量并指向这个内存地址。
Python 具有自动内存管理功能,对于没有任何变量指向的值,Python 自动将其删除。Python 会跟踪所有的值,并自动删除不再有变量指向的值。
显式使用 del 命令删除不需要的值或显式关闭不再需要访问的资源,仍是一个好的习惯,同时也是一个优秀程序员的基本素养之一。
Python 的数字类型
三类:整数类型、浮点类型与复数类型
整数类型
・十进制整数如,0、-1、9、123
・十六进制整数,需要 16 个数字 0、1、2、3、4、5、6、7、8、9、a、b、c、d、e、f 来表示整数,必须以 0x 开头,如 0x10、0xfa、0xabcdef
・八进制整数,只需要 8 个数字 0、1、2、3、4、5、6、7 来表示整数,必须以 0o 开头,如 0o35、0o11
・二进制整数,只需要 2 个数字 0、1 来表示整数,必须以 0b 开头如,0b101、0b100
浮点数类型:浮点数****又称小数
ü15.0、0.37、-11.2、1.2e2、314.15e-2
Python 内置支持复数类型
Python 3.6.x 开始支持在数字中间位置使用单个下划线作为分隔来提高数字的可读性,类似于数学上使用逗号作为千位分隔符。
字符串
n 用单引号、双引号或三引号界定的符号系列称为字符串
n 单引号、双引号、三单引号、三双引号可以互相嵌套,用来表示复杂字符串
例如: __ ‘__ abc ’ 、 ‘123’ 、 ’ 中国 ’ 、 “Python” 、‘‘‘Tom said, “Let’s go”’’’
字符串属于不可变序列
空字符串表示为” 或 “”
三引号”’或””” 表示的字符串可以换行,支持排版较为复杂的字符串;三引号还可以在程序中表示较长的注释。
字符串的拼接
字符串之间可以通过 + 或 * 进行连接
加法操作 (+) 将两个字符串连接成为一个新的字符串
乘法操作 (*) 生成一个由其本身字符串重复连接而成的字符串
len () 函数能否返回一个字符串的长度
字符串的转义
转义字符 | 含义 | 转义字符 | 含义 |
---|---|---|---|
\b | 退格,把光标移动到前一列位置 | \ | 一个斜线\ |
\f | 换页符 | \’ | 单引号’ |
\n | 换行符 | \” | 双引号” |
\r | 回车 | \ooo | 3位八进制数对应的字符 |
\t | 水平制表符 | \xhh | 2位十六进制数对应的字符 |
\v | 垂直制表符 | \uhhhh | 4位十六进制数表示的Unicode字符 |
字符串界定符前面加字母 r 或 R 表示原始字符串,其中的特殊字符不 进行转义,但字符串的最后一个字符不能是 \。
・原始字符串主要用于正则表达式、文件路径或者 URL 的场合。
运算符和表达式
运算符 | 功能说明 |
---|---|
+ | 算术加法,列表、元组、字符串合并与连接,正号 |
– | 算术减法,集合差集,相反数 |
***** | 算术乘法,序列重复 |
/ | 真除法 |
// | 求整商,但如果操作数中有实数的话,结果为实数形式的整数 |
% | 求余数,字符串格式化 |
** | 幂运算 |
<、<=、>、>=、==、!= | (值)大小比较,集合的包含关系比较 |
or,and, not | 逻辑或、逻辑与、逻辑非 |
in | 成员测试 |
is | 对象同一性测试,即测试是否为同一个对象或内存地址是否相同 |
、^、&、«、»、~ | 位或、位异或、位与、左移位、右移位、位求反 |
&、|、^、- | 集合交集、并集、对称差集,差集 |
加法
+ 运算符除了用于算术加法外,还可用于列表、元组、字符串的连接,但不支持不同类型的对象之间相加或连接。
乘法
运算符不仅可以用于 数值乘法 ,还可以用于列表、字符串、元组等类型__
当列表、字符串或元组等类型变量与整数进行“ *” 运算时,表示 对内容进行重复 并返回重复后的新对象。
除法
Python 中的除法有两种, “ /” 和“ //” 分别表示除法和整除运算 。
求余
% 运算符除去可用于字符串格式化之外,也可对整数和浮点数计算余数。
・由于浮点数的精确度影响,计算结果可能略有误差。
关系型运算符
关系运算符 可以连用 ,一般用于同类型对象之间值的大小比较,或者测试集合之间的包含关系
成员测试运算符
成员测试运算符in用于 成员测试
・即测试一个对象是否为另一个对象的元素。
位运算符
位运算符只能用于整数,其内部执行过程为:首先将整数转换为二进制数,然后右对齐,必要的时候左侧补 0,按位进行运算,最后再把计算结果转换为十进制数字返回
集合的交集、并集、对称差集 等运算借助于 位运算符 来实现
而 差集 则使用 减号运算符 实现
and or
and 和 or 具有 惰性求值 特点,只计算必须计算的表达式
注意,此时并没有定义变量 a,and 要两者都对返回对,而第一位的 3>5 已经判断是错的了,这里直接返回错,不会再对后面的 a>3 进行执行。
3>5 的值为 False,所以需要计算后面表达式。
3<5 的值为 True,不需要计算后面表达式。这里是与 and 类似的惰性,or 只要两者中有一个是正确的,则返回正确,这里的 3<5 是对的,or 的代码将不会执行。
这里比较让人迷惑的是下面这个程序:
这里返回的结果是 1,因为 or 后面的程序没有执行。
and 是将最后一个计算的表达式的值作为整个表达式的值。
or 也类似
比较特殊的是 is not,它只能返回 true 或者 false
Python 中 单个任何类型的对象或常数 属于合法表达式,使用 运算符连接的变量和常量以及函数调用的任意组合 也属于 合法的表达式
特殊说明
逗号并不是运算符,只是一个普通分隔符
逗号(,
)在 Python 中用于创建元组(tuple)。当你使用逗号分隔多个值或表达式时,Python 会将这些值或表达式组合成一个元组。
Python 不支持 ++ 和–运算符,虽然在形式上有时似乎可这样用,但实际上是另外的含义
常用内置函数
内置函数不需要导入任何模块即可使用
执行下面的命令可以列出所有内置函数
|
|
函数 | 功能简要说明 |
---|---|
abs(x) | 返回数字 x 的绝对值或复数 x 的模 |
all(iterable) | 如果对于可迭代对象中所有元素 x 都等价于 True,也就是对于所有元素 x 都有 bool (x) 等于 True,则返回 True。对于空的可迭代对象也返回 True |
any(iterable) | 只要可迭代对象 iterable 中存在元素 x 使得 bool (x) 为 True,则返回 True。对于空的可迭代对象,返回 False |
ascii(obj) | 把对象转换为 ASCII 码表示形式,必要的时候使用转义字符来表示特定的字符 |
bin(x) | 把整数 x 转换为二进制串表示形式 |
bool(x) | 返回与 x 等价的布尔值 True 或 False |
bytes(x) | 生成字节串,或把指定对象 x 转换为字节串表示形式 |
callable(obj) | 测试对象 obj 是否可调用。类和函数是可调用的,包含__call__() 方法的类的对象也是可调用的 |
compile() | 用于把 Python 代码编译成可被 exec () 或 eval () 函数执行的代码对象 |
complex(real, [imag]) | 返回复数 |
chr(x) | 返回 Unicode 编码为 x 的字符 |
函数 | 功能简要说明 |
---|---|
delattr(obj, name) | 删除属性,等价于 del obj.name |
dir(obj) | 返回指定对象或模块 obj 的成员列表,如果不带参数则返回当前作用域内所有标识符 |
divmod(x, y) | 返回包含整商和余数的元组 ((x-x% y)/y, x% y) |
enumerate(iterable[, start]) | 返回包含元素形式为 (0, iterable [0]), (1, iterable [1]), (2, iterable [2]), … 的迭代器对象 |
eval(s[, globals[, locals]]) | 计算并返回字符串 s 中表达式的值 |
exec(x) | 执行代码或代码对象 x |
exit() | 退出当前解释器环境 |
filter(func, seq) | 返回 filter 对象,其中包含序列 seq 中使得单参数函数 func 返回值为 True 的那些元素,如果函数 func 为 None 则返回包含 seq 中等价于 True 的元素的 filter 对象 |
float(x) | 把整数或字符串 x 转换为浮点数并返回 |
frozenset([x])) | 创建不可变的集合对象 |
getattr(obj, name[, default]) | 获取对象中指定属性的值,等价于 obj.name,如果不存在指定属性则返回 default 的值,如果要访问的属性不存在并且没有指定 default 则抛出异常 |
函数 | 功能简要说明 |
---|---|
globals() | 返回包含当前作用域内全局变量及其值的字典 |
hasattr(obj, name) | 测试对象 obj 是否具有名为 name 的成员 |
hash(x) | 返回对象 x 的哈希值,如果 x 不可哈希则抛出异常 |
help(obj) | 返回对象 obj 的帮助信息 |
hex(x) | 把整数 x 转换为十六进制串 |
id(obj) | 返回对象 obj 的标识(内存地址) |
input ([提示]) | 显示提示,接收键盘输入的内容,返回字符串 |
int(x[, d]) | 返回实数(float)、分数(Fraction)或高精度实数(Decimal)x 的整数部分,或把 d 进制的字符串 x 转换为十进制并返回,d 默认为十进制 |
isinstance(obj, class-or-type-or-tuple) | 测试对象 obj 是否属于指定类型(如果有多个类型的话需要放到元组中)的实例 |
iter(…) | 返回指定对象的可迭代对象 |
len(obj) | 返回对象 obj 包含的元素个数,适用于列表、元组、集合、字典、字符串以及 range 对象和其他可迭代对象 |
函数 | 功能简要说明 |
---|---|
list([x])、set([x])、tuple([x])、dict([x]) | 把对象 x 转换为列表、集合、元组或字典并返回,或生成空列表、空集合、空元组、空字典 |
locals() | 返回包含当前作用域内局部变量及其值的字典 |
map(func, *iterables) | 返回包含若干函数值的 map 对象,函数 func 的参数分别来自于 iterables 指定的每个迭代对象, |
max(x)、 min(x) | 返回可迭代对象 x 中的最大值、最小值,要求 x 中的所有元素之间可比较大小,允许指定排序规则和 x 为空时返回的默认值 |
next(iterator[, default]) | 返回可迭代对象 x 中的下一个元素,允许指定迭代结束之后继续迭代时返回的默认值 |
oct(x) | 把整数 x 转换为八进制串 |
open(name[, mode]) | 以指定模式 mode 打开文件 name 并返回文件对象 |
ord(x) | 返回 1 个字符 x 的 Unicode 编码 |
pow(x, y, z=None) | 返回 x 的 y 次方,等价于 x ** y 或 (x ** y) % z |
print(value, …, sep=’ ‘, end=’\n’, file = sys. stdout, flush=False) | 基本输出函数 |
quit() | 退出当前解释器环境 |
range([start,] end [, step] ) | 返回 range 对象,其中包含左闭右开区间 [start,end) 内以 step 为步长的整数 |
函数 | 功能简要说明 |
---|---|
repr(obj) | 返回对象 obj 的规范化字符串表示形式,对于大多数对象有 eval (repr (obj))==obj |
reversed(seq) | 返回 seq(可以是列表、元组、字符串、range 以及其他可迭代对象)中所有元素逆序后的迭代器对象 |
round (x [, 小数位数]) | 对 x 进行四舍五入,若不指定小数位数,则返回整数 |
sorted(iterable, key=None, reverse=False) | 返回排序后的列表,其中 iterable 表示要排序的序列或迭代对象,key 用来指定排序规则或依据,reverse 用来指定升序或降序。该函数不改变 iterable 内任何元素的顺序 |
str(obj) | 把对象 obj 直接转换为字符串 |
sum(x, start=0) | 返回序列 x 中所有元素之和,返回 start+sum (x) |
type(obj) | 返回对象 obj 的类型 |
zip(seq1 [, seq2 […]]) | 返回 zip 对象,其中元素为 (seq1 [i], seq2 [i], …) 形式的元组,最终结果中包含的元素个数取决于所有参数序列或可迭代对象中最短的那个 |
重点:
输入函数:input( )
・可输入数字、字符串和其它任意类型对象, 返回结果都是字符串 。
**输出函数:**print () 函数进行输出。
bin ()、oct ()、hex () 用来将整数转换为二进制、八进制和十六进制形式,这三个函数都要求参数必须为整数。
int() 用来把实数转换为整数,或把数字字符串按指定进制转换为十进制数。
eval(< 字符串 >) 函数能够 以 Python 表达式的方式解析并执行字符串 ,将返回结果输出
range() 语法格式为range ([start,] end [, step] ) , 返回具有 惰性求值特点的 __range对象 __ ,其中包含 左闭右开区间 [ start,end ) 内以step为步长的整数 。
range 对象,其中包含左闭右开区间 [start,end) 内以 step 为步长的整数。
ord() 和 chr () 是一对功能相反的函数: ord() 用来返回单个字符的序数或 Unicode 码,而 chr () 则用来返回某序数对应的字符
str () 则直接将其任意类型参数转换为字符串。
max ()、min ()、sum () 这三个内置函数分别用于计算列表、元组或其他可迭代对象中所有元素最大值、最小值以及所有元素之和
内置函数 type () 和 isinstance () 可以判断数据类型。
sorted () 对列表、元组、字典、集合或其他可迭代对象进行排序并返回新列表。
zip()函数用来把多个可迭代对象中的元素压缩到一起,返回一个可迭代的 zip对象 ,其中每个元素都是包含原来的多个可迭代对象对应位置上元素的元组 ,如同拉拉链一样 。
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
zip (*) 是解压
内置函数filter()将一个单参数函数作用到一个序列上,返回该序列中使得该函数返回值为True的那些元素组成的 filter对象 ,如果指定函数为None,则返回序列中等价于True的元素。
enumerate () 函数用来枚举可迭代对象中的元素,返回可迭代的 enumerate 对象,其中每个元素都是包含索引和值的元组。
map、filter、enumerate、zip等对象 不仅具有惰性求值的特点 ,还有另外一个特点: 访问过的元素不可再次访问 。
对象的删除
reversed () 对可迭代对象(生成器对象和具有惰性求值特性的 zip、map、filter、enumerate 等类似对象除外)进行翻转(首尾交换)并返回可迭代的 reversed 对象。
Python 具有 自动内存管理功能 , Python 解释器会跟踪所有的值,一旦发现某个值不再有任何变量指向,将会自动删除该值。
显式释放自己申请的资源是程序员的好习惯之一,也是程序员素养的重要体现之一。
del 命令 :显式删除对象并解除与值之间的指向关系。
del 命令无法删除元组或字符串中的元素,只可以删除整个元组或字符串 因为这两者均属于不可变序列。
模块导入
Python默认安装仅包含部分基本或核心模块,用户可安装大量的 扩展模块 ,pip是管理模块的重要工具。
在Python启动时,仅加载了很少的一部分模块,在需要时由程序员显式地加载(可能需要先安装)其他模块。
减小运行的压力,仅加载真正需要的模块和功能,且具有很强的可扩展性 。
可以使用sys.modules.items()显示所有预加载模块的相关信息。
重新导入一个模块:在 3.x 中,需要使用 imp 模块的 reload 函数
导入模块时的文件搜索顺序
- 当前文件夹
- sys.path 变量指定的文件夹
- 可以使用 sys 模块的 path 变量查看 python 导入模块时搜索模块的路径,也可以向其中 append 自定义的目录以扩展搜索路径。
- 优先导入 pyc 文件
- 如果相应的 pyc 文件与 py 文件时间不相符,则导入 py文件并重新编译该模块。
如需导入多个模块,建议顺序如下:
- 标准库
- 成熟的第三方扩展库
- 自己开发的库
Python文件
- py : Python 源文件,由 Python 解释器负责解释执行。
- pyw : Python 源文件,常用于图形界面程序文件。
- pyc : Python 字节码文件,无法使用文本编辑器直接查看其内容,可用 于隐藏 Python 源代码和提高运行速度。
- 对于Python模块,第一次被导入时将被编译成字节码的形式,并在以后再次导入时优先使用“.pyc”文件,以提高模块的加载和运行速度。
- 对于非模块文件,直接执行时并不生成“.pyc”文件,但可使用py_compile模块的compile()函数进行编译以提高加载和运行速度。
- Python还提供了compileall模块,其中包含compile_dir()、compile_file()和compile_path()等方法,用来支持批量Python源程序文件的编译。
- pyd : 一般是由其他语言编写并编译的二进制文件,常用于实现某些软 件工具的 Python 编程接口插件或 Python 动态链接库。
主要参考课堂ppt总结,仅供参考,可能有误