Python小总结

入门—B站视频—2022-08-08发布
环境安装
2023-09-15 16:02:39(开始)
2023-09-21 18:58:02(结束) (15,18,19,20,21)不偷懒的话,真正看起来,三天就够用了
.
高级—B站视频—2017-09-20发布
2023-09-22 07:17:15(开始)
2023-09-22 18:44:00(结束) 一天耗时

感悟

1
2
3
4
5
6
7
8
9
10
2023-09-20 18:37:32
echarts 刚学完,类似于JavaScript脚本语言,但比它还要精简,并且功能更多,语法特性多。
如:包/模块,set/dict/list/序列,语法的简洁(我老是加上 ; 分号,还有 {} 大括号,忘记加 : 冒号,哈哈)
2023-09-21 18:59:44 完!
对象/继承/多态,有Java继承,学这个很快,
有Java8 Stream,看 pyspark rdd 操作算子,也很快,
正则之前就有基础了,包括设计模式,socket,多线程,之前有基础,很快就过完了,简要做个笔记。
代码都在文章中了。
2023-09-22 18:56:55 补了高级部分
多进程(了解),再次补正则,socket静态服务器,深浅拷贝,日志,生成器,property属性,lock锁,关键字

Python简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 诞生
1989年,为了打发圣诞节假期,龟叔(吉多·范·罗苏姆)开始写Python语言的编译器
1991年,第一个Python编译器诞生
Python这个名字,来自龟叔所挚爱的电视剧 Monty Python‘s Flying Circus

# 初衷
让开发人员把精力放到解决问题上 而不是在编程语言的本身上

# 优点:简单、易学、免费&&开源、可移植性好、丰富的库
# 缺点:唯一的缺点就是执行效率慢,这个是解释型语言所通有的,同时这个缺点也将被计算机越来越强大的性能所弥补。

# 从事业务(大数据、人工智能)
+ Web应用开发
+ 服务器软件(网络软件)
+ 网络爬虫
+ 科学计算
+ 桌面软件
+ 运维测试
+ 游戏

Pycharm设置

1
2
3
1. exe 拦截器 3.11
2. 快捷键 删除F1 (如下:额外添加 F1)
===> Ctrl+Shift+F10 'Other' -> Run context configuration

注释/type/变量/类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 注释
井号空格 三引号

# type 查看类型
type(123)
type(1.23)
type('陶攀峰')

# 变量
# 可直接引用
a = 123

# 严格区分大小写
a = 1
A = 2

# 类型转换
int("123")
float("1.23")
str(123)

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 三种定义
单引号 双引号 三引号(多行)

# 字符串拼接 + (仅字符串)
"a" + "b"
# "a" + "b" + 123 # 拼接错误

# 字符串占位 %s
# print('a'+'b'+123) # 错误
print('a' + 'b') # ab
print('ab%s' % 123) # ab123
print('ab%s' % 123, 456) # ab123 456
# print('ab%s---%s' % 123) # 错误
print('ab%s---%s' % (123, 456)) # ab123---456

# 宽度、小数精度
print("%5d" % 11) # ' 11' 补3个空格
print("%1d" % 11) # '11' 不用补空格
print("%7.2f" % 11.345) # ' 11.35' 补3个空格
print("%.2f" % 11.345) # '11.35' 保留2位小数

# 变量引用
name = '陶攀峰'
age = 18
print(f'我是{name}, 今年{age}岁') # 我是陶攀峰, 今年18岁

# 运算符
print('%s' % 2 * 3) # 222
print('%s' % (2 * 3)) # 6
print('%s' % type('陶攀峰')) # <class 'str'>

# 剪切 ===> 参考 容器:序列
my_str = 'abcde'
print(my_str[:-2]) # abc
print(my_str[:2]) # ab
print(my_str[2:]) # cde
print(my_str[2]) # c

# 多行拼接 => 参考 SQL
name = '陶攀峰'
age = 18

sql = 'insert into student(name, age) ' \
f"values('{name}', {age})"

print(sql) # insert into student(name, age) values('陶攀峰', 18)

输入

1
2
3
4
5
6
7
8
9
10
name = input('你是谁?')
age = input('你的年龄?') # 返回字符串
age = int(age)
print(f'姓名:{name},年龄:{age}')

"""
你是谁?陶攀峰
你的年龄?18
姓名:陶攀峰,年龄:18
"""

运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ==  !=  >  <  >=  <=

print(1 == 1) # True
print(1 == 2) # False
print(1 == 1.0) # True



# 逻辑
and or not

print(1 == 1 and 1 == 2) # False
print(1 == 1 and 2 == 2) # True



# 运算符
+ - * / %
// 取整除
** 指数

判断

1
2
3
4
5
6
7
8
if 条件:
...
elif 条件:
...
elif 条件:
...
else:
...

循环

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
关键字 continue break



# while
while 条件:
...



# for
name = "abc"
for x in name:
print(x) # 打印三次 'a' 'b' 'c'



# range
for x in range(3):
... # x = [0, 1, 2]

for x in range(3, 6):
... # x = [3, 4, 5] [start, end)

for x in range(3, 8, 2):
... # x = [3, 5, 7] [start, end, step]



# 变量作用域
i = 999
for i in range(3):
print(i) # 0, 1, 2

print(i) # 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 定义
def 函数名(参数1, 参数2):
"""
:param 参数1 说明
:param 参数2 说明
:return 说明
"""
...

# 调用
函数名(1, 2)
# 函数 '无return' ===> 返回值 None
# 函数 'return' ===> 返回值 None
# 函数 'return None' ===> 返回值 None
# 函数 'return 123' ===> 返回值 123



# 多返回值
def info():
return '陶攀峰', 18, '男'
name, age, gender = info()



# 参数顺序
def fun(x1, x2, x3):
print(f'{x1} {x2} {x3}')
fun('1', '2', '3') # 1 2 3
fun('1', x3='3', x2='2') # 1 2 3
fun(x2='2', x3='3', x1='1') # 1 2 3



# 参数默认值
def fun(x1, x2, x3=3):
print(f'{x1} {x2} {x3}')
fun('1', '2') # 1 2 3
fun('1', '2', '33') # 1 2 33


# 元组参数 位置不定长
def fun(*args):
print(f"参数类型:{type(args)},内容:{args}")
# 参数类型:<class 'tuple'>,内容:(1, 2, 3, '陶攀峰', '男')
fun(1, 2, 3, '陶攀峰', '男')



# 字典参数 关键字不定长
def fun(**args):
print(f"参数类型:{type(args)},内容:{args}")
# 参数类型:<class 'dict'>,内容:{'name': '陶攀峰', 'age': 18, 'gender': '男'}
fun(name='陶攀峰', age=18, gender='男')
# fun('陶攀峰', 18, '男') # 错误

# 函数作为参数、lambda ===> lambda 只能写一行,多行请使用 函数定义
def fun(compute):
res = compute(1, 2)
print(f'结果:{res}')

def add(x, y):
return x + y

fun(add) # 结果:3
fun(lambda x, y: x - y) # 结果:-1

【日期标记】2023-09-15 19:17:13 以上同步完成

容器

列表 list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# 列表 => 上限2^63-1, 类型混装, 可重复, 有下标, 有序, 可增删改

# 空
my_list = []
my_list = list()

# 数据
my_list = ['a', 'b', 'c']
my_list = ['a', 1, True]
my_list = [[1, 2, 3], [4, 5, 6]]

# 下标 正向(0,1,2,...n-1) 反向(-1,-2,-3,...-n)
my_list = ['a', 'b', 'c']
print(my_list[0]) # a
# print(my_list[3]) # IndexError: list index out of range
print(my_list[-1]) # c
my_list = [[1, 2, 3], [4, 5, 6]]
print(my_list[0][0]) # 1

# 查找索引 找不到会报错
my_list = ['a', 'b', 'c']
print(my_list.index('a')) # 0
# print(my_list.index('d')) # ValueError: 'd' is not in list

# 修改
my_list = ['a', 'b', 'c']
my_list[0] = 'aa'
my_list[-1] = 'cc'
print(my_list) # ['aa', 'b', 'cc']

# 下标插入
my_list = ['a', 'b', 'c']
my_list.insert(1, 'x')
print(my_list) # ['a', 'x', 'b', 'c']

# 尾插
my_list = ['a', 'b', 'c']
my_list.append('d')
my_list.append([1, 2, 3])
print(my_list) # ['a', 'b', 'c', 'd', [1, 2, 3]]

# 追加容器
my_list = ['a', 'b', 'c']
my_list.extend([1, 2, 3])
print(my_list) # ['a', 'b', 'c', 1, 2, 3]

# 下标删除
my_list = ['a', 'b', 'c']
del my_list[0]
print(my_list) # ['b', 'c']
my_list = ['a', 'b', 'c']
my_pop = my_list.pop(1)
print(my_pop) # b 指定下标弹出
print(my_list) # ['a', 'c']
my_list = ['a', 'b', 'c']
my_pop = my_list.pop()
print(my_pop) # c 默认弹出最后一个
print(my_list) # ['a', 'b']

# 删除匹配第一个
my_list = ['a', 'b', 'c', 'b']
my_list.remove('b')
print(my_list) # ['a', 'c', 'b']

# 清空
my_list = ['a', 'b', 'c']
my_list.clear()
print(my_list) # []

# 统计数量
my_list = ['a', 'b', 'c', 'b']
print(my_list.count('b')) # 2

# 长度
my_list = ['a', 'b', 'c', 'b', [1, 2, 3], True]
print(len(my_list)) # 6

# while循环
my_list = ['a', 'b', 'c']
idx = 0
while idx < len(my_list):
print(my_list[idx])
idx += 1

# for循环
my_list = ['a', 'b', 'c']
for e in my_list:
print(e)
print('------------------------------------------------------------------------------------------------------------')

元组 tuple

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
# 元组 => 不可修改

# 空元组
my_tuple = ()
my_tuple = tuple()

# 数据
my_tuple = ('a', 'b', 'c')
my_tuple = ('a', 1, True)
my_tuple = ((1, 2, 3), (4, 5, 6))

# 同list
# + 下标 正向(0,1,2,...n-1) 反向(-1,-2,-3,...-n)
# + 查找索引 找不到会报错
# + 统计数量
# + 长度
# + while循环
# + for循环

# 修改
my_tuple = ('a', 'b', 'c', [1, 2, 3])
# my_tuple[0] = 'aa' # TypeError: 'tuple' object does not support item assignment
my_tuple[-1][1] = 22
print(my_tuple) # ('a', 'b', 'c', [1, 22, 3])
print('------------------------------------------------------------------------------------------------------------')

字符串 str

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 字符串 => 不可修改

# 空
my_str = ''
my_str = str()

# 下标 正向(0,1,2,...n-1) 反向(-1,-2,-3,...-n)
my_str = 'abc'
print(my_str[0]) # 'a'
# print(my_str[3]) # IndexError: string index out of range
print(my_str[-1]) # 'c'

# 查找索引 找不到会报错
my_str = 'abc'
print(my_str.index('a')) # 0
print(my_str.index('bc')) # 1
# print(my_str.index('d')) # ValueError: substring not found

# 修改
my_str = 'abc'
# my_str[0] = 'x' # TypeError: 'str' object does not support item assignment

# 替换
my_str = '我是陶攀峰 我是男生'
my_str2 = my_str.replace('我是', 'I am')
print(my_str) # '我是陶攀峰 我是男生'
print(my_str2) # 'I am陶攀峰 I am男生'

# 分隔
my_str = "a,b,c"
my_str_list = my_str.split(',')
print(my_str) # 'a,b,c'
print(my_str_list) # ['a', 'b', 'c']

# 前后去除
my_str = ' a b c '
print(my_str.strip()) # 'a b c'
my_str = '123 a b c 312'
print(my_str.strip('123')) # ' a b c '

# 统计数量
my_str = 'a b c ab d ab'
print(my_str.count('b')) # 3
print(my_str.count('ab')) # 2

# 长度
my_str = '1234 abcd !@ 我是陶攀峰'
print(len(my_str)) # 18

# while循环
my_str = 'abc'
idx = 0
while idx < len(my_str):
print(my_str[idx])
idx += 1

# for循环
my_str = 'abc'
for e in my_str:
print(e)
print('------------------------------------------------------------------------------------------------------------')

序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 序列 => 内容连续、有序,可使用下标访问的容器 如:列表、元组、字符串
# 语法: 序列[起始下标:结束下标:步长]
my_list = [11, 22, 33, 44, 55]
new_list = my_list[1:4] # 开始下标1, 结束下标4(不含), 步长1
print(new_list) # [22, 33, 44]

