Python 常用内置模块

前言

在开发程序的过程中,无法避免模块的使用,Python具备丰富强大的内置模块及第三方模块,拿来即可使用,且拥有文档说明方便使用者更加了解,熟练的使用模块能够更快提升我们的开发效率,下面来介绍Python中的一些常用模块


JSON 格式转换模块


JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写。

JSON格式的用途

1、存取数据(标准格式)一个程序写入,另一个程序可以读入(两个程序可以是不同的编程语言)这一点说明的JSON格式的通用性
2、后端给前端传递的数据就是JSON格式

查看了解一段JSON格式数据内容,和我们Python字典类似:
在这里插入图片描述
通过下面的了解,我们将上面内容转换成我们Python可用的数据类型


序列化与反序列化

使用JSON需要导入json模块

import json

json两个转换格式的函数

json.dumps()将Python类型序列化成JSON格式字符串
json.loads()将JSON格式字符串反序列化成Python类型

json.dumps()

使用json模块将Python内容序列化

import json # 内置模块
dic = {'name':'jack','age':18,'bl':True,'x':None}

dic_str = json.dumps(dic) # 序列化,python数据类型转换成JSON字符串类型
print(dic_str)

以上输出结果

{"name": "jack", "age": 18, "gen": true, "x": null}
# 仔细观察可以发现,JSON格式内对应的key都是双引号

Python类型转换成JSON格式的对比表

PythonJSON
dictobject(对象)
list, tuplearray(数组)
strstring(字符串)
in,floatnumber(数字)
Truetrue(布尔)
Falsefalse(布尔)
Nonenull(空)

我们还可以将多个json格式内容序列化出来

dic = [
{'name':'jack','age':18,'gen':True,'x':None},
{'name':'tom','age':20,'gen':True,'xx':None}
]

dic_json = json.dumps(dic)
print(dic_json)

以上输出结果:

[
{"name": "jack", "age": 18, "gen": true, "x": null}, 
{"name": "tom", "age": 20, "gen": true, "xx": null}
]

json.loads()

将JSON格式字符串反序列化成Python数据类型

dic_str = '{"name": "jack", "age": 18, "gen": true, "x": null}'
str_dic = json.loads(dic_str) # 反序列化,转换成对应的数据类型
print(str_dic)

以上输出结果

{'name': 'jack', 'age': 18, 'gen': True, 'x': None}

方便在于,我们将数据以json格式存入字符串,那么我们取数据时就不会造成可以以字典类型存入,取出时成了字符串。

可以将多个json格式反序列化出来

res = '''[
{"name": "jack", "age": 18, "gen": true, "x": null},
{"name": "tom", "age": 20, "gen": true, "xx": null}
]'''

dic_json = json.loads(res)
print(dic_json)

以上输出结果

[
{'name': 'jack', 'age': 18, 'gen': True, 'x': None},
{'name': 'tom', 'age': 20, 'gen': True, 'xx': None}
]

需要分享一个小问题,最近写的ATM小项目,存入文件时并未使用json格式,文件存储时是dic类型,而读出来是却是str…
在这里插入图片描述
在这里插入图片描述
裂开,需要通过eval()函数来返回传入值的数据类型,慎用!方便也有危险,请自行百度。


JSON格式内容转换实例

我们将上序的JSON内容拿到,反序列后我们就其的详细数据

res = '''
	{
	"code":200,
	"msg":"",
	"data":[
	        {
	        "adPlanName":"\u683C\u74E6\u62C9PC\u7AEFbanner","positionId":1275,"adVOList":[],"type":1
	        }
	    ],
	"paging":null,
	"attrMaps":
	    {"serverTime":1608002698949},
	"success":true}
''' # 我们将其放入多行字符串内容

dic_json = json.loads(res)
print(dic_json)

以上执行结果

{'code': 200, 'msg': '', 'data': [{'adPlanName': '格瓦拉PC端banner', 'positionId': 1275, 'adVOList': [], 'type': 1}], 'paging': None, 'attrMaps': {'serverTime': 1608002698949}, 'success': True}

我们可以根据key值拿到对应的value,比如:data
data = dic_json['data']
print(data)
> [{'adPlanName': '格瓦拉PC端banner', 'positionId': 1275, 'adVOList': [], 'type': 1}]

JSON数据类型最为常见,因为在开发中大部分数据是通过JSON方式传输的,那么我们拿到这种JSON格式能够将它转换出来即可


使用json模块提供的另外两个函数,可以将内容序列化后存入文件

json.dump序列化内容,加上文件句柄将序列化后的内容存入内存
json.load放入文件句柄,将文件内的json内容序列化后读出来

json.dump()

既然存放格式为json,那么我们定义文件后缀也应该为json更利于识别

