python常用模块
什么是模块?
常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。
但其实import加载的模块分为四个通用类别:
1 使用python编写的代码(.py文件)
2 已被编译为共享库或DLL的C或C++扩展
3 包好一组模块的包
4 使用C编写并链接到python解释器的内置模块
为何要使用模块?
如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。
随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用,
Re模块
讲正题之前我们先来看一个例子:https://reg.jd.com/reg/person?ReturnUrl=https%3A//www.jd.com/
这是京东的注册页面,打开页面我们就看到这些要求输入个人信息的提示。
假如我们随意的在手机号码这一栏输入一个11111111111,它会提示我们格式有误。
这个功能是怎么实现的呢?
假如现在你用python写一段代码,类似:
phone_number = input('please input your phone number : ')
你怎么判断这个phone_number是合法的呢?
根据手机号码一共11位并且是只以13、14、15、18开头的数字这些特点,我们用python写了如下代码:
判断手机号码是否合法1
while True:
    phone_number = input('please input your phone number : ')
    if len(phone_number) == 11 \
            and phone_number.isdigit()\
            and (phone_number.startswith('13') \
            or phone_number.startswith('14') \
            or phone_number.startswith('15') \
            or phone_number.startswith('18')):
        print('是合法的手机号码')
    else:
        print('不是合法的手机号码')
现在需要用正则表达式判断手机号是否合法
phone_number= input('please input your phone number:')
if re.match('^(13|14|15|18|17)[0-9]{9}$',phone_number): 
    print('是合法的手机号码')
else:
    print('这是不合法的手机号码')
re模块下的常用方法
import re
ret = re.findall('a', 'eva egon yuan') # 返回所有满足匹配条件的结果,放在列表里
print(ret) #结果 : ['a', 'a']
ret = re.search('j', 'eva egon yuan')
if ret:
print(ret.group()) #结果 : 'a'
函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None,此时调用group会报错
所以要加IF进行判断下
ret = re.match('a', 'bbc')  # 同search,不过是从在字符串开始处进行匹配
if ret:
    print(ret).group()