my_tuple = (11, 22, 33, 44, 55)
new_tuple = my_tuple[:] # 遍历所有(正序), 步长1
print(new_tuple) # (11, 22, 33, 44, 55)

my_list = [11, 22, 33, 44, 55]
new_list = my_list[::2] # 遍历所有(正序), 步长2
print(new_list) # [11, 33, 55]

my_str = 'abcde'
new_str = my_str[:4:2] # 开始下标0, 结束下标4(不含), 步长2
print(new_str) # 'ac'

my_str = 'abcde'
new_str = my_str[::-1] # 遍历所有(倒序), 步长1
print(new_str) # 'edcba'

my_list = [11, 22, 33, 44, 55]
new_list = my_list[3:1:-1] # 开始下标3, 结束下标1(不含), 步长1
print(new_list) # [44, 33]

my_tuple = (11, 22, 33, 44, 55)
new_tuple = my_tuple[:1:-2] # 开始下标4, 结束下标1(不含), 步长2
print(new_tuple) # (55, 33)
print('------------------------------------------------------------------------------------------------------------')

集合 set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 集合 => 去重, 无下标, 无序, 可增删改

# 空
my_set = {}
my_set = set()

# 数据
my_set = {'a', 'b', 'c'}
print(my_set) # {'c', 'b', 'a'} 或 {'c', 'a', 'b'} 或 ... 因为无序,下同
my_set = {'aa', 'bb', 'cc'}
print(my_set) # {'bb', 'aa', 'cc'}
my_set = {'a', 1, True}
print(my_set) # {1, 'a'} 1和True 重复了
my_set = {False, True, 1, 0}
print(my_set) # {False, True} 1和True 0和False 重复了
# my_set = {[1, 2, 3], [4, 5, 6]} # TypeError: unhashable type: 'list'

# 添加
my_set = {'a', 'b', 'c'}
my_set.add('d')
print(my_set) # {'b', 'c', 'd', 'a'}

# 删除
my_set = {'a', 'b', 'c'}
my_set.remove('b')
# my_set.remove('d') # KeyError: 'd'
print(my_set) # {'a', 'c'}

# 随机取出
my_set = {'a', 'b', 'c'}
my_pop = my_set.pop()
print(my_pop) # 'c'
print(my_set) # {'a', 'b'}

# 清空
my_set = {'a', 'b', 'c'}
my_set.clear()
print(my_set) # set()

# 差集
my_set1 = {'a', 'b', 'c'}
my_set2 = {'a', 'd', 'e'}
my_set3 = my_set1.difference(my_set2)
print(my_set1) # {'c', 'a', 'b'} 不变
print(my_set2) # {'e', 'd', 'a'} 不变
print(my_set3) # {'c', 'b'}

# 差集
my_set1 = {'a', 'b', 'c'}
my_set2 = {'a', 'd', 'e'}
my_set1.difference_update(my_set2)
print(my_set1) # {'c', 'b'} 改变
print(my_set2) # {'d', 'e', 'a'} 不变

# 并集
my_set1 = {'a', 'b', 'c'}
my_set2 = {'a', 'd', 'e'}
my_set3 = my_set1.union(my_set2)
print(my_set1) # {'a', 'c', 'b'} 不变
print(my_set2) # {'a', 'd', 'e'} 不变
print(my_set3) # {'a', 'd', 'b', 'e', 'c'}

# 长度
my_set = {'a', 'b', 'c'}
print(len(my_set)) # 3

# for循环
my_set = {'a', 'b', 'c'}
for e in my_set:
print(e)
print('------------------------------------------------------------------------------------------------------------')

字典 dict

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 字典 => 键值对
# 空
my_dict = {}
my_dict = dict()

# 数据
my_dict = {'name': '陶攀峰', 'age': 18, 123: 456, True: '哈哈'}
print(my_dict['name']) # '陶攀峰'
print(my_dict['age']) # 18
print(my_dict[123]) # 456
print(my_dict[True]) # '哈哈'
my_dict = {
'大牛': {'age': 18},
'二蛋': {'age': 22},
'三驴': {'age': 26}
}
print(my_dict['大牛']) # {'age': 18}
print(my_dict['大牛']['age']) # 18

# 添加
my_dict = {'name': '陶攀峰', 'age': 18}
my_dict['gender'] = 'boy'
print(my_dict) # {'name': '陶攀峰', 'age': 18, 'gender': 'boy'}

# 更新
my_dict = {'name': '陶攀峰', 'age': 18}
my_dict['age'] = '24' # 【日期标记】2023-09-19 16:01:06 以上同步完成
print(my_dict) # {'name': '陶攀峰', 'age': '24'}

# 删除
my_dict = {'name': '陶攀峰', 'age': 18}
my_pop = my_dict.pop('name')
print(my_pop) # '陶攀峰'
print(my_dict) # {'age': 18}

# 清空
my_dict = {'name': '陶攀峰', 'age': 18}
my_dict.clear()
print(my_dict) # {}

# 遍历
my_dict = {'name': '陶攀峰', 'age': 18}
my_keys = my_dict.keys()
print(my_keys) # dict_keys(['name', 'age'])
for k in my_keys:
print(f'k={k}, v={my_dict[k]}')
# k = name, v = 陶攀峰
# k = age, v = 18

# 长度
my_dict = {'name': '陶攀峰', 'age': 18}
print(len(my_dict)) # 2
print('------------------------------------------------------------------------------------------------------------')

转换/排序/压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 总结
# list tuple str set dict
# 5类都支持 for (list tuple str 支持 while 下标遍历)
# 通用方法 len(容器) max(容器) min(容器)
# 转换功能 list(容器) tuple(容器) str(容器) set(容器)
data = [('北京市', 4), ('上海市', 23), ('湖南省', 70), ('台湾省', 199), ('广东省', 499)]
# data = [list(e) for e in data] # 把上面格式变为下面格式
# data = [['北京市', 4], ['上海市', 23], ['湖南省', 70], ['台湾省', 199], ['广东省', 499]]



# 排序 sorted(容器, [reverse=True])
my_set = {33, 11, 22}
my_sorted = sorted(my_set)
my_sorted_r = sorted(my_set, reverse=True)
print(my_set) # {33, 11, 22}
print(my_sorted) # [11, 22, 33]
print(my_sorted_r) # [33, 22, 11]

# 多维数组降序
my_list = [["a", 33], ["b", 55], ["c", 11]]
# def fun(e):
# return e[1]
# my_list.sort(key=fun, reverse=True)
my_list.sort(key=lambda e: e[1], reverse=True)
print(my_list) # [['b', 55], ['a', 33], ['c', 11]]



# 压缩 zip
no = [1, 2, 3]
names = ['a', 'b', 'c']
for i in zip(no, names):
print(i)

文件

读/写/追加

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
f = open('path', 'r/w/a', encoding='UTF-8')
# r 只读(默认)
# w 清除所有内容后,再写。(文件不存在则创建)
# a 追加。(文件不存在则创建)

f.read() # 读取所有
f.read(nByte) # 读取指定字节数

line = f.readline() # 'aaa\n'
line = f.readline() # 'bbb\n'
line = f.readline() # 'ccc'
lines = f.readlines() # ['aaa\n', 'bbb\n', 'ccc']

# for循环读取所有行
for line in open('a.txt', 'r'):
print(line)

# 关闭
f.close()

# with open ===> 自动执行close()
with open('a.txt', 'r') as f:
f.readlines()

# 读写实战 ===> a.txt 在 py文件同级目录
f = open('a.txt', 'w', encoding='UTF-8')
f.write('abc') # 上面已经说到,这里会删除所有内容后,再写入新内容
f.flush() # 刷盘

f = open('a.txt', 'a', encoding='UTF-8')
f.write('def') # 追加内容
f.flush() # 刷盘

是否存在/是否文件夹/查看列表

1
2
3
4
import os
print(os.listdir("C:/Everythings")) # 列出路径下的内容 ['360', '4KVD', 'AnKeYinDrv', 'BaiduNetdisk', 'Bandizip', 'BigData', 'CharlesProxy', 'ChengTong', 'Citrix', 'CLion', 'Cursor', 'DataGrip', 'dbeaver', 'DingDing', 'Ditto', 'douyin', 'FastOrange', 'FinalShell', 'FreeMP4toMP3', 'Git', 'GoGoJumpVPN', 'HBuilderX', 'HEIF', 'i4Tools7', 'IDEA', 'IDM', 'ImageMagick', 'JDK', 'Jianying', 'jmeter', 'KuGou', 'LinkVPN', 'm3u8', 'Maven', 'MeiTuKanKan', 'Microsoft VS Code', 'Navicat', 'netAssist', 'Nginx', 'NodeJs', 'obs', 'PandaVPN', 'PDMan', 'postman', 'pycharm', 'QQ', 'QQke', 'QQPlayer', 'RedisDesktopManager', 'Shadowsocks', 'Snipaste', 'SouGouPinYin', 'Sublime', 'SunloginClient', 'Telegram', 'TencentMeeting', 'ToDesk', 'TreeSize Free', 'Typora', 'v2rayN', 'VirtualBox', 'WebStorm', 'WeChat', 'WeGame', 'WPS', 'Xftp', 'XMind', 'XZHuyanbao', 'YoudaoDict']
print(os.path.isdir("C:/Everythings/360")) # True 判断指定路径是不是文件夹
print(os.path.exists("D:/abcde")) # True 判断指定路径是否存在

递归查看文件夹下所有文件

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

def get_files_recursion_from_dir(path):
"""
从指定的文件夹中使用递归的方式,获取全部的文件列表
:param path: 被判断的文件夹
:return: list,包含全部的文件,如果目录不存在或者无文件就返回一个空list
"""
print(f"当前判断的文件夹是:{path}")
file_list = []
if os.path.exists(path):
for f in os.listdir(path):
new_path = path + "/" + f
if os.path.isdir(new_path):
# 进入到这里,表明这个目录是文件夹不是文件
file_list += get_files_recursion_from_dir(new_path)
else:
file_list.append(new_path)
else:
print(f"指定的目录{path},不存在")
return []

return file_list


print(get_files_recursion_from_dir("C:/Everythings"))

异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try:
open('abcdef', 'r') # FileNotFoundError: [Errno 2] No such file or directory: 'abcdef'
i = 0 / 0 # ZeroDivisionError: division by zero
...
# except:
# except Exception:
# except ZeroDivisionError as e:
except (ZeroDivisionError, FileNotFoundError) as e:
...
# print(e) # division by zero
# print(e) # FileNotFoundError: [Errno 2] No such file or directory: 'abcdef'
else:
print('没有异常')
finally:
print('最后执行')

模块

模块导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# [from 模块名] import [模块 | 类 | 变量 | 函数 | *] [as 别名]
# import 模块名
# from 模块名 import [类 | 变量 | 方法 | ...]
# from 模块名 import *
# from 模块名 as 别名
# from 模块名 import 功能名 as 别名

import time

print('start')
time.sleep(1) # 睡1秒
print('end')



from time import sleep

print('start')
sleep(1) # 睡1秒
print('end')



from time import *

print('start')
sleep(1) # 睡1秒
print('end')



import time as tt

print('start')
tt.sleep(1) # 睡1秒
print('end')



from time import sleep as sl

print('start')
sl(1) # 睡1秒
print('end')

自定义模块

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
1. 导入多模块,同名 最后一个生效
2. 模块测试,使用 if __name__ == '__main__':
3. 模块导入 * 问题, __all__ = ['compute']

# taopanfeng.py
import module_add
module_add.compute(11, 22)

from module_add import compute
from module_sub import compute
compute(11, 22) # -11

from module_add import *
compute(11, 22)



# module_add.py
# from module_add import * 指定*导入的内容
__all__ = ['compute']

def compute(a, b):
print(a + b)

def compute2(a, b):
print(f'compute2 {a + b}')

# 测试代码防止影响
if __name__ == '__main__':
compute(1, 2)

# module_sub.py
def compute(a, b):
print(a - b)

包 无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
# taopanfeng.py
import p.module1
import p.module2
p.module1.print111()
p.module2.print222()
# 导入了 module1
# 导入了 module2
# 111
# 222



# p/__init__.py

# p/module1.py
print('导入了 module1')

def print111():
print(111)
# p/module2.py
print('导入了 module2')

def print222():
print(222)

包 有init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# taopanfeng.py
from p import *
module1.print111()
# module2.print222() # NameError: name 'module2' is not defined.
# 导入了 module1
# 111



# p/__init__.py
__all__ = ['module1']
# p/module1.py
print('导入了 module1')

def print111():
print(111)
# p/module2.py
print('导入了 module2')

def print222():
print(222)