dic = {'name':'jack','age':18,'gen':True,'x':None}
with open('test.json','wt',encoding='utf-8') as f:
    json.dump(dic,f) # 转换的内容,后面跟文件句柄

以上执行结果
在这里插入图片描述


json.load()

将文件内容反序列化

with open('test.json','rt',encoding='utf-8') as f:
    dic = json.load(f) # 反序列化文件内容,赋值给变量
    print(dic)
    print(type(dic))

以上执行结果

{'name': 'jack', 'age': 18, 'gen': True, 'x': None}
<class 'dict'> # 可以看到,我们存入时为dict类型,通过反序列后拿到的也是dict类型

json补充说明

补充一点小细节:当我们当我们将Python内容序列化成JSON格式时,如果内容存在中文那么显示会出现一点小问题

dic = {'name':'杰克','age':18,'gen':True,'x':None}
dic_json = json.dumps(dic)
print(dic_json)

以上输出结果

{"name": "\u6770\u514b", "age": 18, "gen": true, "x": null}

虽说会这样,但我们反序列化后还是可以拿到我们的原内容

print(json.loads(dic_json))
{'name': '杰克', 'age': 18, 'gen': True, 'x': None}

如果要避免这个显示问题的出现,那么我们在序列化时添加一些参数即可

dic = {'name':'杰克','age':18,'gen':True,'x':None}
dic_json = json.dumps(dic,ensure_ascii=False)
print(dic_json)

以上输出结果

{"name": "杰克", "age": 18, "gen": true, "x": null}

json是一个即强大又简单的模块,通过它存入后的数据,当其它编程语言需要读取时,使用其内置库即可进行反序列化,可以做到程序之间数据共享。


JSON快捷预览并分支

json_url:https://www.json.cn/
将一大堆JSON数据放到此页面,可以按层次展示出来,
在这里插入图片描述
熟练JSON解析,可以在后续提高我们快速解析JSON数据,从而提升开发效率


Pickle 格式转换模块


Python内置数据转换的模块,使用方法与json模块类似。

pickle序列化后的内容,只能作用与Python


pickle.dumps()

序列化Python数据

import pickle
dic = {'name':'jack','age':18}
res = pickle.dumps(dic)
print(res) # 返回的是Byte类型,阅读时无法看懂,但是并无问题

以上输出结果

b'\x80\x04\x95\x1b\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x04jack\x94\x8c\x03age\x94K\x12u.'

pickle.loads()

反序列化pickle

dic = b'\x80\x04\x95\x1b\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x04jack\x94\x8c\x03age\x94K\x12u.'

res = pickle.loads(dic) # 反序列化内容
print(res) # 打印时,还是存储时的数据类型
print(type(res))

以上输出结果

print(res) # 打印时,还是存储时的数据类型
print(type(res))

可以看到并无任何区别,那为何说这个模块,因为这个模块可以序列化Python里面任意数据,实例:

import json
import pickle

dic = {'name':'jack','age':input} # 将input函数放入dict
res = pickle.dumps(dic) # 正常转换,无任何问题

res = json.dumps(dic) # 报错,python内置函数不是json可序列化的

pickle.dump()

本质上就是先进行dumps()后再write写入文件,dump进行了二合一序列化后的内容存入文件,需要注意的是,序列化后的内容就成了二进制(Bytes)类型,那么我们写入文件需要改成b模式写入

dic = {'name':'jack','age':18,'inp':input}
with open('test.txt','wb') as f:
    pickle.dump(dic,f) # 转换的内容,后面跟文件句柄

pickle.load()

读取文件内pickle序列化后存入的内容,注意:b模式读取

with open('test.json','rb') as f:
    dic = pickle.load(f) # 反序列化文件内容,赋值给变量
    print(dic)

以上执行结果

{'name': 'jack', 'age': 18, 'inp': <built-in function input>}

我们直接看文件内容是看不懂的,因为存入的是二进制,那么我们是否可以通过decode来解码?答案是不行的,因为我们存入时并没有指定任何编码,是以pickle序列后的格式传入的。

总结:JSON与Pickle的区别

JSON通用格式,任何语言都可以解析,但是可转换成JSON格式的数据类型有限
Pickle只有Python能够使用,转换成b格式,其它语言识别不了,任意数据类型都可以转换

使用json或是pickle根据自身需求决定,json格式数据大部分编程语言都可以转换,Pickle只支持Python内的数据存储


logging日志模块

由于日志模块内容过长,已记录到另一篇博客:logging模块的基本使用


time模块 时间与日期模块


时间戳(timestamp):表示从1970年1月1日 0:0:00(Unix元年) 开始计算,到现在经过的秒数,包括毫秒

import time

print(time.time())

执行结果

1610088461.260176

不过我们最常用此方法是用于计时时间