结果 : 'a'
match是从头开始匹配,如果正则规则从头开始可以匹配上,就返回一个变量。
匹配的内容需要用group才能显示,如果没匹配上,就返回None,调用group会报错。
# ret = re.split('[ab]', 'abcd')  # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
# print(ret)  # ['', '', 'cd']
ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1) #将数字替换成'H',参数1表示只替换1个.不写就全部替换
print(ret) #evaHegon4yuan4
obj = re.compile('\d{3}')  #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())  #结果 : 123
import re
ret = re.finditer('\d', 'ds3sy4784a') #finditer返回一个存放匹配结果的迭代器
print(ret) # <callable_iterator object at 0x10195f940>
print(next(ret).group()) #查看第一个结果
print(next(ret).group()) #查看第二个结果
print([i.group() for i in ret]) #查看剩余的左右结果
注意:
1 findall的优先级查询
ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(ret)  # ['oldboy']     这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
ret = re.findall('www.(?:baidu|oldboy).com', 'www.baidu.com') ? 取消优先级的作业
print(ret)  # ['www.baidu.com'] 
2 split的优先级查询
ret=re.split("\d+","eva3egon4yuan")
print(ret)  #结果 : ['eva', 'egon', 'yuan']
ret=re.split("(\d+)","eva3egon4yuan")
print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan']
#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。
collections模块
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
1.namedtuple: 生成可以使用名字来访问元素内容的tuple
2.deque: 双端队列,可以快速的从另外一侧追加和推出对象
3.Counter: 计数器,主要用来计数
4.OrderedDict: 有序字典
5.defaultdict: 带有默认值的字典
namedtuple 可命名元组
我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:
p = (1, 2)
但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。
这时,namedtuple就派上了用场:
from collections import namedtuple
point = namedtuple('card',['x','y','z'])
p = point(1,2,3)
print(p) # card(x=1, y=2, z=3)
# 花色和数字
Card = namedtuple('card',['suits','number'])
c1 = Card('红桃',2)
print(c1)
print(c1.number)
print(c1.suits)
类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:
#namedtuple('名称', [属性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])
queue 队列 
# 堆栈:先进后出
# 队列:先进先出
不能循环取值,不是可迭代,不能插队
import queue 队列
q = queue.Queue()
q.put(10)
q.put(5)
q.put(6)
print(q)
print(q.qsize())
print(q.get())
print(q.get())
print(q.get())
print(q.get()) # 取不到值会堵塞,一直卡在这里,可以判断长度为0时不取值了
deque 双端队列 
可以插队,可以查看里面的值
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
from collections import deque
dq = deque([1,2])
dq.append('a') # 从后面放数据 [1,2,'a']
dq.appendleft('b') # 从前面放数据 ['b',1,2,'a']
dq.insert(2,3) # 在索引2的前面插入一个3 ['b',1,2,'a']
print(dq)
print(dq.pop()) # 从后面取数据 a
print(dq.popleft()) # 从前面取数 b
deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。
OrderedDict 有序字典
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict:
from collections import OrderedDict
d = dict([('a', 1), ('b', 2), ('c', 3)]) #普通字典
# print(d) # dict的Key是无序的 # {'a': 1, 'c': 3, 'b': 2}
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
# od # OrderedDict的Key是有序的
print(od) # 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
od.keys() # 按照插入的Key的顺序返回
print(od.keys()) # odict_keys(['z', 'y', 'x'])
defaultdict 默认字典
有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。
即: {'k1': 大于66 , 'k2': 小于66}my_dict = {}
for value in values:
if value>66:
if my_dict.has_key('k1'):
my_dict['k1'].append(value)
else:
my_dict['k1'] = [value]
else:
if my_dict.has_key('k2'):
my_dict['k2'].append(value)
else:
my_dict['k2'] = [value]
首先第一次要判断K1是否在字典里,要先创建K1对应的值后才能后里面追加值,如直接追加会报错
defaultdict 解决办法
from collections import defaultdict
values = [11, 22, 33,44,55,66,77,88,99,90]
my_dict = defaultdict(list)
for value in values:
if value>66:
my_dict['k1'].append(value)
else:
my_dict['k2'].append(value)
使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict:
from collections import defaultdict
dd = defaultdict(lambda: 'N/A') # 参数是可以调用的
dd['key1'] = 'abc'
print(dd['key1']) # key1存在 # abc
print(dd['key2']) # 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})
from collections import Counter
c = Counter('abcdeabcdabcaba')
print (c) # 输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
其他详细内容 http://www.cnblogs.com/Eva-J/articles/7291842.html 老师博客
time 时间模块
和时间有关系的我们就要用到时间模块。在使用模块之前,应该首先导入这个模块。
#常用方法
1.time.sleep(secs)#secs 参数以秒为单位的浮点数,就是让时间延长几秒再往下执行
(线程)推迟指定的时间运行。单位为秒。
2.time.time()
获取当前时间戳
表示时间的三种方式
在Python中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串:
(1)时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型:1595742435.4820917
(2)格式化的时间字符串(Format String): ‘1999-12-06’
(3)元组(struct_time) :struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等):time.struct_time(tm_year=2020, tm_mon=7, tm_mday=26, tm_hour=13, tm_min=53, tm_sec=5, tm_wday=6, tm_yday=208, tm_isdst=0)
python中格式化符号
%y 两位数的年份表示(00-99)
%Y 四位数的年份表示(000-9999)
%m 月份(01-12)
%d 月内中的一天(0-31)
%H 24小时制小时数(0-23)
%I 12小时制小时数(01-12)
%M 分钟数(00=59)
%S 秒(00-59)
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366)
%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身
| 索引(Index) | 属性(Attribute) | 值(Values) | 
|---|---|---|
| 0 | tm_year(年) | 比如2011 | 
| 1 | tm_mon(月) | 1 - 12 | 
| 2 | tm_mday(日) | 1 - 31 | 
| 3 | tm_hour(时) | 0 - 23 | 
| 4 | tm_min(分) | 0 - 59 | 
| 5 | tm_sec(秒) | 0 - 60 | 
| 6 | tm_wday(weekday) | 0 - 6(0表示周一) | 
| 7 | tm_yday(一年中的第几天) | 1 - 366 | 
| 8 | tm_isdst(是否是夏令时) | 默认为0 | 
首先,我们先导入time模块,来认识一下python中表示时间的几种格式:
  导入时间模块
   import time