第三方包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 三方包
科学计算中常用的:numpy包
数据分析中常用的:pandas包
大数据计算中常用的:pyspark、apache-flink包
图形可视化常用的:matplotlib、pyecharts
人工智能常用的:tensorflow

# 安装
pip install 包名称
pip install numpy

# -i, --index-url <url> 指定包的索引地址
# https://pypi.tuna.tsinghua.edu.cn/simple 是清华大学提供的一个网站,可供pip程序下载第三方包
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称

# PyCharm 点击右下角安装


【日期标记】2023-09-20 11:11:55 以上同步完成

json

1
2
3
4
5
6
7
8
9
import json

data = [{"name": "大牛", "age": 18}, {"name": "二蛋", "age": 22}]
data = json.dumps(data) # obj => json
data = json.loads(data) # json => obj

data = [{"name": "大牛", "age": 18}, {"name": "二蛋", "age": 22}]
print(json.dumps(data)) # [{"name": "\u5927\u725b", "age": 18}, {"name": "\u4e8c\u86cb", "age": 22}]
print(json.dumps(data, ensure_ascii=False)) # [{"name": "大牛", "age": 18}, {"name": "二蛋", "age": 22}]

numpy

官方网站 https://numpy.org/
conda install -y numpy

2024-11-18补

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
import numpy as np

print("Running script:", __name__)

# 有哪些方法
print(dir(np))

# 一维数组
arr = np.array([1, 2, 3])
print(arr) # [1 2 3]
print(arr.size) # 3
print(type(arr)) # <class 'numpy.ndarray'>

# 二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)
# [[1 2 3]
# [4 5 6]]
print(arr2.ndim) # 2(二维)
print(arr2.dtype) # int32
print(arr2.size) # 6
print(arr2.shape) # (2, 3) --- 2行3列
print(arr2.reshape(1, 6)) # [[1 2 3 4 5 6]] --- 1行6列(返回新的,不改变老的)
# print(arr2.reshape(2, 4)) # ValueError: cannot reshape array of size 6 into shape (2,4)
print(arr2.sum()) # 21
print(arr2.max()) # 6
print(arr2[0]) # [1 2 3] --- 第一行
print(arr2[:, 0]) # [1 4] --- 第一列
print(arr2[:, -1]) # [3 6] --- 最后一列

# 运算
x1 = np.array([1, 2])
x2 = np.array([3, 4])
print(x1 + 10) # [11 12]
print(x1 + x2) # [4 6]
print(x1 * x2) # [3 8]

# 其他
print(np.round(1.234, 2)) # 1.23 --- 四舍五入
print(np.round(1.235, 2)) # 1.24 --- 四舍五入
print(np.abs(-5)) # 5 --- 绝对值

echarts

数据可视化库pyecharts简单入门—CSDN
pyecharts 参数讲解
pyecharts 参考案例
echarts 官方案例
安装 pip install pyecharts

折线图 入门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pyecharts.charts import Line
from pyecharts.options import TitleOpts, LegendOpts, ToolboxOpts, VisualMapOpts

line = Line()

line.set_global_opts(
title_opts=TitleOpts(title="我是标题", pos_left="center", pos_bottom="1%"),
legend_opts=LegendOpts(is_show=True),
toolbox_opts=ToolboxOpts(is_show=True),
visualmap_opts=VisualMapOpts(is_show=True),
)

line.add_xaxis(['a', 'b', 'c'])
line.add_yaxis('num', [30, 20, 10])
line.render() # 默认当前目录生成 render.html 文件
line.render('render.html') # 指定生成文件的名称

折线图 实战

可参考 天气变化折线图 — pyecharts

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
from pyecharts.charts import Line
import pyecharts.options as opts

x = ["2019", "2020", "2021", "2022", "2023"]
y_java = [1, 2, 3, 4, 5]
y_book = [3, 4, 5, 7, 8]
y_other = [2, 4, 5, 6, 7]
(
Line(init_opts=opts.InitOpts(width='800px', height='400px'))
.add_xaxis(xaxis_data=x) # x轴数据 => 时间
.add_yaxis(
series_name='Java', # 图例名称
y_axis=y_java, # y轴数据 => 数量
symbol_size=3, # 设置点的大小
label_opts=opts.LabelOpts(is_show=False), # 标签设置项 显示标签
linestyle_opts=opts.LineStyleOpts(width=1) # 线条宽度和样式
)
.add_yaxis(series_name='书籍', y_axis=y_book, symbol_size=3, label_opts=opts.LabelOpts(is_show=False), linestyle_opts=opts.LineStyleOpts(width=1))
.add_yaxis(series_name='其他', y_axis=y_other, symbol_size=3, label_opts=opts.LabelOpts(is_show=False), linestyle_opts=opts.LineStyleOpts(width=1))
.set_global_opts(
title_opts=opts.TitleOpts(title="博客发布文章累计对比图", pos_left="center"), # 标题居中
xaxis_opts=opts.AxisOpts(name='时间'), # x轴标题
yaxis_opts=opts.AxisOpts(name='数量'), # y轴标题
legend_opts=opts.LegendOpts(pos_left='70%'), # 图例的位置(折线点击按钮位置 距离左边70%)
).render()
)

在这里插入图片描述

地图

地图 — pyecharts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pyecharts.charts import Map
import pyecharts.options as opts

# data = [('北京', 4), ('上海', 23), ('湖南', 70), ('台湾', 199), ('广东', 499)] # 错误:要与现实的名称对应
data = [('北京市', 4), ('上海市', 23), ('湖南省', 70), ('台湾省', 199), ('广东省', 499)]

# data = [list(e) for e in data] # 把上面格式变为下面格式(两种格式都可以使用)
# data = [['北京市', 4], ['上海市', 23], ['湖南省', 70], ['台湾省', 199], ['广东省', 499]]
(
Map(init_opts=opts.InitOpts(width='1000px', height='800px'))
# Map() # width "900px", height "500px",
.add("疫情地图", data, "china") # 默认 "china"
.set_global_opts(
visualmap_opts=opts.VisualMapOpts(
is_show=True,
is_piecewise=True, # 分段
pieces=[
{"min": 1, "max": 9, "label": "1-9人", "color": "#CCFFFF"},
{"min": 10, "max": 99, "label": "10-99人", "color": "#FF6666"},
{"min": 100, "max": 500, "label": "100-500人", "color": "#990033"}
]
)
).render()
)

在这里插入图片描述

柱状图 反转xy

1
2
3
4
5
6
7
8
9
10
from pyecharts.charts import Bar
import pyecharts.options as opts

(
Bar()
.add_xaxis(["语文", "数学", "英语"])
.add_yaxis("成绩", [77, 138, 66], label_opts=opts.LabelOpts(position="right"))
.reversal_axis() # 反转x和y轴
.render()
)

在这里插入图片描述

柱状图 动态

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
from pyecharts.charts import Bar, Timeline
import pyecharts.options as opts
from pyecharts.globals import ThemeType

bar1 = Bar()
bar1.add_xaxis(["语文", "数学", "英语"])
bar1.add_yaxis("成绩", [78, 148, 66], label_opts=opts.LabelOpts(position="right"))
bar1.reversal_axis()

bar2 = Bar()
bar2.add_xaxis(["语文", "数学", "英语"])
bar2.add_yaxis("成绩", [89, 138, 78], label_opts=opts.LabelOpts(position="right"))
bar2.reversal_axis()

bar3 = Bar()
bar3.add_xaxis(["语文", "数学", "英语"])
bar3.add_yaxis("成绩", [110, 132, 67], label_opts=opts.LabelOpts(position="right"))
bar3.reversal_axis()

# 构建时间线对象
timeline = Timeline({"theme": ThemeType.LIGHT})
# 在时间线内添加柱状图对象
timeline.add(bar1, "开学考试")
timeline.add(bar2, "期中考试")
timeline.add(bar3, "期末考试")

# 自动播放设置
timeline.add_schema(
play_interval=1000, # 1秒/次
is_timeline_show=True, # 显示时间控件
is_auto_play=True, # 自动播放
is_loop_play=True # 循环播放
)

# 绘图是用时间线对象绘图,而不是bar对象了
timeline.render("基础时间线柱状图.html")

在这里插入图片描述


柱状图 动态/csv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from pyecharts.charts import Bar, Timeline
import pyecharts.options as opts
from pyecharts.globals import ThemeType

f = open("可视化案例数据/动态柱状图数据/1960-2019全球GDP数据.csv", "r", encoding="GB2312")
lines = f.readlines()
lines.pop(0)

# {1960:[[中国, 1], [美国 2]]}
data = {}
for line in lines:
columns = line.split(',')
year = int(columns[0])
country = columns[1]
gdp = float(columns[2])
try:
data[year].append([country, gdp])
except KeyError:
data[year] = []
data[year].append([country, gdp]) # 尾插

timeline = Timeline({"theme": ThemeType.LIGHT})
for year in sorted(data.keys()):
# 前8名
data[year].sort(key=lambda e: e[1], reverse=True)
top8 = data[year][0:8] # 取出本年份前8名的国家

# 构建数据 x y
x_data = []
y_data = []
for e in top8:
x_data.append(e[0]) # x轴 国家
y_data.append(e[1] / 1_0000_0000) # y轴 gdp 单位:亿

# 构建柱状图
bar = Bar()
x_data.reverse()
y_data.reverse()
bar.add_xaxis(x_data)
bar.add_yaxis("GDP(亿)", y_data, label_opts=opts.LabelOpts(position="right"))
bar.reversal_axis() # 反转x轴和y轴
bar.set_global_opts(
title_opts=opts.TitleOpts(title=f"{year}年全球前8GDP数据") # 设置每一年的图表的标题
)
timeline.add(bar, str(year))

timeline.add_schema(
play_interval=1000,
is_timeline_show=True,
is_auto_play=True,
is_loop_play=False
)
timeline.render("1960-2019全球GDP前8国家.html")

在这里插入图片描述
【日期标记】2023-09-20 18:36:45 以上同步完成

对象

无参构造

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

# def sayHi(self, 形参1, ... , 形参n):
# self必写,用于访问成员变量 调用时,忽略此参数
def sayHi(self):
print(f'大家好,我是{self.name},年龄{self.age}')

def sayHi2(self, prefix):
print(f'{prefix},我是{self.name},年龄{self.age}')

s = Student()
s.name = '陶攀峰'
s.age = 18
s.sayHi() # '大家好,我是陶攀峰,年龄18'
s.sayHi2('你们好') # '你们好,我是陶攀峰,年龄18'

__init__ 有参构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student:
# 可省略
# name = None
# age = None

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

def sayHi(self):
print(f'大家好,我是{self.name},年龄{self.age}')

s = Student('陶攀峰', 18)
s.sayHi() # 大家好,我是陶攀峰,年龄18

__str__ 对象转字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student:
def __init__(self, name, age):
self.name = name
self.age = age

# print 对象 => 不再打印地址
def __str__(self):
return f'Student => name={self.name}, age={self.age}'


s = Student('陶攀峰', 18)

# print(s) # <__main__.Student object at 0x00000144F0177650>
# print(str(s)) # <__main__.Student object at 0x00000144F0177650>

print(s) # Student => name=陶攀峰, age=18
print(str(s)) # Student => name=陶攀峰, age=18

__lt__ 大于 小于 比较

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

# 可使用 > < 比较
def __lt__(self, other):
return self.age < other.age


s1 = Student('大牛', 18)
s2 = Student('二蛋', 22)

# print(s1 < s2) # TypeError: '<' not supported between instances of 'Student' and 'Student'
# print(s1 > s2) # TypeError: '>' not supported between instances of 'Student' and 'Student'

print(s1 < s2) # True
print(s1 > s2) # False

__le__ 大于等于 小于等于 比较

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

# 可使用 >= <= 比较
def __le__(self, other):
return self.age <= other.age


s1 = Student('大牛', 18)
s2 = Student('二蛋', 22)

# print(s1 <= s2) # TypeError: '<=' not supported between instances of 'Student' and 'Student'
# print(s1 >= s2) # TypeError: '>=' not supported between instances of 'Student' and 'Student'

print(s1 <= s2) # True
print(s1 >= s2) # False

__eq__ 等于 不等 比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student:
def __init__(self, name, age):
self.name = name
self.age = age

# 可使用 == != 比较
def __eq__(self, other):
return self.name == other.name and self.age == other.age


s1 = Student('大牛', 18)
s2 = Student('大牛', 18)

# print(s1 == s2) # False 默认内存地址比较

print(s1 == s2) # True

私有属性 私有方法

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
class Student:
name = None
__age = None

def say(self):
print(f'name={self.name},age={self.__age},__say={self.__say()}')

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

