包和常用内置模块(二)

1.包

什么是包?

  它是一系列模块文件的集合体,表现形式就是一个文件夹

  内部通常都会有一个__init__.py文件

  包的本质还是一个模块

为什么要使用包?

  包的本质是一个文件夹形式的模块,他的唯一功能就是将文件组织起来,随着功能越写越多,我们不可能将所有功能都放在一个py文件中,于是我们使用模块来组织功能,随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性

首次导入包发生的三件事

  先产生一个执行文件的名称空间,导入包

    1.先创建包下面的__init__.py文件的名称空间

    2.执行包下面的__init__.py文件的代码,将产生的名字存放在__init__.py文件的名称空间中

    3.在执行文件中拿到一个指向包下面__init__.py文件的名称空间的名字

绝对导入和相对导入的选用:

  当作为包的设计者来说

  1.当模块的功能很多的情况下,应该分文件管理

  2.每个模块之间为了避免后期模块改名的问题,需要使用相对导入(包里的文件都应该是可导入的模块)

绝对路径和相对路径的选用:

  站在包的开发者:如果使用绝对路径来管理自己的模块,它只需永远以包的路径为基准依次导入模块

  站在包的使用者:必须将包所在的文件夹路径添加到system path中

1 # 包中的代码块
2 from day1701.p1 import p2  # 导入p2这个包,从p2里的__init__拿数据
3 p2.f1()  # p2包中根据已导入的模块取出函数f1
4 p2.f3()  # p2包中根据已导入的模块取出函数f3
1 # __init__.py文件中的模块导入代码块
2 from day1701.p1.p2.m1 import f1  # 绝对导入,以执行文件的位置为准
3 from .m3 import f3  # 相对导入,以该文件的位置为基准

  

需要注意的是:在导入语句中 .号的左边肯定是一个包(文件夹)

python2和python3中的区别

  python2如果要导入包,包下面必须要有__init__文件

  python3如果要导入包,包下面没有__init__文件也不会报错

  所以我们在删除文件的时候不能把__init__文件删除,哪怕是他里边没有内容

 

2.logging模块

日志模块:用来记录程序运行信息的模块

日志模块的等级

 1 # logging日志模块
 2 import logging
 3 logging.basicConfig(filename='access.log',
 4                     format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
 5                     datefmt='%Y-%m-%d %H:%M:%S %p',
 6                     level=30
 7                     )
 8 # 日志模块的等级
 9 logging.debug('debug日志')  # 10
10 logging.info('info日志')  # 20
11 logging.warning('warning日志')  # 30
12 logging.error('error日志')  # 40
13 logging.critical('critical日志')  # 50
14 # 等级由上到下越来越高

需要解决三个问题

  1.乱码

  2.日志格式问题

  3.如何既打印到终端又写到文件中

 

输出日志的八个步骤

 1 import logging
 2 # 1.logger对象:负责产生日志
 3 logger = logging.getLogger('转账记录')
 4 
 5 # 2.filter对象:过滤日志(了解)
 6 
 7 # 3.handler对象:控制日志输出的位置(文件或者终端)
 8 hd1 = logging.FileHandler('a1.log',encoding='utf-8')  # 输出到文件
 9 hd2 = logging.StreamHandler()  # 输出到终端
10 
11 # 4.formatter对象:规定日志内容的格式
12 fm1 = logging.Formatter(
13     fmt='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
14     datefmt='%Y-%m-%d %H:%M:%S %p'
15 )
16 fm2 = logging.Formatter(
17     fmt='%(asctime)s - %(name)s :  %(message)s',
18     datefmt='%Y-%m-%d'
19 )
20 
21 # 5.给logger对象绑定handler对象
22 logger.addHandler(hd1)
23 logger.addHandler(hd2)
24 
25 # 6.给handler绑定formatter对象
26 hd1.setFormatter(fm1)
27 hd2.setFormatter(fm2)
28 
29 # 7.设置日志等级
30 logger.setLevel(10)
31 
32 # 8.记录日志
33 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)

# 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,
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        '': {  # 当键不存在的情况下 默认都会使用该k:v配置
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
    },
}

# 使用日志字典配置,这个也需要自己配置
logging.config.dictConfig(LOGGING_DIC)  # 自动加载字典中的配置
logger1 = logging.getLogger('记录')
logger1.debug('日志字典配置的使用')
日志字典的模版

 

3.hashlib模块

什么叫hash

   hash是一种算法,该算法接受传入的内容,经过运算得到一串hash值,常见的hash算法有MD5,SHA系列等

hash值的特点

  1.只要传入的内容一样,得到的hash值必然一样

  2.不能由hash值反解成内容

  3.只要使用的hash算法不变,无论校验的内容有多大,得到的hash值长度是固定的

hash算法就像是一座工厂,工厂接受你的原材料,经过加工返回新材料,返回的值就是hash值

算法有多种,不同的算法,使用的方法时相同的,密文的长度越长,内部对应的算法越复杂,但是也会有相应的缺点

  1.时间消耗更长

  2.占用的内存更大

通常情况下,使用md5算法就足够了

 

一个简单的加密

1 # hashlib模块
2 import hashlib
3 md = hashlib.md5()  # 生成一个加密的工厂
4 md.update(b'hello')  # 只能识别二进制的数据,我们可以在输入之前先转成二进制数据
5 print(md.hexdigest())  # 获取明文对应的密文

 

加密可以分次执行

1 # 可以分多次传入,只要传入的内容一致,他们生成的密文也是一致的
2 import hashlib
3 md = hashlib.md5()  # 生成一个加密的工厂
4 # md.update(b'helloworld')
5 # print(md.hexdigest())  # fc5e038d38a57032085441e7fe7010b0
6 md.update(b'hello')
7 md.update(b'world')
8 print(md.hexdigest())  # fc5e038d38a57032085441e7fe7010b0 两次生成的密文是一致的