计时时间

import time

start = time.time()

time.sleep(3.52) # 睡眠3.52秒

end = time.time()

print(round(end - start,2)) # 打印时保留2位小数

执行结果

3.52

格式化的时间字符串(format string):以字符串的形式格式化时间

import time

format_time = time.strftime('%Y-%m-%d %H:%M:%S')
print(format_time,type(format_time))

format_time = time.strftime('%Y-%m-%d %X') # 与上面效果相同

执行结果

2021-01-08 15:41:42 <class 'str'>
2021-01-08 15:41:42

结构化的时间(struct time):struct_time元组共有9个元素共九个元素,分别为(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)

import time

print(time.localtime()) # 本地时间
print(time.gmtime()) 	# utc:协调世界时(与本地时间相差8小时)

执行结果

time.struct_time(tm_year=2021, tm_mon=1, tm_mday=8, tm_hour=15, tm_min=4, tm_sec=39, tm_wday=4, tm_yday=8, tm_isdst=0)
time.struct_time(tm_year=2021, tm_mon=1, tm_mday=8, tm_hour=7, tm_min=4, tm_sec=39, tm_wday=4, tm_yday=8, tm_isdst=0)

或者跟上参数,只显示某一个

import time

print(time.localtime().tm_hour)
print(time.gmtime().tm_hour)

执行结果

15
7

也可以将时间调回到元年时间

import time

print(time.localtime(0))
print(time.localtime(60 * 60 * 24 * 365)) # 在元年的时间上增加一年时间 

执行结果

time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)
time.struct_time(tm_year=1971, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=1, tm_isdst=0)

不同格式时间的转换

在这里插入图片描述

如上图所示:time模块提供了方法可以让以上3个不同形式的时间来回转换

结构化时间 与 格式化时间的转换

格式化 转 结构化

import time

format_time = time.strftime('%Y-%m-%d %H:%M:%S')

struce_time = time.strptime(format_time,'%Y-%m-%d %H:%M:%S')
print(struce_time)

执行结果

time.struct_time(tm_year=2021, tm_mon=1, tm_mday=8, tm_hour=15, tm_min=42, tm_sec=13, tm_wday=4, tm_yday=8, tm_isdst=-1)

结构化 转 格式化

import time

struce_time = time.localtime()

format_time = time.strftime('%Y-%m-%d %H:%M:%S',struce_time)
print(format_time)

执行结果

2021-01-08 15:46:11

时间戳 与 结构化时间的转换

时间戳 转 结构化

import time

ntime = time.time()

struce_time = time.localtime()

print(time.localtime(ntime)) 	# 本地时间
print(time.gmtime(ntime)) 		# utc时间结构

执行结果

time.struct_time(tm_year=2021, tm_mon=1, tm_mday=8, tm_hour=15, tm_min=48, tm_sec=50, tm_wday=4, tm_yday=8, tm_isdst=0)
time.struct_time(tm_year=2021, tm_mon=1, tm_mday=8, tm_hour=7, tm_min=48, tm_sec=50, tm_wday=4, tm_yday=8, tm_isdst=0)

结构化 转 时间戳

import time

struce_time = time.localtime()

ntime = time.mktime(struce_time)
print(ntime)

执行结果

1610092241.0

时间与日月

import time

print(time.asctime())
print(time.ctime()) # 效果一致

执行结果

Fri Jan  8 15:53:26 2021
Fri Jan  8 15:53:26 2021

小结:定义一个电子时间,执行后走一秒钟

import time
class Digital_Clcok:
    def __init__(self, hour, minute, second):
        if not isinstance(hour, int) or hour < 0 or hour > 23:
            raise Exception('时钟输入不合法!应该为数字:0-23之间')

        elif not isinstance(minute, int) or minute < 0 or minute > 59:
            raise Exception('分钟输入不合法!应该为数字:0-59之间')

        elif not isinstance(second, int) or second < 0 or second > 59:
            raise Exception('秒钟输入不合法!应该为数字:0-59之间')
		
		# 自定义日期,不自定义的话默认1900年,因为win不支持1970之前
        self.time = f'2020-1-1 {hour}:{minute}:{second}' 
        self.struce_time = time.strptime(self.time,'%Y-%m-%d %H:%M:%S')

    # 运行时间——秒钟+1
    def run_time(self):
        # 结构转时间戳+1秒,再时间戳转结构
        self.stamp = time.mktime(self.struce_time) + 1
        self.struce_time = time.localtime(self.stamp)

    # 显示时间方法
    def show_time(self):
    	# 打印结构转格式化内容
        print(time.strftime('%H:%M:%S',self.struce_time)) 


clock = Digital_Clcok(23, 59, 58)

clock.run_time()
clock.show_time()

clock.run_time()
clock.show_time()

