什么是模块?
常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。
但其实import加载的模块分为四个通用类别:
1 使用python编写的代码(.py文件)
2 已被编译为共享库或DLL的C或C++扩展
3 包好一组模块的包
4 使用C编写并链接到python解释器的内置模块
为何要使用模块?
如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。
随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用,
正则表达式
首先你要知道的是,谈到正则,就只和字符串相关了。在我给你提供的工具中,你输入的每一个字都是一个字符串。其次,如果在一个位置的一个值,不会出现什么变化,那么是不需要规则的。 比如你要用"1"去匹配"1",或者用"2"去匹配"2",直接就可以匹配上。这连python的字符串操作都可以轻松做到。那么在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。
字符组 : [字符组] 在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示 字符分为很多类,比如数字、字母、标点等等。 假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。
正则 |
待匹配字符 |
匹配结果 |
说明 |
[0123456789] |
8 |
True |
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符和"待匹配字符"相同都视为可以匹配 |
[0123456789] |
a |
False |
由于字符组中没有"a"字符,所以不能匹配 |
[0-9] |
7 |
True |
也可以用-表示范围,[0-9]就和[0123456789]是一个意思 |
[a-z] |
s |
True |
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示 |
[A-Z] |
B |
True |
[A-Z]就表示所有的大写字母 |
[0-9][a-f][A-F] |
e |
True |
可以匹配数字,大小写形式的a~f,用来验证十六进制字符 |
字符:
元字符
元字符 |
匹配内容 |
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结尾 |
\W |
匹配非字母或数字或下划线或汉字 |
\D |
匹配非空白符 |
\S |
匹配非数字 |
a|b |
匹配字符a或字符b |
() |
匹配括号内的表达式,也表示一个组 |
[...] |
匹配字符组中的字符 |
[^...] |
匹配除了字符组中字符的所有字符 |
量词
量词 |
用法说明 |
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? |
重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
.^$
正则 | 待匹配字符 | 匹配结果 | 匹配说明 |
海. | 海洋海东海角 |
海洋 |
匹配后面任意一个字符 |
^海 | 海洋海东海角 | 海 | 匹配最前面一个是否是海 |
海$ | 海洋海东海角 | 海角 | 匹配最后一个 |
* + ? { }
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李.? | 李杰和李莲英和李二棍子 |
李杰 |
?表示重复零次或一次,即只匹配"李"后面一个任意字符 |
李.* | 李杰和李莲英和李二棍子 | 李杰和李莲英和李二棍子 |
*表示重复零次或多次,即匹配"李"后面0或多个任意字符 |
李.+ | 李杰和李莲英和李二棍子 | 李杰和李莲英和李二棍子 |
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符 |
李.{1,2} | 李杰和李莲英和李二棍子 |
李杰和 |
{1,2}匹配1到2次任意字符 |
注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李.*? | 李杰和李莲英和李二棍子 | 李杰 李莲 李二 |
惰性匹配 |
字符集[][^]
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李[杰莲英二棍子]* | 李杰和李莲英和李二棍子 |
李杰 |
表示匹配"李"字后面[杰莲英二棍子]的字符任意次 |
李[^和]* | 李杰和李莲英和李二棍子 |
李杰 |
表示匹配一个不是"和"的字符任意次 |
[\d] | 456bdha3 |
4 |
表示匹配任意一个数字,匹配到4个结果 |
[\d]+ | 456bdha3 |
456 |
表示匹配任意个数字,匹配到2个结果 |
分组 ()与 或 |[^]
身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部🈶️数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:
正则 | 待匹配字符 | 匹配 结果 |
说明 |
^[1-9]\d{13,16}[0-9x]$ | 110101198001017032 |
110101198001017032 |
表示可以匹配一个正确的身份证号 |
^[1-9]\d{13,16}[0-9x]$ | 1101011980010170 |
1101011980010170 |
表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字 |
^[1-9]\d{14}(\d{2}[0-9x])?$ | 1101011980010170 |
False |
现在不会匹配错误的身份证号了()表示分组,将\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次 |
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ | 110105199812067023 |
110105199812067023 |
表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14} |
转义符 \
在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成'\\'。
在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\d",字符串中要写成'\\d',那么正则里就要写成"\\\\d",这样就太麻烦了。这个时候我们就用到了r'\d'这个概念,此时的正则是r'\\d'就可以了。
正则 | 待匹配字符 | 匹配 结果 |
说明 |
\d | \d | False |
因为在正则表达式中\是有特殊意义的字符,所以要匹配\d本身,用表达式\d无法匹配 |
\\d | \d | True |
转义\之后变成\\,即可匹配 |
"\\\\d" | '\\d' | True |
如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次 |
r'\\d' | r'\d' | True |
在字符串之前加r,让整个字符串不转义 |
贪婪匹配
贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配
正则 | 待匹配字符 | 匹配 结果 |
说明 |
<.*> |
<script>...<script> |
<script>...<script> |
默认为贪婪匹配模式,会匹配尽量长的字符串 |
<.*?> | r'\d' |
<script> |
加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串 |
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复 +? 重复1次或更多次,但尽可能少重复 ?? 重复0次或1次,但尽可能少重复 {n,m}? 重复n到m次,但尽可能少重复 {n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符 * 是取 0 至 无限长度 ? 是非贪婪模式。 何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在: .*?x 就是取前面任意长度的字符,直到一个x出现re模块下的常用方法re.S 多行匹配,并把\n换行符,当作字符串来处理
#re.S 使用 a = '''asdfsafhellopass: 234455 worldafdsf ''' b = re.findall('hello(.*?)world',a) c = re.findall('hello(?:.*?)world',a,re.S) print(b) print(c)
1 import re 2 ret=re.findall('a','eva egon yuan') #匹配在字符串中的a,返回所有满足匹配条件的结果,放在列表里 3 print(ret) 4 结果: 5 ['a', 'a'] 6 7 import re 8 ret=re.search('a','eva egon yuan').group() 9 print(ret) 10 结果: 11 a 12 # 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以 13 # 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。 14 15 16 import re 17 ret=re.match('a','abc').group() #跟search一样,但是不同的是只在开头匹配有则返回结果,没有则报错 18 print(ret) 19 结果: 20 a
import re ret=re.split('[ab]','abcd') #先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割 print(ret) 结果: ['', '', 'cd'] import re ret=re.sub('\d','H','eva3egon4yuan4',1) #\d匹配数字,把数字匹配成H 后面参数1是只匹配一个。 print(ret) 结果: evaHegon4yuan4 import re ret=re.subn('\d','H','eva3egon4yuan4') #将数字替换程'H',返回元组 print(ret) 结果: ('evaHegonHyuanH', 3) import re obj=re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字 ret=obj.search('abc123eeeee') #正则表达式对象调用search,参数为待匹配的字符串 print(ret.group()) 结果: 123 import re ret = re.finditer('\d','ds3sy47245a') #\d匹配数字 finditer返回一个存放匹配结果的迭代器 print(ret) #<callable_iterator object at 0x000002A323829BE0> print(next(ret).group()) #查看第一个匹配的第一个结果 print(next(ret).group()) #查看第二个匹配的结果 print([i.group() for i in ret]) #用推导式循环查看剩余结果 结果: <callable_iterator object at 0x000002705A43D390> 3 4 ['7', '2', '4', '5']
1 findall的优先级查询:
import re ret = re.findall('www.(baidu|oldboy).com','www.oldboy.com')#这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可 print(ret) 结果: ['oldboy'] import re ret = re.findall('www.(?:baidu|oldboy).com','www.oldboy.com') print(ret) 结果: ['www.oldboy.com']
2 split的优先级查询
import re ret =re.split("\d+","eva3egon4yuan") #切分\d+匹配多个数字 print(ret) 结果: ['eva', 'egon', 'yuan'] import re ret =re.split("(\d+)","eva3egon4yuan") print(ret) #在匹配部分加上()之后所切出的结果是不同的, #没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项, #这个在某些需要保留匹配部分的使用过程是非常重要的。
1、匹配标签
import re ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>") #还可以在分组中利用?<name>的形式给分组起名字 #获取的匹配结果可以直接用group('名字')拿到对应的值 print(ret.group('tag_name')) #结果 :h1 print(ret.group()) #结果 :<h1>hello</h1>
collections模块
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
1.namedtuple: 生成可以使用名字来访问元素内容的tuple #优先掌握
2.deque: 双端队列,可以快速的从另外一侧追加和推出对象 #次要掌握
3.Counter: 计数器,主要用来计数 次要掌握
4.OrderedDict: 有序字典 #重要
5.defaultdict: 带有默认值的字典 #重要
namedtuple
from collections import namedtuple point= namedtuple('point',['x','y']) p=point(1,2) print(p.x) print(p.y) 结果: 1 2
deque
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
from collections import deque point= deque(['x','y','c']) point.append('x') point.append('y') print(point) 结果: deque(['x', 'y', 'c', 'x', 'y'])
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)]) #定义字典是无序的 od = OrderedDict([('a',1),('b',2),('c',3)]) #OrderedDice的key是有序的 print(od) 结果: OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注意,OrderedDict
的Key会按照插入的顺序排列,不是Key本身排序:
>>> od = OrderedDict() >>> od['z'] = 1 >>> od['y'] = 2 >>> od['x'] = 3 >>> od.keys() # 按照插入的Key的顺序返回 ['z', 'y', 'x']
defaultdict
from collections import defaultdict values= [11,22,33,44,55,66,77,88,99] my_dict=defaultdict(list) for value in values: if value>66: my_dict['k1'].append(value) else: my_dict['k2'].append(value) print(my_dict) 结果: defaultdict(<class 'list'>, {'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99]})
使用dict
时,如果引用的Key不存在,就会抛出KeyError
。如果希望key不存在时,返回一个默认值,就可以用defaultdict
:
from collections import defaultdict dd=defaultdict(lambda: 'N/A') dd['key1']='abc' print(dd['key1']) print(dd['key2']) 结果: abc 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})
时间模块
和时间有关系的我们就要用到时间模块,在使用模块之前,应该首先导入这个模块
import time 1.time.sleep(1) #推迟程序指定时间运行,单位为秒 2.time.time() 1502176693.790284 #获取当前的时间戳,时间戳为1970到至今的秒数
表示时间的三种方式
在python中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串
(1)时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的
偏移量,我们运行"type(time.time())",返回的是float类型。
(2)格式化的时间字符串(Format String):‘1999-12-06’
%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 当前时区的名称 %% %号本身
(3)元组(struct_time):struct_time元组共有9个元素:(年,月,日,时,分,秒,一年中第几周,)
索引(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 - 61 |
6 | tm_wday(weekday) | 0 - 6(0表示周日) |
7 | tm_yday(一年中的第几天) | 1 - 366 |
8 | tm_isdst(是否是夏令时) | 默认为-1 |
首先,我们先导入time模块,来认识一下
import time #导入时间模块 #时间戳 s=time.time() print(s) 1502179420.0610144 #时间字符串 x=time.strftime("%Y-%m-%d %X") print(x) 2017-08-08 16:03:40 sx=time.strftime("%Y-%m-%d %H-%M-%S") print(sx) 2017-08-08 16-03-40 #时间元组:localtime将一个时间戳转换为当前时区的struct_time str=time.localtime() print(str) time.struct_time(tm_year=2017, tm_mon=8, tm_mday=8, tm_hour=16, tm_min=3, tm_sec=40, tm_wday=1, tm_yday=220, tm_isdst=0)
小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的
几种格式之间的转换
时间戳--->结构化时间 # import time # time.gmtime() #UTC时间,与英国伦敦当地时间一致 # time.localtime() #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8个小时,TC时间+8个小时 # #等于北京时间 import time s=time.gmtime(1500000) print(s) time.struct_time(tm_year=1970, tm_mon=1, tm_mday=18, tm_hour=8, tm_min=40, tm_sec=0, tm_wday=6, tm_yday=18, tm_isdst=0) set=time.localtime(1500000) print(set) time.struct_time(tm_year=1970, tm_mon=1, tm_mday=18, tm_hour=16, tm_min=40, tm_sec=0, tm_wday=6, tm_yday=18, tm_isdst=0) 结构化时间-->时间戳 time.mktime(结构化时间) import time time_tuple = time.localtime(15000000) s=time.mktime(time_tuple) print(s) 15000000.0
import time s=time.strftime("%Y-%m-%d %X") #(“格式定义”,"结构化时间") 结构化时间参数若不传,则显示当前时间 print(s) 2017-08-08 16:20:37 sx=time.strftime("%Y-%m-%d",time.localtime(15000000)) print(sx) 1970-06-23 #字符串时间-->结构化时间 #time.strptime(时间字符串,字符串对应格式) str=time.strptime('2017-03-16',"%Y-%m-%d") print(str) time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1) str=time.strptime('07/24/2017',"%m/%d/%Y") print(str) time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)
#结构化时间-->%a %b %H:%M:%S %Y串 #time.asctime(结构化时间)如果不传参数,直接返回当前时间的格式化串 import time str=time.asctime(time.localtime(150000000)) print(str) 'Thu Oct 3 10:40:00 1974' import time str=time.asctime() print(str) 'Tue Aug 8 16:45:35 2017' #%a %d %d %H:%M:%S %Y串 -->结构化时间 #time.ctime(时间戳) 如果不传参数直接返回当前时间的格式化串 stre=time.ctime() print(stre) 'Tue Aug 8 16:49:50 2017' str=time.ctime(1500000) print(str) 'Sun Jan 18 16:40:00 1970'
random模块
import random #随机小数 str=random.random() #默认是大于0且小于1之间的小数 print(str) '0.12647078933298062' s=random.uniform(1,3) #设置大于1小于3的小数 print(s) '1.9025904328185215' #随机整数 sx=random.randint(1,5) #大于等于1且小于等于5之间的整数 sd=random.randrange(1,10,2) #大于等于1且小于3之间的整数 print(sx) '4' print(sd) '3' 随机选择一个返回 str=random.choice([1,'23',[4,5]]) #随机列表中的一个数 print(str) ‘[4, 5]’ #随机选择多个返回,返回的个数为函数的第二个参数 s=random.sample([1,'23',[4,5]],2) #列表元素任意2个组合 后面是指定不常 print(s) '[[4, 5], '23']'
#打乱列表顺序 item=[1,3,5,7,9] random.shuffle(item) #打乱列表的次序 print(item) '[3, 7, 1, 5, 9]'
OS模块
''' os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd os.curdir 返回当前目录: ('.') os.pardir 获取当前目录的父目录字符串名:('..') 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.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/" os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为: os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' os.system("bash command") 运行shell命令,直接显示 os.popen("bash command) 运行shell命令,获取执行结果 os.environ 获取系统环境变量 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的大小 '''
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)是创建时间(详细信息参见平台的文档)。
sys模块
sys模块是与python解释器交互的一个接口
sys.argv 命令行参数List,第一个元素是程序本身路径 sys.exit(n) 退出程序,正常退出时exit(0) sys.version 获取Python解释程序的版本信息 sys.maxint 最大的Int值 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 sys.platform 返回操作系统平台名称
序列化模块
什么叫序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化。
比如,我们在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模块提供了四个功能:dumps、dump、loads、load
dic={'k1':'v1','k2':'v2','k3':'v3'} str=json.dumps(dic) #序列化:将一个字典转换成一个字符串的形式 print(str,type(str)) {"k1": "v1", "k2": "v2", "k3": "v3"} <class 'str'> #注意,json转换完的字符串类型的字典中的字符串是由""表示的 import json dic={'k1':'v1','k2':'v2','k3':'v3'} str=json.dumps(dic) str_2=json.loads(str) #反序列化:将一个字符串格式的字典转换成一个字典 #注意,要用json的loads功能处理的字符串类型的字典中字符串必须由""表示 print(str_2,type(str_2)) {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'} <class 'dict'>
import json f=open('json_file','w') dic={'k1':'v1','k2':'v2','k3':'v3'} json.dump(dic,f) #dump方法接收一个文件句柄,直接将字典转换为json字符串写入文件 f.close() #关闭文件 import json f=open('json_file') dic={'k1':'v1','k2':'v2','k3':'v3'} dic2=json.load(f) #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回 f.close() print(type(dic2),dic2)
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) #一串二进制内容 b'\x80\x03}q\x00(X\x02\x00\x00\x00k1q\x01X\x02\x00\x00\x00v1q\x02X\x02\x00\x00\x00k2q\x03X\x02\x00\x00\x00v2q\x04X\x02\x00\x00\x00k3q\x05X\x02\x00\x00\x00v3q\x06u.' dic2=pickle.loads(str_dic) #在转为字典 print(dic2) {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'} import time import pickle struct_time = time.localtime(1000000) print(struct_time) f = open('pickle_file','wb') pickle.dump(struct_time,f) #把文件和内容放一起打开是一堆乱码 f.close() f=open('pickle_file','rb') struct_time2=pickle.load(f) print(struct_time.tm_year) #但是现在输出确实正确的了 time.struct_time(tm_year=1970, tm_mon=1, tm_mday=12, tm_hour=21, tm_min=46, tm_sec=40, tm_wday=0, tm_yday=12, tm_isdst=0) 1970
这里我们要说明一下,json是一种所有的语言都可以识别的数据结构。
如果我们将一个字典或者序列化成了一个json存在文件里,那么java代码或者js代码也可以拿来用。
但是如果我们用pickle进行序列化,其他语言就不能读懂这是什么了~
所以,如果你序列化的内容是列表或者字典,我们非常推荐你使用json模块
但如果出于某种原因你不得不序列化其他的数据类型,而未来你还会用python对这个数据进行反序列化的话,那么就可以使用pickle
shelve
shelve也是python提供给我们的序列化工具,比pickle用起来更简单一些。
shelve只提供给我们一个open方法,是用key来访问的,使用起来和字典类似。
import shelve f=shelve.open('shelve_file') f['key'] = {'int':10,'float':9.5,'string':'Sample data'} #直接对文件句柄操作,跟据key直接存入数据 f.close() f1 = shelve.open('shelve_file') existing=f1['key'] #取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错 f1.close() print(existing)
这个模块有个限制,它不支持多个应用同一时间往同一个DB进行写操作。所以当我们知道我们的应用如果只进行读操作,我们可以让shelve通过只读方式打开DB
import shelve f = shelve.open('shelve_file', flag='r') existing = f['key'] f.close() print(existing)
由于shelve在默认情况下是不会记录待持久化对象的任何修改的,所以我们在shelve.open()时候需要修改默认参数,否则对象的修改不会保存。
import shelve f1 = shelve.open('shelve_file') print(f1['key']) f1['key']['new_value'] = 'this was not here before' f1.close() f2 = shelve.open('shelve_file', writeback=True) print(f2['key']) f2['key']['new_value'] = 'this was not here before' f2.close()
来源:https://www.cnblogs.com/zcfx/p/7299648.html