#时间戳
时间戳
print(time.time()) # 1595743401.4365842
# 格式化时间字符串格式
print(time.strftime("%Y-%m-%d %X"))
# 2020-07-26 14:04:36
print(time.strftime("%Y-%m-%d %H-%M-%S"))
# 2020-07-26 14-04-53
#时间元组:localtime将一个时间戳转换为当前时区的struct_time
print(time.localtime())
time.struct_time(tm_year=2020, tm_mon=7, tm_mday=26, tm_hour=14, tm_min=7, tm_sec=55, tm_wday=6, tm_yday=208, tm_isdst=0)
print(time.gmtime()) 外国时间 相差几个小时
time.struct_time(tm_year=2020, tm_mon=7, tm_mday=26, tm_hour=6, tm_min=7, tm_sec=55, tm_wday=6, tm_yday=208, tm_isdst=0)
小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的
几种格式之间的转换

#时间戳-->结构化时间
#time.gmtime(时间戳) #UTC时间,与英国伦敦当地时间一致
#time.localtime(时间戳) #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间
stamp = time.time()
print(time.localtime(stamp))
time.struct_time(tm_year=2020, tm_mon=7, tm_mday=26, tm_hour=14, tm_min=11, tm_sec=48, tm_wday=6, tm_yday=208, tm_isdst=0)
print(time.gmtime(stamp))
time.struct_time(tm_year=2020, tm_mon=7, tm_mday=26, tm_hour=6, tm_min=11, tm_sec=48, tm_wday=6, tm_yday=208, tm_isdst=0)
# 结构化时间-->时间戳
#time.mktime(结构化时间)
stamp = time.time()
time_tuple = time.localtime(stamp)
print(time.mktime(time_tuple)) # 1595744091.0
#结构化时间-->字符串时间
#time.strftime("格式定义","结构化时间") 结构化时间参数若不传,则显示当前时间
print(time.strftime('%Y-%m-%d %X'))
# 2020-07-26 14:19:18
print(time.strftime('%Y-%m-%d',time.localtime(1600000000)))
# 2020-09-13
#字符串时间-->结构化时间
#time.strptime(时间字符串,字符串对应格式)
print(time.strptime("2020-07-25","%Y-%m-%d"))
# time.struct_time(tm_year=2020, tm_mon=7, tm_mday=25, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=207, tm_isdst=-1)
print(time.strptime("07/24/2018","%m/%d/%Y"))