执行结果

'当前时间:23:59:59'
'当前时间:0:0:0'

datetime 日期时间模块


此模块可以对时间进行增加或减去

返回当前时间

import datetime

print(datetime.datetime.now())

执行结果

2021-01-08 16:04:15.827303

返回当前日期

import datetime
import time

print(datetime.date.fromtimestamp(time.time()))

执行结果

2021-01-08

当前时间增加3天

import datetime

print(datetime.datetime.now() + datetime.timedelta(3))

执行结果

2021-01-11 16:07:10.731612

当前时间减去3天

import datetime

print(datetime.datetime.now() + datetime.timedelta(-3))

执行结果

2021-01-05 16:07:46.430545

当前时间增加5小时

import datetime

print(datetime.datetime.now() + datetime.timedelta(hours=5))

执行结果

2021-01-08 21:09:02.505997

当前时间增加30分钟

import datetime

print(datetime.datetime.now() + datetime.timedelta(minutes=30))

执行结果

2021-01-08 16:40:01.410944

替换当前时间替换

import datetime

n_time = datetime.datetime.now()

print(n_time.replace(year=2020,month=1,day=1,hour=20,minute=35,second=30))
		 # 范围
# year年:1-9999
# month月份:1-12
# hour小时:0-23
# minute分钟:0-59
# second秒:0-59

执行结果

2020-01-01 20:35:30.290053

random 模块


该模块特别常见,常用可以用于产生随机数,获取随机元素,或者打乱顺序等等

1、随机数生成

import random

print(random.random())  # 随机生成0-1之间的浮点数
# > 0.8445533671649157
print(int(random.random() * 100))  # 随机生成99以内的整数
# > 48

print(random.randint(1,10)) # 随机生成1-10之间的整数,包含10
# > 5

print(random.randrange(1,10)) # 随机生成1-9之间的数字,不包含10
# > 9

# 这种方式支持反着写,效果一致,但是上序不可以这样写
print(random.uniform(1,2)) # 生成1-2之间的浮点数
# > 1.836761736865789
print(random.uniform(4,2)) # 生成4-2之间的浮点数
# > 3.081370588290436

2、获取随机元素

import random

l = [1,'a',2,'b',3,'c']
print(random.choice(l)) # 传入可迭代类型,随机获取其中的一个元素
# > 3

print(random.sample(l,2))# 传入可迭代类型,可指定获取随机获取元素的个数,但是不能超过传入值的长度,获取到的元素会转成列表
# > ['c','a']

3、列表顺序打乱

import random

l = [1,'a',2,'b',3,'c']
random.shuffle(l)
print(l)
# > ['b', 'a', 1, 'c', 2, 3]

生成随机验证码

利用random模块帮助我们生成5位数的随机验证码,其中包括大写字母、小写字母、数字的集合体

我们可以将数字通过ASCII码表转换成字母,这样更快于我们达到目的

在这里插入图片描述
图片来源于百度百科,实例:

import random

code = '' # 验证码存储
for i in range(5):
	# 97-122之间的数字转换后都是小写字母,我们通过chr将数字转换成对应的英文
    small_ = chr(random.randint(97,122)) 

	# 65-80之间的数字转换后都是大写字母
    big_ = chr(random.randint(65,90))

	# 数字我们随机生成就可以了
    num = str(random.randint(0,9))

	# 每次在它们之间抽选出一个,总共循环5次抽出5个
    code += random.choice([small_,big_,num])

print(f'随机生成5位数验证码:{code}')

执行结果

'随机生成5位数验证码:6RdIN'

os 系统模块


os模块具备了操作系统接口,如:删除、创建、修改文件等等,以及对路径的一些操作,执行系统命令等…

笔者把认为比较常用的进行了加粗显示

