包、logging模块、hashlib加密模块、openpyxl模块、深浅拷贝
一、什么是包?
1.1包是一系列模块文件的总和,就是一个文件夹。该文件夹通常(python3和python的区别)会有一个init文件,包的本质还是一个模块
1.2具体的:包就是一个包含有__init__文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来
需要强调的是:
1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模块
1.3首先看一下之前导入模块,做的事情:
1.先产生一个执行文件的名称空间
2.创建模块文件中的名称空间
3.执行模块文件的代码,将名字放入模块文件的名称空间
4.执行文件中拿到一个指向模块名称空间的名字
1.4那看下调用包发生的事情吧:
1.产生一个执行文件的名称空间
2.创建包下面的__init__.py文件的名称空间
3.执行包下面的__init__.py文件中的代码,将产生的名字放入该名称空间中
4.在执行文件拿到一个指向包下面__init__.py文件名称空间的名字。
1.5注意事项:
1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
2、import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
当你作为包的设计者来说
1.当模块的功能特别多的情况下 应该分文件管理
2.每个模块之间为了避免后期模块改名的问题 你可以使用相对导入(包里面的文件都应该是被导入的模块)
1.6包的嵌套:
只需把导入模块写入到嵌套的init文件文件中,这样外层文件要导入嵌套的模块只需要导入该init文件即可。
相对导入
当存在多及嵌套的包。如果外层的包修改了名字,那么之前在init文件中建立的"绝对路径"全部失效。这时候可以使用“相对导入”,之前提到“相对导入”只能到导入模块中使用,而包就是给人家用的,因此可用。
二、logging模块(瞜一眼):
日志模块记录各种状态。比如某人眨眼睛等
导入日志模块:import logging
2.1日志有五个等级
logging.debug('调试日志,记录简单的东西') # 10
logging.info('info日志,也是简单的日志') # 20
logging.warning('warning日志') # 30
logging.error('error日志,错误信息,但程序还能跑') # 40
logging.critical('critical日志,严重错误') # 50
如何生成日志?copy过来即可
import logging
logging.basicConfig(filename='access.log',
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',#p是上下午
level=30,#级别,比30大的都会记录
)#这么多参数瞄一眼,不需要记住
#
#
logging.debug('debug日志') # 10
logging.info('info日志') # 20
logging.warning('warning日志') # 30
logging.error('error日志') # 40
logging.critical('critical日志') # 50
在pycharm中运行该程序,发现终端什么都没有,但是忽略了左边,左侧目录产生了一个access.log的文件,里面记录着等级信息。
但接下来出现了问题:
1.乱码;2.日志格式3.如何既打印到终端?
回答3.了解知识点(该方式课内没有实现):在logging.basicConfig中将stream=True写入参数,并将filename=‘access.log’参数去掉,一山不容二虎。
增加一个知识点:如何既打印到终端又写到文件中?(依据上述的方法,无法实现)
接下来了解logging模块的四个对象((简单的日志)使用流程:8步,费事不讨好):
参数中的level是控制输出的,大于该阈值的结果会记录下来
1.logger对象:负责产生日志。logger.getLogger()方法填写日志的名字
logger=logging.getLogger('转账记录')#写日志相关的名字,即产生的日志内的名字
2.filter对象:过滤日志(了解即可,不怎么用,只需记得)
3.handler对象:控制日志输出的位置(存放到文件/终端)可以有多个
如下是输出到a1、a2文件和输出到终端。通过logging.FileHandler()方法和logging.StreamHandler()输出到终端和屏幕
hd1=logging.FileHandler('a1.log',encoding='utf-8')##输出到终端(文件名字)(之前因为乱码,所以这里指定编码)
hd2=logging.FileHandler('a2.log',encoding='utf-8')##输出到终端(文件名字)(之前因为乱码,所以这里指定编码)
hd3=logging.StreamHandler()#指定多个handler对象,输出的终端
4.formmater对象:规定日志内容的格式(也不需要记住)
fm1 = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',#第一个格式
)
fm2 = logging.Formatter(
fmt='%(asctime)s - %(name)s: %(message)s',
datefmt='%Y-%m-%d',#第二个格式,只要年月日
)
5.给logger对象绑定handler对象:addHandler()
三个addHandler,代表logger传给三个对象,如下图是两个
logger.addHandler(hd1)
logger.addHandler(hd2)
logger.addHandler(hd3)
六、给handler绑定输出格式(formmate).setFmatter()方式
hd1.setFormatter(fm1)
hd2.setFormatter(fm2)
七、设置日志等级.setLevel()
logger.setLevel(20)
八、写日志.debug()
logger.debug('写了半天好累啊')
日志运行流程图
现在将上面的内容整合成在一起(瞜一眼 )
import logging
# 1.logger对象:负责产生日志
logger = logging.getLogger('转账记录')
# 2.filter对象:过滤日志(了解)
# 3.handler对象:控制日志输出的位置(文件/终端)
hd1 = logging.FileHandler('a1.log',encoding='utf-8') # 输出到文件中
hd2 = logging.FileHandler('a2.log',encoding='utf-8') # 输出到文件中
hd3 = logging.StreamHandler() # 输出到终端
# 4.formmater对象:规定日志内容的格式
fm1 = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
)
fm2 = logging.Formatter(
fmt='%(asctime)s - %(name)s: %(message)s',
datefmt='%Y-%m-%d',
)
# 5.给logger对象绑定handler对象。即给logger对象指定输出位置
logger.addHandler(hd1)
logger.addHandler(hd2)
logger.addHandler(hd3)
# 6.给handler绑定formmate对象。即给输出位置绑定输出内容的格式
hd1.setFormatter(fm1)
hd2.setFormatter(fm2)
hd3.setFormatter(fm2)
# 7.设置日志等级
logger.setLevel(10)
# 8.记录日志
logger.debug('写了半天 好累啊 好热啊 好想释放')
总结:写完这么一大堆代码就实现了这么点功能,费事不讨好。配置字典
三、logging配置字典(了解)
import os
import logging.config
# 定义三种日志输出格式 开始
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'
# 定义日志输出格式 结束
"""
只有下面的两个变量对应的值 需要你手动修改
"""
logfile_dir = os.path.dirname(__file__) # log文件的目录。往上一级
logfile_name = 'a3.log' # log文件名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
#用os模块创建一个出来
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},#标准格式
'simple': {
'format': simple_format
},#简单格式
},
'filters': {}, # 过滤日志(用不到,直接忽略)
'handlers': {
#打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': logfile_path, # 日志文件
'maxBytes': 1024*1024*5, # 日志大小上限 5M
'backupCount': 5,#如果上面文件满了就直接新建一个5M的文件,最多存5个。若又满了,就把开头的文件挨个删除,就好比摄像头记录信息。
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
#产生名字
#logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
}, # 当键不存在的情况下 默认都会使用该k:v配置
},
}
# 使用日志字典配置
logging.config.dictConfig(LOGGING_DIC) # 自动加载字典中的配置
#接下来生成一个对象
logger1 = logging.getLogger('asajdjdskaj')#必须注意该名字必须在上面的logger中定义,否则无内容记录。因此直接将上面的logger内不要定义名字,直接写空即可,程序会自动添加名字
logger1.debug('好好的 不要浮躁 努力就有收获')
搞了 这么多,只需掌握最后的三句话:
logging.config.dictConfig(LOGGING_DIC) # (字典名)自动加载字典中的配置
#接下来生成一个对象
logger1 = logging.getLogger('fdsafsadfsa')#直接将上面的logger内不要定义名字,直接写空即可,程序会自动添加名字
logger1.debug('好好的 不要浮躁 努力就有收获')
四、hashlib模块
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
hashlib模块 加密的模块
import hashlib # 这个加密的过程是无法解密的
md = hashlib.sha3_256() # 生成一个帮你造密文的对象
md.update('hello'.encode('utf-8')) # 往对象里传明文数据 update只能接受bytes类型的数据
md.update(b'Jason_@.') # 往对象里传明文数据 update只能接受bytes类型的数据
print(md.hexdigest()) # 获取明文数据对应的密文
4.1撞库
1.不同的算法 使用方法是相同的
密文的长度越长 内部对应的算法越复杂
但是
1.时间消耗越长
2.占用空间更大
通常情况下使用md5算法 就可以足够了
4.2hashlib模块应用场景
1.密码的密文存储
2.校验文件内容是否一致
分多次传入内容,但是生成的密文还是一样的
md = hashlib.md5()
md.update(b'areyouok?')
md.update(b'are')
md.update(b'you')
md.update(b'ok?')
print(md.hexdigest()) # 408ac8c66b1e988ee8e2862edea06cc7
408ac8c66b1e988ee8e2862edea06cc7
4.3加盐处理
import hashlib
md = hashlib.md5()
# 公司自己在每一个需要加密的数据之前 先手动添加一些内容
md.update(b'oldboy.com') # 加盐处理
md.update(b'hello') # 真正的内容
print(md.hexdigest())
4.4动态加盐
import hashlib
def get_md5(data):
md = hashlib.md5()
md.update('加盐'.encode('utf-8'))
md.update(data.encode('utf-8'))
return md.hexdigest()
password = input('password>>>:')
res = get_md5(password)
print(res)
五、openpyxl:excel表格模块
03版本之前 excel文件的后缀名 叫xls
03版本之后 excel文件的后缀名 叫xlsx
xlwd 写excel
xlrt 读excel
xlwd和xlrt既支持03版本之前的excel文件也支持03版本之后的excel文件
openpyxl 只支持03版本之后的 xlsx
5.1写模块:Workbook()
from openpyxl import Workbook
wb = Workbook() #先生成一个工作簿
wb.save('test.xlsx')# 保存新建的excel文件
5.2创建新的表单页.create_sheet()
wb1 = wb.create_sheet('index',0)
后面的索引控制表单创建的位置。默认是从1往后
5.3修改表单页名称:.title
wb1.title = 'login'
5.4为表单赋值
在新创建(wb1=wb.create_sheet('名称',索引))的表单页上填写数据
wb1['A3'] = 666
wb1['A4'] = 444
指定具体某行某列的数据
wb1.cell(row=6,column=3,value=88888888)
加入函数功能
wb1['A5'] = '=sum(A3:A4)'
按照一行一行的顺序传值
wb1.append(['username','age','hobby'])
wb1.append(['jason',18,'study'])
wb1.append(['tank',72,'吃生蚝'])
wb1.append(['egon',84,'女教练'])
wb1.append(['sean',23,'会所'])
# 保存新建的excel文件
# wb.save('test.xlsx')
5.5openpyxl读文件load_workbook
导入模块
from openpyxl import load_workbook#读文件
wb = load_workbook('test.xlsx',read_only=True,data_only=True)
print(wb['login']['A3'].value)
print(wb['login']['A4'].value)
print(wb['login']['A5'].value) # 通过代码产生的excel表格必须经过人为操作之后才能读取出函数计算出来的结果值
res = wb['login']
# print(res)
ge1 = res.rows
for i in ge1:
for j in i:
print(j.value)
六、深浅拷贝
6.1浅拷贝:copy.copy()
l = [1,2,[1,2]]
l1 = l
print(id(l),id(l1))#得出内存地址相同
l1 = copy.copy(l) # 拷贝一份 .. 浅拷贝
print(id(l),id(l1))
#得出内存地址不相同。
l[2].append(666)
print(l,l1)
#l1和l的打印结果相同
#得出结论,当浅拷贝内容中出现可变类型数据,那么复制的内容是直接引用该可变类型的。
6.2深拷贝
l1=copy.deepcopy(l)
print(l)
l1[2].append(666)
print(l1)
"""
[1, 2, [1, 2]]
[1, 2, [1, 2, 666]]
"""
l1为可变类型的列表增加值,现在l不会一起发生变化了
综合得出:深浅拷贝,
不可变类型的值,一个发生变大,拷贝的那个必定发生变化。而
可变类型的值,发生改变,只要模式为深拷贝的情况下,拷贝的那份才会发生变化