#结构化时间 --> %a %b %d %H:%M:%S %Y串
#time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
>>>time.asctime(time.localtime(1500000000))
'Fri Jul 14 10:40:00 2017'
>>>time.asctime()
'Mon Jul 24 15:18:33 2017'
#时间戳 --> %a %b %d %H:%M:%S %Y串
#time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串
>>>time.ctime()
'Mon Jul 24 15:19:07 2017'
>>>time.ctime(1500000000)
'Fri Jul 14 10:40:00 2017'
计算时间差
import time
true_time=time.mktime(time.strptime('2017-09-11 08:30:00','%Y-%m-%d %H:%M:%S'))
time_now=time.mktime(time.strptime('2017-09-12 11:00:00','%Y-%m-%d %H:%M:%S'))
dif_time=time_now-true_time
struct_time=time.gmtime(dif_time)
print('过去了%d年%d月%d天%d小时%d分钟%d秒'%(struct_time.tm_year-1970,struct_time.tm_mon-1,
struct_time.tm_mday-1,struct_time.tm_hour,
struct_time.tm_min,struct_time.tm_sec))
datetime模块
1.datetime.now() # 获取当前datetime
datetime.utcnow() # 获取当前格林威治时间
from datetime import datetime #获取当前本地时间
a=datetime.now()
print('当前日期:',a)
#获取当前世界时间
b=datetime.utcnow()
print('世界时间:',b)
2.datetime(2017, 5, 23, 12, 20) # 用指定日期时间创建datetime
from datetime import datetime
#用指定日期创建
c = datetime(2017, 5, 23, 12, 20)
print('指定日期:',c) # 指定日期: 2017-05-23 12:20:00
3.将以下字符串转换成datetime类型:
'2017/9/30'
'2017年9月30日星期六'
'2017年9月30日星期六8时42分24秒'
'9/30/2017'
'9/30/2017 8:42:50 '
4.时间字符串格式化
from datetime import datetime
d=datetime.strptime('2017/9/30','%Y/%m/%d')
print(d) # 2017-09-30 00:00:00
e=datetime.strptime('2017年9月30日星期六','%Y年%m月%d日星期六')
print(e) #2017-09-30 00:00:00
f=datetime.strptime('2017年9月30日星期六8时42分24秒','%Y年%m月%d日星期六%H时%M分%S秒')
print(f) # 2017-09-30 08:42:24
g=datetime.strptime('9/30/2017','%m/%d/%Y')
print(g) # 2017-09-30 00:00:00
h=datetime.strptime('9/30/2017 8:42:50 ','%m/%d/%Y %H:%M:%S ')
print(h) #2017-09-30 08:42:50
4.将以下datetime类型转换成字符串:
2017年9月28日星期4,10时3分43秒
Saturday, September 30, 2017
9/30/2017 9:22:17 AM
September 30, 2017
时间字符串格式化
from datetime import datetime
i=datetime(2017,9,28,10,3,43)
print(i.strftime('%Y年%m月%d日%A,%H时%M分%S秒'))
j=datetime(2017,9,30,10,3,43)
print(j.strftime('%A,%B %d,%Y'))
k=datetime(2017,9,30,9,22,17)
print(k.strftime('%m/%d/%Y %I:%M:%S%p'))
l=datetime(2017,9,30)
print(l.strftime('%B %d,%Y'))
5.用系统时间输出以下字符串:
今天是2017年9月30日
今天是这周的第?天 
今天是今年的第?天 
今周是今年的第?周 
今天是当月的第?天
from datetime import datetime #获取当前系统时间
m=datetime.now()
print(m.strftime('今天是%Y年%m月%d日'))
print(m.strftime('今天是这周的第%w天'))
print(m.strftime('今天是今年的第%j天'))
print(m.strftime('今周是今年的第%W周'))
print(m.strftime('今天是当月的第%d天'))
random模块
#随机小数
>>> random.random()      # 大于0且小于1之间的小数
0.7664338663654585
>>> random.uniform(1,3) #大于1小于3的小数
1.6270147180533838
#随机整数
random.randint(1,5)  # 大于等于1且小于等于5之间的整数
random.randrange(1,10,2) # 大于等于1且小于10之间的奇数
#随机选择一个返回
random.choice([1,'23',[4,5]])  # #1或者23或者[4,5]
#随机选择多个返回,返回的个数为函数的第二个参数
random.sample([1,'23',[4,5]],2) # #列表元素任意2个组合
[[4, 5], '23']
#打乱列表顺序
item=[1,3,5,7,9]
random.shuffle(item) # 打乱次序
item
[5, 1, 3, 7, 9]
random.shuffle(item)
item
[5, 9, 7, 1, 3]
练习生成随机数
import random
def v_code():
    code = ''
    for i in range(5):
        num=random.randint(0,9)
        alf=chr(random.randint(65,90))
        add=random.choice([num,alf])
        code="".join([code,str(add)])
    return code
print(v_code())
os模块
os模块是与操作系统交互的一个接口
os.makedirs('dirname1/dirname2')    可生成多层递归目录
os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat('path/filename')  获取文件/目录信息
os.system("bash command")  运行shell命令,直接显示
os.popen("bash command).read()  运行shell命令,获取执行结果
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
os.path
os.path.abspath(path) 返回path规范化的绝对路径
os.path.split(path) 将path分割成目录和文件名二元组返回
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path) 如果path是绝对路径,返回True
os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path) 返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小
注意:os.stat('path/filename') 获取文件/目录信息 的结构说明
stat结构
stat 结构:
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: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。
os.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep 输出当前平台使用的行终止符,win下为"\r\n",Linux下为"\n"
os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为:
sys模块
sys模块是与python解释器交互的一个接口
sys.argv 命令行参数List,第一个元素是程序本身路径
import sys
ret = sys.argv
name = ret[1]
pwd = ret[2]
if name == 'alex' and pwd == 'alex123':
print('登录成功')
else:
print('错误的用户名和密码')
sys.exit()
print('你可以使用计算器了')
sys.exit(n)        退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version        获取Python解释程序的版本信息
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称
异常处理和status
import sys
try:
    sys.exit(1)