os.name获取当前操作系统 win:NT,LInux:posix
os.getcwd()获取当前工作路径,相当于Linux的pwd命令
os.chdir(path)切换工作路径
os.makedirs(‘a/b/c’)递归创建文件夹,创建a下面b下面c文件夹
os.mkdir(‘a’)创建文件夹,已存在同名文件夹再次创建则报错
os.removedirs(‘a/b/c’)递归删除文件夹
os.rmdir(‘a’)删除单个文件夹,且文件夹必须为空
os.listdir()获取当前工作路径下的所有文件以及文件夹
os.remove(‘a.txt’)删除文件
os.rename(‘a.txt’,‘new_a.txt’)修改文件或文件夹名称
os.sep获取当前操作系统的路径分隔符 win:\,Linux:/
os.pathsep获取当前操作系统用于分隔文件路径的符号 win为;(分号) LInux为:(冒号)
os.linesep获取当前操作使用的行终止符 win:"\t\n" Linux:"\n"
os.system(‘系统命令’)执行系统命令,命令不存在会发出提示,但是不会报错不影响后面代码运行
os.environ获取当前系统环境变量,拿到的结果是一个字典
os.environ.get(key)获取这个key对应的环境变量内容
os.environ[key] = value向环境变量设置内容,临时设置,程序关闭后失效
os.path.split(path)将路径分成目录与文件名,以元组的形式呈现
os.path.dirname(path)获取路径最后一个文件或文件夹的父目录的路径
os.path.join(path,文件夹名)路径拼接,将路径与文件夹拼接起来(可多个),返回拼接结果
os.path.exists(path)判断该路径是否存在,存在返回True,不存在返回False
os.path.basename(path)如果路径不是以/ 或者 \结尾,则返回路径最后一个文件或文件夹名,否则没有返回值
os.path.isabs(path)如果路径是绝对路径则返回True,否则返回False
os.path.isdir(path)如果path是否存在文件夹则返回True,否则返回False
os.path.isfile(path)如果path是否存在文件则返回True,否则返回False
os.path.getsize(path)查看文件或文件夹夹大小,以字节为单位

获取目录下面所有文件的方法

os.walk(path)方法

传递一个路径进去,返回一个元组,带有3个参数:

dirpath :string,代表目录的路径,
dirnames :list,包含了dirpath下所有子目录的名字。
filenames :list,包含了非目录文件的名字。

不断遍历dirnames里面的内容,遍历到的文件都传给filenames

# dirs遍历root下面的所有路径,files表示dirs找到的所有文件
for root, dirs, files in os.walk(path): 
    for name in files:
        join = os.path.join(root, name) # 获取到的文件,加上目录变成绝对路径

sys模块


由于笔者操作系统为mac,展示的效果不同,所以这里就不一定每个功能都展示出效果了。敬请见谅!

sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时环境。

返回操作系统平台名称,各平台返回结果不同

win:win32
Linux(2x and 3x):linux2
Mac OS X:darwin

print(sys.platform)

获取当前处理的异常详细信息,返回结果3个值,分别是:异常类型、异常信息、异常对象(可捕获异常产生的位置)

import sys

try:
    raise TypeError("类型错误!!")
except:
    print(sys.exc_info())
    # print(sys.exc_info()[-1].tb_frame) # 异常出现的文件及产生的位置
    # print(sys.exc_info()[-1].tb_lineno) # 只显示异常产生的位置

执行结果:

(<class 'TypeError'>, TypeError('类型错误!!'), <traceback object at 0x7fabcc896240>)

系统导入的模块字段,返回的是一个字典,key是模块名,value是模块,

import sys

print(sys.modules)
print(sys.modules.keys()) # 返回模块名列表

获取Python解释器的版本信息

print(sys.version)

3.8.2 (v3.8.2:7b3ab5921f, Feb 24 2020, 17:52:18) 
[Clang 6.0 (clang-600.0.57)]

Python模块的搜索路径,返回的是一个列表。如果要查找的模块不能找到,则可以往这里面追加查找模块的路径

print(sys.path)

sys.argv接收外部执行该文件时传递的参数

print(sys.argv)

外部使用解释器运行该文件的执行结果
在这里插入图片描述

退出整个py程序的

sys.exit()

返回Python解释器的安装位置

print(sys.exec_prefix)

shutil 模块


高级文件及文件夹操作模块

拷贝文件,目标必须是一个文件,如果是已存在的文件夹则报错

import shutil

shutil.copyfile('a.txt','b.txt') # 如果b.txt文件不存在,则a拷贝一份改名为b,如果存在则覆盖内容

shutil.copyfile('a.txt','test') # test文件夹存在的话,此行报错!

注意:目标不能是一个已存在的文件夹,如果是,则报错


拷贝文件和权限,目标不受约束,如果是文件夹则将源文件拷贝一份到目标文件夹下

import shutil

shutil.copy('a.txt','test_s') # 将a文件拷贝一份放到test_s文件夹下
shutil.copy('a.txt','b.txt') # 将a拷贝一份改名为b,如果b已存在,则覆盖

拷贝文件和状态

import shutil

shutil.copy2('a.txt','b.txt')

仅拷贝文件状态和信息。目标文件必须存在

import shutil

shutil.copystat('a.txt','b.txt')

仅拷贝权限。内容、组、用户均不变。目标文件必须存在

shutil.copymode('a.txt','b.txt')

删除文件夹、包含其下面所有文件及文件夹

先递归创建文件夹

import os
os.makedirs('a/b/c/d')

递归删除,删除文件夹下面的所有子文件及子文件夹

import shutil

shutil.rmtree('a')

文件或文件夹移动。相当于Linux:mv命令。且移动后的路径不能有重名文件。

import shutil

shutil.move('a.txt','test_s')

