Python之旅.第四章.模块与包.总结
一、模块
模块:
一系列功能的集合体,在python中一个py文件就是一个模块,模块名就是py文件的文件名;
模块的好处:
1.减少重复的代码
2.拿来主义
定义模块:
就是创建一个py文件;
使用模块:
关键字:import
用法:import 模块名(不要加py后缀)
注意区分执行文件和模块文件,执行文件是你当前逻辑程序,模块文件是你用来调用功能的文件;
首次导入模块:
1.创建一个模块的名称空间
2.执行模块文件,将模块文件产生的名字存放于创建的模块名称空间
3.当执行文件中拿到一个模块名,该模块名指向模块名称空间中的模块名
强调:当第一次导入模块名,就会把模块内容导入到内存空间,在同一文件再次导用模块名时调用的就是第一次导入内存空间内的模块文件,不会从新导入模块文件;
调用模块内的功能:
用模块名.功能名()
注意:
模块中功能的执行始终以模块自己的名称空间为准;
也就是调用的模块功能用的都是该模块内的值,修改的也是该模块内的值,并不影响当前执行文件;
为模块起别名:
关键字as
定义:
import 模块名 as 想定义的模块名;
用处:
1.模块名太长,把模块名定义短一点,减少繁琐
2.比如有两个模块,两个模块有相同的功能名,但其中的功能效果不同,这时候就可以把两个模块名都改成相同的模块名,
用一个输入加判断,来确定你要导入的是哪个功能,你输入的是哪个模块名,就是哪个模块内功能的效果,模块名(修改过)和功能名未变,
,使用的是什么功能取决于你输入的模块名,其他的代码都无需改动,以后修改只需要,多添加一个判断模块名就可以了;
导入多个模块(不推荐)
import 模块名1,模块名2,模块名3
推荐还是多行导入
第二种导入方式:
关键字 from
from 模块名 import 功能名1,功能名2,功能名3
首次导入:
1.创建一个模块的名称空间
2.执行模块文件,将模块文件产生的名字存放于创建的模块名称空间
3.在当前名称空间中可以直接拿到模块中的名字,可以直接使用,不用加任何前缀
注意:这种情况,因为没有模块名.功能名,如果在当前执行文件中有与模块名内功能相同的名字,就会执行当前文件内的功能名(变量名),
所以要注意,不能取与模块功能相同的名称;
多个调用:
关键字:*
from 模块名 import *
*代替所有能调用的功能的名字;
注意:因为有些功能常用,有些功能不常用,模块的设计者,一般都会控制*所能调用的模块名;
关键字:__all__=[]
__all__=[功能名1,功能名2....]
限制以后,*所能调用的就只能是all以内的功能名;
测试模块功能:
关键字:__name__
__name__的值
1.在文件被直接执行的情况下等于__main__
2.在文件被导入的情况下等于模块名
也就是可以用__name__来判断,是否是直接执行测试功能,还是导入
if __name__ == '__main__':
#代码块
print('再代码块中输入你所要测试的功能')
模块的搜索路径
sys.path:当前执行文件所在的文件夹;只看第一个路径;
可以用print(sys.path)在终端输出路径,第一个就是当前执行文件所在的文件夹
模块的查找顺序:
1.内存空间
2.内置空间
3.sys.path路径
sys.modules:当前内存中已经被导入的模块
print(sys.modules) :在终端输出内存中所有被导入的模块
在sys.path中添加路径
sys.path.append(需要添加的路径)
比如你把模块文件放在了别的文件夹,sys.path只是当前文件夹,这时候用append把放模块的文件夹的路径加到sys.path中,
这样就能在sys.path找到模块所在的文件夹,就可以调用模块
软件开发目录规范
路径:用常量名,后面赋予路径
常量路径名=r'路径'
常用目录:(当前水平用这个目录足够了)
ATM:文件夹,所需要做的项目名,用于存放项目相关所有文件和文件夹
readme:文件,用于介绍程序功能
bin:文件夹,只放启动文件
start:文件,启动文件
conf:文件夹,用于存放配置文件
settings:文件名,配置文件
core:文件夹,存放核心逻辑
src:文件名,业务核心逻辑文件
db:文件夹,用于存放数据文件
db:文件名,用于存放数据
lib:文件夹,用于存放常用工具,也就是模块文件
common:文件名,常用功能文件,也就是常用模块文件
log:文件夹,用于存放文件日志
access.log:文件名,日志文件
应该把项目的根目录添加到环境变量中;
就比如上方的ATM项目:
根目录就是ATM目录,以后需要调用什么功能,在根目录下都可以查找到
取目录:
os.path.dirname(__file__) :取当前文件所在的目录
os.path.dirname(os.path.dirname(__file__) ) :取当前所在在文件目录的上一级目录
以此类推,取第几层就写几段,后面有跟方便的方法,此方法仅供参考
然后把你需要的路径,赋值到一个常量当中,当以后有需要调用的时候,直接调用常量名
不必输入固定目录,以限制程序
比如,sys.path.append(os.path.dirname(__file__) ):直接加入当前路径
第二种方法:推荐
os.path.normpath(os.path.join(
__file__,
'..',
'..'
))
logging模块
debug:代表调试信息,级别10
info:代表正常运行信息,级别20
warning:代表警告信息,级别30
errot:代表错误信息,级别40
critical:代表崩溃信息,级别50
设置成什么级别,打印什么级别信息
logging模块模板
"""
logging配置
"""
#定义日志文件路径
#把日志路径赋予一个变量
LOG_PATH=r'a3.log'
# 定义三种日志输出格式 开始
#把自己定义的个数赋予一个变量用于后面的调用
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
#多个日志格式,不可更改
'formatters': {
#日志格式名,可更改,这名字的意思是往终端打
'standard': {
#日志格式:上方定义的格式的变量名
'format': standard_format
},
#这个名字的意思是往文件打
'simple': {
'format': simple_format
},
},
#过滤,没什么用,可以无视
'filters': {},
#定义日志输出的格式:文件或终端,不可更改
'handlers': {
#打印到终端的日志名,可以更改
'console': {
#定义日志级别,不可更改:日志级别,可更改为DEBUG/INFO/WARNING/ERROT/CRITICAL
'level': 'DEBUG',
#定义日志输出格式,不可更改
'class': 'logging.StreamHandler', # 打印到屏幕
#绑定格式,不可更改:具体格式根据上方定义的格式更改
'formatter': 'simple'
},
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
#日志写到哪个文件:变量名,此变量我们在上面以赋予路径
'filename': LOG_PATH, # 日志文件
#规定日志切分大小,以字节为单位
'maxBytes': 1024*1024*5, # 日志大小 5M
#规定日志切分后保存的日志次数
'backupCount': 5,
#日志文件编码
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
#产生日志,并且传的名字,名字空为默认
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'': {
#把日志丢给谁,列表内存放的上上方定义的日志名
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
#设置级别,两层关卡,与上方级别相同即可
'level': 'DEBUG',
#向父级传递,通常关闭就可以了
'propagate': False, # 向上(更高level的logger)传递
},
},
}
import logging
import logging.config
#加载上方定义的字典
def get_logger(name):
#让logging模块从字典里加载配置
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
#拿到logging名,因为上面logging名为空,所以这里用__name__,如果上方定义了实名,就用上方定义的实名
logger = logging.getLogger(name) # 生成一个log实例
return logger # 记录该文件的运行状态
#用来测试是否在当前文件执行
if __name__ == '__main__':
get_logger()
什么是序列化:
我们把对象(变量)从内存中变成可储存或可传输的过程叫做序列化;
为什么要有序列化:
1.持久保存状态
2.跨平台数据交互
json模块
json只能处理字典,列表,字符串,数字,bool,空类型
json内都是双引号
序列化:内存中的数据结构——转成一种中间格式(字符串)——存储到文件中
关键字:json.dump()
用法:json.dump(需要序列化的值,打开文件的变量名)
反序列化:文件——读取中间格式(字符串)——转成内存中的数据结构
只用W写模式
关键字:json.load
用法:json.load(打开文件的变量名)
pickle模块
pickle可以转换所有的类型,但只能在python中使用
序列化:内存中的数据结构——转成一种中间格式(字符串)——存储到文件中
关键字:pickle.dump()
用法:pickle.dump(需要序列化的值,打开文件的变量名)
反序列化:文件——读取中间格式(字符串)——转成内存中的数据结构
只用W写模式
关键字:pickle.load
用法:pickle.load(打开文件的变量名)
os模块
os.path.abspath(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.getsize(path)统计文件大小,单位为字节
os.path.join('a','b','c.txt') 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
不用写路径符
os.path.normpath(path) 规范化路径,把里面的斜杠同一,..(..返回上一层符号)执行等
用于取需要的绝对路径:
so.listdir():查看当前目录下的文件名:
包:
包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来
需要强调的是:
1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模块
为什么要使用包:
包的本质就是一个文件夹,那么文件夹唯一的功能就是将文件组织起来
随着功能越写越多,我们无法将所以功能都放到一个文件中,于是我们使用模块去组织功能,而随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性
导入包:
1 产生一个包的名称空间
2 执行包下的__init__.py文件,将产生的名字存放于包的名称空间中
3 在当前执行文件中拿到一个名字aaa,该名字指向包的名称空间
print(aaa.m1) #aaa.m1 就是问aaa.__init__.py要一个名字m1
1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:
凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
2、import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
3、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间
包的导入之import:
import 包名
包的使用之from ... import ...:
from 包名 import 模块
需要注意的是from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法
绝对导入和相对导入:
我们的最顶级包glance是写给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
绝对导入:以glance作为起始
相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)
强调:
1、在导入时带点的,点的左边必须是一个包,这是导入包特有的语法
2、包内,模块直接的导入应该使用from。。。import 。。。
3、from 。。。 import。。。,import后必须是一个明确的名字,没有任何的前缀
from a.b.c.d.f import g.h.x #错误
1、f左边必须都是包
2、import后的名字不能有任何前缀
包以及包所包含的模块都是用来被导入的,而不是被直接执行的。而环境变量都是以执行文件为准的
time模块:
时间分为三种形式:
1、时间戳
time.time()#获得时间秒数
print(time.time())
start_time=time.time()
time.sleep(3)
stop_time=time.time()
print(stop_time-start_time)
2、格式化的字符串
time.strftime(格式化):根据你输入的格式化,得到你想要的时间
print(time.strftime('%Y-%m-%d %H:%M:%S %p')) #年月日时分秒 上下午判断
print(time.strftime('%Y-%m-%d %X %p'))
3、struct_time对象
time.localtime(): #本地时区的struct_time
print(time.localtime()) # 上海:东八区
print(time.localtime().tm_year)
print(time.localtime().tm_mday)
print(time.gmtime()) # UTC时区struct_time
datatime模块:
# print(datetime.datetime.now()) #返回具体时间 #print(datetime.date.fromtimestamp(time.time()) ) # 时间戳直接转成日期格式 年-月-日 # print(datetime.datetime.now() ) # print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天 # print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天 # print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时 # print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分 # c_time = datetime.datetime.now() # print(c_time.replace(minute=3,hour=2)) #时间替换
shutil模块:
高级的 文件、文件夹、压缩包 处理模块
shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中
压缩文件:
import shutil
import time
ret = shutil.make_archive(
"day15_bak_%s" %time.strftime('%Y-%m-%d'),
'gztar',
root_dir=r'D:\code\SH_fullstack_s1\day15'
)
解压文件:
import tarfile
t=tarfile.open('day15_bak_2018-04-08.tar.gz','r')
t.extractall(r'D:\code\SH_fullstack_s1\day16\解包目录')
t.close()
randon模块:
1.print(random.random())#(0,1)----float 大于0且小于1之间的小数
2.print(random.random())#(0,1)----float 大于0且小于1之间的小数
3.print(random.choice([1,'23',[4,5]]))#1或者23或者[4,5]
4.print(random.sample([1,'23',[4,5]],2))#列表元素任意2个组合
5.print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716
6.item=[1,3,5,7,9]
random.shuffle(item) #打乱item的顺序,相当于"洗牌"
shelve模块
Shelve(了解),是更高程度的封装。使用时只针对之前设计生成的文件,可以无视不同平台自动生成的其他文件。
Json的中间格式为字符串,用w写入文件
Pickle的中间格式为bytes,用b写入文件
序列化时更常用Json
xml模块
xml时一种组织数据的形式
xml下的元素对应三个特质,tag, attrib, text
res=root.iter('rank') # 会在整个树中进行查找,而且是查找到所有
res=root.find('country') # 只能在当前元素的下一级开始查找。并且只找到一个就结束
cy=root.findall('country') # 只能在当前元素的下一级开始查找,
re模块(正则)
#1 print(re.findall('e','alex make love') ) #['e', 'e', 'e'],返回所有满足匹配条件的结果,放在列表里 #2 print(re.search('e','alex make love').group()) #e,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。 #3 print(re.match('e','alex make love')) #None,同search,不过在字符串开始处进行匹配,完全可以用search+^代替match #4 print(re.split('[ab]','abcd')) #['', '', 'cd'],先按'a'分割得到''和'bcd',再对''和'bcd'分别按'b'分割 #5 print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,默认替换所有 print('===>',re.sub('a','A','alex make love',1)) #===> Alex make love print('===>',re.sub('a','A','alex make love',2)) #===> Alex mAke love print('===>',re.sub('^(\w+)(.*?\s)(\w+)(.*?\s)(\w+)(.*?)$',r'\5\2\3\4\1','alex make love')) #===> love make alex print('===>',re.subn('a','A','alex make love')) #===> ('Alex mAke love', 2),结果带有总共替换的个数
hashlib模块
什么叫hash:hash是一种算法,该算法接受传入的内容,经过运算得到一串hash值
hash值的特点是:
1)只要传入的内容一样,得到的hash值必然一样=====>要用明文传输密码文件完整性校验
2)不能由hash值返解成内容=======》把密码做成hash值,不应该在网络传输明文密码
3)只要使用的hash算法不变,无论校验的内容有多大,得到的hash值长度是固定的
import hashlib
#hash普通数据
m=hashlib.md5()
m.update('hello'.encode('utf-8'))
m.update('world'.encode('utf-8'))
m.update('egon'.encode('utf-8')) #update接受bytes,可以辅助分批导入
print(m.hexdigest()) #3801fab9b8c8d9fcb481017969843ed5
______________________________________________________________________
#hash文件
m=hashlib.md5()
with open(r'D:\code\SH_fullstack_s1\day18\上节课复习','rb') as f:
for line in f:
m.update(line)
hv=m.hexdigest()
print(hv) #f2a3a94efd0809e8a9c5ac8794c4bb2d
953cd74a08f4fbb7e69a4bda8dfad056
subprocess模块 #系统命令
dos命令
tasklist | findstr python # |为管道,tasklist的结果不直接丢给屏幕而是传入管道,findstr从管道接到结果进行筛选
taskkill /? #/?可以用来查看用法
D:\code>tasklist | findstr python
python.exe 12360 Console 1 11,024 K
D:\code>taskkill /F /PID 12360 #F表示强制关闭, 12360为PID时随机分配的
linux系统(了解)
ps aux | grep python #查询系统里的python进程
kill -9 PID 停止进程
#创建终端
obj=subprocess.Popen('dir',
shell=True, #调用命令解释器;shell为命令解释器
stdout=subprocess.PIPE, #将正确运行的结果传入管道
stderr=subprocess.PIPE #将错误运行的结果传入管道
)
res1=obj.stdout.read()
print('正确结果1111: ',res1)
res2=obj.stdout.read()
print('正确结果2222: ',res2) #只能取一次,取走了就没有了
res2=obj.stderr.read()
print('错误结果:',res2.decode('gbk'))
configparser模块 #用于处理ini后缀文件(知道就行)
my.ini文件中主要有两种类型,section和option