包的使用
什么是包
# 官方解释
Packages are a way of structuring Python’s module namespace by using “dotted module names”
包是一种通过使用‘.模块名’来组织python模块名称空间的方式。
# 具体的:包是一系列模块的结合体,表示形式就是一个文件夹 该文件内部通常会有一个__init__.py文件,包的本质还是一个模块。
#需要强调的是:
1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模块
- 为何要使用包
包的本质就是一个文件夹,那么文件夹唯一的功能就是将文件组织起来
随着功能越写越多,我们无法将所以功能都放到一个文件中,于是我们使用模块去组织功能,而随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性
- 导入包的执行流程
# 首次导入包
先产生一个执行文件的名称空间
1. 创建包下面的__init__.py文件的名称空间
2. 执行包下面的__init__.py文件中的代码,将产生的名字放入包下面的__init__.py文件名称空间中
3. 在执行文件中拿到一个指向包下面的__init__.py文件名称空间的名字
- 注意:
当作为包的设计者来说:
1. 当模块的功能特别多的情况下 应该分文件管理
2. 每个模块之间为了避免后期模块改名问题 可以使用相对导入(包里面的文件都应该是被导入的模块)
站在包的开发者来说 如果使用绝对路径来管理自己的模块 那么它只需要永远以包的路径为基准一次导入模块
站在包的使用者来说 必须将包所在的文件夹路径添加至系统环境变量中
logging模块
- 日志级别
import logging
logging.debug('debug日志')
logging.info('info日志')
logging.warning('warning日志')
logging.error('error日志')
logging.critical('critical日志')
默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING
(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。
- 日志对象
1. logger对象:负责产生日志
2. filter对象:过滤日志
3. handler对象:控制日志输出的位置
4. formater对象:规定日志内容的格式
灵活配置日志级别,日志格式,输出位置:
import logging
file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8',)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
handlers=[file_handler,],
level=logging.ERROR
)
logging.error('你好')
日志切割
import time
import logging
from logging import handlers
sh = logging.StreamHandler()
rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024,backupCount=5)
fh = handlers.TimedRotatingFileHandler(filename='x2.log', when='s', interval=5, encoding='utf-8')
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
handlers=[fh,sh,rh],
level=logging.ERROR
)
for i in range(1,100000):
time.sleep(1)
logging.error('KeyboardInterrupt error %s'%str(i))
配置参数
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),
默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息
logger对象配置
import logging
logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log',encoding='utf-8')
# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh) #logger对象可以添加多个fh和ch对象
logger.addHandler(ch)
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
hashlib模块
- 算法介绍
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。
摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。
我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:
import hashlib
md5 = hashlib.md5() # 生成一个帮助造密文的对象
md5.update('how to use md5 in python hashlib?') # 往对象里面传明文数据 update只能接受bytes类型的数据
print md5.hexdigest() # 获取明文数据对应的密文
计算结果如下:
5d41402abc4b2a76b9719d911017c592
MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:
import hashlib
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in ')
sha1.update('python hashlib?')
print sha1.hexdigest()
SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长
- 应用场景
# hashlib模块应用场景
1.密码的密文存储
2.效验文件内容是否一致
-
加盐处理
由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”
def get_md5(data): # 封装成函数方便下次调用
md = hashlib.md5() # 使用md5的加密方式
md.update('自己加的盐'.encode('utf-8')) # 将对象中传入自己加的盐
md.update(data.encode('utf-8')) # 将函数中传参传入对象中
return md.hexdigest() # 获取明文数据对应的密文
password = input('清输出你需要加密的值>>>:')
res = get_md5(password) # 将变量传入函数中直接调用
print(res) # 打印加密结果
openpyxl模块
- 使用模块写文件
# openpyxl 比较火的操作excel的模块
"""
excel 03版本前后缀名叫 xlx
excel 03版本后后缀名叫 xlsx
xlwd 写excel
xlrt 读excel
xlwd和xlrt既支持03版本之前的excel文件也支持03版本之后的excel文件
openpyxl 只支持03版本会后的xlsx
"""
from openpyxl import Workbook
wb = Workbook # 生成一个工作簿
wb1 = wb.create_sheet('index',0) # 创建表index表单页,控制表单位置
wb2 = wb.create_sheet('index1',2) # 创建表index1表单页,控制表单位置
wb1.title = 'login' # 修改表单页的名称为login
wb1.cell(row=6,column=3,value=888888) # 在表单index的第六行第三列添加内容888
# wb1['A3'] = 666 # 在表单index的A3位置写入内容666
# wb1['A4'] = 444 # 在表单index的A4位置写入内容444
# wb1['A5'] = '=sum(A3:A4)' # 在表单A5位置对A3和A4进行求和
wb1.append(['username','age','hobby']) # 添加表头
wb1.append(['jason',18,'study']) # 对应表头添加内容
wb1.append(['tank',11,'read']) # 对应表头添加内容
wb1.append(['agon',22,'女教练']) # 对应表头添加内容
wb.save('test,xlsx') # 保存工作簿起名为test
- 读文件
from openpyxl import load_workbook # 读文件
wb = load_workbook('test.xlsx',read_only=True,data_only=True) # data_only表示只读数据,不读公式
print(wb.sheetnames) # 打印表单页名称
print(wb['login']['A3'].value) # 查看表单页login A3位置对应的值
print(wb['login']['A5'].value) # 查看表单页login A3位置对应的值
res = wb['login']
ge1 = res.rows
for i in ge1:
for j in i:
print(j.value)
copy模块
import copy
# l = [1,3,[1,2]]
# l1 = copy.copy(l) # 拷贝一份,浅拷贝
# print(id(l),id(l1)) # 2123774630408 2123774630472 拷贝出来的文件与源文件di并不一致
# # l[0] = 222 # 仅针对l的索引0修改数据
# # print(l,l1) # [222, 3, [1, 2]] [1, 3, [1, 2]]
# l[2].append(666) #
# print(l,l1) # [1, 3, [1, 2, 666]] [1, 3, [1, 2, 666]]
# 浅拷贝说明:
#仅仅是拷贝了一份和源文件同样的数据,但是实则上指向的内容还是和源文件一致,针对源文件进行修改,如上例,如修改源文件,针对不可变类型,源文件会断掉指向内容的值重新指向,而可变类型
l = [1,2,[1,2]]
l1 = copy.deepcopy(l)
l[2].append(666)
print(l,l1) # [1, 2, [1, 2, 666]] [1, 2, [1, 2]] 深拷贝,源文件新增,新文件并不修改
# 深拷贝说明:
#拷贝和源文件一样的数据,但是和浅拷贝不一样之处在于,深拷贝对于可变类型会新建内容,再将内容进行指向,这样对于源文件的可变类型修改,并不会改变新文件
#对于可变不可变到底是指向原来的内容,还是新建内容