也可以起到改名的作用

import shutil

shutil.move('test_s/a.txt','b.txt') # 将a.txt移动出来后改名为b.txt

灭霸程序(结合os、random模块)

添加上路径以后,执行后删除该路径下一半文件

import os
import random

# 执行删除程序的文件路径
work_catalog = '路径'

if not os.path.exists(work_catalog):
    raise FileNotFoundError('当前输入的路径不存在!')

file_lis = [] # 所有文件的路径

for root,dirs,files in os.walk(work_catalog): # 获取所有文件的路径
    for i in files:
        file_lis.append(os.path.join(root,files[0]))


half_num = int(len(file_lis) / 2)

if len(file_lis) <= 0:
    raise FileNotFoundError('当前路径不存在文件')
elif half_num == 0:
    raise FileNotFoundError('当前目录下只存在一个文件,执行无效!')

half = random.sample(file_lis,half_num) # 随机获取一半文件路径

print(f'路径总文件数量:{len(file_lis)}')

count = 0 # 删除文件计数

for i in half:
    os.remove(i)
    count += 1
    print('已删除文件:',i)

print('已删除文件数量:',count)

文件解压缩模块 zip、tar


zipfile模块

用于将文件压缩成zip格式的模块,或者将zip格式解压出来,基于zip压缩算法

import zipfile

# 创建一个压缩包文件,w模式,与打开文件的模式相同,a模式则是追加
z = zipfile.ZipFile('test.zip','w')
z.write('a.txt')
z.write('b.txt') # 将两个文件写到test.zip这个压缩包里面去
z.close() # 关闭压缩包

解压zip压缩包

import zipfile

z = zipfile.ZipFile('test.zip','r') # 表示将压缩包的内容读出来

z.extractall('ts/') # 将压缩包内容解压到ts文件夹下
z.close() # 关闭压缩包

tarfile

用于将文件压缩成tar的模块,或者将tar格式解压出来,基于tar压缩算法

import zipfile

z = zipfile.ZipFile('test.tar','w') # 创建一个压缩包文件,w表示接受往里写
t.add('a.txt')
t.add('b.txt')# 将两个文件写到test.zip这个压缩包里面去
t.close() # 关闭压缩包

解压tar压缩包

import tarfile

t = tarfile.TarFile('test.tar','r') # 表示将压缩包的内容读出来

t.extractall('ts/') # 将压缩包内容解压到ts文件夹下
t.close() # 关闭压缩包

通过with来管理它们,和打开文件的方式相同

import zipfile

# 如果该压缩包存在则清空,不存在则创建往里添加文件压缩
with zipfile.ZipFile('test.zip','w') as z:
    z.write('a.txt')
    z.write('b.txt')

# 往该压缩包追加文件压缩
with zipfile.ZipFile('test.zip','a') as z:
    z.write('c.txt')

注意:不能用tar来压缩通过zip算法解压缩的文件,反之亦然。


我们可能会碰到带有密码的压缩包,这里提供一种破解方式

撞库破解压缩包密码

import zipfile

# 定义我们密码字典(当然这里并不是字典,我们平常听到的也是密码字典等等)
passwd_lis = [
    '123',
    '321',
    '222',
    '111',
    '666',
    '333',
    '1010', # 笔者给压缩包设置的密码是1010
    '8090',
    '2020',
    '0000'
]

for i in passwd_lis: # 在密码字典里面获取到密码
    z = zipfile.ZipFile('skin.zip','r')
    try:
        z.extractall('test/',pwd=i.encode('utf-8')) # 尝试使用密码解压
        print(f'破解成功,解压密码为:{i},已解压至test文件夹下')
        break
    except: # 报错则解压失败,重新尝试
        print(f'尝试密码:{i}破解失败,正在继续尝试破解中....')

我们来看看破解效果:

尝试密码:123破解失败,正在继续尝试破解中....
尝试密码:321破解失败,正在继续尝试破解中....
尝试密码:222破解失败,正在继续尝试破解中....
尝试密码:111破解失败,正在继续尝试破解中....
尝试密码:666破解失败,正在继续尝试破解中....
尝试密码:333破解失败,正在继续尝试破解中....
破解成功,解压密码为:1010,已解压至test文件夹下

hashlib 加密算法模块


Hash(哈希)算法,也可以称为:散列算法,将一个不定长的输入,通过该算法变成一个固定长度的Hash值。常见的Hash算法有:MD5、SHA1,只要我们通过相同的Hash算法校验过的值,不管值有多长,得到的Hash值长度都是固定的

通过算法后得到的内容称之为:密文、秘钥等。未加密的数据称之为:明文

这里主要学习最为常用的Hash算法:MD5摘要算法

主要特点具有:

1、将数据计算成md5特别容易
2、不可逆性,就就算得到md5值,如果要反解成内容几乎是特别困难的
3、任何数据通过md5计算后得到的长度都是固定的
4、哪怕修改数据的一个字符,得到的md5值也会完全不同

网络上破解所说破解MD5加密的信息并不可靠,只有过一次在2004年被山大教授王小云成功破解过,有兴趣可以百度。但后面又经过修改直至今天并未被破解过

虽说网络上有MD5解密的网页,都是属于碰撞破解的,但是它们也只是将一些常用的明文与MD5值存储到数据库内,我们在上面输入的内容,它们将输入的内容转换成MD5值,再与它们数据内存储的MD5值对比,匹配成功再拿到该MD5值对应的明文。仅此而已,并没有达到真正意义上的解密。

MD5应用场景:

1、密码加密
2、请求参数校验
3、文件校验,如:在第三方下载的软件的md5值与官方的md5值不匹配,说明该软件被修改或添加过什么内容。


简单举例:文件下载是通过多线程分段下载,下载完后需要文件进行合并,合并后的md5值与原文件的md5进行匹配,如果配对了则说明下载成功。如果下载完后匹配的分段不对,md5无法匹配上,则下载失败。这就是我们平时所看到下载到99%时一直加载的原因,甚至有可能直接失败重新下载。

实例:

import hashlib

md5 = hashlib.md5()

# 将需要加密的内容转换成2进制传入到md5内进行计算
md5.update('hello'.encode('utf-8')) 

print(md5.hexdigest()) # 打印md5计算后得到的内容

执行结果

5d41402abc4b2a76b9719d911017c592

相同的内容得到的md5值一定相同

只要修改任意字符,计算出来的md5值将完全不同

import hashlib

md5 = hashlib.md5()

# 将hello字符该为了ello
md5.update('ello'.encode('utf-8')) 

print(md5.hexdigest()) # 打印md5计算后得到的内容

执行结果

9ecb0b2f7994a8a3a2919212f764b81a

两次往md5里面放入值它计算,和一次往里让入值计算得到的md5值相同

import hashlib

md5 = hashlib.md5()
md5.update('ello'.encode('utf-8'))
print(md5.hexdigest())

# 等同于:
md55 = hashlib.md5()
md55.update('ell'.encode('utf-8'))
md55.update('o'.encode('utf-8')) # 分两次放入

print(md55.hexdigest()) # 打印md5计算后得到的内容

执行结果

9ecb0b2f7994a8a3a2919212f764b81a
9ecb0b2f7994a8a3a2919212f764b81a

模拟撞库破解md5

如果在我们截获到一个md5值后,想要得到其对应的明文,只能通过这种方式。

import hashlib

# 比如这里是我们截获到的md5值
intercept = '9ecb0b2f7994a8a3a2919212f764b81a'

# 我们就拿几个元素进行产生撞库破解
dic = ['hello','world','helloworld','ello']

for i in lis:
    md5 = hashlib.md5()
    md5.update(i.encode('utf-8'))

    if md5.hexdigest() == intercept:
        print(f'{intercept},对应的明文为:{i}')
        break
else:
    print('撞库匹配失败!')        

执行结果:

'9ecb0b2f7994a8a3a2919212f764b81a,对应的明文为:ello'

为了防止密码被撞库破解,我们还可以进行加盐处理,使用到一个新的模块:hmac

第一个参数为key,表示与我们的内容经过某种处理后再进行加密

import hmac

# 可以选择基于MD5算法,但是在我们要加密的数据里进行了加盐(11hash)
m = hmac.new(b'11hash',digestmod='md5')
m.update(b'hello')

# 加完盐之后,它会与我们输入的数据混合在一起,即不一定在头也不一定在尾,加密成md5

print(m.hexdigest())

执行结果

dd32a70e0c9ed84e084cef2780ca08bf

这样的话,如果需要撞库破解这段密文,需要知道我们加的盐是什么才能作为第一步开始。这就更使我们的信息更加安全了


typing 类型提示模块

很多人在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果,就不得不去阅读代码的具体内容,降低了阅读的速度,加上Python本身就是一门弱类型的语言,这种现象就变得更加的严重,而typing这个模块很好的解决了这个问题。typing模块只有在python3.5以上的版本中才可以使用,pycharm目前支持typing,检测到与我们定义的不同会划出警告

typing模块的作用:

1、类型检查,防止运行时出现参数和返回值类型不符合。
2、作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
3、该模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒。

实例:

from typing import List

# 这样虽然代码上来看没问题,但是实际上会报错,因为python不能这样表示
def test(x:str,y:int) -> list[int]:

# 借助这个模块就不会报错,并且能很好的告诉使用者该传递什么参数,返回的参数是什么
def test(x:str,y:int) -> List[int]:
	pass

