1、定义:
序列化的本质就是将一种数据结构(如字典、列表)等转换成一个特殊的序列(字符串或者bytes)的过程就叫做序列化
2、作用:
序列化除了可以解决写入文件的问题,还可以解决网络传输的问题,比如你将一个list数据结构通过网络传给另个开发者,那么你要想传输出去必须用bytes类型。但是bytes类型只能与字符串类型互相转化,它不能与其他数据结构直接转化,所以,你只能将list → 字符串 → bytes 然后发送,对方收到之后,在decode() 解码成原字符串
1、json模块是将满足条件的数据结构转化成特殊的字符串,并且也可以反序列化还原回去
2、json序列化只支持部分Python数据结构:dict,list,tuple,str,int,float,True,False,None
3、用于网络传输:dumps、loads
import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic) # 序列化:将一个字典转换成一个字符串
print(type(str_dic)) # <class 'str'>
print(str_dic)
# 输出结果:
<class 'str'>
{"k1": "v1", "k2": "v2", "k3": "v3"}
# 注意,json转换完的字符串类型后,字典中的字符串是由""表示的
import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic) # 序列化
dic2 = json.loads(str_dic) # 反序列化:将一个字符串格式的字典转换成一个字典
print(type(dic2)) # <class 'dict'>
print(dic2)
# 输出结果:
<class 'dict'>
{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
# 注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
import json
list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic) # 序列化
print(type(str_dic)) # <class 'str'>
print(str_dic)
# 输出结果:
<class 'str'>
[1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
import json
list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic) # 序列化
list_dic2 = json.loads(str_dic) # 反序列化
print(type(list_dic2)) # <class 'list'>
print(list_dic2)
# 输出结果:
<class 'list'>
[1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
4、用于文件写读:dump、load
5、其他参数说明
ensure_ascii:当它为True的时候,所有非ASCII码字符显示为\uXXXX序列,只需在dump时设置为ensure_ascii = False
即可,此时存入json的中文即可正常显示
separators:分隔符,实际上是(item_separator,dict_separator)的一个元组,默认的就是逗号或者冒号;这表示dictionary内keys之间用,
隔开,而key和value之间用冒号(:)
隔开
sort_keys:将数据根据keys的值进行排序
6、json序列化存储多个数据到同一个文件中
对于json序列化,存储多个数据到一个文件中是有问题的,默认一个json文件只能存储一个json数据,但是也可以解决,举例说明:
解决方案:每次转化后写入,都加上换行
1、pickle模块是将Python所有的数据结构以及对象等转化成bytes类型,然后还可以反序列化还原回去
2、只能是Python语言遵循的一种数据转化格式,只能在Python语言中使用,支持Python所有的数据类型包括实例化对象
3、dumps、loads有两个作用
import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
dic2 = pickle.loads(str_dic)
print(type(str_dic)) # bytes类型
print(dic2) # 字典
# 输出结果:
<class 'bytes'>
{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
import pickle
def func():
print(666)
ret = pickle.dumps(func)
print(ret)
print(type(ret)) # <class 'bytes'>
f1 = pickle.loads(ret) # f1得到 func函数的内存地址
f1() # 执行func函数,打印666
4、dump、load:用于文件写读
import pickle
dic = {(1,2):'yuluo', 1:True, 'set':{1,2,3}}
f = open('pickle序列化',mode='wb')
pickle.dump(dic,f)
f.close()
with open('pickle序列化',mode='wb') as f1:
pickle.dump(dic,f1)
5、pickle序列化存储多个数据到一个文件中
类似于字典的操作方式去操作特殊的字符串
os模块是与操作系统交互的一个接口,它提供的功能多与工作目录,路径,文件等相关
目录指的是:本文件所在的文件夹(当前目录 / 工作目录 / 父级目录)
# 1、获取当前工作目录,即当前python脚本工作的目录路径
os.getcwd()
# 2、改变当前脚本工作目录;相当于shell下cd
os.chdir("dirname")
# 3、返回当前目录: ('.')
os.curdir
# 4、获取当前目录的父目录字符串名:('..')
os.pardir
# 1、可生成多层递归目录(重要)
os.makedirs('dirname1/dirname2')
# 2、若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.removedirs('dirname1')
# 3、生成单级目录;相当于shell中mkdir dirname
os.mkdir('dirname')
# 4、删除单级空目录,若目录不为空则无法删除,报错;
# 相当于shell中rmdir dirname os.listdir('dirname')
os.rmdir('dirname')
# 5、列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.listdir('dirname')
# 1、删除一个文件
os.remove()
# 2、重命名文件/目录
os.rename("oldname","newname")
# 3、获取文件/目录信息
os.stat('path/filename')
注意:os.stat('path/filename') 获取文件/目录信息
的结构说明
os.stat('path/filename') | “获取文件/目录信息 ”的结构说明 |
---|---|
st_mode | inode 保护模式 |
st_ino | inode 节点号 |
st_dev | inode 驻留的设备 |
st_nlink | inode 的链接数 |
st_uid | 所有者的用户ID |
st_gid | 所有者的组ID |
st_size | 普通文件以字节为单位的大小;包含等待某些特殊文件的数据 |
st_atime | 上次访问的时间 |
st_mtime | 最后一次修改的时间 |
st_ctime | 元数据最后一次被访问的时间,或者创建时间 |
# 1、输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.sep
# 2、输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.linesep
# 3、输出用于分割文件路径的字符串 win下为";",Linux下为":"
os.pathsep
# 4、输出字符串指示当前使用平台 win→'nt'; Linux→'posix'
os.name
# 1、运行shell命令,直接显示
os.system("bash command")
# 2、运行shell命令,获取执行结果
os.popen("bash command).read()
# 3、获取系统环境变量
os.environ
# 1、返回path规范化的绝对路径
os.path.abspath(path)
# 2、将path分割成目录和文件名按元组返回
os.path.split(path)
# 3、返回path的目录,其实就是os.path.split(path)的第一个元素
os.path.dirname(path)
# 3、返回path最后的文件名,如何path以/或\结尾,那么就会返回空值,
# 即os.path.split(path)的第二个元素
os.path.basename(path)
# 4、如果path存在,返回True;如果path不存在,返回False
os.path.exists(path)
# 5、如果path是绝对路径,返回True
os.path.isabs(path)
# 6、如果path是一个存在的文件,返回True。否则返回False
os.path.isfile(path)
# 7、如果path是一个存在的目录,则返回True。否则返回False
os.path.isdir(path)
# 8、将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.join(path1[, path2[, ...]])
# 9、返回path所指向的文件或者目录的最后访问时间
os.path.getatime(path)
# 10、返回path所指向的文件或者目录的最后修改时间
os.path.getmtime(path)
# 11、返回path的大小
os.path.getsize(path)
sys模块是与python解释器交互的一个接口
sys.argv # 命令行参数List,第一个元素是程序本身路径
sys.exit(n) # 退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version # 获取Python解释程序的版本信息
sys.path # 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform # 返回操作系统平台名称
此模块有人称为摘要算法,也叫做加密算法,或者是哈希算法,散列算法等。就是做加密和校验使用,工作原理:它通过一个函数,把任意长度的数据按照一定规则转换为一个固定长度的数据串(通常用16进制的字符串表示)
1、hashlib的主要用途有两个:
密码的加密
文件一致性校验
2、hashlib的特征以及使用要点:
① bytes类型数据 → 通过hashlib算法 → 固定长度的字符串(16位长度)
② 不同的bytes类型数据转化成的结果一定不同
③ 相同的bytes类型数据转化成的结果一定相同
④ 此转化过程不可逆
我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值
"""-------- 正常加密用法 --------"""
import hashlib
md5 = hashlib.md5()
md5.update('123456'.encode('utf-8'))
print(md5.hexdigest())
# 输出结果:
'e10adc3949ba59abbe56e057f20f883e'
"""---- 验证:相同的bytes数据转化的结果一定相同 ----"""
import hashlib
md5 = hashlib.md5()
md5.update('123456'.encode('utf-8'))
print(md5.hexdigest())
# 输出结果(同一bytes数据多次转化,结果相同):
'e10adc3949ba59abbe56e057f20f883e'
"""---- 验证:不相同的bytes数据转化的结果一定不相同 ----"""
import hashlib
md5 = hashlib.md5()
md5.update('12345'.encode('utf-8'))
print(md5.hexdigest())
# 输出结果:
'827ccb0eea8a706c4c34a16891f84e7b'
比较复杂的加密方式称之为加盐,“固定密码”就是固定的盐
import hashlib
ret = hashlib.md5('固定密码'.encode('utf-8'))
ret.update('a'.encode('utf-8'))
print(ret.hexdigest())
import hashlib
username = 'yuluo'
ret = hashlib.md5(username[::2].encode('utf-8'))
ret.update('a'.encode('utf-8'))
print(ret.hexdigest())
MD5算法是比较常用的一种加密算法,一般的企业用MD5就够用了。但是对安全要求比较高的企业,比如金融行业,MD5加密的方式就不够了,得需要加密方式更高的,比如sha系列,sha1,sha224,sha512等,数字越大,加密的方法越复杂,安全性越高,但是效率就会越慢
"""----- 加固定的盐 -----"""
import hashlib
ret = hashlib.sha384(b'yuluo')
ret.update('a'.encode('utf-8'))
print(ret.hexdigest())
"""----- 加动态的盐 -----"""
import hashlib
ret = hashlib.sha384(b'yuluo'[::2])
ret.update('a'.encode('utf-8'))
print(ret.hexdigest())
默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(filename)s[line:%(lineno)d] '
'%(levelname)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename='/tmp/test.log',
filemode='w')
logging.debug('debug message') # 调试
logging.info('info message') # 重要 输出信息
logging.warning('warning message') # 警告
logging.error('error message') # 错误
logging.critical('critical message') # 严重错误
1、logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
2、format参数中可能用到的格式化串:
%(name)s # Logger的名字
%(levelno)s # 数字形式的日志级别
%(levelname)s # 文本形式的日志级别
%(pathname)s # 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s # 调用日志输出函数的模块的文件名
%(module)s # 调用日志输出函数的模块名
%(funcName)s # 调用日志输出函数的函数名
%(lineno)d # 调用日志输出函数的语句所在的代码行
%(created)f # 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d # 输出日志信息时的,自Logger创建以来的毫秒数
%(asctime)s # 字符串形式的当前时间。默认是 “2003-07-08 16:49:45,896”
%(thread)d # 线程ID。可能没有
%(threadName)s # 线程名。可能没有
%(process)d # 进程ID。可能没有
%(message)s # 用户输出的消息
logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过fh.setLevel(logging.Debug)单对文件流设置某个级别
import logging
logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log',encoding='utf-8')
# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - '
'%(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh) #logger对象可以添加多个fh和ch对象
logger.addHandler(ch)
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
上面这种方式需要创建各种对象,比如logger对象、fileHandler对象、ScreamHandler对象等,比较麻烦,那么下面这种字典的方式,创建logger配置文件,这种才是工作中经常使用的实现日志功能的方法,真正的做到拿来即用(简单改改),如下便是 logging配置
import os
import logging.config
"""----- 定义日志输出格式开始:可选择以下3种版本其一 -----"""
# 标准版 其中name为getlogger指定的名字
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d]' \
'[task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]'
# 简易版
simple_format = '[%(levelname)s][%(asctime)s]' \
'[%(filename)s:%(lineno)d]%(message)s'
# 极简版
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
"""----- 定义日志输出目录及名称结束 -----"""
# log文件的目录 其中__file__为当前文件名
logfile_dir = os.path.dirname(os.path.abspath(__file__))
# log文件名
logfile_name = 'all2.log'
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)
"""----- log配置字典 -----"""
LOGGING_DIC = {
'version': 1, # 版本号(本行和下一行均无需改变)
'disable_existing_loggers': False, # 设置文件中作为设置目标的记录器将采用该设置
'formatters': { # 输出格式,可自行配置如下几种
'standard': { # 输出格式一:标准版
'format': standard_format
},
'simple': { # 输出格式二:简易版
'format': simple_format
},
},
'filters': {}, # 过滤器(一般无需改变)
'handlers': { # 任务处理(可设置记录到日志/输出到屏幕)
'console': { # 打印到终端的日志,收集debug及以上的日志
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
'default': { # 记录到文件的日志,收集info及以上的日志
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': logfile_path, # 日志文件
'maxBytes': 1024 * 1024 * 5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,中文log也不乱码
},
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置
'': {
# 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'handlers': ['default', 'console'],
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
},
},
}
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger(__name__) # 生成一个log实例
logger.info('It works!') # 记录该文件的运行状态
if __name__ == '__main__':
load_my_logging_cfg()
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等
namedtuple:生成可以使用名字来访问元素内容的tuple
我们知道 tuple
可以表示不变集合,例如,一个点的二维坐标就可以表示成:
p = (1, 2)
但是看到(1, 2),很难看出这个tuple是用来表示一个坐标的,这时namedtuple
就派上了用场:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y']) # 此处定义了一个名为'Point'的数据类型
p = Point(1, 2)
print(p) #
print(p.x) # 可以使用点的方式
print(p[0]) # 可以使用索引的方式
# 输出结果:
Point(x=1, y=2)
1
1
"""---------- 类似于格式化时间 ----------"""
import time
struct_time = time.strptime('2022-1-1','%Y-%m-%d')
print(struct_time)
print(struct_time[0]) # 找到了索引为0的 tm_year = 2022
print(struct_time.tm_yday) # 找到了键为 tm_mday = 1
# 输出结果:
time.struct_time(tm_year=2022, tm_mon=1, tm_mday=1, tm_hour=0,
tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)
2022
1
"""------- 我们利用上方的办法给解决出来 -------"""
from collections import namedtuple
struct_time = namedtuple('struct_time',['tm_year', 'tm_mon', 'tm_mday'])
st = struct_time(2022,1,1)
print(st)
# 输出结果:
struct_time(tm_year=2022, tm_mon=1, tm_mday=1)
类似的,如果要用坐标和半径表示一个圆,也可以用 namedtuple
定义:
namedtuple('名称', [属性list])
# 例如定义一个在坐标(x,y),半径为r的圆
Circle = namedtuple('Circle', ['x', 'y', 'r'])
deque:双端队列,可以快速的从另外一侧追加和推出对象
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。而deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈
deque除了实现list的append()
和pop()
外,还支持appendleft()
和popleft()
,这样就可以非常高效地往头部添加或删除元素
from collections import deque
q = deque(['a', 'b', 'c'])
q.append('x') # 右端添加x
print(q) # deque(['a', 'b', 'c', 'x'])
q.appendleft('y') # 左端再添加y
print(q) # deque(['y', 'a', 'b', 'c', 'x'])
OrderedDict:有序字典
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序,如果要保持Key的顺序,可以用OrderedDict
:
from collections import OrderedDict
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(od) # OrderedDict的Key是有序的
# 输出结果:
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注意,OrderedDict
的Key会按照插入的顺序排列,不是Key本身排序
from collections import OrderedDict
od = OrderedDict()
od['z'] = 1
od['y'] = 2
od['x'] = 3
print(od)
print(od.keys()) # 按照插入的Key的顺序返回
# 输出结果:
OrderedDict([('z', 1), ('y', 2), ('x', 3)])
odict_keys(['z', 'y', 'x'])
defaultdict:带有默认值的字典
我们先来看一道例题,把列表[11, 22, 33, 44, 55, 66, 77, 88, 99],将所有大于66的值保存到一个key中,另外的值保存到另一个key中
"""--------- 常规方法 ---------"""
l1 = [11,22,33,44,55,66,77,88,99]
dic = {}
for i in l1:
if i < 66:
if 'key1' not in dic:
dic['key1'] = []
dic['key1'].append(i)
else:
if 'key2' not in dic:
dic['key2'] = []
dic['key2'].append(i)
print(dic)
# 输出结果:
{'key1': [11, 22, 33, 44, 55], 'key2': [66, 77, 88, 99]}
"""--------- defaultdict方法 ---------"""
from collections import defaultdict
l1 = [11,22,33,44,55,66,77,88,99]
dic = defaultdict(list) # 设置了一个list,此处所设置的必须为可回调的参数
for i in l1:
if i < 66:
dic['key1'].append(i)
else:
dic['key2'].append(i)
print(dic)
# 输出结果:
defaultdict(<class 'list'>,
{'key1': [11, 22, 33, 44, 55], 'key2': [66, 77, 88, 99]})
使用dict
时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict
from collections import defaultdict
dd = defaultdict(lambda: 'N/A') # 函数也属于可回调的,此处设定的默认值为'N/A'
dd['key1'] = 'abc'
print(dd['key1']) # abc key1存在故返回设定好的'abc'
print(dd['key2']) # N/A key2不存在,返回默认值'N/A'
Counter:计数器,主要用来计数
Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数),Counter类和其他语言的bags或multisets很相似
c = Counter('abcdeabcdabcaba') # 此处放上可迭代对象
print(c)
# 输出结果:
Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})