def __say(self):
return '我是私有方法'


s = Student()
s.name = '陶攀峰'

# print(s.__age) # AttributeError: 'Student' object has no attribute '__age'
s.__age = 22 # 不报错,但对象内部的 self.__age 仍为 None
print(s.__age) # 22 ===> 不报错,因为上面一步赋值了

s.say() # name=陶攀峰,age=None,__say=我是私有方法
# s.__say() # AttributeError: 'Student' object has no attribute '__say'

s.set_age(18) # 对象内部的 self.__age = 18 s.__age 仍为 22
print(s.__age) # 22 ===> 不报错,因为上面一步赋值了
s.say() # name=陶攀峰,age=18,__say=我是私有方法

继承

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
class A:
name = 'a'
class X(A):
pass
x = X()
print(x.name) # 'a'



class A:
name = 'a'
class X(A):
name='x'
x = X()
print(x.name) # 'x'



class A:
name = 'a'
class B:
name = 'b'
class X(B, A):
pass
x = X()
print(x.name) # 'b'


# 总结:
# 访问顺序 1. 访问自己的 2. 从左往右,访问第一个父亲的 3. 从左往右,访问第二个父亲的 ...
# pass关键字 只进行类的定义,不定义任何变量/方法

super使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class A:
name = 'a'

def aa(self):
return 'aa'


class X(A):
name = 'x'

def say(self):
print(f'name={self.name} super={super().name} super.aa()={super().aa()}')
print(f'name={self.name} super={A.name} super.aa()={A.aa(self)}')


x = X()
x.say()
# name=x super=a super.aa()=aa
# name=x super=a super.aa()=aa

# 父类属性 super().属性名 父类名.属性名
# 父类方法 super().方法名() 父类名.方法名(self)

类型注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# Python 3.5 引入'类型注解'
# 一眼明了-无需注解,模糊不清-要加注解
# 变量,方法入参,方法返回值
# 语法1 变量: 类型
# 语法2 在注释中 # type: 类型

# 方法参数
def fun(a: int, b: int):
print(a + b)

# 方法参数 + 方法返回值
def fun(a: int, b: int) -> int:
return a + b

# 方法参数 + 方法返回值:明细
def fun(k: str, v: int) -> dict[str, int]:
return {k: v}

# 变量定义:对象
class Student:
pass
def fun():
return Student()
s: Student = fun()

# 变量定义、注释定义
import random
i: int = random.randint(1, 2)
i = random.randint(1, 2) # type: int

# 指定类型
my_list: list = [1, 2, 3]
my_tuple: tuple = (1, 2, 3)
my_set: set = {1, 2, 3}
my_dict: dict = {'name': '陶攀峰'}
my_str: str = '陶攀峰'

# 指定类型:明细
my_list: list[int] = [1, 2, 3]
my_tuple: tuple[str, int, bool] = ('陶攀峰', 666, True)
my_set: set[int] = {1, 2, 3}
my_dict: dict[str, int] = {'taopanfeng': 666}

# Union类型
from typing import Union

# str|int
my_tuple: tuple[Union[str, int]] = ('陶攀峰', 666)

# k=str, v=str|int
my_dict: dict[str, Union[str, int]] = {'name': '陶攀峰', 'age': 18}

def fun(data: Union[int, str]) -> Union[dict[int, int], dict[str, str]]:
print(str(data))
return {data: data}

多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Animal:
def speak(self):
pass

class Cat(Animal):
def speak(self):
print('喵喵喵')

class Dog(Animal):
def speak(self):
print('汪汪汪')



def make_noise(animal: Animal):
animal.speak()

make_noise(Cat()) # 喵喵喵
make_noise(Dog()) # 汪汪汪

面向对象-pyecharts

在这里插入图片描述

【日期标记】2023-09-21 10:45:52 以上同步完成

SQL

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

"""
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) COLLATE utf8_bin NOT NULL,
`password` varchar(255) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
AUTO_INCREMENT=1 ;
"""

import pymysql.cursors

# Connect to the database
connection = pymysql.connect(host='49.234.60.52',
port=3306,
user='root',
password='Qq1801957499..',
# database='pymysql', # 你的数据库
autocommit=False, # 修改后,手动提交
cursorclass=pymysql.cursors.DictCursor # 以字典形式返回结果的游标
)

print(connection.get_server_info()) # '8.0.27' 数据库版本

connection.select_db('pymysql') # 手动设置数据库

with connection:
# with connection.cursor() as cursor:
# # Create a new record
# sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
# cursor.execute(sql, ('webmaster@python.org', 'very-secret'))
#
# # 默认非自动提交
# connection.commit()

with connection.cursor() as cursor:
# Read a single record
sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s"
cursor.execute(sql, ('webmaster@python.org',))
# result = cursor.fetchone() # 查询一条 {'id': 1, 'password': 'very-secret'}
# result = cursor.fetchmany(2) # 查询一条 [{'id': 1, 'password': 'very-secret'}, {'id': 2, 'password': 'very-secret'}]
result = cursor.fetchall() # 查询多条 [{'id': 1, 'password': 'very-secret'}, {'id': 2, 'password': 'very-secret'}, {'id': 3, 'password': 'very-secret'}]
print(result)

Spark

初探pyspark

1
2
3
4
5
6
7
8
9
10
11
# pip install pyspark # 需要开启代理
# pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark # 清华


from pyspark import SparkConf, SparkContext
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
sc = SparkContext(conf=conf)
# 打印PySpark的运行版本
print(sc.version) # 3.4.1 (运行后,需要等一会)
# 停止SparkContext对象的运行(停止PySpark程序)
sc.stop()

RDD

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
"""
PySpark支持多种数据的输入,在输入完成后,都会得到一个:RDD类的对象

RDD全称为:弹性分布式数据集(Resilient Distributed Datasets)

源数据 -> RDD -> 结果数据
"""
from pyspark import SparkConf, SparkContext

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 通过parallelize方法将Python对象加载到Spark内,成为RDD对象
# 用过textFile方法,读取文件数据加载到Spark内,成为RDD对象
rdd1 = sc.parallelize([1, 2, 3, 4, 5])
rdd2 = sc.parallelize((1, 2, 3, 4, 5))
rdd3 = sc.parallelize("abcde")
rdd4 = sc.parallelize({1, 2, 3, 4, 5})
rdd5 = sc.parallelize({"k1": "v1", "k2": "v2"})
rdd6 = sc.textFile("./样例数据/hello.txt")

# rdd.collect() 查看rdd内容
print(rdd1.collect()) # [1, 2, 3, 4, 5]
print(rdd2.collect()) # [1, 2, 3, 4, 5]
print(rdd3.collect()) # ['a', 'b', 'c', 'd', 'e'] ===> 字符列表
print(rdd4.collect()) # [1, 2, 3, 4, 5]
print(rdd5.collect()) # ['k1', 'k2'] ===> 字典只有key
print(rdd6.collect()) # ['taopanfeng taopanfeng kevin taopanfeng', 'spark python spark python taopanfeng', 'taopanfeng kevin kevin taopanfeng python', 'python python spark pyspark pyspark', 'taopanfeng python pyspark kevin spark']

sc.stop()

map

1
2
3
4
5
6
7
8
9
10
11
12
13
from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 准备一个RDD
rdd = sc.parallelize([1, 2, 3, 4, 5])

# 链式调用
rdd2 = rdd.map(lambda x: x * 10).map(lambda x: x + 1)
print(rdd2.collect()) # [11, 21, 31, 41, 51]

flatMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 准备一个RDD
rdd = sc.parallelize(["taopanfeng kevin 666", "taopanfeng taopanfeng kevin", "python taopanfeng"])

# rdd2 = rdd.map(lambda x: x.split(" "))
# print(rdd2.collect()) # [['taopanfeng', 'kevin', '666'], ['taopanfeng', 'taopanfeng', 'kevin'], ['python', 'taopanfeng']]

rdd3 = rdd.flatMap(lambda x: x.split(" "))
print(rdd3.collect()) # ['taopanfeng', 'kevin', '666', 'taopanfeng', 'taopanfeng', 'kevin', 'python', 'taopanfeng']

reduceByKey

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 准备一个RDD [(k,v), (k,v), (k,v), ...]
rdd = sc.parallelize([('男', 11), ('男', 22), ('女', 33), ('女', 44)])

# 防止如下报错,请安装 pip install psutil
# UserWarning: Please install psutil to have better support with spilling
rdd2 = rdd.reduceByKey(lambda a, b: a + b)
print(rdd2.collect()) # [('女', 77), ('男', 33)]

filter

1
2
3
4
5
6
7
8
9
10
11
12
13
from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 准备一个RDD
rdd = sc.parallelize([1, 2, 3, 4, 5])

# 对RDD的数据进行过滤
rdd2 = rdd.filter(lambda num: num % 2 == 0)
print(rdd2.collect()) # [2, 4]

distinct

1
2
3
4
5
6
7
8
9
10
11
12
13
from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 准备一个RDD
rdd = sc.parallelize([1, 1, 3, 3, 5, 5, 7, 8, 8, 9, 10])

# 对RDD的数据进行去重
rdd2 = rdd.distinct()
print(rdd2.collect())

sortBy

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
from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 1. 读取数据文件
rdd = sc.textFile("./样例数据/hello.txt")
"""
taopanfeng taopanfeng kevin taopanfeng
spark python spark python taopanfeng
taopanfeng kevin kevin taopanfeng python
python python spark pyspark pyspark
taopanfeng python pyspark kevin spark
"""
# 2. 取出全部单词
word_rdd = rdd.flatMap(lambda x: x.split(" "))
# 3. 将所有单词都转换成二元元组,单词为Key,value设置为1
word_with_one_rdd = word_rdd.map(lambda word: (word, 1)) # 'word' ===> (word, 1)
# 4. 分组并求和 => 二元元组第一个元素作为key 第二个作为value
result_rdd = word_with_one_rdd.reduceByKey(lambda a, b: a + b)
# 5. 对结果进行排序 ===> 数量降序 无分布式:分区数这里设为1
final_rdd = result_rdd.sortBy(lambda x: x[1], ascending=False, numPartitions=1)
print(final_rdd.collect()) # [('taopanfeng', 7), ('python', 6), ('kevin', 4), ('spark', 4), ('pyspark', 3)]

collect/reduce/take/count

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
from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 准备RDD
rdd = sc.parallelize([1, 2, 3, 4, 5])

# collect算子,输出RDD为list对象
rdd_list: list = rdd.collect()
print(rdd_list) # [1, 2, 3, 4, 5]
print(type(rdd_list)) # <class 'list'>

# reduce算子,对RDD进行两两聚合
num = rdd.reduce(lambda a, b: a + b)
print(num) # 15

# take算子,取出RDD前N个元素,组成list返回
take_list = rdd.take(3)
print(take_list) # [1, 2, 3]

# count,统计rdd内有多少条数据,返回值为数字
num_count = rdd.count()
print(f"rdd内有{num_count}个元素") # rdd内有5个元素

sc.stop()

saveAsTextFile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
"""
rdd.saveAsTextFile("文件路径")

上面代码,调用保存文件,需要配置Hadoop依赖

1. 下载Hadoop安装包 'hadoop-3.0.0.tar.gz'
http://archive.apache.org/dist/hadoop/common/hadoop-3.0.0/hadoop-3.0.0.tar.gz
2. 解压到电脑任意位置 我的解压位置 'C:/Everythings/BigData/hadoop/hadoop-3.0.0'

3. 在Python代码中使用os模块配置: os.environ['HADOOP_HOME'] = "C:/Everythings/BigData/hadoop/hadoop-3.0.0"

4. 下载 'winutils.exe',并放入Hadoop解压文件夹的bin目录内
https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop-3.0.0/bin/winutils.exe

5. 下载 'hadoop.dll',并放入:C:/Windows/System32 文件夹内
https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop-3.0.0/bin/hadoop.dll

"""

from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
os.environ['HADOOP_HOME'] = "C:/Everythings/BigData/hadoop/hadoop-3.0.0"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
# conf.set("spark.default.parallelism", "1")

sc = SparkContext(conf=conf)

# 准备RDD1
rdd1 = sc.parallelize([1, 2, 3, 4, 5], numSlices=1)

# 准备RDD2
rdd2 = sc.parallelize([("Hello", 3), ("Spark", 5), ("Hi", 7)], 1)

# 准备RDD3
rdd3 = sc.parallelize([[1, 3, 5], [6, 7, 9], [11, 13, 11]], 1)

# 输出到文件中
# rdd1.saveAsTextFile("D:/output1") # 绝对路径
rdd1.saveAsTextFile("样例数据/output/1")
rdd2.saveAsTextFile("样例数据/output/2")
rdd3.saveAsTextFile("样例数据/output/3")