except SystemExit as e:
    print(e)
序列化模块
什么叫序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化。
比如,我们在python代码中计算的一个数据需要给另外一段程序使用,那我们怎么给?
现在我们能想到的方法就是存在文件里,然后另一个python程序再从文件里读出来。
但是我们都知道,对于文件来说是没有字典这个概念的,所以我们只能将数据转换成字典放到文件中。
你一定会问,将字典转换成一个字符串很简单,就是str(dic)就可以办到了,为什么我们还要学习序列化模块呢?
没错序列化的过程就是从dic 变成str(dic)的过程。现在你可以通过str(dic),将一个名为dic的字典转换成一个字符串,
但是你要怎么把一个字符串转换成字典呢?
聪明的你肯定想到了eval(),如果我们将一个字符串类型的字典str_dic传给eval,就会得到一个返回的字典类型了。
eval()函数十分强大,但是eval是做什么的?e官方demo解释为:将字符串str当成有效的表达式来求值并返回计算结果。
BUT!强大的函数有代价。安全性是其最大的缺点。
想象一下,如果我们从文件中读出的不是一个数据结构,而是一句"删除文件"类似的破坏性语句,那么后果实在不堪设设想。
而使用eval就要担这个风险。
所以,我们并不推荐用eval方法来进行反序列化操作(将str转换成python中的数据结构)
序列化的目的

通用的序列化格式即其他语言中也可以使用,但是只有很少的一部分数据类型能够通过JSON转化成字符串类型。例如:可序列化数据类型、数字、字符串、列表、字典、元组(反序列化回来是一个字典而不是元组)
集合数据类型不能转json序列化。
import json
dic = {'k1':'v1'}
print(type(dic),dic) # <class 'dict'> {'k1': 'v1'}
str_d = json.dumps(dic) # 序列化将一个字典转换成一个字符串
print(type(str_d),str_d) # <class 'str'> {"k1": "v1"}
#注意,json转换完的字符串类型的字典中的字符串是由""表示的
dic_d = json.loads(str_d) # 反序列化 将一个字符串格式的字典转换成一个字典
注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
print(type(dic_d),dic_d) # <class 'dict'> {'k1': 'v1'}
list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic) #也可以处理嵌套的数据类型
print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
load和dumps
json dump操作的是文件
import json
dic = {1:"a",2:"b"}
f = open('ttt','w',encoding='utf-8') # 后面的编码可以不指定
json.dump(dic,f) #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
f.close()
此时ttt文件里写入了dic字典
f = open('ttt')
res = json.load(f)
f.close()
print(type(res),res) #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
<class 'dict'> {'1': 'a', '2': 'b'}
多次写入文件,循环读出
import json
l = [{1:'a'},{1:'b'},{1:'b'}]
srt_d = json.dumps(l)
f = open('fff','w',encoding='utf-8')
for dic in l:
srt_d = json.dumps(dic)
f.write(srt_d+'\n')
f.close()
读出来又还原回去一个大列表
l = []
f = open('fff')
for i in f:
dic = json.loads(i.strip())
# print(dic,type(dic))
l.append(dic)
f.close()
print(l)
ensure_ascii关键字函数
import json
f = open('file','w')
json.dump({'国籍':'中国'},f)
ret = json.dumps({'国籍':'中国'})
f.write(ret+'\n')
json.dump({'国籍':'美国'},f,ensure_ascii=False) # 如写入的内容包含中文要设置为False
ret = json.dumps({'国籍':'美国'},ensure_ascii=False)
f.write(ret+'\n')
f.close()
其他参数说明
Serialize obj to a JSON formatted str.(字符串表示的json对象)
Skipkeys:默认值是False,如果dict的keys内的数据不是python的基本类型(str,unicode,int,long,float,bool,None),设置为False时,就会报TypeError的错误。此时设置成True,则会跳过这类key
ensure_ascii:,当它为True的时候,所有非ASCII码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的中文即可正常显示。)
If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse).
If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity).
indent:应该是一个非负的整型,如果是0就是顶格分行显示,如果为空就是一行最紧凑显示,否则会换行且按照indent的数值显示前面的空白分行显示,这样打印出来的json数据也叫pretty-printed json
separators:分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。
default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError.
sort_keys:将数据根据keys的值进行排序。
To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.
json 格式化输出
import json
data = {'username':['李华','二愣子'],'sex':'male','age':16}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
print(json_dic2)
pickle 序列
所有的python中的数据类型都可以转化成字符串形式
pickle序列化的内容只有python能理解
且部分反序列化依赖python代码
json & pickle 模块
用于序列化的两个模块
- json,用于字符串 和 python数据类型间进行转换
 - pickle,用于python特有的类型 和 python的数据类型间进行转换
 
pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load (不仅可以序列化字典,列表...可以把python中任意的数据类型序列化)
import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
print(str_dic) #一串二进制内容 二进制类型,看不懂但是不影响loads
dic2 = pickle.loads(str_dic) # loads 过来后也是一个正常的dic
print(dic2) #字典 {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
import pickle
import time
struct_time = time.localtime(1000000000)
print(struct_time)
f = open('pickle_file','wb') # pickle_file文件打不开
pickle.dump(struct_time,f)
f.close()
注意要使用bytes模式写入
f = open('pickle_file','rb')
struct_time2 = pickle.load(f)
print(struct_time2.tm_year)
注意读取时也要使用bytes类型读取
import pickle
import time
struct_time1 = time.localtime(1000000000)
struct_time2 = time.localtime(1500000000)
f = open('pickle_file','wb') # pickle_file肉眼读不懂
pickle.dump(struct_time1,f)
pickle.dump(struct_time2,f)
f.close()
# pickle 支持分区写入
f = open('pickle_file','rb')
struct_time1 = pickle.load(f)
struct_time2 = pickle.load(f)
print(struct_time1.tm_year)
print(struct_time2.tm_year)
# 注意支持分布读取
shelve 序列
shelve 也是Python提供给我们的序列化工具,比pickle用起来更简单一些。
shelve 只提供给我们一个open方法,是用key来访问的,使用起来和字典类似
import shelve
f = shelve.open('shel_file')
f['key1'] = {'int':10,'flot':9.5,'string':'sample data'} # 直接对文件句柄操作,就可以存入数据
f.close()
f1 = shelve.open('shel_file')
data = f1['key1'] # 取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
f.close()
print(data)
# {'int': 10, 'flot': 9.5, 'string': 'sample data'}
这模块有个限制,它不支持多个应用同一时间往同一DB进行写操作。
所以当我们知道我们的应用如果只进行读操作,我们可以让shelve通过只读方式打开DB
import shelve
f = shelve.open('shel_file',flag='r') 只读模式不允许版本低的好像是可以允许修改
existing = f['key1']
f['key1']['flot'] = 10
f.close()
# print(existing)
f = shelve.open('shel_file',flag='r')
existing2 = f['key1']
f.close()
print(existing2)
由于shenlve在默认情况下是不会记录待持久化对象的任何修改的,所以我们在shelve.open()
时候需要修改默认参数,否则对象的修改不会保存
writeback 参数设置
import shelve
f1 = shelve.open('shel_file')
print(f1['key1'])
f1['key1']['new_value'] = 'this was not here before' # 这里没有写入成功
f1.close()
f1 = shelve.open('shel_file',writeback=True) # 要设置此参数才能写入成功
print(f1['key1'])
f1['key1']['new_value'] = 'this was not here before'
f1.close()
wiriteback 方式有有点也有缺点。有点是减少了我们出错的概率,并且让对象的持久化对用户更加的透明了
但这种方式并不是所有的情况下都需要,首先,使用writeback以后,shelve在open()的时候会增加额外的内存消耗
,并且当DB在colse()的时候会讲缓存中的每个对象都写入到DB,这也会带来额外的等待时间。因为shelve没有
办法知道缓存中哪些对象修改了,哪些对象没有修改,因此所以的对象都会被写入。
 
 
 
                    
                
                
            
        
浙公网安备 33010602011771号