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