# 默认 核数分区
# 方式1(全局)
# 设置为1 conf.set("spark.default.parallelism", "1")
# 方式2(单个)
# rdd1 = sc.parallelize([1, 2, 3, 4, 5], numSlices=1)
# rdd2 = sc.parallelize([("Hello", 3), ("Spark", 5), ("Hi", 7)], 1)
# rdd3 = sc.parallelize([[1, 3, 5], [6, 7, 9], [11, 13, 11]], 1)

案例 小时段单词检索 v1:本地 单机运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from pyspark import SparkConf, SparkContext
import os

os.environ['PYSPARK_PYTHON'] = "C:/Program Files/Python311/python.exe"
os.environ['HADOOP_HOME'] = "C:/Everythings/BigData/hadoop/hadoop-3.0.0"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
conf.set("spark.default.parallelism", "1")
sc = SparkContext(conf=conf)

# 读取文件转换成RDD
file_rdd = sc.textFile("样例数据/search_log.txt")
# TODO 需求1: 热门搜索时间段Top3(小时精度)
# 1.1 取出全部的时间并转换为小时
# 1.2 转换为(小时, 1) 的二元元组
# 1.3 Key分组聚合Value
# 1.4 排序(降序)
# 1.5 取前3
result1 = file_rdd.map(lambda x: (x.split("\t")[0][:2], 1)). \
reduceByKey(lambda a, b: a + b). \
sortBy(lambda x: x[1], ascending=False, numPartitions=1). \
take(3)
print("需求1的结果:", result1)

# TODO 需求2: 热门搜索词Top3
# 2.1 取出全部的搜索词
# 2.2 (词, 1) 二元元组
# 2.3 分组聚合
# 2.4 排序
# 2.5 Top3
result2 = file_rdd.map(lambda x: (x.split("\t")[2], 1)). \
reduceByKey(lambda a, b: a + b). \
sortBy(lambda x: x[1], ascending=False, numPartitions=1). \
take(3)
print("需求2的结果:", result2)

# TODO 需求3: 统计黑马程序员关键字在什么时段被搜索的最多
# 3.1 过滤内容,只保留黑马程序员关键词
# 3.2 转换为(小时, 1) 的二元元组
# 3.3 Key分组聚合Value
# 3.4 排序(降序)
# 3.5 取前1
result3 = file_rdd.map(lambda x: x.split("\t")). \
filter(lambda x: x[2] == '黑马程序员'). \
map(lambda x: (x[0][:2], 1)). \
reduceByKey(lambda a, b: a + b). \
sortBy(lambda x: x[1], ascending=False, numPartitions=1). \
take(1)
print("需求3的结果:", result3)

# TODO 需求4: 将数据转换为JSON格式,写出到文件中
# 4.1 转换为JSON格式的RDD
# 4.2 写出为文件
file_rdd.map(lambda x: x.split("\t")). \
map(lambda x: {"time": x[0], "user_id": x[1], "key_word": x[2], "rank1": x[3], "rank2": x[4], "url": x[5]}). \
saveAsTextFile("样例数据/search_log_json")

案例 小时段单词检索 v2:服务器 集群运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from pyspark import SparkConf, SparkContext
import os

# 需要把此py文件放到 Linux 服务器运行,所以这里配置 Linux 路径
# 提交命令: bin/spark-submit --master yarn --num-executors 3 --queue root.teach --executor-cores 4 --executor-memory 4g /home/hadoop/demo.py
os.environ['PYSPARK_PYTHON'] = '/export/server/anaconda3/bin/python'
os.environ['HADOOP_HOME'] = "/export/server/hadoop-3.3.1"
conf = SparkConf().setAppName("spark_cluster")
conf.set("spark.default.parallelism", "24") # 并行度:24核 3台服务器 每台8核
sc = SparkContext(conf=conf)

# 读取文件转换成RDD
file_rdd = sc.textFile("hdfs://m1:8020/data/search_log.txt")
# TODO 需求1: 热门搜索时间段Top3(小时精度)
# 1.1 取出全部的时间并转换为小时
# 1.2 转换为(小时, 1) 的二元元组
# 1.3 Key分组聚合Value
# 1.4 排序(降序)
# 1.5 取前3
result1 = file_rdd.map(lambda x: (x.split("\t")[0][:2], 1)). \
reduceByKey(lambda a, b: a + b). \
sortBy(lambda x: x[1], ascending=False, numPartitions=1). \
take(3)
print("需求1的结果:", result1)

# TODO 需求2: 热门搜索词Top3
# 2.1 取出全部的搜索词
# 2.2 (词, 1) 二元元组
# 2.3 分组聚合
# 2.4 排序
# 2.5 Top3
result2 = file_rdd.map(lambda x: (x.split("\t")[2], 1)). \
reduceByKey(lambda a, b: a + b). \
sortBy(lambda x: x[1], ascending=False, numPartitions=1). \
take(3)
print("需求2的结果:", result2)

# TODO 需求3: 统计黑马程序员关键字在什么时段被搜索的最多
# 3.1 过滤内容,只保留黑马程序员关键词
# 3.2 转换为(小时, 1) 的二元元组
# 3.3 Key分组聚合Value
# 3.4 排序(降序)
# 3.5 取前1
result3 = file_rdd.map(lambda x: x.split("\t")). \
filter(lambda x: x[2] == '黑马程序员'). \
map(lambda x: (x[0][:2], 1)). \
reduceByKey(lambda a, b: a + b). \
sortBy(lambda x: x[1], ascending=False, numPartitions=1). \
take(1)
print("需求3的结果:", result3)

# TODO 需求4: 将数据转换为JSON格式,写出到文件中
# 4.1 转换为JSON格式的RDD
# 4.2 写出为文件
file_rdd.map(lambda x: x.split("\t")). \
map(lambda x: {"time": x[0], "user_id": x[1], "key_word": x[2], "rank1": x[3], "rank2": x[4], "url": x[5]}). \
saveAsTextFile("hdfs://m1:8020/output/output_json")

【日期标记】2023-09-21 16:35:41 以上同步完成

global

未使用 global

1
2
3
4
5
6
7
8
9
10
11
x = 4

def my():
x = 8
print("x = ", x)

my()
print("x = ", x)
# 结果是:
# x = 8
# x = 4

global 定义单个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
x = 4

def my():
global x
x = 8
print("x = ", x)

print("x = ", x)
my()
print("x = ", x)

# 结果是:
# x = 4
# x = 8
# x = 8

global 定义多个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
x = 1
y = 2
z = 3

def my():
global x, y, z # 一个global关键字定义多个全局变量
x = 4
y = 5
z = 6
k = 7
print(f"x = {x}, y = {y}, z = {z}, k = {k}")

print(f"x = {x}, y = {y}, z = {z}")
my()
print(f"x = {x}, y = {y}, z = {z}")
# print(f"k = {k}") # NameError: name 'k' is not defined

# 结果是:
# x = 1, y = 2, z = 3
# x = 4, y = 5, z = 6, k = 7
# x = 4, y = 5, z = 6

闭包

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
闭包的作用:闭包可以保存函数内的变量,不会随着函数调用完而销毁。

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
"""
1. 什么是闭包
定义双层嵌套函数, 内层函数可以访问外层函数的变量
将内存函数作为外层函数的返回,此内层函数就是闭包函数
2. 闭包的好处和缺点
优点:无需定义全局变量即可实现通过函数,持续的访问、修改某个值
优点:闭包使用的变量的所用于在函数内,难以被错误的调用修改
缺点:由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存
3. nonlocal关键字的作用
在闭包函数(内部函数中)想要修改外部函数的变量值
需要用nonlocal声明这个外部变量
"""
# 使用闭包实现ATM小案例
def account_create(init_amount=0):
# deposit True=存 False=取
def atm(num, deposit=True):
# 需要使用nonlocal关键字修饰外部函数的变量,才可在内部函数中修改它
nonlocal init_amount
if deposit:
init_amount += num
print(f"存款:+{num}, 账户余额:{init_amount}")
else:
init_amount -= num
print(f"取款:-{num}, 账户余额:{init_amount}")

return atm

# atm => 就是确定了 init_amount 变量为 0 的 atm 函数
atm = account_create()

atm(100) # 存
atm(200) # 存
atm(100, deposit=False) # 取
# 存款:+100, 账户余额:100
# 存款:+200, 账户余额:300
# 取款:-100, 账户余额:200

装饰器

装饰器其实也是一种闭包, 其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能。

装饰器 闭包写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def outer(func):
def inner():
print("我睡觉了")
func()
print("我起床了")
return inner

# 此函数不会被破坏
def sleep():
import random
import time
print("睡眠中......")
time.sleep(random.randint(1, 5))

fn = outer(sleep)
fn()
# 我睡觉了
# 睡眠中......
# 我起床了

装饰器 语法糖写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def outer(func):
def inner():
print("我睡觉了")
func()
print("我起床了")
return inner

# 此函数不会被破坏
@outer
def sleep():
import random
import time
print("睡眠中......")
time.sleep(random.randint(1, 5))

# 解释器发现上面有 @outer
# 代码执行同下:
# inner = outer(sleep)
# inner()
sleep()
# 我睡觉了
# 睡眠中......
# 我起床了

装饰器 方法耗时打印

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

def get_time(fn):
def inner():
start = time.time() # 秒
fn()
end = time.time()
print(f"耗时(秒):{end - start}")
return inner

@get_time
def func():
for i in range(100000):
print(i)

func()

装饰器 参数

1
2
3
4
5
6
7
8
9
10
def logging(fn):
def inner(a, b):
fn(a, b)
return inner

@logging
def sum_num(a, b):
print(a + b)

sum_num(1, 2) # 3

装饰器 返回值

1
2
3
4
5
6
7
8
9
10
11
def logging(fn):
def inner(a, b):
return fn(a, b)
return inner

@logging
def sum_num(a, b):
return a + b

result = sum_num(1, 2)
print(result) # 3

装饰器 不定长参数

1
2
3
4
5
6
7
8
9
10
def logging(fn):
def inner(*args, **kwargs):
fn(*args, **kwargs)
return inner

@logging
def sum_num(*args, **kwargs):
print(args, kwargs)

sum_num(1, 2, 3, age="18") # (1, 2, 3) {'age': '18'}

装饰器 通用

1
2
3
4
5
6
7
8
9
10
def logging(fn):
def inner(*args, **kwargs):
return fn(*args, **kwargs)
return inner

@logging
def sum_num(*args, **kwargs):
print(args, kwargs)

sum_num(1, 2, 3, age="18") # (1, 2, 3) {'age': '18'}

装饰器 多个装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def check1(fn1):
def inner():
print("登陆验证1")
fn1()
return inner

def check2(fn2):
def inner():
print("登陆验证2")
fn2()
return inner

@check1
@check2
def comment():
print("发表评论")

comment()
# 登陆验证1
# 登陆验证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
# 装饰器
# 外部函数
def decorator(fn, flag):
# 内部函数
def inner(num1, num2):
# 判断流程
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
return fn(num1, num2)

return inner


# 被带有参数的装饰器装饰的函数
@decorator('+')
def add(a, b):
return a + b


# 执行函数
result = add(1, 2)
print(result)

# TypeError: decorator() missing 1 required positional argument: 'flag'

装饰器 带参-正确用法

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
# 装饰器
def logging(flag): # flag = "+"

# 外部函数
def decorator(fn):
# 内部函数
def inner(num1, num2):
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
return fn(num1, num2)

return inner

return decorator


# 被带有参数的装饰器装饰的函数
@logging('+') # 1 logging("+") 2 @decorator起到装饰器的功能了
def add(a, b):
return a + b


# 执行函数
result = add(1, 2)
print(result) # 3

__call__

1
2
3
4
5
6
7
# 定义一个类,实现__call__方法
class Check(object):
def __call__(self, *args, **kwargs):
print("我是call方法")

c = Check()
c() # 我是call方法

类装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 定义类装饰器
class Check(object):
def __init__(self, fn): # fn = comment
self.__fn = fn

def __call__(self, *args, **kwargs):
print("登陆")
self.__fn() # comment()

# 被装饰的函数
@Check # comment = Check(comment)
def comment():
print("发表评论")

comment()
# 登陆
# 发表评论

单例

单例模式就是对一个类,只获取其唯一的类实例对象,持续复用它。
1、节省内存
2、节省创建对象的开销

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# str_tools_py.py
class StrTools:
pass

str_tool = StrTools()

# 单例测试.py
from str_tools_py import str_tool

s1 = str_tool
s2 = str_tool