hashlib模块内容一致,密文一致的应用场景

  1.密码的密文存储

  2.检验文件的内容是否一致

 

加盐处理

1 # 加盐处理
2 import hashlib
3 md = hashlib.md5()  # 生成一个加密的工厂
4 md.update(b'head')  # 可以在真正需要存储的数据前加一些固定的内容
5 md.update(b'hello')  # 这是真正的需要存储的数据,这样他人想要窃取密文的时候就不知道用户的真正信息
6 print(md.hexdigest())

生成的新这个密文和真正的密文不同,取出来也分辨不出哪个是真正需要的数据

但是这种自己定义的盐是固定的,只要取出来之后一个个试,很简单就能获取用户的数据

这时候就需要动态加盐

  动态加盐:可以截取用户输入的内容的某一段或者某几个字符作为盐,这样盐就不是固定的,而是动态改变的

 1 # 动态加盐
 2 import hashlib
 3 def get_hash(yan,name):
 4     md = hashlib.md5()
 5     md.update(yan.encode('utf-8'))  # 需要加的盐
 6     md.update(name.encode('utf-8'))  # 真正的数据
 7     return md.hexdigest()
 8 
 9 name = input('密码>>>:').strip()
10 yan = name[0]  # 把输入的第一位给取出来
11 res = get_hash(yan,name)  # 把第一位作为盐和真正的数据一起加密
12 print(res)  # 结果就是动态加盐过的密文

 

4.openyxl模块

openyxl模块是比较火的操作execel表格的模块,这个模块不是python解释器内置的,是第三方添加的

在这之前还有两个小的模块,xlwd写excel,xlrt读excel

  03版本之前 excel文件的后缀名 叫xls
  03版本之后 excel文件的后缀名 叫xlsx

  xlwd和xlrt既支持03版本之前的excel文件也支持03版本之后的excel文件

  但openpyxl 只支持03版本之后的 xlsx

写文件操作

 1 # openpyxl模块
 2 # 写文件
 3 from openpyxl import Workbook
 4 # 文件的生成
 5 wb = Workbook()  # 生成一个excel文件
 6 wb1 = wb.create_sheet('sheet1')  # 生成一个表单页
 7 wb2 = wb.create_sheet('sheet2',0)  # 可以通过第二个参数改变位置
 8 wb2.title = 'sheet3'  # 可以修改表单页名
 9 # 写的方法
10 wb1['D6'] = 123  # 直接按照索引添加值
11 wb1['C7'] = 789
12 wb2.cell(row=3,column=3,value=456)  # 也可以根据行列数添加值
13 wb1['C6'] = '=sum(B6:C5)'  # 两个数的求和
14 # 多行格式化输出
15 wb1.append(['name','age','pwd'])  # 输出多个元素
16 wb1.append(['sxc',18,'sss'])  # 输入对应的元素
17 wb1.append(['zzj',19,'zzz'])
18 wb1.append(['zzp',20,'ppp'])
19 wb1.append(['lzx',21,'lll'])
20 # 保存文件
21 wb.save('workbook.xlsx')  # 保存新建的excel文件

 

读文件操作

1 # 读文件
2 from openpyxl import load_workbook
3 # 读文件能单个的值
4 rwb = load_workbook('workbook.xlsx',read_only=True,data_only=True)
5 print(rwb)  # 是一个内存地址
6 print(rwb.sheetnames)  # 以列表的形式输出表单页
7 print(rwb['sheet1']['D6'].value)  # 输出对应索引的值
8 print(rwb['sheet1']['C7'].value)
9 print(rwb['sheet1']['C6'].value)  # 输出的还是表达式,表格必须经过人为的修改才能正确的显示结果

 注意:想要读取内部函数进行求和或者其他的值需要人为的修改过才能显示

 

读多个值

1 # 输出列表内所有的值
2 res = rwb['sheet1']
3 get = res.rows
4 for i in get:
5     # print(i)  # 输出的是列表所有的值
6     for j in i:
7         # print(j)  # 输出的是每一行的值
8         print(j.value)  # 输出正确的值

 

5.深浅拷贝

拷贝就是复制一份的意思,深浅拷贝其实就是完全复制和只复制一部分,另一部分是大家共用的

浅拷贝

1 # 深浅拷贝
2 # 浅拷贝
3 import copy
4 l = [1,2,[3,4]]
5 l1 = copy.copy(l)  # 浅拷贝
6 print(id(l),id(l1))  # 他们本身的id是不一样的
7 print(id(l[2]),id(l1[2]))  # 但是他们内部的可变类型列表的id是一样的
8 l[2].append(555)
9 print(l,l1)  # 也就是说我可以更改他们内部的列表,从而使两个列表一起改动

也就是说浅拷贝内部的可变类型是共用的,一旦改变一个,另一个也会改变,如图所示

深拷贝

1 # 深拷贝
2 import copy
3 l = [1,2,[3,4]]
4 l1 = copy.deepcopy(l)  # 深拷贝
5 print(id(l),id(l1))  # 他们本身的id是不一样的
6 print(id(l[2]),id(l1[2]))  # 他们内部的可变类型列表的id也相应的改变了
7 l[2].append(555)
8 print(l,l1)  # 改变了一个内部的列表不会影响另一个列表

深拷贝内部可以理解为没有共用的可变元素,改变一个可变类型也不会影响另一个

 17

posted @ 2019-07-19 23:54  瓜落桥下塘  阅读(174)  评论(0编辑  收藏  举报