# 上序代码表示:接收一个字符串和一个整型参数,然后会返回一个纯整型数字的列表

注意事项:

# 也可以这样写
def test(x:List[int],y:List[str]) -> int:
	return '123'
	# Pycharm会划出警告信息,因为返回值不符合定义的,但是执行也不会报错

# 错误写法!!!!!笔者理解的是:因为列表内容不固定
def test(x:str,y:int) -> List[int,str]:
	pass

# 如果返回的是一个列表,且列表里面元素的类型不同,可以这样写就行了
def test(x:str,y:int) -> List:
	pass

使用这种的好处就是,如果代码很多,使用者在看开头就知道,应该传递什么类型参数,会返回到什么类型内容等等

typing不仅仅只有List(列表)提示,还有其它的数据类型等等,特别多就不一一赘述了。

from typing import Tuple

# 元组可以这样写返回值,因为元组的元素是固定的,拿到返回值以后也不能更改了
def test(x:str,y:int) -> Tuple[int,str]:
    return (1,'2')

有些提示是typing模块所没有的,如:str、int、bool等等

from typing import List
def test(x:int,y:List) -> str:
	return 'abc'

# 告诉使用者返回的是一个字符串

def test(x:int,y:List) -> bool:
	return True

# 告诉使用者返回的是一个字符串

def test(x:int,y:List) -> None:
	return None

# 告诉使用者,返回的是一个空值

这种提示方式特别常见,在开发过程中以及使用功能、或阅读源码的过程中,可以根据自己需要制定提示信息。


subprocess 模块


subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。

我们可以使用此模块代替os.system、os.popen来执行系统命令,且可以得到执行后返回正确结果或错误结果

这里主要学习它的Popen。因为子进程的创建和管理都需要靠它处理

import subprocess

res = subprocess.Popen('ls /', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# 第一个参数执行的是命令
# shell=True,打开一个新的终端,执行命令
# stdout:输出内容,这里丢到了PIPE管道里
# stderr:输出的错误内容,这里也是丢到了PIPE管道里

# 解析:开启一个进程,打开一个终端,执行ls命令,执行完后正确结果放到PIPE管道里,执行出错了也放到管道里

# 获取命令执行的正确结果,默认是bytes类型,需要解码
print(res.stdout.read().decode('utf-8')) 

如果是windows需要将命令修改,这里针对的LInux或Mac演示

执行结果

'''
Applications
Library
System
....
'''

但是需要注意的是,管道里面的内容取一次就不能再次获取了

print(res.stdout.read().decode('utf-8'))
print(res.stdout.read().decode('utf-8'))

执行结果

'''
Applications
Library
System
....
'''

我们可以再次开启一个进程打开一个终端,将上一次执行的命令结果,放入到这个终端作为输入,也就是Linux里面的管道符|

res2 = subprocess.Popen('grep Library',shell=True,stdin=res.stdout,stdout=subprocess.PIPE)

# 注意:里面多加了一个stdin参数,它表示需要往这个终端输入什么内容,这里输入的是上一次终端的执行结果:res.stdout

# grep Library表示:过滤出Library这个内容,如果我们向这个终端传递了内容
# 它就在传递的内容里面过滤,否则在整个系统过滤找出这个内容

print(res2.stdout.read().decode('utf-8'))

执行结果

'Library'

此时会出现一个问题,当创建两个进程后执行操作,然后分别打印,我们看一下效果

res = subprocess.Popen('ls /', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res2 = subprocess.Popen('grep Library',shell=True,stdin=res.stdout,stdout=subprocess.PIPE)

print(res.stdout.read().decode('utf-8')) # 有结果
print(res2.stdout.read().decode('utf-8')) # 无结果

执行结果

'''
Applications
Library
System
....
'''

结果发现,下面打印的并无效果。

原因:

在第二次创建进程时,还没有创建完毕就执行了下面的print打印,所以打印时管道里面只有第一个进程的执行的内容,所以我们下面拿不到结果。

我们可以增加一个睡眠时间,来解决这个问题

import time

res = subprocess.Popen('ls /', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res2 = subprocess.Popen('grep Library',shell=True,stdin=res.stdout,stdout=subprocess.PIPE)

# 睡眠1秒,等待res2这个变量代表的进程打开的终端运行完毕,就会将内容放入管道里覆盖上一个
time.sleep(1) 

print(res.stdout.read().decode('utf-8')) # 无结果
print(res2.stdout.read().decode('utf-8')) # 有结果

执行结果

'Library'

因为是后来创建的进程将内容放入管道占用了,所以我们可以通过它来获取管道里面的内容


re 正则模块

由于re模块内容过长,已记录到另一篇博客:re正则表达式模块


正在更新中…

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值