print(id(s1)) # 1615164413456 ===> id 函数,返回对象唯一标识
print(id(s2)) # 1615164413456

# 非单例测试.py
class StrTools:
pass

s1 = StrTools()
s2 = StrTools()
print(id(s1)) # 2252236553168
print(id(s2)) # 2252236552400

工厂

将对象的创建由使用原生类本身创建 》》》变为》》》由特定的工厂方法来创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person:
pass
class Worker(Person):
pass
class Student(Person):
pass
class Teacher(Person):
pass
class PersonFactory:
def get_person(self, p_type):
if p_type == 'w':
return Worker()
elif p_type == 's':
return Student()
else:
return Teacher()

factory = PersonFactory()
worker = factory.get_person('w')
student = factory.get_person('s')
teacher = factory.get_person('t')

进程

2023-09-22 17:50:55 补

创建/启动/pid ppid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
"""
导入进程包
import multiprocessing

Process([group [, target [, name [, args [, kwargs]]]]])
group:指定进程组,目前只能使用None
target:表示执行的目标任务名(函数名、方法名)
name:进程名字, 默认是Process-1, ... 从1开始递增的整数
args:以元组方式给执行任务传参
kwargs:以字典方式给执行任务传参

Process创建的实例对象的常用方法:
start():启动子进程实例(创建子进程)
join():等待子进程执行结束
terminate():不管任务是否完成,立即终止子进程

Process创建的实例对象的常用属性:
name:当前进程的别名,默认为Process-N,N为从1开始递增的整数

获取当前进程
multiprocessing.current_process()

获取 pid ppid
import os
os.getpid()
os.getppid()
"""
import multiprocessing
import time
import os


# 跳舞任务
def dance():
print("dance 当前进程编号:", os.getpid()) # 'dance 当前进程编号: 7628'
print("dance 父进程编号:", os.getppid()) # 'dance 父进程编号: 10060'
for i in range(3):
print("跳舞中...")
time.sleep(0.2)


# 唱歌任务
def sing(count):
print("sing 当前进程编号:", os.getpid()) # 'sing 当前进程编号: 22356'
print("sing 父进程编号:", os.getppid()) # 'sing 父进程编号: 10060'
for i in range(count):
print("唱歌中...")
time.sleep(0.2)


if __name__ == '__main__':
print("main 当前进程编号:", os.getpid()) # main 当前进程编号: 10060
print("main 父进程编号:", os.getppid()) # main 父进程编号: 196
print("main:", multiprocessing.current_process()) # 获取当前进程 'main: <_MainProcess name='MainProcess' parent=None started>'

# 无参
dance_process = multiprocessing.Process(target=dance, name="dance_process")

# 有参
# sing_process = multiprocessing.Process(target=sing, args=(5,))
sing_process = multiprocessing.Process(target=sing, kwargs={'count': 3})

# 启动子进程执行对应的任务
dance_process.start()
sing_process.start()

print(dance_process.pid) # 22744
print(sing_process.pid) # 23340

进程不共享全局变量

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

# 定义全局变量
g_list = list()


# 添加数据的任务
def add_data():
global g_list # 无效,即使加上 global,当前线程的修改依然对其他线程不可见
for i in range(3):
g_list.append(i)
print("add:", i)
time.sleep(0.2)

# 代码执行到此,说明数据添加完成
print("add_data:", g_list)


def read_data():
print("read_data", g_list)


if __name__ == '__main__':
# 创建添加数据的子进程
add_data_process = multiprocessing.Process(target=add_data)
# 创建读取数据的子进程
read_data_process = multiprocessing.Process(target=read_data)

# 启动子进程执行对应的任务
add_data_process.start()
# 主进程等待添加数据的子进程执行完成以后程序再继续往下执行,读取数据
add_data_process.join()
read_data_process.start()

print("main:", g_list)

# 结果:
# add: 0
# add: 1
# add: 2
# add_data: [0, 1, 2]
# main: []
# read_data []

# 总结: 多进程之间不共享全局变量
# ===> 每个进程在初始化的时候,都有一份自己的全局变量 g_list = []
# ===> 操作的是自己进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已。

等待所有子进程结束

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
import multiprocessing
import time


# 定义进程所需要执行的任务
def task():
for i in range(10):
print("任务执行中...")
time.sleep(0.2)


if __name__ == '__main__':
# 创建子进程
sub_process = multiprocessing.Process(target=task)
sub_process.start()

# 主进程延时0.5秒钟
time.sleep(0.5)
print("over")

sub_process.terminate() # 让子进程销毁 ===> 子进程打印了两次 就会销毁

exit() # 退出当前进程,主进程什么也不干,等待所有子进程执行完 再一起退出
print('exit') # exit() 后面的代码不会执行

# 总结: 主进程会等待所有的子进程执行完成以后程序再退出

线程

创建/启动/传参

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
"""
进程/线程

2023-09-21 17:19:30 哇!这是我见过最屌的说法了。

1. 进程就是一个程序,运行在操作系统上,程序开始起来就是一个运行进程,并分配唯一的进程ID(process id 简称 pid),方便系统管理。
2. 线程是属于进程的,一个进程可以开启多个线程,每个线程都执行着自己的工作,是进行的实际工作的最小单元。
3. 进程之间内存隔离,即不同进程有各自的内存空间。
4. 线程之间内存共享,线程属于进程的,一个进程内的多个线程之间是共享所拥有的内存空间的。
5. 并行执行:一个线程正在输出:你好 一个线程正在输出:Hello
6. 一个程序在同一时间做两件或多件不同的事情,就是:多线程并行执行

1. 进程就好比于一个公司,公司要运行,就要有面向市场的唯一公司编号,
2. 线程好比于员工,公司要运行,就要有员工,且多个员工共同运行,各自执行着不同的工作。
3. 公司之间办公场所隔离,即不同公司又各自的办公场所。
4. 员工之间办公场所共享,员工属于公司,一个公司的多个员工之间是共享公司的办公地方的。
5. 一个员工正在写 Java,一个员工正在写 Vue
6. 一个公司在同一时间做两件或多件不同的事情,就是:多员工并行执行
"""

import time
import threading

def sing(msg):
print(msg)
time.sleep(1) # 睡眠1秒

def dance(msg):
print(msg)
time.sleep(1) # 睡眠1秒

"""
group => 线程组,暂时无用
target => 方法名
args => 元组方式传参
kwargs => 字典方式传参
name => 线程名,一般不用设置
"""
sing_thread = threading.Thread(target=sing, args=("我要唱歌 汪汪汪",))
dance_thread = threading.Thread(target=dance, kwargs={"msg": "我在跳舞 啦啦啦"})

sing_thread.start()
dance_thread.start()

线程执行无序

线程之间执行是无序的,它是由cpu调度决定的 ,cpu调度哪个线程,哪个线程就先执行,没有调度的线程不能执行。
进程之间执行也是无序的,它是由操作系统调度决定的,操作系统调度哪个进程,哪个进程就先执行,没有调度的进程不能执行。

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

def task():
time.sleep(1)
print("当前线程:", threading.current_thread().name)

if __name__ == '__main__':

for _ in range(5):
sub_thread = threading.Thread(target=task)
sub_thread.start()

# 当前线程: Thread-1 (task)
# 当前线程: Thread-3 (task)
# 当前线程: Thread-2 (task)
# 当前线程: Thread-4 (task)
# 当前线程: Thread-5 (task)

等待所有子线程结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import threading
import time

# 测试主线程是否会等待子线程执行完成以后程序再退出
def show_info():
for i in range(5):
print("test:", i)
time.sleep(0.5)

if __name__ == '__main__':
sub_thread = threading.Thread(target=show_info)
sub_thread.start()

# 主线程延时1秒
time.sleep(1)
print("over")

# test: 0
# test: 1
# over
# test: 2
# test: 3
# test: 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
import threading
import time

# 测试主线程是否会等待子线程执行完成以后程序再退出
def show_info():
for i in range(5):
print("test:", i)
time.sleep(0.5)

if __name__ == '__main__':
# 创建子线程守护主线程
# daemon=True 守护主线程
# 守护主线程方式1
sub_thread = threading.Thread(target=show_info, daemon=True)
# 设置成为守护主线程,主线程退出后子线程直接销毁不再执行子线程的代码
# 守护主线程方式2
# sub_thread.setDaemon(True)
sub_thread.start()

# 主线程延时1秒
time.sleep(1)
print("over")

# test: 0
# test: 1
# test: 2over

共享全局变量

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

my_list = list() # 定义全局变量

# 写入数据任务
def write_data():
for i in range(5):
my_list.append(i)
time.sleep(0.1)
print("write_data:", my_list)

# 读取数据任务
def read_data():
print("read_data:", my_list)

if __name__ == '__main__':
# 创建写入数据的线程
write_thread = threading.Thread(target=write_data)
# 创建读取数据的线程
read_thread = threading.Thread(target=read_data)

write_thread.start()
# 延时
# time.sleep(1)
# 主线程等待写入线程执行完成以后代码在继续往下执行
write_thread.join()
print("开始读取数据啦")
read_thread.start()

# write_data: [0, 1, 2, 3, 4]
# 开始读取数据啦
# read_data: [0, 1, 2, 3, 4]

共享变量:不准确

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

g_num = 0 # 定义全局变量

# 循环一次给全局变量加1
def sum_num1():
for i in range(100_0000):
global g_num
g_num += 1
print("sum1:", g_num)

# 循环一次给全局变量加1
def sum_num2():
for i in range(100_0000):
global g_num
g_num += 1
print("sum2:", g_num)

if __name__ == '__main__':
# 创建两个线程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)

# 启动线程
first_thread.start()
# 启动线程
second_thread.start()

# sum1: 1395007
# sum2: 2000000

共享变量:join

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

g_num = 0 # 定义全局变量

# 循环一次给全局变量加1
def sum_num1():
for i in range(100_0000):
global g_num
g_num += 1

print("sum1:", g_num)

# 循环一次给全局变量加1
def sum_num2():
for i in range(100_0000):
global g_num
g_num += 1
print("sum2:", g_num)

if __name__ == '__main__':
# 创建两个线程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)

# 启动线程
first_thread.start()
first_thread.join()
# 启动线程
second_thread.start()

# sum1: 1000000
# sum2: 2000000

共享变量:lock

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

g_num = 0 # 定义全局变量
lock = threading.Lock() # 创建全局互斥锁

# 循环一次给全局变量加1
def sum_num1():
# 上锁
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1

print("sum1:", g_num)
# 释放锁
lock.release()

# 循环一次给全局变量加1
def sum_num2():
# 上锁
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("sum2:", g_num)
# 释放锁
lock.release()

if __name__ == '__main__':
# 创建两个线程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
# 启动线程
first_thread.start()
second_thread.start()

# sum1: 1000000
# sum2: 2000000

# 提示:加上互斥锁,那个线程抢到这个锁我们决定不了,那线程抢到锁那个线程先执行,没有抢到的线程需要等待
# 加上互斥锁多任务瞬间变成单任务,性能会下降,也就是说同一时刻只能有一个线程去执行

网络编程

socket(套接字;插座;)
socket 是进程之间通信一个工具,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行,进程之间想要进行网络通信需要socket。
下载 netAssist.exe 网络调试助手

请求交互图

在这里插入图片描述

简单的聊天

server 服务器

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

# 创建Socket对象
socket_server = socket.socket()
# 绑定ip地址和端口
socket_server.bind(("localhost", 8888))
# 监听端口
socket_server.listen(1)
# listen方法内接受一个整数传参数,表示接受的链接数量
# 等待客户端链接
# result: tuple = socket_server.accept()
# conn = result[0] # 客户端和服务端的链接对象
# address = result[1] # 客户端的地址信息
conn, address = socket_server.accept()
# accept方法返回的是二元元组(链接对象, 客户端地址信息)
# 可以通过 变量1, 变量2 = socket_server.accept()的形式,直接接受二元元组内的两个元素
# accept()方法,是阻塞的方法,等待客户端的链接,如果没有链接,就卡在这一行不往下执行了

print(f"接收到了客户端的链接,客户端的信息是:{address}")

while True:
# 接受客户端信息,要使用客户端和服务端的本次链接对象,而非socket_server对象
# recv方法 传参是buffsize,缓冲区大小,一般设置1024即可
# recv方法 返回值是字节数组(Bytes),通过decode转换为字符串
data: str = conn.recv(1024).decode("UTF-8")
print(f"客户端发来的消息是:{data}")
# 发送回复消息
msg = input("请输入你要和客户端回复的消息:")
if msg == 'exit':
break
conn.send(msg.encode("UTF-8"))
# 关闭链接
conn.close()
socket_server.close()

