【干货】三万字长文总结python从基础到面向对象知识点
#来点儿干货# 元旦假三天总结的干货,望大佬们指正
1. Python 简介
Python是一种解释型的动态强类型语言,它是一种面向对象的高级交互式语言。
- Python是解释型语言:Python程序没有编译环节,而是边解释边执行。Python程序(.py)经过解释器解释成字节码(.pyc)后可以直接运行。
- Python是动态强类型语言:虽然不需要声明变量类型,解释器可以动态的推断出变量的数据类型,但是数据类型是不可以被忽略的。
- Python是一种面向对象的高级语言:支持面向对象编程,但因是动态语言,所以多态、重载等功能被弱化。
- Python是一种交互式语言:支持在一个Python提示符>>>后直接执行代码
2. Python 解释器简介
- Python 解释器是一种可以解释执行Python代码的软件程序。 在编写Python代码时,需要使用相应版本的Python解释器来解释执行代码,以便产生正确的结果。可以通过命令行或者IDE等方式来运行Python解释器,并执行Python代码。
- Python 官方提供了多个解释器,包括CPython、Jython、IronPython、PyPy等。
- CPython 是由C语言开发的Python解释器,它可以解释执行Python的源代码,并将其转化为字节码来提高性能。它可以很好地支持Python的标准库和第三方库,也可以很好地支持Python与C语言的胶合。
- 除了CPython,还有其他的解释器:
- Jython:可以将Python代码转化为Java字节码,在Java虚拟机中执行
- IronPython:可以在.NET平台上运行Python代码
- PyPy:一个Python实现的解释器,具有很高的性能
3. Python 解释器组成
Python 解释器核心由编译器、解析器、运行时环境、标准库等几个组件构成
- 编译器:Python 解释器有一个简单的编译器,用于将源代码转换为字节码。
- 解析器:Python 解释器使用解析器来解析源代码。解析器将源代码转换为解释器可以理解的抽象语法树(AST)。
- 运行时环境:Python 解释器还包括运行时环境,它负责管理内存和执行Python程序中的各种操作,例如对象创建、垃圾回收和异常处理等。
- 标准库:Python 标准库是由一组模块组成的集合,这些模块包含了各种用于处理文件、网络、日期、时间等操作的工具和函数。
4. Python 解释器执行过程
Python 解释器执行代码的过程包括词法分析、语法分析、字节码编译、解释执行和内存管理等步骤,其中字节码编译和解释执行是最核心的部分
- 词法分析:将输入的源代码分解成多个单词(token),每个单词代表一种语法结构,如标识符、关键字、运算符等。词法分析器会忽略空格和注释,并将相邻的单词组合成更复杂的语法结构,如表达式、语句等。
- 语法分析:将单词序列转化成抽象语法树(AST),AST是一种树状结构,用来表示程序的语法结构。在这个过程中,解释器会检查语法错误,如缺少括号、缺少分号等。
- 字节码编译:将AST转化成字节码,字节码是一种类似于汇编语言的中间代码,它是一种跨平台的代码表示形式,可以在不同的操作系统和硬件平台上运行。
- 解释执行:将字节码转化成机器码并执行。Python解释器使用栈来管理数据,执行过程中会不断压入、弹出数据。解释器会根据操作码来执行相应的操作,如加减乘除、函数调用等。
- 内存管理:Python解释器使用自动垃圾回收机制来管理内存。当一个对象不再被引用时,解释器会自动将其从内存中删除,释放空间。
5. Python全局解释器锁(GIL)
Python的全局解释锁(Global Interpreter Lock,简称GIL)是一种线程同步机制,它在CPython解释器中存在,属于互斥锁,用于确保同一时刻只有一个线程执行Python字节码。这个锁的存在是因为CPython解释器本身不是线程安全的,即使在多核处理器上也只允许一个线程执行Python代码,这样可以避免一些潜在的线程安全问题,但也限制了Python的多线程并行性能。
- GIL 锁释放机制:
- 在Python 3.2前是通过引用计数来控制,默认是100;
- 在Python 3.2时GIL已被重写,通过时间间隔来控制,默认是5毫秒。可以通过sys.setswitchinterval() 调节
- GIL 锁规避措施:
- 更换解释器
- 将计算密集型程序由多线程改写为多进程;IO密集型可以使用多线程
- 使用ctypes模块将计算密集型程序用C语言编写
- 使用协程代替多线程来处理计算密集型
6. 计算机内存RAM
随机存取存储器(Random Access Memory,RAM):是计算机中用于临时存储数据的一种硬件组件。它是计算机的主要内存之一,用于存储正在运行的程序和操作系统所需的数据
- RAM特点
- 临时存储:RAM 存储的数据是临时的,意味着当计算机关闭或重启时,其中的数据会被清空。这与永久存储设备(如硬盘驱动器)不同,后者可以长期保存数据。
- 随机存取:RAM 具备随机访问能力,这意味着它可以快速访问存储中的任何数据,而无需按照特定的顺序来读取。这使得 RAM 非常适合用作计算机的工作内存,以快速存取和处理数据。
- 高速存储:RAM 是一种高速存储设备,数据可以在毫秒甚至纳秒级别的时间内被读取或写入。这使得计算机能够快速执行任务,提高性能。
- 容量较小:RAM 的容量通常相对较小,通常以兆字节(MB)或千兆字节(GB)来衡量。计算机的 RAM 容量会影响其多任务处理能力和运行大型程序的性能。
- RAM 内存分配
- 操作系统:操作系统需要占用一部分RAM,以便运行系统的核心功能。
- 正在运行的应用程序:每个打开的应用程序需要分配一部分RAM,以存储其数据和代码。更大的应用程序和多任务处理可能需要更多的RAM。
- 正在处理的数据:如打开的文档、图像、视频或音频文件,都需要RAM来存储。
- 缓存和临时数据:操作系统和应用程序通常使用RAM来加速数据的读取和写入,因此一些RAM也会被用于缓存和临时存储。
- 当超过RAM容量的数据和程序被加载时,计算机性能会受到影响,因为它将不得不频繁地从硬盘或固态硬盘等永久存储设备中读取数据,这通常较慢,导致性能下降。
7. Python 内存池
- 第-1层,第-2层:由操作系统特定的虚拟内存管理器控制(OS-specific virtual memory manger(VMM))
(第-1层):内核动态存储分配和管理
(第-2层):物理内存(ROM / RAM) + 二级存储
- ROM(只读存储器,Read-Only Memory):用于存储计算机或其他电子设备的固件和固定数据的存储设备。常用于存储计算机的引导程序(BIOS)。与RAM不同,ROM中的数据通常无法被修改。
- RAM(随机访问存储器,Random Access Memory):用于存储正在运行的程序和数据的临时内存存储设备。常用于计算机快速读取和写入数据。RAM是易失性的,断电时数据会丢失。
- 二级存储(交换,Secondary Storage):指非易失性的大容量存储设备,如硬盘驱动器(HDD)或固态驱动器(SSD)。它用于长期存储数据、文件和操作系统。与RAM不同,二级存储的数据在断电时不会丢失。
- 第0层:由C标准库中底层分配器(underlying general-purpose allocator)的malloc、free进行内存分配和内存释放;
- 第1层:当申请的内存 >256KB 时,内存分配由 Python 原生的内存分配器(raw memory allocator)进行分配,本质上是调用C标准库中的malloc、realloc等函数。
- 第2层:当申请的内存 <256KB 时,内存分配由 Python 对象分配器(object allocator)实施。
- 第3层:用户使用对象的直接操作层。特点:对于python内置对象(如:int、dict、list、string等),每个数据类型都有独立的私有内存池,对象之间的内存池不共享。如:int释放的内存,不会被分配给float使用。
8. Python垃圾回收机制
- 手动垃圾回收:采用gc.collect()进行手动强制执行垃圾回收,用来解决在内存敏感时(例如:内存不足)加速释放不再需要的内存。
- Python采用自动垃圾回收机制,在后台定期自动检测不再被引用的对象并释放它们的内存。自动垃圾回收与gc.collect()相比,其会有一个等待期(定期检测)。
- 在一般情况下,不需要手动使用 gc.collect(),因为Python的垃圾回收机制通常是足够智能的,会在合适的时候自动运行以释放不再需要的内存。
- gc.collect()是加速自动垃圾回收的执行速度,而不是立即释放内存(但也几乎等同)。
- gc.collect()本身也会引起额外的开销,故不建议频繁触发手动垃圾回收。
- 引用计数(reference count):垃圾回收机制会记录每个对象被其他对象所引用的次数。
- 引用计数从0开始;
- 当对象有新的引用指向它时,则引用次数 +1;当该对象指向它的引用失效时(如:del 对象),则引用 -1。
- 当对象的引用计数为0时(如:对象=None),则列入垃圾回收队列,等待自动垃圾回收,而不是立即释放内存。
【del 对象】: 使用del语句将对象从命名空间中删除,内存将被立即释放,但Python的内存池机制导致不会立即释放内存给计算机,而是等待复用。
【对象=None】: 将对象设置为None,该对象的引用计数变为零,但不会立即释放,而是等待自动垃圾回收机制进行内存释放。
- 循环引用检测:若对象之间存在相互引用,则对象间将形成一个环状结构,使得引用计数不会降为零,因此内存无法被自动回收,导致内存泄漏。 分代回收
- 引用链:用于跟踪对象之间的引用关系。当引用计数不为零,但对象之间形成循环引用时。
- 分代回收(generation):将所有对象分为0,1,2三代。所有新创建的对象都是第0代对象;当某一代对象经过垃圾回收后仍然存活,就会升级到下一代
- 垃圾回收启动时,一定会扫描所有的0代对象。
- 如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。
- 当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。
- 内存池:用于提高小内存对象的内存分配和释放效率。若频繁的进行小内存对象的分配和释放,可能导致内存碎片化和性能下降。
- 预分配固定大小的内存块:Python会根据对象的大小选择一个合适的内存块。每个内存块包含多个相同大小的小块。通常以8字节为一块。
- 内存块状态:内存块可以有不同的状态,包括空闲、已分配和已释放。Python会维护内存块的状态。
- 对象复用:如果内存块中包含已释放的小块,Python会首先复用这些小块以减少内存分配开销。这意味着相同大小的内存块可以多次分配和释放,而不需要每次都与操作系统进行交互。
- 延迟释放:对于不再使用的内存块不会立即释放回操作系统,而是将其保留在内存池中,以备将来再次使用(对象复用)。
- 优点:有助于减少频繁的内存分配和释放带来的性能开销。
- 缺点:可能导致内存泄漏,因为不再使用的内存块不会立即被操作系统回收。因此,开发者应避免长期保留对不再使用的对象的引用,以避免内存泄漏。
9. Python 自动内存管理机制的缺点
- 内存泄漏:程序在分配内存后,无法正常释放不再使用的内存。最终可能导致程序运行变得缓慢或崩溃。 常见的几种情况如下:
- 如果程序分配了内存,但未在不再需要时释放它,内存将泄漏。
- 如果数据结构设计不正确,可能在不再需要时保留对对象的引用,导致内存泄漏。
- 如果循环引用或不正确的引用计数,可能发生内存泄漏。
- 如果打开文件却未在使用后正确关闭它们,内存将泄漏。
- 性能下降:需要同时分配与释放内存,程序可能会变慢。
- 内存不足:当程序需要的内存大于系统空间的内存,则会出现内存不足的问题。
10. Python 内存优化的方法
- 降低全局变量的使用率:全局变量会一直存在到程序结束,因此会始终占用内存。若非必要,请尽可能地使用局部变量,并在不需要时尽快将其释放。
- 避免创建非必要的对象:在Python中,创建对象是分配内存的一种方式。因此,尽量避免创建不必要的对象,并通过复用对象的方式来减少内存分配的次数。
- 手动释放非必要的对象:采用gc.collect()进行手动强制执行垃圾回收,用来在内存敏感时(例如:内存不足)立即释放不再需要的内存。
11. 注释
- 单行:
# 注释内容
- 多行:
"""
注释内容
"""
'''
注释内容
'''
- 解释器不执行注释内容
12. 变量
- 变量就是一个存储数据的时候当前数据所在的内存地址名字而已
a = 123
a:int = 123
13. 标识符
- 标识符命名规则是Python中定义各种名字的时候的统一规范,具体如下:
- 由数字、字母、下划线组成
- 不能数字开头
- 不能使用内置关键字
- 严格区分大小写
14. Python 变量命名规范
- 见名知义。
- 大驼峰:即每个单词首字母都大写,例如:MyClass。
- 小驼峰:第二个(含)以后的单词首字母大写,例如:myModule。
- 下划线:例如:my_function
15. Python 数据类型
- 数据类型分类
- 数据类型检测:type()
a = 1
print(type(a)) # -- 整型
b = 1.1
print(type(b)) # -- 浮点型
c = True
print(type(c)) # -- 布尔型
d = '12345'
print(type(d)) # -- 字符串
e = [10, 20, 30]
print(type(e)) # -- 列表
f = (10, 20, 30)
print(type(f)) # -- 元组
h = {10, 20, 30}
print(type(h)) # -- 集合
g = {'name': 'TOM', 'age': 20}
print(type(g)) # -- 字典
16. 输出
- print()
print('hello world')
print('hello world', end='\n') # 带结束符
- sys.stdout.write()
import sys
sys.stdout.write('hello world')
17. 输入
- input 函数支持命令行输入
x = input("请输入:")
- sys.stdin 用于各种交互输入的情况,内部调用的input()
import sys
# 换行输入(输入换行符)
>>> lines = sys.stdin.read()
>>> 123
>>> 456
>>> 789
>>> line
'123\n456\n789\n'
# Ctrl + D结束输入
# 以行的形式输入
>>> lines = sys.stdin.readline()
>>> 123456789
>>>
>>> lines
123456789
# 返回列表
>>> lines = sys.stdin.readlines()
>>> ['123\n', '456\n', '789\n']
- fileinput.input,输入文本文件
import fileinput
with fileinput.input(files=('2023-10.log', '2023-11.log'), encoding='utf-8') as fobj:
for line in fobj:
print(line)
18. 格式化符号
格式符号 | 转换 |
%s | 字符串 |
%d | 有符号的十进制整数 |
%f | 浮点数 |
%c | 字符 |
%u | 无符号十进制整数 |
%o | 八进制整数 |
%x | 十六进制整数(小写ox) |
%X | 十六进制整数(大写OX) |
%e | 科学计数法(小写'e') |
%E | 科学计数法(大写'E') |
%g | %f和%e的简写 |
%G | %F和%E的简写 |
- \n:换行
- \t:制表符,一个tab键(4个空格)的距离
19. 转换数据类型
函数 | 说明 |
int(x [,base ]) | 将x转换为一个整数,base是进制 |
float(x) | 将x转换为一个浮点数 |
complex(real[, imag]) | 创建一个复数,real为实部,imag为虚部 |
str(x) | 将对象 x 转换为字符串 |
repr(x) | 将对象 x 转换为表达式字符串 |
eval(str) | 用来计算在字符串中的有效Python表达式,并返回一个对象 |
tuple(s) | 将序列 s 转换为一个元组 |
list(s) | 将序列 s 转换为一个列表 |
chr(x) | 将一个整数转换为一个Unicode字符 |
ord(x ) | 将一个字符转换为它的ASCII整数值 |
hex(x ) | 将一个整数转换为一个十六进制字符串 |
oct(x ) | 将一个整数转换为一个八进制字符串 |
bin(x ) | 将一个整数转换为一个二进制字符串 |
20. 算数运算符
运算符 | 描述 | 实例 |
+ | 加 | 1+1 输出结果为 2 |
- | 减 | 1-1 输出结果为 0 |
* | 乘 | 2*2 输出结果为 4 |
/ | 除 | 10/2 输出结果为 5 |
// | 整除 | 9//4 输出结果为2 |
% | 取余 | 9%4 输出结果为 1 |
** | 指数 | 2**4 输出结果为 16,即 2 * 2 * 2 * 2 |
() | 小括号 | 小括号用来提高运算优先级,即 (1 + 2) * 3 输出结果为 9 |
21. 赋值运算符
运算符 | 描述 | 实例 |
= | 赋值 | 将=右侧的结果赋值给等号左侧的变量 |
22. 复合赋值运算符
运算符 | 描述 | 实例 |
+= | 加法赋值运算符 | 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 |
23. 比较运算符
运算符 | 描述 | 实例 |
== | 判断相等。如果两个操作数的结果相等,则条件结果为真(True),否则条件结果为假(False) | 如a=3,b=3,则(a == b) 为 True |
!= | 不等于 。如果两个操作数的结果不相等,则条件为真(True),否则条件结果为假(False) | 如a=3,b=3,则(a == b) 为 True如a=1,b=3,则(a != b) 为 True |
> | 运算符左侧操作数结果是否大于右侧操作数结果,如果大于,则条件为真,否则为假 | 如a=7,b=3,则(a > b) 为 True |
< | 运算符左侧操作数结果是否小于右侧操作数结果,如果小于,则条件为真,否则为假 | 如a=7,b=3,则(a < b) 为 False |
>= | 运算符左侧操作数结果是否大于等于右侧操作数结果,如果大于,则条件为真,否则为假 | 如a=7,b=3,则(a < b) 为 False如a=3,b=3,则(a >= b) 为 True |
<= | 运算符左侧操作数结果是否小于等于右侧操作数结果,如果小于,则条件为真,否则为假 | 如a=3,b=3,则(a <= b) 为 True |
24. 逻辑运算符
运算符 | 逻辑表达式 | 描述 | 实例 |
and | x and y | 布尔"与":如果 x 为 False,x and y 返回 False,否则它返回 y 的值 | True and False, 返回 False |
or | x or y | 布尔"或":如果 x 是 True,它返回 True,否则它返回 y 的值 | False or True, 返回 True |
not | not x | 布尔"非":如果 x 为 True,返回 False 。如果 x 为 False,它返回 True | not True 返回 False, not False 返回 True |
25. 条件语句
if 条件1:
条件1成立执行的代码
elif 条件2:
条件2成立执行的代码
else:
所有条件不成立执行的代码
26. 三目运算符
- 语法
变量1 = 值1 if 条件 else 值2
- 示例
num = 1 if True else 2
27. while循环
- 语法
while 条件:
条件成立重复执行的代码1
条件成立重复执行的代码2
# 注意循环中止条件,否则死循环
- 示例
i = 1
result = 0
while i <= 100:
result += i
i += 1
print(f'result: {result}')
28. while...else
- 语法
while 条件:
条件成立重复执行的代码
else:
循环正常结束之后要执行的代码
- 示例
i = 1
while i < 5:
print('媳妇儿,我错了')
i += 1
else:
print('媳妇原谅我了,真开心,哈哈哈哈')
29. for 循环
for 临时变量 in 序列:
重复执行的代码1
重复执行的代码2
......
30. for...else
- 语法
for 临时变量 in 序列:
重复执行的代码
...
else:
循环正常结束之后要执行的代码
注意:所谓else指的是循环正常结束之后要执行的代码,即如果是break终止循环的情况,else下方缩进的代码将不执行;continue退出循环的情况,else下方缩进的代码会执行
- 示例
words = 'itheima'
for i in words:
if i == 'e':
break # break 结束的循环不执行else下的语句
elif i == 'i':
continue # continue 结束的循环执行else下的语句
else:
print(i)
else:
print('循环正常结束之后执行的代码')
31. 字符串
31.1. 查找
31.1.1. 索引
python中的索引即下标是从0开始的,可以通过索引找到相应的存储空间
words = 'abcdefg'
print(name[0]) # a
print(name[1]) # b
......
print(name[len(words)-1]) # g
31.1.2. 切片
切片是指对操作对象截取其中一部分,字符串、列表、元组都支持切片操作
切片的语法:[起始:结束:步长]也可以简化为[起始:结束]
# 索引是通过下标取某一个元素
# 切片是通过下标去某一段元素
words = 'Hello World!'
print(words)
print(words[4]) # o 字符串里的第4个元素
print(words[3:7]) # lo W 包含下标 3,不含下标 7
print(words[:]) # Hello World! 取出所有元素(没有起始位和结束位之分),默认步长为1
print(words[1:]) # ello World! 从下标为1开始,取出 后面所有的元素(没有结束位)
print(words[:4]) # Hell 从起始位置开始,取到 下标为4的前一个元素(不包括结束位本身)
print(words[:-1]) # Hello World 从起始位置开始,取到 倒数第一个元素(不包括结束位本身)
print(words[-4:-1]) # rld 从倒数第4个元素开始,取到 倒数第1个元素(不包括结束位本身)
print(words[1:5:2]) # el 从下标为1开始,取到下标为5的前一个元素,步长为2(不包括结束位本身)
print(words[7:2:-1]) # ow ol 从下标为7的元素开始(包含下标为7的元素),倒着取到下标为2的元素(不包括下标为2的元素)
# python 字符串快速逆置
print(words[::-1]) # !dlroW olleH 从后向前,按步长为1进行取值
31.1.3. len(str) -> int
len函数可以获取字符串长度
words = '今天天气好晴朗,处处好风光呀好风光'
print(len(words))
31.1.4. str.find(sub[, start[, end]]) ->int
查找指定内容在字符串中是否存在,如果存在就返回该内容在字符串中第一次出现的开始位置索引值,如果不存在,则返回-1.
words = '今天天气好晴朗,处处好风光呀好风光'
print(words.find('好风光')) # 10 '好风光'第一次出现时,'好'所在的位置
print(words.find('你好')) # -1 '你好'不存在,返回 -1
print(words.find('风', 12)) # 15 从下标12开始查找'风',找到风所在的位置试15
print(words.find('风光',1,10)) # -1 从下标1开始到12查找"风光",未找到,返回 -1
31.1.5. str.rfind() -> int
类似于find()函数,不过是从右边开始查找
words = '今天天气好晴朗,处处好风光呀好风光'
print(words.rfind('好')) # 14
31.1.6. str.index(sub[, start[, end]]) -> int
跟find()方法一样,只不过,find方法未找到时,返回-1,而index未找到时,会报异常
words = '今天天气好晴朗,处处好风光呀好风光'
print(words.index('好')) # 4
31.2. 校验
31.2.1. str.startswith(prefix[, start[, end]]) -> bool:校验是否以prefix指定的内容开始
wrods= '今天天气好晴朗,处处好风光呀好风光'
print(wrods.startswith('今')) # True
print(wrods.startswith('今日')) # False
31.2.2. str.endswith(prefix[, start[, end]]) -> bool:校验是否以prefix指定的内容结束
wrods = '今天天气好晴朗,处处好风光呀好风光'
print(wrods.endswith('好风光')) # True
print(wrods.endswith('好日子')) # False
31.2.3. str.isalpha():判断字符串是否是纯字母
words = 'hello'
print(words.isalpha()) # True
words = 'hello world'
print(words.isalpha()) # False 因为中间有空格
31.2.4. str.isdigit()
判断一个字符串是否是纯数字,只要出现非0~9的数字,结果就是False
words = '1234'
print(words.isdigit()) # True
words = '123.4'
print(words.isdigit()) # False
words = '-1234'
print(words.isdigit()) # False
31.2.5. str.isalnum()
判断是否由数字和字母组成。只要出现了非数字和字母,就返回False
words = 'abcd'
print(words.isalnum()) # True
words = '1234'
print(words.isalnum()) # True
words = 'abcd1234'
print(words.isalnum()) # True
words = 'abcd1234_'
print(words.isalnum()) # False
31.2.6. str.isspace()
判断是否只包含空格,只要出现非空格,则返回False
words = ''
print(words.isspace()) # False words是一个空字符串
words = ' '
print(words.isspace()) # True 只有空格
words = ' d'
print(words.isspace()) # False 除了空格外还有其他内容
31.2.7. str.count(sub[, start[, end]]) -> int
返回字符串mystr在start和end之间出现sub子字符串的次数
words = '今天天气好晴朗,处处好风光呀好风光'
print(words.count('好')) # 3. '好'字出现三次
31.3. 修改
31.3.1. str.replace(key, sub, nums)
替换字符串中指定的内容,如果指定次数count,则替换不会超过count次
words = '今天天气好晴朗,处处好风光呀好风光'
new_words = mystr.replace('好', '坏')
print(words) # 今天天气好晴朗,处处好风光呀好风光 原字符串未改变!
print(new_words) # 今天天气坏晴朗,处处坏风光呀坏风光 得到的新字符串里,'好'被修改成了'坏'
new_words = words.replace('好','坏',2) # 指定了替换的次数
print(new_words) # 今天天气坏晴朗,处处坏风光呀好风光 只有两处的'好'被替换成了'坏'
31.3.2. str.split(key [, maxsplit])
以指定字符串为分隔符切片,如果 maxsplit有指定值,则仅分隔 maxsplit+1 个子字符串。返回的结果是一个列表
words = '今天天气好晴朗,处处好风光呀好风光'
result = words.split() # 没有指定分隔符,默认使用空格,换行等空白字符进行分隔
print(result) #['今天天气好晴朗,处处好风光呀好风光'] 没有空白字符,所以,字符串未被分隔
result = words.split('好') # 以 '好' 为分隔符
print(result) # ['今天天气', '晴朗,处处','风光呀,'风光']
result = wrods.split("好",2) # 以 '好' 为分隔符,最多切割成3份
print(result) # ['今天天气', '晴朗,处处', '风光呀好风光']
31.3.3. rsplit
用法和split基本一致,只不过是从右往左分隔
mystr = '今天天气好晴朗,处处好风光呀好风光'
print(mystr.rsplit('好',1)) #['今天天气好晴朗,处处好风光呀', '风光']
31.3.4. splitlines
按照行分隔,返回一个包含各行作为元素的列表。
words = 'hello \nworld'
print(words.splitlines())
31.3.5. partition
把字符串以key分割成三部分,key前,key和key后,三部分组成一个元组
words = '今天天气好晴朗,处处好风光呀好风光'
print(words.partition('好')) # ('今天天气', '好', '晴朗,处处好风光呀好风光')
31.3.6. rpartition
类似于 partition()函数,不过是从右边开始
mystr = '今天天气好晴朗,处处好风光呀好风光'
print(mystr.rpartition('好')) # ('今天天气好晴朗,处处好风光呀', '好', '风光')
31.3.7. capitalize
将第一个单词首字母大写
words = 'hello world'
print(words.capitalize()) # Hello world
31.3.8. title
每个单词的首字母大写
words = 'hello world'
print(words.title()) # Hello World
31.3.9. lower
所有大写字母变小写
words = 'hElLO WorLD'
print(words.lower()) # hello world
31.3.10. upper
所有小写字母变大写
words = 'hello world'
print(words) # HELLO WORLD
31.3.11. ljust
返回指定长度的字符串,并在右侧使用空白字符补全(左对齐)
words = 'hello'
print(words.ljust(10)) # hello 在右边补了五个空格
31.3.12. rjust
返回指定长度的字符串,并在左侧使用空白字符补全(右对齐)。
words = 'hello'
print(words.rjust(10)) # hello在左边补了五个空格
31.3.13. center
返回指定长度的字符串,并在两端使用空白字符补全(居中对齐)
words = 'hello'
print(words.center(10)) # hello 两端加空格,让内容居中
31.3.14. lstrip
删除字符串左边的空白字符
words = ' he llo '
print(words.lstrip()) #he llo 只去掉了左边的空格,中间和右边的空格被保留
31.3.15. rstrip
删除字符串右边的空白字符
words = ' he llo '
print(words.rstrip()) # he llo右边的空格被删除
31.3.16. strip
删除字符串两边的空白字符
words = ' he llo '
print(words.strip()) #he llo
31.3.17. join
把参数进行遍历,取出参数里的每一项,然后再在后面加上str
words = '-'
print(words.join('hxmdq')) #h-x-m-d-q 把hxmd一个个取出,并在后面添加字符-. 最后的 q 保留,没有加-
print(words.join(['hi','hello','good'])) #hi-hello-good
31.3.18. 字符串运算符
- 字符串和字符串之间能够使用加法运算符,作用是将两个字符串拼接成为一个字符串。例如:'hello' + 'world'的结果是 'helloworld'
- 字符串和数字之间可以做乘法运算,结果是将指定的字符串重复多次。例如:'hello'*2的结果是hellohello
- 字符串和字符串之间,如果使用比较运算符进行计算,会获取字符对应的编码,然后进行比较。
- 除上述几种运算符以外,字符串默认不支持其他运算符
31.3.19. 字符和编码相互转换
使用chr和ord方法,可以实现字符和编码之间的相互转换
print(ord('a')) # 使用ord方法,可以获取一个字符对应的编码
print(chr(100)) # 使用chr方法,可以获取一个编码对应的字符
31.3.20. 字符串编码规则
常用的字符编码有 GBK,Big5和utf8这三种编码规则。
使用字符串的encode方法,可以将字符串按照指定的编码格式转换成为二进制;使用decode方法,可以将一个二进制按照指定的编码格式转换成为字符串
s1 = '你'.encode('utf8') # 将字符 你 按照utf8格式编码成为二进制
print(type(s1)) #
print(s1) # b'\xe4\xbd\xa0'
s2 = s1.decode('utf8') # 将二进制按照utf8格式解码成为字符串
print(s2)
s3 = '你'.encode('gbk') # 将字符 你 按照gbk格式转换成为二进制
print(s3) # b'\xc4\xe3'
s4 = s3.decode('gbk') # 将二进制按照gbk格式解码成为字符
print(s4)
31.3.21. in 与 not in
成员运算符 (in 和 not in) 可以用来快速的判断元素是否在指定的可迭代对象里,语法格式 要判断的元素 in 可迭代对象
'h' in 'hello'
'zhangsan' not in ['lisi','henry','merry','jack']
1 in 123 # 报错,数字 123 不是一个可迭代对象
31.4. 格式化
31.4.1. 概念
- str.format() 方法通过字符串中的大括号{} 来识别替换字段 replacement field,从而完成字符串的格式化。
- 替换字段 由字段名 field name 和转换字段 conversion field 以及格式说明符 format specifier组成,即一般形式为 {字段名!转换字段:格式说明符}。
- 字段名分为简单字段名 simple field name 和复合字段名 compound field name。而转换字段和格式说明符都是可选的
31.4.2. 字段名
format的完整格式是{字段名!转换字符:格式说明符}。其中字段名是必须的,而且可以分为简单字段名和复合字段名。
31.4.2.1. 简单字段名
- 省略字段名:{},大括号个数可以少于位置参数的个数
# 省略字段名传递位置参数
print('我叫{},今年{}岁。'.format('小明', 18))
"""
我叫小明,今年18岁。
"""
# 大括号个数可以少于位置参数的个数
print('我爱吃{}和{}。'.format('香蕉', '苹果', '大鸭梨'))
"""
我爱吃香蕉和苹果。
"""
# 大括号个数多于位置参数的个数则会报错
# print('我还吃{}和{}。'.format('西红柿'))
"""
IndexError: tuple index out of range
"""
- 使用非负十进制整数:{0}
# 通过数字形式的简单字段名传递位置参数
print('身高{0},家住{1}。'.format(1.8, '铜锣湾'))
"""
身高1.8,家住铜锣湾
"""
# 数字形式的简单字段名可以重复使用。
print('我爱{0}。\n她今年{1}。\n我也爱{0}。'.format('阿香', 17))
"""
我爱阿香。
她今年17。
我也爱阿香。
"""
# 体会把所有位置参数整体当成元组来取值
print('阿香爱吃{1}、{3}和{0}。'.format(
'榴莲', '臭豆腐', '皮蛋', '鲱鱼罐头', '螺狮粉'))
"""
阿香爱吃臭豆腐、鲱鱼罐头和榴莲。
"""
# 尝试一下越界错误
# print('{1}'.format('错误用法'))
"""
IndexError: tuple index out of range
"""
- 变量名:{name}
# 使用变量名形式的简单字段名传递关键字参数
print('我大哥是{name},今年{age}岁。'.format(name='阿飞', age=20))
"""
我大哥是阿飞,今年20岁。
"""
# 关键字参数的顺序可以随意调换
print('我大哥是{name},今年{age}岁。'.format(age=20, name='阿飞'))
"""
我大哥是阿飞,今年20岁。
"""
- 混合使用
- 混合使用数字形式和变量名形式的字段名,可以同时传递位置参数和关键字参数。
- 关键字参数必须位于位置参数之后。
- 混合使用时可以省略数字。
- 省略字段名 {} 不能和数字形式的字段名 {非负整数} 同时使用
# 混合使用数字形式和变量名形式的字段名
# 可以同时传递位置参数和关键字参数
print('这是一个关于{0}、{1}和{girl}的故事。'.format(
'小明', '阿飞', girl='阿香'))
"""
这是一个关于小明、阿飞和阿香的故事。
"""
# 但是关键字参数必须位于位置参数之后
# print('这是一个关于{0}、{1}和{girl}的故事。'.format(
# '小明', girl='阿香' , '阿飞'))
"""
SyntaxError: positional argument follows keyword argument
"""
# 数字也可以省略
print('这是一个关于{}、{}和{girl}的故事。'.format(
'小明', '阿飞', girl='阿香'))
# 但是省略字段名不能和数字形式的字段名同时出现
# print('这是一个关于{}、{1}和{girl}的故事。'.format(
# '小明', '阿飞', girl='阿香'))
"""
ValueError: cannot switch from automatic field numbering to manual field specification
"""
- 使用元组和字典传参
- str.format() 方法还可以使用 *元组 和 **字典 的形式传参,两者可以混合使用。
- 位置参数、关键字参数、*元组 和 **字典 也可以同时使用,但是要注意,位置参数要在关键字参数前面,*元组 要在 **字典 前面
# 使用元组传参
infos = '钢铁侠', 66, '小辣椒'
print('我是{},身价{}亿。'.format(*infos))
"""
我是钢铁侠,身家66亿。
"""
print('我是{2},身价{1}亿。'.format(*infos))
"""
我是小辣椒,身家66亿。
"""
# 使用字典传参
venom = {'name': '毒液', 'weakness': '火'}
print('我是{name},我怕{weakness}。'.format(**venom))
"""
我是毒液,我怕火。
"""
# 同时使用元组和字典传参
hulk = '绿巨人', '拳头'
captain = {'name': '美国队长', 'weapon': '盾'}
print('我是{}, 我怕{weapon}。'.format(*hulk, **captain))
print('我是{name}, 我怕{1}。'.format(*hulk, **captain))
"""
我是绿巨人, 我怕盾。
我是美国队长, 我怕拳头。
"""
# 同时使用位置参数、元组、关键字参数、字典传参
# 注意:
# 位置参数要在关键字参数前面
# *元组要在**字典前面
tup = '鹰眼',
dic = {'weapon': '箭'}
text = '我是{1},我怕{weakness}。我是{0},我用{weapon}。'
text = text.format(
*tup, '黑寡妇', weakness='男人', **dic)
print(text)
"""
我是黑寡妇,我怕男人。我是鹰眼,我用箭。
"""
31.4.2.2. 复合字段名
- 同时使用了数字和变量名两种形式的字段名就是复合字段名
- 使用 . 点号传递位置参数
- 替换字段形式:{数字.属性名}
- 只有一个替换字段的时候可以省略数字
class Person(object):
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
p = Person('zhangsan',18,'female')
print('姓名是{0.name},年龄是{0.age},性别是{0.gender}'.format(p))
print('姓名是{.name}'.format(p)) # 只有一个替换字段时,可以省略数字
- 用[] 中括号,可以传递列表、元组、字典为位置参数
# 中括号用法:用列表传递位置参数
infos = ['阿星', 9527]
food = ['霸王花', '爆米花']
print('我叫{0[0]},警号{0[1]},爱吃{1[0]}。'.format(infos, food))
"""
我叫阿星,警号9527,爱吃霸王花。
"""
# 中括号用法:用元组传递位置参数
food = ('僵尸', '脑子')
print('我叫{0[0]},年龄{1},爱吃{0[1]}。'.format(food, 66))
"""
我叫僵尸,年龄66,爱吃脑子。
"""
# 中括号用法:用字典传递位置参数
dic = dict(name='阿星', pid=9527)
print('我是{[name]}!'.format(dic))
# 多个替换字段,不能省略数字
print('我是{0[name]},警号{0[pid]}。'.format(dic))
"""
我是阿星!
我是阿星,警号9527。
"""
31.4.2.3. 转换字段
转换字段 conversion field 的取值有三种,前面要加 !
- s:传递参数之前先对参数调用 str()
- r:传递参数之前先对参数调用 repr()
- a:传递参数之前先对参数调用 ascii()
# 转换字段
print('I am {!s}!'.format('Bruce Lee 李小龙'))
print('I am {!r}!'.format('Bruce Lee 李小龙'))
print('I am {!a}!'.format('Bruce Lee 李小龙'))
"""
I am Bruce Lee 李小龙!
I am 'Bruce Lee 李小龙'!
I am 'Bruce Lee \u674e\u5c0f\u9f99'!
"""
32. 列表
32.1. 列表格式
[元素1, 元素2, 元素3, 元素4......]
32.2. 列表常用操作
32.2.1. 查找
32.2.1.1. 下标
name_list = ['Tom', 'Lily', 'Rose']
print(name_list[0]) # Tom
print(name_list[1]) # Lily
print(name_list[2]) # Rose
32.2.1.2. 函数
- index():返回指定数据所在位置的下标
# 列表序列.index(数据, 开始位置下标, 结束位置下标)
name_list = ['Tom', 'Lily', 'Rose']
print(name_list.index('Lily', 0, 2)) # 1
# 如果查找的数据不存在则报错
- count(): 统计指定数据在当前列表中出现的次数
name_list = ['Tom', 'Lily', 'Rose']
print(name_list.count('Lily')) # 1
- len():访问列表长度,即列表中数据的个数
name_list = ['Tom', 'Lily', 'Rose']
print(len(name_list)) # 3
32.2.1.3. 判断是否存在
- in:判断指定数据在某个列表序列,如果在返回True,否则返回False
name_list = ['Tom', 'Lily', 'Rose']
# 结果:True
print('Lily' in name_list)
# 结果:False
print('Lilys' in name_list)
- not in:判断指定数据不在某个列表序列,如果不在返回True,否则返回False
name_list = ['Tom', 'Lily', 'Rose']
# 结果:False
print('Lily' not in name_list)
# 结果:True
print('Lilys' not in name_list)
32.2.2. 增加
32.2.2.1. append(): 列表结尾追加数据
# 列表序列.append(数据)
name_list = ['Tom', 'Lily', 'Rose']
name_list.append('xiaoming')
# 结果:['Tom', 'Lily', 'Rose', 'xiaoming']
print(name_list)
#
# 注意点1:如果append()追加的数据是一个序列,则追加整个序列到列表
#
name_list = ['Tom', 'Lily', 'Rose']
name_list.append(['xiaoming', 'xiaohong'])
# 结果:['Tom', 'Lily', 'Rose', ['xiaoming', 'xiaohong']]
print(name_list)
32.2.2.2. extend():列表结尾追加数据,如果数据是一个序列,则将这个序列的数据逐一添加到列表
# 列表序列.extend(数据)
# extend 字符串
name_list = ['Tom', 'Lily', 'Rose']
name_list.extend('xiaoming')
# 结果:['Tom', 'Lily', 'Rose', 'x', 'i', 'a', 'o', 'm', 'i', 'n', 'g']
print(name_list)
# extend 列表
name_list = ['Tom', 'Lily', 'Rose']
name_list.extend(['xiaoming', 'xiaohong'])
# 结果:['Tom', 'Lily', 'Rose', 'xiaoming', 'xiaohong']
print(name_list)
32.2.2.3. insert():指定位置新增数据
# 列表序列.insert(位置下标, 数据)
name_list = ['Tom', 'Lily', 'Rose']
name_list.insert(1, 'xiaoming')
# 结果:['Tom', 'xiaoming', 'Lily', 'Rose']
print(name_list)
32.2.3. 删除
32.2.3.1. del
# del 目标
# 删除整个列表
name_list = ['Tom', 'Lily', 'Rose']
# 结果:报错提示:name 'name_list' is not defined
del name_list
print(name_list)
# 删除指定数据
name_list = ['Tom', 'Lily', 'Rose']
del name_list[0]
# 结果:['Lily', 'Rose']
print(name_list)
32.2.3.2. pop():删除指定下标的数据(默认为最后一个),并返回该数据
# 列表序列.pop(下标)
name_list = ['Tom', 'Lily', 'Rose']
del_name = name_list.pop(1)
# 结果:Lily
print(del_name)
# 结果:['Tom', 'Rose']
print(name_list)
32.2.3.3. remove():移除列表中某个数据的第一个匹配项
# 列表序列.remove(数据)
name_list = ['Tom', 'Lily', 'Rose']
name_list.remove('Rose')
# 结果:['Tom', 'Lily']
print(name_list)
32.2.3.4. clear():清空列表
name_list = ['Tom', 'Lily', 'Rose']
name_list.clear()
print(name_list) # 结果: []
32.2.4. 修改
32.2.4.1. 修改指定下标数据
name_list = ['Tom', 'Lily', 'Rose']
name_list[0] = 'aaa'
# 结果:['aaa', 'Lily', 'Rose']
print(name_list)
32.2.4.2. reverse():逆置
num_list = [1, 5, 2, 3, 6, 8]
num_list.reverse()
# 结果:[8, 6, 3, 2, 5, 1]
print(num_list)
32.2.4.3. sort():排序
# 列表序列.sort( key=None, reverse=False)
# 注意:reverse表示排序规则,reverse = True 降序, reverse = False 升序(默认)
num_list = [1, 5, 2, 3, 6, 8]
num_list.sort()
# 结果:[1, 2, 3, 5, 6, 8]
print(num_list)
32.2.4.4. copy():复制
name_list = ['Tom', 'Lily', 'Rose']
name_li2 = name_list.copy()
# 结果:['Tom', 'Lily', 'Rose']
print(name_li2)
33. 元组
一个元组可以存储多个数据,元组内的数据是不能修改的
33.1. 定义
# 多个数据元组
t1 = (10, 20, 30)
# 单个数据元组
t2 = (10,)
33.2. 查找
33.2.1. 按下标查找
tuple1 = ('aa', 'bb', 'cc', 'bb')
print(tuple1[0]) # aa
33.2.2. index()
查找某个数据,如果数据存在返回对应的下标,否则报错,语法和列表、字符串的index方法相同
tuple1 = ('aa', 'bb', 'cc', 'bb')
print(tuple1.index('aa')) # 0
33.2.3. count():统计某个数据在当前元组出现的次数
tuple1 = ('aa', 'bb', 'cc', 'bb')
print(tuple1.count('bb')) # 2
33.2.4. len():统计元组中数据的个数
tuple1 = ('aa', 'bb', 'cc', 'bb')
print(len(tuple1)) # 4
33.3. 修改
元组内的直接数据如果修改则立即报错,但是可以修改元组内列表里的数据
# 报错
tuple1 = ('aa', 'bb', 'cc', 'bb')
tuple1[0] = 'aaa'
# 支持
tuple2 = (10, 20, ['aa', 'bb', 'cc'], 50, 30)
print(tuple2[2]) # 访问到列表
# 结果:(10, 20, ['aaaaa', 'bb', 'cc'], 50, 30)
tuple2[2][0] = 'aaaaa'
print(tuple2)
34. 字典
34.1. 创建字典
# 有数据字典
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
# 空字典
dict2 = {}
dict3 = dict()
# 元组转换
dict4 = dict((('name', 'gbk'),))
# 字典的键必须是不可变的,如字符串,数字或元组
34.2. 增
34.2.1. 字典序列[key] = 值
如果key存在则修改这个key对应的值,如果key不存在则新增此键值对
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
dict1['name'] = 'Rose'
# 结果:{'name': 'Rose', 'age': 20, 'gender': '男'}
print(dict1)
dict1['id'] = 110
# {'name': 'Rose', 'age': 20, 'gender': '男', 'id': 110}
print(dict1)
34.2.2. setdefault()
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
dict1.setdefault('id', 110)
# {'name': 'Rose', 'age': 20, 'gender': '男', 'id': 110}
print(dict1)
34.2.3. dict.fromkeys()
# 将 MAC 统一分配给前面三个host
dict1 = dict.fromkeys(['host1', 'host2', 'host3'], 'MAC')
# 结果:{'host1': 'MAC', 'host2': 'MAC', 'host3': 'MAC'}
print(dict1)
34.2.4. copy()
对字典进行浅拷贝,返回一个和原字典有相同键值对的新字典,
dict1 = dict.fromkeys(['host1', 'host2', 'host3'], 'MAC')
dict2 = dict1.copy()
# 结果:{'host1': 'MAC', 'host2': 'MAC', 'host3': 'MAC'}
print(dict1)
# 结果:{'host1': 'MAC', 'host2': 'MAC', 'host3': 'MAC'}
print(dict2)
# **修改dict2,不会影响dict1的值
dict2['host1'] = 'WINDOWS'
# 结果:{'host1': 'WINDOWS', 'host2': 'MAC', 'host3': 'MAC'}
print(dict2)
# 结果:{'host1': 'MAC', 'host2': 'MAC', 'host3': 'MAC'}
print(dict1)
# **修改dict4,也不会影响dict3的值
dict3 = dict.fromkeys(['host1', 'host2', 'host3'], ['MAC', 'WINDOWS'])
dict4 = dict3.copy()
dict4['host1'] = ['XIAOMI']
# 结果:{'host1': ['MAC', 'WINDOWS'], 'host2': ['MAC', 'WINDOWS'], 'host3': ['MAC', 'WINDOWS']}
print(dict3)
# 结果:{'host1': ['XIAOMI'], 'host2': ['MAC', 'WINDOWS'], 'host3': ['MAC', 'WINDOWS']}
print(dict4)
34.3. 删
34.3.1. del()/ del:删除字典或删除字典中指定的键值对
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
del dict1['gender']
# 结果:{'name': 'Tom', 'age': 20}
print(dict1)
34.3.2. clear():清空字典
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
dict1.clear()
print(dict1) # {}
34.3.3. pop(key)
移除键,同时返回键所对应的值
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
age = dict1.pop('age')
# {'name': 'Tom', 'gender': '男'}
print(dict1)
# 结果:20
print(age)
34.3.4. popitem()
随机删除一组键值对,同时返回对应的值
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
item = dict1.popitem()
# {'name': 'Tom', 'age': 20}
print(dict1)
# 结果:('gender', '男')
print(item)
34.4. 改
字典序列[key] = 值
如果key存在则修改这个key对应的值;如果key不存在则新增此键值对
34.4.1. update
dict4 = {'name': 'cgk', 'age': '20', 'hobby': 'girl'}
dict5 = {1: "1", 'age': "30"}
dict4.update(dict5) #把dict5更新进dict4里面,如果有重复的键,则覆盖
print(dict4)
34.4.2. sorted(dict)
dic = {5: '555', 2: '222', 4: '444'}
print(sorted(dic)) #默认根据键排序 [2, 4, 5]
print(sorted(dic.values())) #根据值排序 ['222', '444', '555']
print(sorted(dic.items())) #根据键排序 [(2, '222'), (4, '444'), (5, '555')]
34.5. 查
34.5.1. key查找
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
print(dict1['name']) # Tom
print(dict1['id']) # 报错
# 如果当前查找的key存在,则返回对应的值;否则报错
34.5.2. get()
# 字典序列.get(key, 默认值)
# 如果当前查找的key不存在,则返回第二个参数(默认值),如果省略第二个参数,
# 则返回None
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
print(dict1.get('name')) # Tom
print(dict1.get('id', 110)) # 110
print(dict1.get('id')) # None
34.5.3. keys()
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
print(dict1.keys()) # dict_keys(['name', 'age', 'gender'])
34.5.4. values()
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
print(dict1.values()) # dict_values(['Tom', 20, '男'])
34.5.5. items()
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
print(dict1.items()) # dict_items([('name', 'Tom'), ('age', 20), ('gender', '男')])
34.5.6. len()
# 计算字典元素个数,即键的总数
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
# 结果:3
print(len(dict1))
35. 集合
35.1. 创建集合
创建集合使用{}或者set(),但是如果要创建空集合只能使用set(),因为{}用来创建空字典。
s1 = {10, 20, 30, 40, 50}
print(s1)
s2 = set('abcdefg')
print(s3)
s3 = set()
print(type(s4)) # set
s4 = {}
print(type(s5)) # dict
- 集合可以去掉重复数据;
- 集合数据是无序的,故不支持下标
35.2. 增加数据
35.2.1. add()
s1 = {10, 20}
s1.add(100)
s1.add(10)
print(s1) # {100, 10, 20}
# 集合有去重功能,所以,当向集合内追加的数据是当前集合已有数据的话,则不进行任何操作。
35.2.2. update()
s1 = {10, 20}
# s1.update(100) # 报错,只能update一个可迭代对象
s1.update([100, 200])
s1.update('abc')
print(s1)
35.3. 删除数据
35.3.1. remove()
删除集合中的指定数据,如果数据不存在则报错
s1 = {10, 20}
s1.remove(10)
print(s1)
s1.remove(10) # 报错
print(s1)
35.3.2. discard()
删除集合中的指定数据,如果数据不存在也不会报错
s1 = {10, 20}
s1.discard(10)
print(s1)
s1.discard(10)
print(s1)
35.3.3. pop()
随机删除集合中的某个数据,并返回这个数据
s1 = {10, 20, 30, 40, 50}
del_num = s1.pop()
print(del_num)
print(s1)
35.4. 查找数据
35.4.1. in 与 not in
s1 = {10, 20, 30, 40, 50}
print(10 in s1)
print(10 not in s1)
36. 字符串、列表、元组、字典公共操作
36.1. 运算符
运算符 | 描述 | 支持的容器类型 |
+ | 合并 | 字符串、列表、元组 |
* | 复制 | 字符串、列表、元组 |
in | 元素是否存在 | 字符串、列表、元组、字典 |
not in | 元素是否不存在 | 字符串、列表、元组、字典 |
36.2. 公共方法
函数 | 描述 | 示例 |
len() | 计算容器中元素个数 | len(str1) |
del 或 del() | 删除 | del(list1[0]) |
max() | 返回容器中元素最大值 | max(list1) |
min() | 返回容器中元素最小值 | min(str1) |
range(start, end, step) | 生成从start到end的数字,步长为step,供for循环使用 | range(1, 10, 2) |
enumerate() | 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。 | enumerate(可遍历对象,start=0) |
36.3. 容器类型转换
36.3.1. tuple()
将某个序列转换成元组
list1 = [10, 20, 30, 40, 50, 20]
s1 = {100, 200, 300, 400, 500}
print(tuple(list1))
print(tuple(s1))
36.3.2. list()
将某个序列转换成列表
t1 = ('a', 'b', 'c', 'd', 'e')
s1 = {100, 200, 300, 400, 500}
print(list(t1))
print(list(s1))
36.3.3. set()
将某个序列转换成集合
list1 = [10, 20, 30, 40, 50, 20]
t1 = ('a', 'b', 'c', 'd', 'e')
print(set(list1))
print(set(t1))
37. 推导式
用一个表达式创建一个有规律的列表或控制一个有规律的列表
37.1. 列表推导式
# 简单模式
list1 = [i for i in range(10)]
print(list1) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# if
list1 = [i for i in range(10) if i % 2 == 0]
print(list1)
# 多元素
list1 = [(i, j) for i in range(1, 3) for j in range(3)]
print(list1) # [(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
37.2. 字典推导式
# 简单模式:创建字典
dict1 = {i: i**2 for i in range(1, 5)}
print(dict1) # {1: 1, 2: 4, 3: 9, 4: 16}
# 将两个相同长度列表合并为一个字典
list1 = ['name', 'age', 'gender']
list2 = ['Tom', 20, 'man']
dict1 = {list1[i]: list2[i] for i in range(len(list1))}
print(dict1)
# 提取字典中目标数据
counts = {'MBP': 268, 'HP': 125, 'DELL': 201, 'Lenovo': 199, 'acer': 99}
# 需求:提取上述电脑数量大于等于200的字典数据
count1 = {key: value for key, value in counts.items() if value >= 200}
print(count1) # {'MBP': 268, 'DELL': 201}
37.3. 集合推导式
list1 = [1, 1, 2]
set1 = {i ** 2 for i in list1}
print(set1) # {1, 4}
38. lambda函数
38.1. 语法
lambda arguments:expersion
'
rguments: 参数列表,与Python中函数的参数列表是一样的
expression:是一个关于参数的表达式,表达式中出现的参数需要在arguments中定义
并且表达式只能是单行
'''
38.2. 为什么使用lambda匿名函数
- 语法更简洁
- 不用想函数名
38.3. 示例
38.3.1. 直接赋给一个变量,然后再像一般函数那样调用
c=lambda x,y,z:x*y*z
# 直接用变量
c(2,3,4) # 24
# 也可以在匿名函数后调用
(lambda x:x**2)(3)
38.3.2. 将lambda函数作为参数传递给其他函数比如说结合map、filter、sorted、reduce等一些Python内置函数使用
result = filter(lambda x:x%3==0,[1,2,3,4,5,6])
print(list(result)) # [3, 6]
squares = list(map(lambda x:x**2,range(5))) # [0,1,4,9,16]
a=[('b',3),('a',2),('d',4),('c',1)]
sorted(a,key=lambda x:x[0]) # [('a',2),('b',3),('c',1),('d',4)]
from functools import reduce
print(list(reduce(lambda a,b:'{},{}'.format(a,b),[1,2,3,4,5,6,7,8,9])))
# [1,2,3,4,5,6,7,8,9]
38.3.3. 嵌套使用将lambda函数嵌套到普通函数中,lambda函数本身做为return的值
def increment(n):
return lambda x:x+n
f=increment(4)
f(2) # 6
38.3.4. 字符串联合,有默认值,也可以用x=(lambda...)这种格式
x=(lambda x='Boo',y='Too',z='Z00':x+y+z)
print(x('Foo'))
'FooTooZoo'
38.3.5. 在tkinter中定义内联的callback函数
import sys
from tkinter import Button,mainloop
x=Button(text='Press me',command=(lambda :sys.stdout.write('Hello,World\n')))
x.pack()
x.mainloop()
38.3.6. 判断字符串是否以某个字母开头有
Names = ['Anne', 'Amy', 'Bob', 'David', 'Carrie', 'Barbara', 'Zach']
B_Name= filter(lambda x: x.startswith('B'),Names)
print(list(B_Name)) # ['Bob', 'Barbara']
38.3.7. 求两个列表元素的和
= [1,2,3,4]
b = [5,6,7,8]
print(list(map(lambda x,y:x+y, a,b))) # [6,8,10,12]
38.3.8. 求字符串每个单词的长度
sentence = "Welcome To Beijing!"
words = sentence.split()
lengths = map(lambda x:len(x),words)
print(list(lengths)) # [7,2,8]
38.4. lambda 缺点
- Google开源项目指南里面原话:"比本地函数更难阅读和调试,没有函数名意味着堆栈跟踪更难理解,由于lambda函数通常只包含一个表达式因,此其表达能力有限“。
39. 高阶函数:map、filter和reduce
39.1. filter(过滤)函数
39.1.1. 说明
filter函数是Python中常用的高级函数之一,其作用是从一个序列中过滤出符合条件的元素,将结果以一个filter类型返回
39.1.2. 语法
filter(function, sequence)
'''
- function: 过滤函数
- sequence:过滤对象
- filter函数会对序列中的每个元素依次调用function函数,
将返回True的元素组成一个Filter类型对象输出
'''
39.1.3. 示例
def is_even(n):
return n % 2 == 0
num_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
new_list = list(filter(is_even, num_list))
print(new_list) # [2, 4, 6, 9. 10]
39.2. reduce(归约) 函数
39.2.1. 说明
reduce函数是Python中另一个常用的高级函数,其作用是对一个序列进行归约操作,将其简化为一个数。
39.2.2. 语法
reduce(function, sequence[, initial])
'''
- function 是归约函数
- sequence 是归约对象
- inital 归约初始值,可选
- reduce函数会将归约函数function递归作用于序列sequence的每个元素,
将结果与下一个元素一起作为function的输入,最终仅得到一个结果值。
如果指定了initial,则将该值与序列的第一个元素一起进行归约操作
'''
39.2.3. 示例
from functools import reduce
def add(x, y):
return x + y
num_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = reduce(add, num_list)
print(result) # 55
'''
reduce函数 是将num_list[0]与num_list[1]代入add函数,
得到结果再与num_list[2]代入add函数,依次类推得到最终结果
'''
# initial
result = reduce(add, num_list, 10)
print(result) # 65
'''
reduce函数 是将inital值10与num_list[0]代入add函数,
得到的结果与num_list[1]代入add函数,依次得到最终结果
'''
39.3. map(映射)函数
39.3.1. 说明
map函数是Python中常用的高级函数之一,其作用是对一个序列中的每个元素进行函数操作,返回一个新的序列
39.3.2. 语法
map(function, sequence)
- function: 映射的函数
- sequence:映射的对象
- map函数会对序列中的每个元素依次调用function函数,返回一个新的序列
39.3.3. 示例
def square(n):
return n ** 2
num_list = [1, 2, 3, 4, 5]
new_list = list(map(square, num_list))
print(new_list) # [1, 4, 9, 25]
40. 函数
40.1. 定义函数
def 函数名(参数):
代码块1
代码块2
......
40.2. 调用函数
函数名(参数)
* 函数一定是先定义,后调用
40.3. 函数参数的作用
- 定义函数的时候,指定形参,可以在实现函数体时更方便灵活
- 调用函数的时候,通过实参,可以实现函数的灵活复用
40.4. 函数返回值的作用
- 可以将函数处理后的结果返回出来,方便做进一步的处理;
40.5. 函数说明文档
- 定义函数说明文档
def 函数名(参数):
"""说明文档的位置"""
代码
......
- 查看函数的说明文档
help(函数名)
40.6. 函数变量作用域
变量作用域指的是变量生效的范围,主要分为两类:局部变量和全局变量
40.6.1. 局部变量
局部变量是定义在函数体内部的变量,即只在函数体内部生效。
def test():
a = 100
print(a)
test() # 100
print(a) # 报错:name 'a' is not defined
- 变量a是定义在test函数内部的变量,在函数外部访问则立即报错
- 局部变量的作用:在函数体内部,临时保存数据,即当函数调用完成后,则销毁变量。
40.6.2. 全局变量
全局变量是指在函数体内、外都能生效的变量。
# 定义全局变量a
a = 100
def testA():
print(a) # 访问全局变量a,并打印变量a存储的数据
def testB():
print(a) # 访问全局变量a,并打印变量a存储的数据
testA() # 100
testB() # 100
40.6.3. 如何在函数内部修改全局变量(global)
a = 100
def testA():
print(a)
def testB():
# global 关键字声明a是全局变量
global a
a = 200
print(a)
testA() # 100
testB() # 200
print(f'全局变量a = {a}') # 全局变量a = 200
40.7. 函数的参数
40.7.1. 位置参数
位置参数:调用函数时根据函数定义的参数位置来传递参数。函数调用时,传递和定义参数的顺序及个数必须一致
def user_info(name, age, gender): # 位置参数
print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
user_info('TOM', 20, '男')
40.7.2. 关键字参数
函数调用时,通过“键=值”形式加以指定,可以让函数更加清晰、容易使用,同时也清除了参数的顺序需求。
函数调用时,如果有位置参数,位置参数必须在关键字参数的前面,但关键字参数之间不存在先后顺序。
def user_info(name, age, gender):
print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
user_info('Rose', age=20, gender='女')
user_info('小明', gender='男', age=16)
40.7.3. 缺省参数
缺省参数也叫默认参数,用于定义函数时为参数提供的默认值,调用函数时可不传该默认参数的值。
所有位置参数必须出现在默认参数前,包括函数定义和调用。
函数调用时,如果为缺省参数传值则修改默认参数值;否则使用这个默认值。
def user_info(name, age, gender='男'):
print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
user_info('TOM', 20)
user_info('Rose', 18, '女')
40.7.4. 不定长参数
不定长参数也叫可变参数,用于不确定调用的时候会传递多少个参数的场景。此时,可用包裹(packing)位置参数,或者包裹关键字参数来进行参数传递。
40.7.4.1. 包裹位置传递
传进的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元组
def user_info(*args):
print(args)
# ('TOM',)
user_info('TOM')
# ('TOM', 18)
user_info('TOM', 18)
40.7.4.2. 包裹关键字传递
传递进的所有参数都包裹在kwargs中,以字典的形式保存
def user_info(**kwargs):
print(kwargs)
# {'name': 'TOM', 'age': 18, 'id': 110}
user_info(name='TOM', age=18, id=110)
40.7.5. 函数返回值拆包
40.7.5.1. 拆包:元组
def return_num():
return 100, 200
num1, num2 = return_num()
print(num1) # 100
print(num2) # 200
40.7.5.2. 拆包:字典
dict1 = {'name': 'TOM', 'age': 18}
a, b = dict1
# 对字典进行拆包,取出来的是字典的key
print(a) # name
print(b) # age
print(dict1[a]) # TOM
print(dict1[b]) # 18
40.7.6. 引用传递
40.7.6.1. 了解Python中引用传递
在Python中,值是靠引用来传递的。
要吧用id()来判断两个变量是否为同一个值的引用,id可以查看两个变更是否指向同一块内存地址
# ******************************
# 1. int类型
# ******************************
a = 1
b = a
print(b) # 1
print(id(a)) # 140708464157520
print(id(b)) # 140708464157520
a = 2
print(b) # 1,说明int类型为不可变类型
print(id(a)) # 140708464157552,此时得到是的数据2的内存地址
print(id(b)) # 140708464157520
# ******************************
# 2. 列表
# ******************************
aa = [10, 20]
bb = aa
print(id(aa)) # 2325297783432
print(id(bb)) # 2325297783432
aa.append(30)
print(bb) # [10, 20, 30], 列表为可变类型
print(id(aa)) # 2325297783432
print(id(bb)) # 2325297783432
40.7.6.2. 引用当做实参
def test1(a):
print(a)
print(id(a))
a += a
print(a)
print(id(a))
# int:计算前后id值不同
b = 100
test1(b)
# 列表:计算前后id值相同
c = [11, 22]
test1(c)
40.7.6.3. 可变与不可变类型
所谓可变类型与不可变类型是指:值修改后,数据内存地址不变的为可变类型,数据内存地址变化的为不可变类型。
- 可变类型
- 列表
- 字典
- 集合
- 不可变类型
- 整型
- 浮点型
- 字符串
- 元组
41. 递归函数
递归是一种编程思想,应用场景:
- 在我们日常开发中,如果要遍历一个文件夹下面所有的文件,通常会使用递归来实现;
- 在后续的算法课程中,很多算法都离不开递归,例如:快速排序。
41.1. 递归的特点
- 函数内部自己调用自己
- 必须有终止条件
42. 文件操作
42.1. open(name, mode)
- name: 是要打开的目标文件名的字符串(可以包含文件所在的具体路径)
- mode: 设置打开文件的模式(访问模式):只读、写入、追加等
文件打开模式
模式 描述
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
42.2. 文件对象方法
- 对象对象.write('内容') # 写内容
- 文件对象.read(num) # 读内容
- 文件对象.readlines(num) # 按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表
- 文件对象.readline(num) # 一次读取一行内容
- 文件对象.seek(偏移量, 起始位置) 用来移动文件指针
- 起始位置:
- 0:文件开头
- 1:当前位置
- 2:文件结尾
- 文件对象.close() # 关闭文件
43. 文件和文件夹的操作
在Python中文件和文件夹的操作要借助os模块里面的相关功能
- os.rename(目标文件名, 新文件名) # 文件重命名
- os.remove(目标文件名) # 删除文件
- os.mkdir(文件夹名字) # 创建文件夹
- os.rmdir(文件夹名字) # 删除文件夹
- os.getcwd() # 获取当前目录
- os.chdir(目录) # 改变默认目录
- os.listdir(目录) # 获取目录列表
44. 类与对象
44.1. 类
类是对一系列具有相同特征和行为的事物的统称,是一个抽象的概念,不是真实存在的事物
- 特征即属性
- 行为即方法
44.2. 对象
对象是类创建出来的真实存在的事物,必须先有类,可能通过类创建出对象
44.3. 定义类与对象
# 定义类
class 类名:
属性
方法
......
# 创建对象(实例化对象)
对象名 = 类名()
44.4. 魔法方法
在Python中,__xx__()的函数叫做魔法方法,指的是具有特殊功能的函数
- __init__():初始化对象,在创建一个对象时默认被调用,不需要手动调用,self参数不需要开发者传递,python解释器会自动把当前的对象引用传递过去
- __str__():当使用print输出对象的时候,默认打印对象的内存地址,如果类定义了__str__() 方法,那么就会打印这个方法中return的数据
- __del__():当删除对象时,Python解释器会默认调用__del__()方法
44.5. 类属性
- 类属性就是类对象所拥有的属性,它被该类的所有实例对象所共有。
- 类属性可以使用类对象或实例对象访问。
- 类属性只能通过类对象修改,不能通过实例对象修改
class Dog(object):
tooth = 10
wangcai = Dog()
xiaohei = Dog()
# 修改类属性
Dog.tooth = 12
print(Dog.tooth) # 12
print(wangcai.tooth) # 12
print(xiaohei.tooth) # 12
# 不能通过对象修改属性,如果这样操作,实则是创建了一个实例属性
wangcai.tooth = 20
print(Dog.tooth) # 12
print(wangcai.tooth) # 20
print(xiaohei.tooth) # 12
类属性的优点
- 记录的某项数据始终保持一致时,则定义类属性。
- 实例属性要求每个对象为其单独开辟一份内存空间来记录数据,而类属性为全类所共有 ,仅占用一份内存,更加节省内存空间
- 实例属性:类的方法中,通过self.属性名来声明的变量
44.6. 类方法
使用装饰器@classmethod来标识的方法称为类方法。对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数
class Dog(object):
__tooth = 10
@classmethod
def get_tooth(cls):
return cls.__tooth
wangcai = Dog()
result = wangcai.get_tooth()
print(result) # 10
44.7. 静态方法
使用装饰器@staticmethod标识的方法称为静态方法。静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls),一般用于方法中既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时。
class Dog(object):
@staticmethod
def info_print():
print('这是一个狗类,用于创建狗实例....')
wangcai = Dog()
# 静态方法既可以使用对象访问又可以使用类访问
wangcai.info_print()
Dog.info_print()
45. 面向对象编程
45.1. 继承
Python面向对象的继承指的是多个类之间的所属关系,即子类默认继承父类的所有属性和方法。Python支持多继承和单继承,当多继承时,默认使用从左到右第一个父类中的同名属性和方法。
# 创建WWE基类
class WWE:
def __init__(self):
self.organization = '[WWE]'
def introduce(self):
print(f'{self.organization} 大布')
# 创建UFC基类
class UFC:
def __init__(self):
self.organization = '[UFC]'
def introduce(self):
print(f'{self.organization} 大布')
class Introduction(WWE, UFC):
pass
da_bu = Introduction()
da_bu.introduce()
# console output
[WWE] 大布
注意:
- 子类和父类具有同名属性和方法,默认使用子类的同名属性和方法。因此子类可以重写父类中的属性和方法。
- 子类中调用父类的属性和方法:
- 单继承建议使用super(子类名, self).属性/方法
- 多继承建议使用 指定的父类名.属性/方法
- 多继承中也可以使用super(),Python解释器会自动查找父类,调用顺序遵循__mro__类属性的顺序
45.2. 封装
在Python中,可以为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类。
- 私有属性设置方法:__变量名__
- 获取私有属性的方法:可以使用setter/getter方法或者装饰器来完成
class Person:
def __init__(self):
self.__name = None
@property
def name(self):
return self.__name.upper()
@name.setter
def name(self, value):
self.__name = value
p = Person()
p.name = "wwe"
print(p.name) # 输出 "WWE"
45.3. 多态
多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。但是Python是动态语言,python解释器会自动推测变量类型,因此多态在python中的体现并不严格,而在Java,C#这类强类型语言中才会有明确体现以增加代码灵活性。
python中更多的是一种“鸭子类型”。所谓鸭子类型的概念是:它并不要求严格的继承体系,关注的不是对象的类型,而是它是否具有要调用的方法(行为)。
46. 异常
46.1. 异常语法
try:
可能发生错误的代码
except (异常类型1,异常类型2):
如果捕获到该异常类型执行的代码
46.2. else
try:
print(1)
except Exception as result:
print(result)
else:
print('我是else,是没有异常的时候执行的代码')
46.3. finally
try:
f = open('test.txt', 'r')
except Exception as result:
f = open('test.txt', 'w')
else:
print('没有异常,真开心')
finally:
f.close()
46.4. 自定义异常
# 自定义异常类,继承Exception
class ShortInputError(Exception):
def __init__(self, length, min_len):
self.length = length
self.min_len = min_len
# 设置抛出异常的描述信息
def __str__(self):
return f'你输入的长度是{self.length}, 不能少于{self.min_len}个字符'
def main():
try:
con = input('请输入密码:')
if len(con) < 3:
raise ShortInputError(len(con), 3) # 使用自定义异常
except Exception as result:
print(result)
else:
print('密码已经输入完成')
main()
47. 模块和包
47.1. 导入模块和包的方式:
- import 模块名
- from 模块名 import 功能名
- from 模块名 import *
- import 模块名 as 别名
- from 模块名 import 功能名 as 别名
47.2. 模块定位顺序
当导入一个模块,Python解析器对模块位置的搜索顺序是:
- 当前目录
- 如果不在当前目录,Python则搜索在shell变量PYTHONPATH下的每个目录。
- 如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/
模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
47.3. __all__
如果一个模块文件中有__all__变量,当使用from xxx import *导入时,只能导入这个__all__列表中的元素。