一、上节补充
之前讲正则的时候涉及到的方法都是findall,也可以用search、match、split、sub等!
search:
import re print(re.search('e','alex like play'))#输出<_sre.SRE_Match object; span=(2, 3), match='e'>,是一个对象 print(re.search('e','alex like play').group())#查看匹配的结果 #注意:search...group和findall的使用对比: print(re.search('al(e)xslik(e)','alex like play').group())#输出结果:alex like,当group为0或者为空,代表默认匹配的全部 print(re.findall('al(?:e)xslik(?:e)','alex like play'))#用findall默认匹配所有的,输出结果:['alex like'] print(re.search('al(e)xslik(e)','alex like play').group(1))#输出结果为e,group里面写1,代表查第一个分组里面的e,如果写2,代表查第二个分组里面的e print(re.findall('al(e)xslik(e)','alex like play'))#就相当于findall的这种用法,返回: [('e', 'e')]
match:
import re print(re.match('e','alex like play'))#显示none,因为match默认是从头开始找,找不到就不往后找了,故报none print(re.match('a','alex like play'))#<_sre.SRE_Match object; span=(0, 1), match='a'>返回的是一个对象 print(re.match('a','alex like play').group())#查看匹配的结果
split:
import re print(re.split('[ab]','abcd'))#输出结果:['', '', 'cd'] 以a或者b为分隔符,对abcd进行分割
sub:
import re print(re.sub('^a','A','alex like play'))#输出结果:Alex like play 把首字母换成大写,如果不加^,就是找到所有的a都换成大写的 print(re.sub('^(w+)(s)(w+)(s)(w+)',r'52341','alex like play'))#输出结果:play like alex print(re.sub('^(w+)(s+)(w+)(s+)(w+)',r'52341','alex like play'))#输出结果:play like alex 多个空格的情况颠倒顺序输出,w+指的是至少匹配一个字母,s+指的是至少一个空格 print(re.sub('^(w+)(W+)(w+)(W+)(w+)',r'52341','alex " + = like ----== play'))#输出结果:play " + = like ----== alex 含非字母字符
注意:
import re print(re.search('companies|company','my company is already done,all companies will be done').group()) #“或”这种匹配,只要前面的匹配成功,那么就不会匹配下面的了。这个返回结果是company ,原因是 拿着companies和company和后面的每一个单词进行匹配,哪个先匹配,就不会再继续匹配下去了
补充:
import re #找出所有的数字 print(re.findall(r'-?d+.?d*',"1-12*(60+(-40.35/5)-(-4*3))"))#输出结果:['1', '-12', '60', '-40.35', '5', '-4', '3'] #匹配所有的整数 print(re.findall(r'-?d+.d+|(-?d+)',"1-2*(60+(-40.35/5.3+1.2)-(-4*3))"))#输出结果:['1', '-2', '60', '', '', '', '-4', '3']
二、常用模块
1、时间模块
time :时间分成三种时间-----1)时间戳 2)结构化的时间 3)格式化的字符串
下面来看代码解说:
时间戳:
import time print(time.time())#输出结果:1501732321.0198536 1970年一直到今天经历过的秒数,通常给计算机用的
结构化的时间(这种调的是对象的一种形式):
import time print(time.localtime())#输出结果:time.struct_time(tm_year=2017, tm_mon=8, tm_mday=3, tm_hour=11, tm_min=54, tm_sec=42, tm_wday=3, tm_yday=215, tm_isdst=0) #如果是取具体的时间呢 print(time.localtime().tm_year) print(time.gmtime())#跟time.localtime()的区别是:跟标准时间差了8小时,用法一样
格式化的字符串
print(time.strftime('%Y-%m-%d %H:%M:%S'))#注意这个分隔符可以任意指定 print(time.strftime('%Y-%m-%d %X'))
这三种格式之间存在着一种转换的关系:因为你有可能拿到从计算机读出来的时间戳,时间戳对我来说又看不懂,所以有这种需要转换成你所需要的看得懂的格式的需求!
如果是时间戳要想转成结构化的时间可以用localtime或者gmtime
print(time.localtime(13213123))#随便输入的秒,以对象的形式输出,转成了结构化形式的时间 print(time.localtime(time.time()))#把当前的时间戳转换成结构化的时间
结构化的时间转换成时间戳
print(time.mktime(time.localtime()))#把当前的结构化时间转换成时间戳,最后以秒的形式输出
结构化的时间怎么转换成格式化的字符串
print(time.strftime('%Y %X',time.localtime()))
把格式化的字符串转换成结构化的时间
print(time.strptime('2017-06-04 11:59:59','%Y-%m-%d %X'))
注意:你在Linux里面看到过这种时间格式:Thu Aug 3 14:31:53 2017
print(time.asctime())
把时间戳转换成 %a %b %d %H %M %S %Y串
print(time.ctime(123123123)) #输出结果:Mon Nov 26 08:52:03 1973
把结构化的时间转换成%a %b %d %H %M %S %Y
print(time.asctime(time.localtime()))#输出结果:Thu Aug 3 14:43:40 2017
2、random模块
做运维的你会发现,当你看日志的时候会有很多很多的ip,你会尝试着把ip禁掉。根据某一个时间点之内,他访问的次数超过一定次数,会把它加到IPtable里面,就是把他禁掉,这就是禁止别人爬虫你网站的行为!被你禁止掉之后,过一段时间,又有大量ip来了,他换了一个代理的ip地址,他是怎么做到的呢?首先他要有多个ip地址。看下面的代码,每次可以随机取一个ip:
import random proxy_ip = [ '1.1.1.1', '1.1.1.2', '1.1.1.3', '1.1.1.4', ] print(random.choice(proxy_ip))
下面是random的一些方法简介:
#!/usr/bin/python # -*- coding:utf-8 -*- import random print(random.random())#(0,1)----float 大于0且小于1之间的小数 print(random.randint(1,3)) #[1,3]大于等于1且小于等于3之间的整数 print(random.randrange(1,3))#[1,3)大于等于1且小于3之间的整数 print(random.choice([1,'23',[4,5]]))#1或者23或者[4,5] print(random.sample([1,'23',[4,5]],2))#sample的意思是每次随机选出两个,拼成一个列表(列表元素任意2个组合) print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716 item = [1,3,5,7,9] random.shuffle(item)#打乱item的顺序,相当于"洗牌" print(item)
完成下面的需求,生成随机验证码:
#!/usr/bin/python # -*- coding:utf-8 -*- import random def v_code(n=5):#定义一个验证码 比方说有五位 res = ''#首先要有个空串,将循环的内容加进去 for i in range(n):#有五位,for要循环五次,每一次要么选择一个数字要么选择一个字母 num = random.randint(0,9)#每一次都要从字母或者数字里面随机拿出一个来 s = chr(random.randint(65,90))#从26个英文字母里面随机拿出一个来 add = random.choice([num,s])#每一次循环要么选择一个数字要么选择一个字母 res+=str(add)#每循环一次添加一次 return res print(v_code(6))#随意指定验证码的位数
3、os模块
我们在初始学习Python的时候,经常有同学将Python和shell联想到一起,怎么用Python完成shell脚本的那种功能呢?shell写脚本直接写的都是操作操作系统的命令!Python里面怎么样去操作你系统的命令呢?os模块可以帮你达到这种效果!
#!/usr/bin/python # -*- coding:utf-8 -*- import os os.getcwd() #获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") #改变当前脚本工作目录;相当于shell下cd os.curdir #返回当前目录: ('.') os.pardir #获取当前目录的父目录字符串名:('..') os.makedirs('dirname1/dirname2') #可生成多层递归目录,相当于make -p 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下为" ",Linux下为" " os.pathsep #输出用于分割文件路径的字符串 win下为;,Linux下为: os.name #输出字符串指示当前使用平台。win->'nt'; Linux->'posix' os.system("bash command") # 运行shell命令,直接显示 os.environ #获取系统环境变量
有关路径:
# os.path.abspath(path) #返回path规范化的绝对路径 print(os.path.abspath('a/b/c.txt'))#输出结果:D:python201720170423s17_zcday06ac.txt print(os.path.abspath('/a/b/c.txt'))#输出结果:D:ac.txt # os.path.split(path) #将path分割成目录和文件名二元组返回 print(os.path.split('E:\a\c.txt'))#输出结果:('E:\a', 'c.txt') # os.path.dirname(path) #返回path的目录。其实就是os.path.split(path)的第一个元素 print(os.path.dirname('E:\a'))#输出结果:E: # os.path.basename(path) #返回path最后的文件名。如何path以/或结尾,那么就会返回空值。即os.path.split(path)的第二个元素 print(os.path.basename('E:\a\a.txt'))#输出结果:a.txt # os.path.exists(path) #如果path存在,返回True;如果path不存在,返回False print(os.path.exists('E:\a'))#输出结果:False print(os.path.exists('D:\python2017\20170423\s17_zc'))#输出结果:True # os.path.isabs(path) #如果path是绝对路径,返回True print(os.path.isabs('E:\day06'))#输出结果:True print(os.path.isabs('day06'))#输出结果:False # os.path.isfile(path) #如果path是一个存在的文件,返回True。否则返回False # os.path.isdir(path) #如果path是一个存在的目录,则返回True。否则返回False # os.path.join(path1[, path2[, ...]]) # 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 print(os.path.join('a','E://b','c.txt'))#输出结果:E://bc.txt print(os.path.join('a','b','c.txt'))#输出结果:ac.txt #注意:如果是Linux下,print(os.path.join('a','/b','c.txt')),输出 /b/c.txt # os.path.getatime(path) #返回path所指向的文件或者目录的最后存取时间 # os.path.getmtime(path) #返回path所指向的文件或者目录的最后修改时间 # os.path.getsize(path) #返回path的大小 print(os.path.getsize(r'D:\python2017\20170423\s17_zc\day06\os模块.py'))#单位是字节,只能查看文件大小,不能查看目录大小
有关路径规划:
#在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。 print(os.path.normcase('c:/windows\system32\'))#输出结果:'c:\windows\system32\' #规范化路径,如..和 / print(os.path.normpath('c://windows\System32\../Temp/'))#输出结果:'c:\windows\Temp' a = '/Users/jieli/test1/\a1/\\aa.py/../..' print(os.path.normpath(a))#输出结果:#/ Users / jieli / test1
完成下面的需求:如何把项目目录加到环境变量里面去?比如你平时安装软件包的时候,下载下来的ngnix,安装完了之后会有个ngnix目录,ngnix算是你的项目目录,ngnix下面还有bin目录等文件!day06是项目路径,他下面有子文件spam.py,目录结构如图!在spam.py这个文件中实现把day06项目目录加入到环境变量中,怎么实现呢?以spam.py这个文件为基准,找到他的上级目录及上上级目录,然后把它变成一个绝对路径!
代码实现:
#!/usr/bin/python # -*- coding:utf-8 -*- import os,sys BASE_DIR=os.path.dirname(os.path.dirname((os.path.abspath(__file__)))) sys.path.append(BASE_DIR)
可不可以用其他方式实现呢?
#!/usr/bin/python # -*- coding:utf-8 -*- import os,sys BASE_DIR = os.path.normpath( os.path.join(__file__, os.pardir, os.pardir) ) print(BASE_DIR) #输出结果:D:python201720170423s17_zcday06
4、sys模块
sys模块.py里面代码如下:
#!/usr/bin/python # -*- coding:utf-8 -*- import sys print(sys.argv)
在命令行执行:
python D:python201720170423s17_zcday06sys模块.py #输出结果:['D:\python2017\20170423\s17_zc\day06\sys模块.py'] C:Userszhaichao.DS>python D:python201720170423s17_zcday06sys模块.py --host 192.168.1.3 --port 8080 #输出结果:['D:\python2017\20170423\s17_zc\day06\sys模块.py', '--host', '192.168.1.3','--port', '8080']
这样argv就可以根据索引取元素了:print(sys.argv[0])
sys模块的其他使用:
1 sys.argv 命令行参数List,第一个元素是程序本身路径 2 sys.exit(n) 退出程序,正常退出时exit(0) 3 sys.version 获取Python解释程序的版本信息 4 sys.maxint 最大的Int值 5 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 6 sys.platform 返回操作系统平台名称
进度条实现:
#!/usr/bin/python # -*- coding:utf-8 -*- import sys import time for i in range(100): sys.stdout.write(' %s' %('#'*i))# 表示跳到行首,第一次打印了一个※,不换行,跳到行首,第二次打印的※覆盖第一次的※,有打印两个※ sys.stdout.flush()#把打印的结果刷出来 time.sleep(0.5) #注意:如果打印不出来,在终端执行
5、shutil模块
主要对文件进行处理,比如拷贝、压缩等!
1)将文件内容拷贝到另一个文件中
#!/usr/bin/python # -*- coding:utf-8 -*- import shutil shutil.copyfileobj(open('test.py','r'), open('test1.py', 'w'))
另一种拷贝方式:
import shutil shutil.copyfile('test.py', 'test2.py')#目标文件无需存在,如果存在,执行之后,内容会覆盖
2)仅拷贝权限。内容、组、用户均不变
import shutil shutil.copymode('f1.log', 'f2.log') #目标文件必须存在
3)仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
import shutil shutil.copystat('f1.log', 'f2.log') #目标文件必须存在
4)拷贝文件和权限
import shutil shutil.copy('f1.log', 'f2.log')
5)拷贝文件和状态信息
import shutil shutil.copy2('f1.log', 'f2.log')
6)递归的去拷贝文件夹
import shutil shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除
import shutil shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #通常的拷贝都把软连接拷贝成硬链接,即对待软连接来说,创建新的文件
7)递归的去删除文件
import shutil shutil.rmtree('folder1')
8)递归的去移动文件,它类似mv命令,其实就是重命名。
import shutil shutil.move('folder1', 'folder3')
9)创建压缩包并返回文件路径,例如:zip、tar
#!/usr/bin/python # -*- coding:utf-8 -*- import shutil
#将D:Python201720170423s17_zcday06bb下的文件打包放置当前程序目录 shutil.make_archive("data_bak", 'gztar', root_dir=r'D:python201720170423s17_zcday06bb')#data_bak打完包之后的包名,gztar指的是压缩的格式,tar包,用gz压缩的结果,root_dir指要压缩的路径
注意:如果将 D:python201720170423s17_zcday06bb下的文件打包放置 /tmp/目录,怎么操作?
import shutil shutil.make_archive("/tmp/data_bak", 'gztar', root_dir=r'D:python201720170423s17_zcday06bb')
base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如 data_bak =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/
format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
root_dir: 要压缩的文件夹路径(默认当前目录)
owner: 用户,默认当前用户
group: 组,默认当前组
logger: 用于记录日志,通常是logging.Logger对象
10)shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:
tarFile归档和解压缩
#归档 import tarfile t = tarfile.open('egon.tar','w') t.add(r'D:python201720170423s17_zcday06os模块.py',arcname='a.bak') t.add(r'D:python201720170423s17_zcday06 andom模块.py',arcname='b.bak') t.close() #解压缩 import tarfile t = tarfile.open('data_bak.tar.gz','r') t.extractall('extract_dir') t.close()
为什么要加arcname呢?如下面加与不加的对比图,如果不加那么在解压的时候连根目录就解压出来了,如果加了,显示如下红框:
zipfile压缩与解压
import zipfile #压缩 z = zipfile.ZipFile('laxi.zip','w') z.write('a.log') z.write('data.data') z.close() #解压 z = zipfile.ZipFile('laxi.zip','r') z.extractall(path='.') z.close()
6、json&pickle模块----这两个模块是用来进行序列化的。
什么是序列化?
程序的运行是在内存中运行的!内存有什么特点?断电数据就没了。下次再运行还得重头再来,所以要把内存的数据存下来,永久的存下来只能网硬盘里面存,那就涉及到一个问题:把内存的数据网硬盘里面存,躺在硬盘上的都是二进制,但是你操作硬盘都是以什么单位来操作的!在程序级别,你以什么操作硬盘---文件处理!文件是操作系统给你提供的一个抽象的概念,来帮你操作硬盘,那文件里面存的又是什么---字符串!也就是说你内存里面的数据要想保存到文件里面,文件里面存得又是字符串,那内存里面有没有可能有字典、列表,这些东西都保存成字符串的形式,你在取出来会有什么问题?再取出来还是字符串的形式,就没有原来的数据结构的概念了!所以你要存一定要把数据结构存下来,那现在就矛盾了。内存当中是数据结构,文件里面是字符串,最终肯定还是要保存成字符串,那怎么解决这个问题?这就需要加一个中间过程!在内存的数据往硬盘转的过程来一个中间环节,把内存中的数据按照某种固定的格式转换成字符串,取的时候再按照这种固定的格式转换成我想要的字典、字符串、列表!
你玩过vmware么?里面有个做快照的功能:当你虚拟机运行到一定状态的时候,里面有大量的数据肯定不只字符串,你做快照实际上就是把他们保存到硬盘中,他是按照某种固定的格式转成字符串的,读的时候也是按照某种格式转成字典等我想要看的格式。----这就叫序列化!
为什么要序列化?
1、持久保存状态 2、跨平台数据交互: 比如你们公司是可能全用Python么,这种状况非常少,你用Python单独做了一个功能,那你怎样和其他的Java程序对接呢?一个语言写的程序与另外一种语言写的程度对接,对接的只能是数据,我程序处理完的数据交给你,数据一定是有结构的,比如列表、元组、字典,Python的字典Java能识别么?不能!所以需要把Python的字典转成一种通用的格式,Java跟Python都认识,转完这格式之后就可以把他交给Java了,Java在按照同样的格式在转成Java能认识的字典!
如何序列化之json和pickle:如果要跨平台要用Python,如果想Python自己玩,就用pickle
有同学会有疑问,这种不是可以用我们之前学过的一个函数eval来实现么?比如下面的场景,写入文件之前是字典,读出来还是字典:
#!/usr/bin/python # -*- coding:utf-8 -*- dic = { 'name':'alex', 'age':9000, 'height':'150cm', } # with open('a.txt','w') as f: # f.write(str(dic)) with open('a.txt','r') as f: res = eval(f.read()) print(res,type(res))
再看下面的代码:
x = "[null,true,false,1]"#比方说从文件里面已经读出字符串 eval(x)#用eval进行转化,结果报NameError: name 'null' is not defined
改成如下 呢?
x = "[None,True,1]" res = eval(x) print(res,type(res)) #eval是把这个字符串里内容读出来相当于在当前位置执行一下,得到的就是一个列表,不能读出我想要的结果 #我能不能用他根据第几个值来取值?----是可以的? print(res[1],type(res))
思考:那为什么上面[null,true,false,1]的不行?因为Python里面没有null这个类型!说明eval不支持统一的这种格式!那我们可以用json这个模块来实现!
import json x = "[null,true,false,1]" res = json.loads(x) print(res) 输出结果: [None, True, False, 1]
[null,true,false,1]---->[None, True, False, 1]把Python不认识的格式转换成了Python识别的格式!如下是json对应的Python的类型:
上面的json达到的目的是否可以用picked完成呢?不能----说明pickle无法读出此种类型,因为他只识别Python类型
import pickle x = "[null,true,false,1]" res = pickle.loads(x) print(res) #输出结果:TypeError: a bytes-like object is required, not 'str' 要求是bytes的,不能是字符串的
那下面我们看看这两个模块是怎么玩的?
首先来Jason序列化的过程:dic ----->res=json.dumps(dic)---->f.write(res)
import json dic = { 'name':'alex', 'age':9000, 'height':'150cm' } res = json.dumps(dic) print(res,type(res))#直接加上你要序列化的对象 #输出结果:{"height": "150cm", "age": 9000, "name": "alex"} <class 'str'> #json.dumps实际上就是把字典转换成一个字符串,但这个字符串是json能识别的字符串 #拿到这个字符串我就可以写到文件里面去了! with open('a.json','w') as f: f.write(res)
下面怎么把数据读出来呢?反序列话的过程:f.read()---->res=json.loads(res)---->dic=res
with open('a.json','r') as f: dic = json.loads(f.read()) print(dic,type(dic)) #输出结果:{'height': '150cm', 'age': 9000, 'name': 'alex'} <class 'dict'> print(dic['name']) #输出结果:alex
上面的过程比较麻烦,我们还可以用下面的json便捷操作(转换的同时往文件里面写):
import json dic = { 'name':'alex', 'age':9000, 'height':'150cm' } json.dump(dic,open('b.json','w')) #以上把dic存入到文件,下面对文件内容的输出 import json res = json.load(open('b.json','r')) print(res,type(res))
7、pickle模块
#!/usr/bin/python # -*- coding:utf-8 -*- import pickle dic = { 'name':'alex', 'age':13 } print(pickle.dumps(dic)) #输出结果:b'x80x03}qx00(Xx04x00x00x00nameqx01Xx04x00x00x00alexqx02Xx03x00x00x00ageqx03K u.'
与json不同的是,他转换成了bytes类型,要是往文件里面写该怎么写?
with open('a.pkl','wb') as f: f.write(pickle.dumps(dic))#查看写进去的内容都是bytes格式的
写完之后如何读出来呢?
with open('a.pkl','rb') as f: d = pickle.loads(f.read()) print(d,type(d)) #输出结果:{'age': 13, 'name': 'alex'} <class 'dict'>
跟json一样,是否存在更简单的dump和load方法呢?
import pickle dic = { 'name':'alex', 'age':13 } pickle.dump(dic,open('b.pkl','wb')) #以上为写文件,以下为读文件 res = pickle.load(open('b.pkl','rb')) print(res,type(res)) #输出结果:{'name': 'alex', 'age': 13} <class 'dict'>
pickle的特殊之处在于可以序列化Python任意的对象。
比如我想把下面的函数给序列化:
def func(): print('from func')
首先来看json是否能实现?---说明不能实现,json不支持Python的函数类型
import json def func(): print('from func') json.dumps(func) #报错TypeError: <function func at 0x00A36738> is not JSON serializable
那来看看pickle呢?---说明支持
import pickle def func(): print('from func') f = pickle.dumps(func) print(f)#输出结果:b'x80x03c__main__ func qx00.'
既然支持,那我们就可以完成下列对函数func序列化操作:
def func(): print('from func') import pickle pickle.dump(func,open('c.pkl','wb'))
进行反序列化:
def func(): print('from func') import pickle res = pickle.load(open('c.pkl','rb'))
res()#可以进行执行,输出结果:from func print(res)#获取函数的地址:<function func at 0x005B6738>
如果我单独创建Python文件,进行反序列化会有什么问题呢?----报错
#!/usr/bin/python # -*- coding:utf-8 -*- import pickle pickle.load(open('c.pkl','rb')) #报错:AttributeError: Can't get attribute 'func' on <module '__main__' from 'D:/python2017/20170423/s17_zc/day06/pickle模块/pickle反序列化.py'>
如果我反序列化的是字典、元组等,是不会有问题的。但是我现在序列化的是函数,那现在就有问题了!为什么?
序列化的时候从这句pickle.dump(func,open('c.pkl','wb'))开始序列化的,func是内存地址,这个内存地址指向内存一段函数,我的值是那个函数,现在序列化的是函数名,相当于把内存地址给dump下来了,现在文件一运行完,内存地址确实是保存下来了,但函数的数据从内存中已经释放掉了,然后你在另外一个文件中反序列化,你是往内存中加载了一个内存地址,这个内存地址试图要找到他对应的那个数据,但那块数据已经没了。所以现在出错了。那在原文件为什么没问题呢?实际上也是有问题的,比如我把func函数定义去掉,也是报相同的问题。
对于其他普通的数据类型为什么没问题呢?因为类似于字典、元组,你序列化的是他们本身的数据类型,所以在反序列化的时候相当于在内存中重新定义了一下数据类型。
8、shelve模块
这个模块也和序列化有关,和pickle是一样的,可以序列化所有的Python类型,但是他只是换了一种形式进行序列化。
#!/usr/bin/python # -*- coding:utf-8 -*- import shelve f = shelve.open(r'sheve.txt') f['student1'] = {'name':'egon','age':18,'height':'180cm'} print(f['student1'])#输出结果:{'age': 18, 'height': '180cm', 'name': 'egon'} print(f['student1']['name'])#输出结果:egon f.close()
9、xml模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。
xml的格式如下,就是通过<>节点来区别数据结构的(a.xml):
<?xml version="1.0"?> <data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year>2008</year> <gdppc>141100</gdppc> <neighbor name="Austria" direction="E"/> <neighbor name="Switzerland" direction="W"/> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year>2011</year> <gdppc>59900</gdppc> <neighbor name="Malaysia" direction="N"/> </country> <country name="Panama"> <rank updated="yes">69</rank> <year>2011</year> <gdppc>13600</gdppc> <neighbor name="Costa Rica" direction="W"/> <neighbor name="Colombia" direction="E"/> </country> </data>
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:
# print(root.iter('year')) #全文搜索,不考虑层级结构 # print(root.find('country')) #在root的子节点找,只找一个 # print(root.findall('country')) #在root的子节点找,找所有
首先来看iter,把所有的year遍历出来
#!/usr/bin/python # -*- coding:utf-8 -*- import xml.etree.ElementTree as ET tree = ET.parse("a")#先解析一下这个文件 root = tree.getroot()#获取根节点,就是a.xml里面的 data #print(root.iter('year'))#返回的是一个迭代器<_elementtree._element_iterator object at 0x010F8ED0> for i in root.iter('year'): #print(i)#全文搜索year print(i.tag,i.text,i.attrib) #tag指的是标签 text指的是标签中间加的内容 attrib指的是标签中可以设置的属性,最后返回的结果是字典格式的,没有属性所以返回空字典 输出结果: year 2008 {} year 2011 {} year 2011 {}
来看find:
import xml.etree.ElementTree as ET tree = ET.parse("a")#先解析一下这个文件 root = tree.getroot()#获取根节点,就是a.xml里面的 data print(root.find('year'))#输出结果:None 因为他是在root下面的子节点找
print(root.find('country'))#输出结果:<Element 'country' at 0x00868EA0>
#这么多country,怎么证明输出的是第一个呢?
print(root.find('country').attrib)#输出结果:{'name': 'Liechtenstein'} 看到这个属性和a.xml对比,发现是第一个
最后看findall:
import xml.etree.ElementTree as ET tree = ET.parse("a")#先解析一下这个文件 root = tree.getroot()#获取根节点,就是a.xml里面的 data print(root.findall('country')) 输出结果: [<Element 'country' at 0x00768EA0>, <Element 'country' at 0x00766030>, <Element 'country' at 0x00766120>]
怎么样来遍历整个xml文档:---遍历是指把每个节点及子节点都取出来
import xml.etree.ElementTree as ET tree = ET.parse("a")#先解析一下这个文件 root = tree.getroot()#获取根节点,就是a.xml里面的 data for country in root:#遍历根节点下的子节点country print('======>',country.attrib['name']) for item in country:#遍历country下面的子节点 #print(item) print(item.tag,item.text,item.attrib)
需求:把所有国家的年加1,同时加上属性update=yes ,最终达到效果:<year updated="yes">2011</year>
#!/usr/bin/python # -*- coding:utf-8 -*- import xml.etree.ElementTree as ET tree = ET.parse("a")#先解析一下这个文件 root = tree.getroot()#获取根节点,就是a.xml里面的 data for year in root.iter('year'): year.text=str(int(year.text)+1) year.set('update','yes') #注意执行之后只是在内存中进行了修改,要是写到文件里面得加上下面的: tree.write('b.xml')#这样就会新生出b.xml文件,如果想在原文件改,
如何删除大于2010年的year,怎么操作呢?
import xml.etree.ElementTree as ET tree = ET.parse("a")#先解析一下这个文件 root = tree.getroot()#获取根节点,就是a.xml里面的 data for country in root:#首先找到year的父节点 print(country) print('=====>',country.find('year'))#找到每个country节点下面的year year = country.find('year') if int(year.text) > 2010: country.remove(year) tree.write('d.xml')
如何添加一个节点呢?比如添加如下节点:
<year2 update="yes">2012</year2></country>
import xml.etree.ElementTree as ET tree = ET.parse("a")#先解析一下这个文件 root = tree.getroot()#获取根节点,就是a.xml里面的 data for country in root:#首先找到year的父节点 year = country.find('year') if int(year.text) > 2010: year2 = ET.Element('year2') year2.text = str(int(year.text)+1) year2.set('update','yes') country.append(year2) tree.write('e.xml')
自己创建xml文档:
#!/usr/bin/python # -*- coding:utf-8 -*- import xml.etree.ElementTree as ET new_xml = ET.Element("namelist")#获取初始节点,拿到对象new_xml name = ET.SubElement(new_xml, "name", attrib={"enrolled": "yes"})#往new_xml这个对象里面添加子节点,名称为name,属性为"enrolled": "yes" age = ET.SubElement(name, "age", attrib={"checked": "no"}) sex = ET.SubElement(name, "sex") sex.text = '33' name2 = ET.SubElement(new_xml, "name", attrib={"enrolled": "no"}) age = ET.SubElement(name2, "age") age.text = '19' et = ET.ElementTree(new_xml) # 生成文档对象 et.write("test.xml", encoding="utf-8", xml_declaration=True) ET.dump(new_xml) # 打印生成的格式
10、hashlib模块
哈希模块主要是做文本校验的。说一下他的根本的用法:
#!/usr/bin/python # -*- coding:utf-8 -*- import hashlib m = hashlib.md5() m.update('23fdsfafdfdf你愁啥啊'.encode('utf-8')) print(m.hexdigest())#输出结果:798b394282d42108c05afb576bc589a4
哈希的特点:
1.只要你校验的内容相同则hash运算结果相同,内容稍微改变则hash值则变
2.不可逆推
3.相同算法:无论校验多长的数据,得到的哈希值长度固定。
怎么样来校验一个文件呢?如果文件内容变了,最后的校验结果也 变了
import hashlib m = hashlib.md5() with open(r'D:python201720170423s17_zcday06data_bak.tar.gz','rb') as f: for line in f: m.update(line) md5_num = m.hexdigest() print(md5_num)#输出结果:c9e35048b2ca373718df07af3ad1a7d5
以上加密算法虽然依然非常厉害,但有时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。 下面的代码比md5更安全一些。但是也有方法将其破解掉--撞库
import hashlib s = hashlib.sha256() s.update('helloword'.encode('utf-8')) print(s.hexdigest())#输出结果:0b322d15ea034793a8646baa1744ffacbdf7f959b66c68970f032c4ac8b9c8cb
模拟撞库破解密码
import hashlib #首先得有个密码列表 passwds=[ 'alex3714', 'alex1313', 'alex94139413', 'alex123456', '123456alex', 'a123lex', ] def make_passwd_dic(passwds): dic={} for passwd in passwds: m=hashlib.md5() m.update(passwd.encode('utf-8')) dic[passwd]=m.hexdigest() #以密码作为key,以最后校验的结果做为value return dic#返回密码字典 def break_code(cryptograph,passwd_dic): for k,v in passwd_dic.items():#for循环这个字典 if v == cryptograph:#用value和这个校验结果走比对 print('密码是===>