"""
# 多线程示例
while True:
conn, address = socket_server.accept()
threading.Thread(target=handle_client_request, args=(conn, address)).start()
"""

client 客户端

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

# 创建socket对象
socket_client = socket.socket()
# 连接到服务端
socket_client.connect(("localhost", 8888))

while True:
# 发送消息
msg = input("请输入要给服务端发送的消息:")
if msg == 'exit':
break
socket_client.send(msg.encode("UTF-8"))
# 接收返回消息
recv_data = socket_client.recv(1024) # 1024是缓冲区的大小,一般1024即可。 同样recv方法是阻塞的
print(f"服务端回复的消息是:{recv_data.decode('UTF-8')}")
# 关闭链接
socket_client.close()

静态服务器:固定页面

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

# 访问 http://127.0.0.1:8080/
if __name__ == '__main__':
# 1.编写一个TCP服务端程序
# 创建socekt
tcp_server_socekt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用 
tcp_server_socekt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定地址
tcp_server_socekt.bind(("", 8080))
# 设置监听
tcp_server_socekt.listen(128)

while True:
# 2.获取浏览器发送的HTTP请求报文数据
# 建立链接
client_socekt, client_addr = tcp_server_socekt.accept()
# 获取浏览器的请求信息
client_request_data = client_socekt.recv(1024).decode()
print(client_request_data)

# 3.读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器
with open("./static/index.html", "rb") as f:
file_data = f.read()

# 应答行
response_line = "HTTP/1.1 200 OK\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = file_data
# 应答数据
response_data = (response_line + response_header + "\r\n").encode() + response_body

client_socekt.send(response_data)

# 4.HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字
client_socekt.close()

静态服务器:指定页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import socket

# 获取用户请求资源的路径
# 根据请求资源的路径,读取指定文件的数据
# 组装指定文件数据的响应报文,发送给浏览器
# 判断请求的文件在服务端不存在,组装404状态的响应报文,发送给浏览器

# http://127.0.0.1:8080/
# http://127.0.0.1:8080/index.html
# http://127.0.0.1:8080/index2.html
# http://127.0.0.1:8080/js/jquery-1.12.4.min.js
# http://127.0.0.1:8080/images/001.jpg
if __name__ == '__main__':
# 1.编写一个TCP服务端程序
# 创建socekt
tcp_server_socekt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用 
tcp_server_socekt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定地址
tcp_server_socekt.bind(("", 8080))
# 设置监听
tcp_server_socekt.listen(128)

while True:
# 2.获取浏览器发送的HTTP请求报文数据
# 建立链接
client_socekt, client_addr = tcp_server_socekt.accept()
# 获取浏览器的请求信息
client_request_data = client_socekt.recv(1024).decode()
print(client_request_data)
# 获取用户请求资源的路径
requst_data = client_request_data.split(" ")
print(requst_data)
# 求资源的路径
request_path = requst_data[1]

if request_path == "/":
request_path = "/index.html"

# 3.读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器
# 根据请求资源的路径,读取指定文件的数据
try:
with open("./static" + request_path, "rb") as f:
file_data = f.read()
except Exception as e:
# 返回404错误数据
# 应答行
response_line = "HTTP/1.1 404 Not Found\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = "404 Not Found sorry"
# 应答数据
# 组装指定文件数据的响应报文,发送给浏览器
response_data = (response_line + response_header + "\r\n" + response_body).encode()

client_socekt.send(response_data)
else:
# 应答行
response_line = "HTTP/1.1 200 OK\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = file_data
# 应答数据
# 组装指定文件数据的响应报文,发送给浏览器
response_data = (response_line + response_header + "\r\n").encode() + response_body

client_socekt.send(response_data)
finally:
# 4.HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字
client_socekt.close()

静态服务器:多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import socket
import threading

# 获取用户请求资源的路径
# 根据请求资源的路径,读取指定文件的数据
# 组装指定文件数据的响应报文,发送给浏览器
# 判断请求的文件在服务端不存在,组装404状态的响应报文,发送给浏览器

def handle_client_request(client_socekt):
# 获取浏览器的请求信息
client_request_data = client_socekt.recv(1024).decode()
print(client_request_data)
# 获取用户请求资源的路径
requst_data = client_request_data.split(" ")
print(requst_data)

# 判断客户端是否关闭
if len(requst_data) == 1:
client_socekt.close()
return
# 求资源的路径
request_path = requst_data[1]

if request_path == "/":
request_path = "/index.html"

# 3.读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器
# 根据请求资源的路径,读取指定文件的数据
try:
with open("./static" + request_path, "rb") as f:
file_data = f.read()
except Exception as e:
# 返回404错误数据
# 应答行
response_line = "HTTP/1.1 404 Not Found\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = "404 Not Found sorry"
# 应答数据
# 组装指定文件数据的响应报文,发送给浏览器
response_data = (response_line + response_header + "\r\n" + response_body).encode()

client_socekt.send(response_data)
else:
# 应答行
response_line = "HTTP/1.1 200 OK\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = file_data
# 应答数据
# 组装指定文件数据的响应报文,发送给浏览器
response_data = (response_line + response_header + "\r\n").encode() + response_body

client_socekt.send(response_data)
finally:
# 4.HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字
client_socekt.close()

if __name__ == '__main__':
# 1.编写一个TCP服务端程序
# 创建socekt
tcp_server_socekt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用 
tcp_server_socekt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定地址
tcp_server_socekt.bind(("", 8080))
# 设置监听
tcp_server_socekt.listen(128)

while True:
# 2.获取浏览器发送的HTTP请求报文数据
# 建立链接
client_socekt, client_addr = tcp_server_socekt.accept()
# 创建子线程
sub_thread = threading.Thread(target=handle_client_request, args=(client_socekt,))
sub_thread.start()

静态服务器:面向对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import socket
import threading

# 获取用户请求资源的路径
# 根据请求资源的路径,读取指定文件的数据
# 组装指定文件数据的响应报文,发送给浏览器
# 判断请求的文件在服务端不存在,组装404状态的响应报文,发送给浏览器
class HttpWebServer:
def __init__(self):
# 1.编写一个TCP服务端程序
# 创建socekt
self.tcp_server_socekt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用 
self.tcp_server_socekt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定地址
self.tcp_server_socekt.bind(("", 8080))
# 设置监听
self.tcp_server_socekt.listen(128)

def handle_client_request(self, client_socekt):
# 获取浏览器的请求信息
client_request_data = client_socekt.recv(1024).decode()
print(client_request_data)
# 获取用户请求资源的路径
requst_data = client_request_data.split(" ")
print(requst_data)

# 判断客户端是否关闭
if len(requst_data) == 1:
client_socekt.close()
return
# 求资源的路径
request_path = requst_data[1]

if request_path == "/":
request_path = "/index.html"

# 3.读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器
# 根据请求资源的路径,读取指定文件的数据
try:
with open("./static" + request_path, "rb") as f:
file_data = f.read()
except Exception as e:
# 返回404错误数据
# 应答行
response_line = "HTTP/1.1 404 Not Found\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = "404 Not Found sorry"
# 应答数据
# 组装指定文件数据的响应报文,发送给浏览器
response_data = (response_line + response_header + "\r\n" + response_body).encode()

client_socekt.send(response_data)
else:
# 应答行
response_line = "HTTP/1.1 200 OK\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = file_data
# 应答数据
# 组装指定文件数据的响应报文,发送给浏览器
response_data = (response_line + response_header + "\r\n").encode() + response_body

client_socekt.send(response_data)
finally:
# 4.HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字
client_socekt.close()

def start(self):
while True:
# 2.获取浏览器发送的HTTP请求报文数据
# 建立链接
client_socekt, client_addr = self.tcp_server_socekt.accept()
# 创建子线程
sub_thread = threading.Thread(target=self.handle_client_request, args=(client_socekt,))
sub_thread.start()


if __name__ == '__main__':
# 创建服务器对象
my_web_server = HttpWebServer()
# 启动服务器
my_web_server.start()

静态服务器:指定端口号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import socket
import threading
import sys

# 获取用户请求资源的路径
# 根据请求资源的路径,读取指定文件的数据
# 组装指定文件数据的响应报文,发送给浏览器
# 判断请求的文件在服务端不存在,组装404状态的响应报文,发送给浏览器
#
# 把此文件 和 static 放入 Linux 服务器上,执行如下命令
# 命令“python3 xxx.py 12345”
class HttpWebServer:
def __init__(self, port):
# 1.编写一个TCP服务端程序
# 创建socekt
self.tcp_server_socekt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用 
self.tcp_server_socekt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定地址
self.tcp_server_socekt.bind(("", port))
# 设置监听
self.tcp_server_socekt.listen(128)

def handle_client_request(self, client_socekt):
# 获取浏览器的请求信息
client_request_data = client_socekt.recv(1024).decode()
print(client_request_data)
# 获取用户请求资源的路径
requst_data = client_request_data.split(" ")
print(requst_data)

# 判断客户端是否关闭
if len(requst_data) == 1:
client_socekt.close()
return
# 求资源的路径
request_path = requst_data[1]

if request_path == "/":
request_path = "/index.html"

# 3.读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器
# 根据请求资源的路径,读取指定文件的数据
try:
with open("./static" + request_path, "rb") as f:
file_data = f.read()
except Exception as e:
# 返回404错误数据
# 应答行
response_line = "HTTP/1.1 404 Not Found\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = "404 Not Found sorry"
# 应答数据
# 组装指定文件数据的响应报文,发送给浏览器
response_data = (response_line + response_header + "\r\n" + response_body).encode()

client_socekt.send(response_data)
else:
# 应答行
response_line = "HTTP/1.1 200 OK\r\n"
# 应答头
response_header = "Server:pwb\r\n"
# 应答体
response_body = file_data
# 应答数据
# 组装指定文件数据的响应报文,发送给浏览器
response_data = (response_line + response_header + "\r\n").encode() + response_body

client_socekt.send(response_data)
finally:
# 4.HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字
client_socekt.close()

def start(self):
while True:
# 2.获取浏览器发送的HTTP请求报文数据
# 建立链接
client_socekt, client_addr = self.tcp_server_socekt.accept()
# 创建子线程
sub_thread = threading.Thread(target=self.handle_client_request, args=(client_socekt,))
sub_thread.start()


def main():
# 获取执行python程序的终端命令行参数
print(sys.argv)
if len(sys.argv) != 2:
print("格式错误 python3 xxx.py 9090")
return
# 判断参数的类型,设置端口号必须是整型
if not sys.argv[1].isdigit():
print("格式错误 python3 xxx.py 9090")
return
port = int(sys.argv[1])
# 创建服务器对象
# 给Web服务器类的初始化方法添加一个端口号参数,用于绑定端口号
my_web_server = HttpWebServer(port)
# 启动服务器
my_web_server.start()


if __name__ == '__main__':
main()

正则表达式

正则表达式,又称规则表达式(Regular Expression)

基础

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

s = "1python taopanfeng python python"
# match 从头匹配
result = re.match("python", s)
print(result) # None ===> 不是以 'python' 开头

result = re.match("1python", s)
print(result) # None ===> 不是以 'python' 开头
print(result.span()) # (0, 7) 不包含下标7
print(result.group()) # '1python'

# search 搜索匹配
result = re.search("python", s)
print(result.span()) # (1, 7) 不包含下标7
print(result.group()) # 'python'

# findall 搜索全部匹配
result = re.findall("python", s)
print(result) # ['python', 'python', 'python']

进阶

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import re

s = "tpf1 @@python2 !!666 ##kv3"

# 找出全部数字 => 二者等同 推荐 r 写法
print(re.findall('\\d', s)) # ['1', '2', '6', '6', '6', '3']
print(re.findall(r'\d', s)) # ['1', '2', '6', '6', '6', '3']

# 找出特殊字符
print(re.findall(r'\W', s)) # [' ', '@', '@', ' ', '!', '!', ' ', '#', '#']

# 找出全部英文字母
print(re.findall(r'[a-zA-Z]', s)) # ['t', 'p', 'f', 'p', 'y', 't', 'h', 'o', 'n', 'k', 'v']

# 找出含有 tp字母 16数字
print(re.findall(r'[tp16]', s)) # ['t', 'p', '1', 'p', 't', '6', '6', '6']

