模块与包
2019-08-21 13:38:03 0 举报
AI智能生成
python知识点:模块、包以及异常处理
作者其他创作
大纲/内容
模块*****
定义:
别人写好的,具有相同类别的一组功能<br>具体样式可能是 文件夹/py文件/C语言编译好的一些编译文件
作用:
1.分类管理方法
2.节省内存
3.提供更多的功能
分类:
内置模块
跟着python解释器一起安装的那些方法
第三方模块/扩展模块
没在安装python解释器一起装上的那些功能
自定义模块
自己写的功能如果是个通用的功能,就可以当做一个模块
模块的导入:
import 文件名 #导入一个py文件的名字,但不加.py
import 一个模块,相当于执行了这个模块所在的py文件
一个模块不会被重复导入
所有的模块导入都应该尽量放在这个文件的开头
导入模块的过程发生了什么?
1.找到这个my_module模块
2.创建一个属于my_module的内存空间
3.从上到下执行my_module的代码
4.将这个模块所在的内存空间建立一个个my_module之间的引用关系
模块有自己的内存空间,与本文件(即自己写的代码,导入模块的文件)的内存空间是相互隔离的,只是可以通过模块名引用 ##
导入多个模块:
import os, my_module (×)
import os<br>import my_module (√)
模块导入顺序
# 先导入内置模块<br><br> # 再导入第三方模块<br><br> # 最后导入自定义模块
使用方法:
模块名.方法名() #my_module.login()
模块名相当于一个变量来使用,因此,模块的名字必须遵循变量的命名规则
一般模块名,都是小写字母开头
模块的重命名:
import my_module as m
并非是把模块的名字改了,只是把引用模块的变量名改了
导入模块的指定部分
语法:from 模块名 import 需要导入的变量/方法名
任然相当于执行了一遍整个py文件
from my_module import login的时候发生了什么?
1.找到这个my_module模块
2.创建一个属于my_module的内存空间
3.从上到下执行my_module的代码
4.知道了要imprt的是login这个方法,那么就在本文件中<br>创建一个变量login,指向模块命名空间中的login方法
注意:
# 导入了什么就能使用什么,没有导入的变量不能使用 <br>
# 没有导入不等于不存在,只是没有建立本文件到模块中其他名字的引用
# 当模块中导入的方法或变量与本文件中的重名时,那么这个名字只代表最后一次对他赋值的方法或者变量
# 被导入的模块不能反向引用本文件中的变量和方法
# 在文件中对全局变量的修改,完全不会影响对模块的引用<br><br>要对模块中的变量进行修改,只有带着模块名的限定时(my_module.name = 'Zoey')才能修改,不过都不会这么做的
在本文件中对name重新赋值,只会断开name到模块中的name = 'Alex' 的引用,并将本文件的name赋值为'太亮',而模块中的name还是'alex'
重命名:
语法:from 模块名 import 需要导入的变量/方法名 as 新名字
导入多个变量/方法:
语法:from 模块名 import 变量名,方法名(每个用,隔开)
PEP8允许在一行导入多个变量
导入多个再重命名:
语法:from my_module import login as l,name as n
from 模块 import * :
模块中的所有东西都可以引用了,在本文件中都有一个与模块中的东西相同的变量名来引用
模块中写__all__可以控制*导入的内容
__all__ = ['login','name'] # 只让*导入login和name,别的都导入不了<br>必须是列表,列表中的变量名、方法名也必须是字符串类型,不能直接写login、name这样
模块其他知识
1.把模块当成脚本运行
运行一个py文件的两种方式
①以模块的方式运行:import my_module
②以脚本的形式运行:即直接用 pycharm 运行或者用 cmd 运行
__name__内置变量
模块方式执行时__name__ = 'my_module'
直接运行时__name__ = '__main__'
模块代码中没有直接封装在函数或者类,不需要调用就能执行的代码都应该写在 if __name__ = 'main': 这句话下面(快捷写法main写完按TAB键)。这样,模块在作为脚本运行时就会正常运行,而在以模块的形式导入其他文件中时,导入后就不会自动输出一些有的没的
2.模块搜索路径
模块的搜索路径指的就是在导入模块时需要检索的文件夹
导入模块时查找模块的顺序是:<br>①先从内存中已经导入的模块中寻找<br>②内置的模块<br>③环境变量sys.path中找
能否导入自定义模块,要看sys.path列表中是否有要调用的文件的绝对路径<br>可以用sys.path.append('文件路径')来添加<br>可以用del sys.path[-1]来删除新添加的路径
3.pyc编译文件
4.重新加载模块
模块只导入一次,不会重复导入<br>因此,模块导入之后,再修改模块,即使再重新导入也与我无关了
要想重新加载只有用importlib.reload(模块名),就可以强制重新加载一遍模块<br>此方法就只是用来玩一下,不可以用在开发中!!!
5.模块的循环引入
在模块导入中,坚决不要发生循环导入问题<br>如果发生了循环导入,则会发现,明明写在模块的方法,却提示找不到<br># 多个文件之间有导入关系,画个图看看导入关系是否成环
6.整一个base_path作为项目的基本路径,使导模块更方便
print(__file__)会输出执行此语句的文件所在的绝对路径
7.永远不要给一个文件起一个与所有已知模块相同的文件名,不然会影响模块导入使用的<br> <font color="#f15a23">(注意有坑!!!)</font>
包***
1.定义:
集合了一组py文件,提供了一组复杂功能的,有__init__的文件夹
2.作用:
提供的功能比较复杂,一个py文件写不下
3.包中都有什么:
至少拥有一个__init__.py
4.直接导入模块:
①import 包.包.模块<br> 使用时:包.包.模块.变量
②from 包.包 import 模块(推荐使用)<br>使用时:模块.变量
从包中导入模块,要注意这个包所在的目录是否早sys.path中
5.导入包<br>(读框架源码的时候会用到)
导入包,相当于执行了这个包下面的__init__.py文件<br>如果希望导入包之后,模块能正常使用,需要自己去完成__inti__文件的开发<br>可以设计一下__init__.py来完成一些模块的导入:在init里面导入下一层包/模块<br>
包中模块的绝对导入:
from glance import api<br>from glance.api import policy<br>绝对导入时,当前执行文件与包的相对位置不能变
包中模块的相对导入:
from . import api<br>from . import policy<br># .表示当前目录位置(指正在编辑的__init__文件与需要导入的模块是否是在相同目录)<br># 使用了相对导入的模块,只能被当做模块执行,不能被当作脚本执行(即不能用pycharm直接run)
模块的总结
补充:项目开发规范
常用模块1<br>(方法模块)
1.re模块*****
正则表达式
概念:一种匹配字符串的规则
作用:可以定制一个规则,①来确认某一字符串是否符合规则;<br> ②从大段的字符串中找到符合规则的内容
在程序领域的应用:
①登录注册页的表单验证
②爬虫
③自动化开发:日志分析
正则表达式语法:
字符组:
[小-大]规定一个位置上能出现的内容(大和小是指ASCII码的值)<br> [1-9A-Zabc] ==》匹配一个字符<br> [a-z][1-9][A-Z] ==》 匹配三个字符
元字符:
/d ==[0-9] 即/d也表示匹配数字 (digit)
/w == [0-9A-Za-z_] 即/w也表示匹配一个数字字母下划线 (word)
/s ==[/n /t] 即/s表示匹配所有的空白符,包括回车、空格、制表符tab (space)<br> # /n表示匹配回车<br> # /t表示匹配制表符
/D 表示匹配非数字
/W 表示匹配非数字字母下划线
/S 表示匹配非空白符
^ 表示匹配字符串的开始 ****
$ 表示匹配字符串的结尾 ****
^hello$ ==》 只有一个hello可以匹配 (多个hallo就无法匹配)
. 匹配除换行符以外的任意字符(如果.要作为字符本身来匹配的话,需要加转意符/.)<br> #在爬虫中用的多,在表单验证是很少用
a|b 表示匹配a或者b,任然是匹配一个字符
!!若两个字符有重叠部分的,要把长的放前面
() 分组
[...] 匹配字符组中的字符
[^…] 匹配字符组中的字符以外的
量词:
放在字符之后对字符进行约束,一个量词约束一个元字符或者一个字符组
? 重复零次或一次<br><br>+ 重复至少一次 <br><br>* 重复至少零次<br><br>{n} 重复n次<br><br>{n,} 重复至少n次<br><br>{n,m} 重复n到m次
量词匹配的一个原则:贪婪匹配 会给你匹配尽量多的次数<br>(贪婪匹配内部使用的是回溯算法)
特殊用法和现象:
1.?的用法:
①紧跟在字符后面:字符重复一次或零次
②跟在量词后面:取消贪婪匹配,进行惰性匹配,在能匹配上的情况下尽量少的重复量词所约束的字符<br>例如:李.{1,3} ==> <font color="#fdb813">李杰</font>和<font color="#fdb813">李莲</font>英;李.{1,3}和 ==> <font color="#fdb813">李莲英和</font>他的小弟<br>最常用: .*?x ==>匹配任意字符,直到找到x ==> 爬虫中很常见
2.字符组中一些特殊字符会现原形,不用转义
[ () +*/?$. ] 会现原形<br> 而^以及所有带\的都不会,只表示其本身含义<br>[-]只有写在字符组首位的时候才表示普通负号,写在其他位置时都默认表示范围
3.要匹配正则表达式中的一些特殊字符本身,需要转义
在正则表达式中要匹配字符串/n ==> 正则表达式就应该写为//n ,在python代码中就直接两个都加r写成 r'/n' ==>r'//n'<br>另外还如 /( 、//t、/^等等也是一样的
re模块:<br>操作字符串的模块
1)匹配的方法
findall *****
语法及参数:re.findall('正则表达式','需要操作的字符串')
返回值类型:列表
返回值个数:1
返回值内容:所有匹配上的项(没匹配上就是一个空列表)
<font color="#f15a23">Python中分组遇见findall: 分组优先显示!!有坑!!!<br>findall会优先显示正则表达式分组中的内容,要取消分组优先,需要在分组中最前面加上?:</font><br>例:ret = re.findall('www.(baidu|oldboy).com','www.oldboy.com') --> ret = 'oldboy'<br>ret = re.findall('www.(?:baidu|oldboy).com','www.oldboy.com') --> ret = 'www.oldboy.com'<br>
search *****
语法及参数:re.search('正则表达式','需要操作的字符串')
返回值类型:匹配上了 --> 正则匹配结果对象<br> 没匹配上 --> None
返回值个数:1
返回值内容:匹配上了 --> 对象<br> 没匹配上 --> None
返回的对象通过group来获取匹配到的第一个结果<br>ret = re.search('/d+','1416asdg2342sdf2').group() ==> ret = 1416<br> #用findall能获取所有匹配结果,用search只能获取第一个
<font color="#f15a23">Python中分组遇见search:<br>当search正则表达式中有分组时,可以通过给group(n)传参,拿到正则表达式中第n个分组所对应的匹配的内容<br>不传n则默认是0,则拿到完整匹配结果</font>
match **
与search语法、返回值都完全一样,不过match是从头匹配(即默认正则表达式最前面有一个^),因此匹配结果可能不同
对比:
ret = re.match('/d+','1416asdg2342sdf2').group() ==> ret = 1416
ret = re.match('/d+','sa1416asdg2342sdf2') ==> ret = None <br>(#没匹配上就没有 .group()方法。因此常用写法是if ret:print(ret.group())
2)替换的方法
sub ***
语法及参数:re.sub('正则表达式','替换成什么','需要操作的字符串',替换的次数)<br> #如不指定替换的次数,默认全替换
返回值类型:字符串
返回值个数:1
返回值内容:替换后的字符串
subn ***
语法及参数:re.subn('正则表达式','替换成什么','需要操作的字符串')
返回值类型:元祖
返回值个数:1
返回值内容:(替换后的字符串,替换的次数)组成的元祖
3)切割的方法
split ***
语法及参数:re.split('正则表达式','需要操作的字符串')<br># 以所有满足正则表达式的项为'刀'来切割需要操作的字符串
返回值类型:列表
返回值个数:1
返回值内容:所有切割后的字符串组成的列表
<font color="#f15a23">Python中分组遇见split:<br>split中将正则表达式用分组括号括起来,返回值将保留切割的'刀'。<br>如果正则表达式只有部分括起来了,那么就只将括起来的部分匹配的内容作为'刀'保留</font><br>ret = re.split('\d+','alex83egon20') -->ret = ['alex','egon','']<br>ret = re.split('(\d+)','alex83egon20taibai40')-->ret = ['alex','83','egon','20','']<br>
4)进阶方法<br>(爬虫和自动化开发必备)
compile *****<br>(对复杂正则表达式预编译)
正则表达式在python中运行过程:<br>\d*\s?adsf*. --> 将正则表达式编译为python能理解的代码 --> 执行代码
语法及参数:re.compile('正则表达式').findall('需要操作的字符串')<br># 将增则表达式编译为代码储存起来,以便后面每次都可以直接用
用法:ret = re.compile('很长的正则表达式')<br> ret.findall('需要操作的字符串')<br> ret.search('需要操作的字符串')等等
作用:节省时间(时间效率)<br>只有多次使用同一个正则表达式的时候,才会提高代码效率
finditer *****<br>(对海量数据进行处理)
一个海量数据中,匹配正则表达式的字符串结果也是一个庞大的数据量,finditer就将这个庞大数据量的<br>匹配结果转化成为一个可迭代对象,就可以使用for循环来进行下一步处理了
用法:ret = re.finditer('正则表达式','需要操作的海量数据')<br> for r in ret:<br> print(r.group())
相关值的类型:ret 可迭代对象的内存地址<br> r 正则匹配结果对象
作用:节省内存(空间效率)
分组命名
语法:(?P<name>正则表达式) 表示给分组起名字<br> (?P=name) 表示使用这个分组
通过索引使用分组
\1 表示使用第一组,匹配到的内容必须和第一组相同
2.random模块*****<br> (随机)
应用:抽奖、彩票、发红包、验证码
1)获取随机小数的方法
①random<br>获取0-1之间的随机小数
语法:random.random()
②uniform<br>获取n到m之间的随机小数
语法:random.uniform(n,m)
2)获取随机整数的方法<br> *****
①randint<br>获取[n,m]闭区间内的随机整数<br> 可以取到m
语法:random.randint(n,m)<br> 没有step,只有两个参数
②randrange<br>获取[n,m)左闭右开区间内的随机整数<br> 顾头不顾尾~可以有步长<br>step= 2,就可以获取一定范围的奇数/偶数
语法:random.randrange(n,m,step)
3)随机抽取<br>
①choice<br>随机抽取一个值<br><font color="#f15a23">元祖/列表/字符串/range()</font><br>
语法:<br>a=['wahaha',12,(3,'wangzai')]<br>random.choice(a)
②sample<br>随机抽取n个值<br><font color="#f15a23">元祖/列表/字符串/range()</font><br>
语法:<br>a=['wahaha',12,(3,'wangzai')]<br>random.sample(a,n)
4)打乱顺序<br>在原列表的基础上做乱序<br>只能操作<font color="#f15a23">列表</font>,dict都不行
shuffle<br>语法:<br>a=['wahaha',12,(3,'wangzai')]<br>random.shuffle(a)
3.time模块****<br>(描述/获取时间)
1)三种时间的格式
①时间戳时间(格林威治时间/float数据类型时间)(是给机器用的)<br>语法:time.time()<br>获取的是伦敦时间1970年1月1日0:0:0(北京时间1970年1月1日8:0:0)起至当前所经历的秒数
②结构化时间:是一个时间对象,可以通过.属性名来获取对象的值,结构类似于一个元祖。(前两种格式的转换桥梁)<br>语法:time.localtime()<br> 通过结构化时间,可以将时间戳时间转换为格式化时间
③格式化时间(字符串时间/str数据类型时间)(给人看的)<br>语法:time.strftime(%Y-%m-%d) #2019-08-14<br> time.strftime(%H:%M:%S) #21:33:50<br>可以根据你需要的格式来显示时间
%y 两位数的年份表示(00-99)<br>%Y 四位数的年份表示(000-9999)<br>%m 月份(01-12)<br>%d 月内中的一天(0-31)<br>%H 24小时制小时数(0-23)<br>%I 12小时制小时数(01-12)<br>%M 分钟数(00=59)<br>%S 秒(00-59)
2)三种格式之间的转换
4.sys模块****<br>(与python解释器交互)
1)sys.path:当前python解释器寻找模块的路径<br>
2)sys.modules:当前文件在内存中导入的所有路径
用的最多的地方:反射本模块的时候用,sys.modules[__name__],其他时候几乎不会用到
3)sys.exit():强制python解释器结束程序
基本不会用,都直接用内置函数exit()
4)sys.argv:
①返回值类型:列表(在执行python文件的时候,在python指令之后的所有内容,以空格隔开都会成为argv返回值列表的项)
②返回值解读:第一个元素是执行这个文件时,写在python命令后面的第一个值。<br>之后的元素,执行Python启动的时候,在python命令后面写其他内容,都会被添加到这个列表中
③应用:执行py文件的时候先做一个登录认证,认证成功才给你执行这个py文件,认证不成功就直接给退出<br>(可以不用再执行python文件之后再input认证)
这样做的优势:一是符合运维人员的工作习惯;二是可以避免input阻塞,顺畅系统调度,提高系统执行效率<br>
代码:<br>name = sys.argv[1]<br>pwd = sys.argv[2]<br>if name == 'alex' and pwd == 'alex3714':<br> print('执行以下代码')<br>else:<br> exit()
5.os模块*****<br>(与操作系统交互)
1)os.系列
(1)和工作目录相关的
①os.getcwd():获取当前文件的工作路径(即在哪里执行的当前文件)
②os.chdir('新路径'):修改当前文件的工作路径(不管在哪执行,都把工作路径给强制改为新路径)<br>相当于shell下cd
(2)创建/删除文件、文件夹相关的****
①os.mkdir('dirname'):创建单级目录(这个文件夹与当前文件同级)
应用:网盘
②os.makedirs('dir1/dir2/dir3'):创建多级目录<br>
还可以传第二个参数exist_ok = True,则如果创建的文件已经存在,也不会报错。该参数默认值为False。
③os.rmdir('dir1/dir2/dir3'):删除单级目录
#只能删除一级文件夹(即dir3)
#只能删除空文件夹,非空的删不了
④os.removedirs('dir1/dir2/dir3'):删除多级目录
#递归向上删除文件夹,直至遇到非空文件夹为止
#只能删除空文件夹,非空的删不了。如果dir1空,dir2非空,dir3空,运行结果就只是删dir3
⑤os.remove('aaa.py'):删除一个文件
⑥os.rename("oldname","newname"):重命名文件/目录
⑦os.listdir('路径')*****
列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表格式返回
(3)和操作系统差异相关的
①os.stat('path/filename'):获取文件/目录信息
②os.sep:查看当前所在的操作系统的目录分割符
Windows:\<br>Linux:/
③os.linesep:输出当前平台使用的行终止符
win:"\r\n"<br>Linux:"\n"
要打印出确切的符号,应该写为:print([os.linesep]),不然只会看到一个空行
④os.pathsep:查看用于分割文件路径的字符串
win下为;<br>Linux下为:
⑤os.name:查看字符串指示当前使用平台
win:'nt'<br>Linux、Mac:'posix'
(4)使用python来和操作系统命令交互相关的
①os.system('命令'):运行shell命令
#直接按操作系统的编码方式显示,且不需要print
只执行,不返回结果,因此不能后续操作
②os.popen('命令'):运行shell命令
返回一个对象:ret = os.popen('dir') ret是个对象,然后ret.read()就能读取到内存,就可以直接操作了,ret.read()类型是str
应用场景:查看当前路径、查看某些信息的时候
(5)查看环境变量
③os.environ:查看操作系统的环境变量
返回值类型:字典。环境变量:值
2)os.path.系列
①os.path.abspath('路径'):返回path规范化的绝对路径
如果给的是个相对路径,他会给你返回绝对路径
如果给的是一个不规范的路径(即左斜杠/),他会给你转成一个规范化的路径(即右斜杠\),并且这个\是已经转义好的\
②os.path.split('路径'):将路径分割成目录和文件名的二元元组返回
返回值类型:('目录路径','文件名')
print(os.path.split('D:/sylar/python_workspace/day25/5.os模块.py'))<br>#('D:/sylar/python_workspace/day25','5.os模块.py')
③os.path.dirname('路径'):目录路径,就是split的第一个元素
应用:去拿项目的base_path,用__file__拿到当前目录之后,直接网上翻几层就得到了<br>os.path.dirname(os.path.dirname(__file__))<br>
④os.path.basename('路径'):文件名,就是split的第二个元素
⑤os.path.exist('路径'):判断路径是否存在
返回值:True/False
⑥os.path.isabs('路径'):判断路径是否是绝对路径
返回值:True/False
⑥os.path.isfile('路径'):判断路径是否是一个存在的文件
返回值:True/False
⑥os.path.isdir('路径'):判断路径是否是一个存在的目录
返回值:True/False
⑦os.path.join(path1,path2, ...):将多个路径用\连接后返回
#第一个绝对路径之前的参数将被忽略
⑧os.path.getatime('路径'):返回路径所指向的文件或者目录的最后<font color="#f15a23">访问</font>时间
⑨os.path.getmtime('路径'):返回路径所指向的文件或者目录的最后<font color="#f15a23">修改</font>时间
⑩os.path.getsize('路径'):返回路径指向的文件或者目录的大小*****
#文件的大小都能准确统计,但目录的大小统一都返回4096<br>单位:字节
面试题:统计一个文件夹中所有文件的总的大小
6.序列化模块*****
1)定义<br>
序列化:将原本的字典、列表、数字、对象等内容转换成一个字符串的过程
反序列化:将字符串装换成原本的列表、对象等数据类型的过程
2)为什么要序列化?
①要把内容写入文件的时候就要序列化,因为写文件只能写字符串
②网络传输数据时就要序列化,因为网络通过高低电压传输10101二进制,二进制由于bytes最接近,而bytes与字符串最接近
3)方法
(1)json模块
方法:
①json.dumps():序列化
返回值类型:字符串
#字符串是由“”括起来的
②json.loads():反序列化
返回值类型:原数据类型
③json.dump('要写入的内容',f):存。接收一个文件句柄,直接将内容写入文件中
# dic = {'aaa':'bbb','ccc':'ddd'}<br># with open('json_dump2','w') as f:<br># json.dump(dic,f)
④json.load(f):读。直接将文件中的内容反序列化
# with open('json_dump2') as f:<br># print(json.load(f))
json的限制情况:
①json格式的限制1:json格式的key必须是字符串数据类型,如果是数字为key,那么dump之后会强行转成字符串数据类型
json格式的限制2:经过json序列化之后的数据,字符串都是用“”来的,经过反序列化会回到‘’。因此如果要对一个数据进行反序列化,那么所有字符串类型的数据都必须是“”括起来,否则会报错
③json是否支持元组,:对元组做value的字典会把元组强制转换成列表;对元组做key的字典会报错
④能否多次dump数据到一个文件里:可以多次dump,但没法load<br>(写可以写,但读不了,只能一次读一个变量)
代码:<br># dic = {'abc':(1,2,3)}<br># lst = ['aaa',123,'bbb',12.456]<br># with open('json_demo','w') as f:<br># json.dump(lst,f)<br># json.dump(dic,f)
用dumps可以实现:
代码:<br># dic = {'abc':(1,2,3)}<br># lst = ['aaa',123,'bbb',12.456]<br># with open('json_demo','w') as f:<br># str_lst = json.dumps(lst)<br># str_dic = json.dumps(dic)<br># f.write(str_lst+'\n')<br># f.write(str_dic+'\n')<br><br># with open('json_demo') as f:<br># for line in f:<br># ret = json.loads(line)<br># print(ret)
⑤对中文格式的数据dump之后,会在文件里显示编码,需要将参数ensure_ascii = False
⑥set不能被dump/dumps
⑦json的其他参数:为了让程序员看得更方便,但写入文件的话,会占不必要的空间
2)pickle模块
pickle序列化之后的返回值是bytes数据类型
优缺点:
优点:可以处理任意数据类型,且可以将数据原封不动的反序列化而不作任何改变。
缺点:序列化之后无法直接看出原数据内容<br>(但实际无所谓,因为序列化的目的是把数据存入内存,之后要再拿出来的,如果只是储存来供人看得话,根本不要序列化)。
方法:
①pickle.dumps()
对象都可以序列化
②pickle.loads()
③pickle.dump()
注意,因为是bytes类型,open(........., mode = 'wb')
④pickle.load()
注意,因为是bytes类型,open(........., mode = 'rb')
能多次dump和load数据:
# with open('pickle_demo','wb') as f:<br># pickle.dump({'k1':'v1'}, f)<br># pickle.dump({'k11':'v1'}, f)<br># pickle.dump({'k11':'v1'}, f)<br># pickle.dump({'k12':[1,2,3]}, f)<br># pickle.dump(['k1','v1','l1'], f)<br><br># with open('pickle_demo','rb') as f:<br># while True:<br># try:<br># print(pickle.load(f))<br># except EOFError:<br># break <br>
3)shelve模块(削微模块^_^)<br>(用的较少)
适用于:
如果写定了一个文件,且改动的比较少,读文件的操作比较多,并且你大部分的读取都需要基于某个key获得某个value,name就适合使用shelve
方法:
存值:<br># f = shelve.open('shelve_demo')<br># f['key'] = {'k1':(1,2,3),'k2':'v2'}<br># f.close()<br>
取值:<br># f = shelve.open('shelve_demo')<br># content = f['key']<br># f.close()<br># print(content)<br>
7.collections模块***<br>(数据类型的扩展模块)
双端队列deque:
什么是队列?先进先出的一列数据
一端放,另一端取,且看不到队列里的值和顺序
适用于网上购票等排队的事项
什么是双端队列?
两端都可以放,两端都可以取,先放的在中间,后方的在两端,取得时候先取两端的,且能看到队列里的值和顺序
collection实际是个包,因此需要from collection import deque
①默认从右边操作
②可以按内容删除
③可以在指定位置插入内容
双端队列的操作效果和list差不多,区别在哪呢?
①列表是一个连续内存空间,如果使用remove/insert比较多的话,每一次所有元素都需要挪一遍位置,列表的效率很低
②双端队列的底层使用的是C语言的一个叫链表的数据类型,链表的内存空间不连续,是一个个独立有用地址相连系的内存空间
总结:
在insert/remove的时候,deque的平均效率要高于列表
列表根据索引查看某个值的效率要高于deque
append和pop对于列表的效率是没有影响
常用模块2<br>面向对象相关
1.hashlib模块*****<br>(提供常用的摘要算法的模块)
能够把一个字符串数据类型的变量,转变成一个定长的、密文的字符串,且这个字符串的每一个字符都是一个16进制数。
1)摘要算法特点:
①只能将字符串转为密文,不能将密文再转回原字符串(转密不可逆)
②对于同一字符串(不管有多长),无论在任何环境下、多少次执行、在任何语言中,用相同的算法、相同的手段进行摘要,获得的值也总是相同的
③只要不是相同的字符串,得到的结果一定不同
2)常用的摘要算法:
(1)md5算法:
结果:32位的字符串,每个字符都是一个16进制数
应用:
①注册时用户名和密码的加密储存
代码:<br>#s='alex3714'<br>#md5_obj = hashlib.md5()<br>#md5_obj.update(s.encoding('utf-8'))<br>#res = md5_obj.hexdigist()<br>#print(res)
注:可以对s进行分段update,最终结果不变
②文件的一致性校验
代码:<br># md5_obj = hashlib.md5()<br># with open('5.序列化模块_shelve.py','rb') as f:<br># md5_obj.update(f.read())<br># ret1 = md5_obj.hexdigest()<br><br># md5_obj = hashlib.md5()<br># with open('5.序列化模块_shelve.py.bak','rb') as f:<br># md5_obj.update(f.read())<br># ret2 = md5_obj.hexdigest()<br><br># print(ret1,ret2)
大文件的一致性校验:<br><br>
md5不够安全:因为使用的人太多,存在撞库现象
怎样解决?<br>
①加"盐"<br># md5_obj = hashlib.md5('任意的字符串作为盐'.encode('utf-8'))<br># md5_obj.update(s1.encode('utf-8'))<br># res = md5_obj.hexdigest()<br>
②动态加盐<br>将username作为盐
(2)sha1算法:
结果:40位的字符串,每个字符都是一个16进制数
语法:<br>#s='alex3714'<br>#sha1_obj = hashlib.sha1()<br>#sha1_obj.update(s.encoding('utf-8'))<br>#res = sha1_obj.hexdigist()<br>#print(res)
也适合动态加盐
sha算法后面的数字越大,算法越复杂,结果越长,计算<br>速度越慢,安全性更高(是因为用的人少所以更安全)
2.configparse模块*<br>(处理配置文件的模块)
可以将按固定格式写的.ini配置文件直接解析,然后可以进行字符串的操作
.ini配置文件的格式:
[section1]<br>键1 = 值1<br>键2 = 值2<br><br>[section2]<br>键3 = 值3<br>
3.logging模块*****
1)功能:
①日志格式的规范
②操作的简化
③日志的分级管理
2)使用
(1)普通配置型
简单的,可定制化差
(2)对象配置型
复杂的,可定制化强
3)认识日志的分级
①调试模式:logging.debug('debug message')
②基础信息模式:logging.info('info message')
③警告:logging.warning('warning message')
④错误:logging.error('error message')
⑤严重错误:logging.critical('critical message')
4)logging.basicConfig()函数可配置logging模块的参数
# import logging<br># logging.basicConfig(level=logging.DEBUG,<br># format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',<br># datefmt='%a, %d %b %Y %H:%M:%S',<br># filename='test.log')<br>然后输出日志文件:<br># logging.debug('debug message')<br># logging.info('info message')<br># logging.warning('warning message')<br># logging.error('error message')<br># logging.critical('critical message')
basicConfig矛盾点:不能将一个log信息既输出到文件,又输出到屏幕
5)logger对象的形式来操作日志文件
语法:<br>import logging<br><br># 创建一个logger对象<br>logger = logging.getLogger()<br># 创建一个文件管理操作符<br>fh = logging.FileHandler('logger.log',encoding='utf-8')<br># 创建一个屏幕管理操作符<br>sh = logging.StreamHandler()<br># 创建一个日志输出的格式<br>format1 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')<br><br># 文件管理操作符 绑定一个 格式<br>fh.setFormatter(format1)<br># 屏幕管理操作符 绑定一个 格式<br>sh.setFormatter(format1)<br># 设置日志分级显示<br>logger.setLevel(logging.DEBUG)<br><br># logger对象 绑定 文件管理操作符<br>logger.addHandler(fh)<br># logger对象 绑定 屏幕管理操作符<br>logger.addHandler(sh)<br><br>logger.debug('debug message') # 调试模式<br>logger.info('我的信息') # 基础信息<br>logger.warning('warning message') # 警告<br>logger.error('error message') # 错误<br>logger.critical('critical message')# 严重错误<br>
异常处理
1.什么是一异常,以及异常与错误的区别:
Iteration:异常,是在代码执行过程中引发的
Error:语法错误,在编译代码阶段就检测出来,比较明显
2.异常发生时候的效果:
一旦在程序中发生异常,程序就不再执行了
3.如何看报错信息:
从下到上的看,往往最后一条就是错误的点
4.处理最简单的异常:
# lis = ['登录','注册','退出']<br># for i in enumerate(lis,1):<br># print(i[0],i[1])<br># try:<br># num = int(input('num :'))<br># print(lis[num-1])<br># except IndexError:<br># print('请输入一个数字')
5.多分支异常处理:
# try:<br># num = int(input('num :'))<br># print(l[num-1])<br># except ValueError:<br># print('请输入一个数字')<br># except IndexError:<br># print('您输入的数字无效')
第二种写法:<br># except (IndexError,NameError) as e:<br># print(e)
6.万能异常:
Exception是所有异常类的父类,包含99.9%的异常;BaseException是Exception的父类,包含剩余的几个不继承Exception的异常。
#try:<br># pass<br>#except Exeption as 变量名:<br># print(type(变量名),变量名,变量名.__traceback__.tb_lineno)
这里的变量名就是异常的那个实例化对象
7.万能异常与其他分支合作:
except会从上往下走,找一第一个符合的就执行,因此万能异常必须放在所有except的最后
# except NameError:pass<br># except IndexError:pass<br># except Exception:pass
8.异常处理的其他机制
finally的意义在于:程序的中断、函数的返回,均不影响finally的执行。即使有异常没有被处理到,<br>最后执行报错了,或者前面的代码里面有exit(),finally也依旧会执行。
9.主动抛异常:
比如填验证码,用户填半天才填好,就可以主动扔一个异常到用户脸上
raise NameError(这里可以写任意的内置异常)
如果只写raise,那么本来是什么异常,就会抛什么异常
10.自定义异常:
11.断言:<br>(基本不用 源码中才会看到)
语法:assert 布尔值
assert True就往下走<br>
assert False就抛异常
12.使用异常处理的注意事项:
(1)assert断言、raise主动抛异常,一般只有在写框架的时候才会用到
(2)代码开发过程中
①尽量少用异常处理,能用逻辑规避的应该用代码逻辑规避掉,
②用异常处理时,应该对某一句或几句话进行处理,而不应该在一个函数外部进行try/except
(3)当这个程序已经全部测试完成,没有问题了,那么应该在最外层加一个异常处理,避免程序动不动就闪退或者崩溃
0 条评论
下一页