# 匹配账号,只能由字母和数字组成,长度限制6到10位 规则为: ^[0-9a-zA-Z]{6, 10}$
#
# 匹配QQ号,要求纯数字,长度5-11,第一位不为0 规则为:^[1-9][0-9]{4, 10}&
# [1-9]匹配第一位,[0-9]匹配后面4到10位
#
# 匹配邮箱地址,只允许qq、163、gmail这三种邮箱地址 规则为:^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+&
# 1. [\w-]+ 表示出现 a-z A-Z 0-9 _ - 字符最少一个,最多不限
# 2. (\.[\w-]+)*,表示出现组合 . 和 a-z A-Z 0-9 _ - 的组合最少0次,最多不限
# 用于匹配:abc.ced.efg@123.com中的ced.efg这部分
# 3. @表示匹配@符号
# 4. (qq|163|gmail) 表示只匹配这3个邮箱提供商
# 5. (\.[\w-]+)+ 同上面2.,这里 + 标识表示最少1次,最多不限
# 用于匹配abc.ced.efg@123.com.cn中的.com.cn这种
# 最后使用+表示最少一次,如:.com 多次可以是:.com.cn.eu这样
# 【日期标记】2023-09-21 18:58:02 以上同步完成


# 3需求:匹配qq:10567这样的数据,提取出来qq文字和qq号码
# group(0)代表的是匹配的所有数据 1:第一个分组的数据 2:第二个分组的数据 顺序是从左到右依次排序的
result = re.match("(qq):([1-9]\d{4,11})", "qq:10567")
if result:
info = result.group(0) # 等同于 result.group()
print(info) # 'qq:10567'

type = result.group(1) # 第一个小括号
print(type) # 'qq'

num = result.group(2) # 第二个小括号
print(num) # '10567'
else:
print("匹配失败")


# 4需求:匹配出<html>hh</html>
# \num 引用分组num匹配到的字符串
result = re.match("<([a-zA-Z1-6]{4})>.*</\\1>", "<html>hh</html>")
if result:
info = result.group()
print(info) # '<html>hh</html>'
else:
print("匹配失败")


# 5需求:匹配出<html><h1>www.taopanfeng.com</h1></html>
result = re.match(
"<([a-zA-Z1-6]{4})><([a-zA-Z1-6]{2})>.*</\\2></\\1>",
"<html><h1>www.taopanfeng.com</h1></html>"
)
if result:
info = result.group()
print(info) # '<html><h1>www.taopanfeng.com</h1></html>'
else:
print("匹配失败")


# 6需求:匹配出<html><h1>www.taopanfeng.com</h1></html>
# (?P<name>) 分组起别名
# (?P=name) 引用别名为name分组匹配到的字符串
result = re.match(
"<(?P<html>[a-zA-Z1-6]{4})><(?P<h1>[a-zA-Z1-6]{2})>.*</(?P=h1)></(?P=html)>",
"<html><h1>www.taopanfeng.com</h1></html>"
)
if result:
info = result.group()
print(info) # <html><h1>www.taopanfeng.com</h1></html>
else:
print("匹配失败")


# 取出姓陶的人名,且不为 '陶攀' '陶瑞' 开头
result = re.findall(
"陶[^攀瑞陶]{1,2}",
"陶攀峰陶奥龙陶长城陶熠陶瑞陶奥林"
)
print(result) # ['陶奥龙', '陶长城', '陶熠', '陶奥林']


# \b 单词边界
# \b匹配一个单词边界。匹配这样一个位置:前面字符和后面字符不全是\w。也就是\b前后都是\w就不匹配。
# 'abcd\b' 例如一个字符串是 'abcd12 abcd123 abcd aa'
# 此时的\b前面是d,因为\b前后都是\w就不匹配,也就是说如果此时\b后面是\w就不匹配。
# 第一个字符串abcd12这里的\b后面是1,此时两边都是\w不符合。同理abcd123也不符合。
# 第三个abcd是符合的。因为\b后面是空白符号,不属于\w。所以匹配的最终结果高亮显示:['abcd']
# result = re.findall(
# "abcd\\b",
# "abcd12 abcd123 abcd aa"
# )
# print(result) # ['abcd']


# 前后断言
# (?=exp)后面能匹配的表达式。
# (?!exp)后面不能匹配的表达式。
# (?<=exp)前面能匹配的表达式。
# (?<!exp)前面不能匹配的表达式。
result = re.findall(
"[a-z]+(?=\\d+)", # 匹配连续的字母,但后面必须有数字
"ab a3b abb6de ab34cae24e3e 898pzaj97"
)
print(result) # ['a', 'abb', 'ab', 'cae', 'e', 'pzaj']

property属性

装饰器方式

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

@property # 获取属性
def age(self):
return self.__age

@age.setter # 修改属性
def age(self, new_age):
self.__age = new_age

p = Person()
print(p.age) # 0

p.age = 100
print(p.age) # 100

类属性方式

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

def get_age(self):
return self.__age

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

age = property(get_age, set_age)

p = Person()
print(p.age) # 0

p.age = 100
print(p.age) # 100

上下文管理器

with关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# with关键字
文件操作的时候使用with语句可以自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作。
在上面“文件”小结已经讲到,这里再补充一下
# with open ===> 自动执行close()
with open('a.txt', 'r') as f:
f.readlines()

# with 原理
with背后是由上下文管理器做支撑的
也就是说刚才使用 f = open('XXX') 中 open函数 创建的 f 文件对象 就是一个上下文管理器对象

# 什么是上下文管理器?
一个类只要实现了__enter__()和__exit__()这个两个方法
通过该类创建的对象我们就称之为上下文管理器

上下文管理器

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
# 1定义一个File类
class File(object):
def __init__(self, file_name, file_model):
self.file_name = file_name
self.file_model = file_model

# 2实现方法
# __enter__表示上文方法,需要返回一个操作文件对象
# __exit__表示下文方法,with语句执行完成会自动执行,即使出现异常也会执行该方法
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()


# 3然后使用 with 语句来完成操作文件
with File("1.txt", "r") as f:
file_data = f.read()
print(file_data)
# 这是上文
# 123
# 这是下文

生成器

每次调用生成器只生成一个值,可以节省大量内存。

生成器推导式

小括号 => 生成器推导式

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
my_list = [1, 2, 3, 4, 5]

# 小括号 => 生成器推导式
my_generator = (e * 2 for e in my_list)
print(my_generator) # <generator object <genexpr> at 0x000001FDE706C2B0>
print(type(my_generator)) # <class 'generator'>
print('------')

# next 函数获取生成器中的下一个值
# next(生成器) 等同于 生成器.__next__()
print(next(my_generator)) # 2
print(next(my_generator)) # 4
print(next(my_generator)) # 6
# print(my_generator.__next__()) # 2
# print(my_generator.__next__()) # 4
# print(my_generator.__next__()) # 6
print('------')

# for 循环遍历生成器中的每一个值
# 可以for循环 => 不可以 while循环,无法下标访问
for e in my_generator:
print(e) # 依次打印 8 10 (上面三次被 next 拿出了)

# 可以转为其他容器
print(list(my_generator)) # [2, 4, 6, 8, 10]

# 中括号 => 列表推导式【之前有讲到 '容器' => '转换/排序'】
my_generator = [e * 2 for e in my_list]
print(my_generator) # [2, 4, 6, 8, 10]
print(type(my_generator)) # <class 'list'>

yield关键字

def函数中具有yield关键字

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
def generater(num):
for i in range(num):
print("start")
yield i
print("end")

# 1. 手动 next
g = generater(3)
print(next(g)) # start 0
print(next(g)) # end start 1
print(next(g)) # end start 2
# print(next(g)) # end => 报错 StopIteration
print('--------')

# 2. for(推荐)
g = generater(3)
for i in g:
print(i) # start 0 end start 1 end start 2 end
print('--------')

# 3. while True
g = generater(3)
while True:
try:
print(next(g)) # start 0 end start 1 end start 2 end
except StopIteration as e:
break

"""
① 代码执行到 yield 会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
② 生成器如果把数据生成完成,再次获取生成器中的下一个数据会抛出一个StopIteration 异常,表示停止迭代异常
③ while 循环内部没有处理异常操作,需要手动添加处理异常操作(while 本质还是 next(生成器))
④ for 循环内部自动处理了停止迭代异常,使用起来更加方便,推荐大家使用。
"""

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

# 浅拷贝 target = copy.copy(source)
# source 为不可变类型 => 例如:元组 则 target = source
# source 为可变类型 => 例如:列表 则 target 指向新列表,但二者元素一致
# 深拷贝 target = copy.deepcopy(source)
# 同浅拷贝(但,可变类型,不同对象,元素也不一致)
a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]
d = c
e = copy.copy(c) #
c2 = (a, b)
e2 = copy.copy(c2)

print(id(a)) # 1461043910080
print(id(b)) # 1461043817024
print(id(c)) # 1461043817408
print(id(d)) # 1461043817408
print(id(e)) # 1461043905024
print(id(c[0])) # 1461043910080
print(id(e[0])) # 1461043910080
print(id(c2)) # 1461043906176
print(id(e2)) # 1461043906176

logging 日志

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

"""
日志等级说明:
DEBUG:程序调试bug时使用
INFO:程序正常运行时使用
WARNING:程序未按预期运行时使用,但并不是错误,如:用户登录密码错误
ERROR:程序出错误时使用,如:IO操作失败
CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用

logging日志等级和输出格式的设置
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(filename)s [line:%(lineno)d] - %(levelname)s: %(message)s'
)
%(levelname)s: 打印日志级别名称
%(filename)s: 打印当前执行程序名
%(lineno)d: 打印日志的当前行号
%(asctime)s: 打印日志的时间
%(message)s: 打印日志信息
"""
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(filename)s [line:%(lineno)d] - %(levelname)s: %(message)s',
# filename='log.txt', # 当前py文件同级目录下生成 (设置后,PyCharm控制台不会显示)
# filemode='a', # 'a'追加 'w'覆盖
# encoding='utf-8'
)

logging.debug('这是一个debug级别日志')
logging.info('这是一个info级别日志')
logging.warning('这是一个warning级别日志')
logging.error('这是一个error级别日志')
logging.critical('这是一个critical级别日志')

关键字

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

class def pass lambda return None

global ===> 在函数内部访问全局变量
nonlocal ===> 在嵌套函数中访问父级函数的变量

with ===> 创建一个上下文管理器

del ===> 删除一个变量或对象

while for continue break yield

if elif else and not or True False

try except finally raise ===> 用于引发异常

in ===> 判断一个成员是否属于一个序列
is ===> 这个关键字用于检查两个对象是否相等
assert ===> 检查一个表达式的真假

奇奇怪怪

1、1.0、’1’、True 比较

1
2
3
4
5
6
7
8
9
a = 1  # <class 'int'>
b = 1.0 # <class 'float'>
c = '1' # <class 'str'>
d = True # <class 'bool'>
print(a == b) # True
print(a == c) # False
print(a == d) # True
print(b == d) # True
print(c == d) # False

元组 不能修改

1
2
3
t = (11, 22, 33)  # <class 'tuple'>
print(t) # (11, 22, 33)
# t.append(44) # AttributeError: 'tuple' object has no attribute 'append'

input 返回 str

1
2
3
x = input('请输入:')
print(x) # 12
print(type(x)) # <class 'str'>

多变量赋值

先旧值计算右,再右赋值左

1
2
3
4
5
6
7
8
9
10
11
x = y = 10
x, y, z = y + 6, x + 1, x + 2
print(x, y, z) # 16 11 12

# 可以理解为:
# 1、计算赋值给临时变量
# 2、临时变量赋值给左侧变量
x = y = 10
tmp_x, tmp_y, tmp_z = y + 6, x + 1, x + 2
x, y, z = tmp_x, tmp_y, tmp_z
print(x, y, z) # 16 11 12

布尔运算问题

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
# 数值与布尔 布尔运算    数值转化(非零True 零False)
print(1 and True) # True
print(5 and True) # True
print(-5 and True) # True
print(0 and True) # 0
print(0 or True) # True
print(not 5) # False
print(not 0) # True



# 数值与数值 布尔运算
# 结果True --- and运算返回最后1个,or运算返回第一个True的数值
# 结果False --- 返回0
print(1 and 5) # 5
print(1 and 0) # 0
print(0 and 1) # 0
print(0 or 9) # 9
print(1 or 8) # 1



# 数值与布尔 ==比较 True=1 False=0
print(1 == True) # True
print(5 == True) # False
print(0 == True) # False
print(0 == False) # True



# 多数值比较
print(3 > 2 > 2) # False
print(3 > 2 > 1) # True
# print(5 + 4j > 2 - 3j) # TypeError: '>' not supported between instances of 'complex' and 'complex'

错误

执行两次的错误

2024-11-18

1
2
3
4
5
6
7
8
9
10
11
# numpy.py

import numpy as np

print("Running script:", __name__)
# Running script: numpy
# Running script: __main

# 原因分析:
# 这里导入的是自己的包,而不是系统自带的
# 避免文件名与导入的名称一致