代码改变世界

上海 day17-- Python深浅拷贝、包以及常用模块2

2019-07-19 16:03  在上海的日子里  阅读(231)  评论(0编辑  收藏  举报

目  录

  • logging 模块

  • logging 模块的配置字典

  • hashlib 模块

  • openpyxl 模块

  • 深浅拷贝

 

一、包

模块的3种来源:

  1、python解释器内置的模块

  2、第三方模块

  3、自定义的模块

模块的4种表现形式:

  1、自己写的py文件

  2、包:文件夹,一系列py文件集合

  3、使用C编写的连接Python解释器内置的模块

  4、第三方共享库

研究模块与包,我们可以站在另外两个角度来分析不同的问题:

  1、模块的开发者

  2、模块的使用者

提出问题:什么是包?

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

  包的内部里面有一个__init__.py 的文件夹,包的本质就是一个模块。

讨论:导入包和导入模块发生的几件事有什么不同?

导入模块:

  首先会在执行文件生成一个名字空间;

    1、执行import md,生成模块的名称空间

    2、运行模块代码,生成的名字(变量名、函数名)丢到模块的名称空间

    3、在执行文件名称空间内生成一个指向模块名称空间的名字。

导入包:

  首先会在执行文件生成一个名称空间;

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

    2、执行包下面的__init__.py文件代码,将生成的名字放入包下面的__init__.py文件的名称空间

    3、在执行文件的名称空间生成一个指向包下面__init__.py文件名称空间的名字。

总结:导入包和导入模块的区别就在于前两步的不同,包的执行文件就是包下面的__init__.py文件。

包的使用或为什么要使用包?

  一个项目要实现很多功能,我们不能将那么多功能全部放在一个py文件中,所以需要分块,便于管理。

注意事项:每个模块之间为了避免后期模块改名带来的麻烦,导入的时候应该使用相对导入(包里面的文件应该都是作为模块用来导入的,不需要担心执行问题!)

包的导入路径问题:

  站在包开发者角度来说,如果使用绝对路径来管理包,只需要以包所在的目录为基准依次导入模块就可以;

  站在包使用者角度来说,必须先将导入包的存储路径添加到 systeam path中(******)

导入包时Python2 与Python3的区别:

  python2 导入包时,包下面必须要有__init__.py 文件夹,否则报错;

  python3 导入包时,包下面没有__init__.py 文件夹也可以成功导入。

所以综上,当删除一个程序的文件时不要随便删除__init__.py 文件,即便__init__.py文件为空也不能删除,否则在python2解释器上运行会报错!

 

二、logging 模块

logging 模块的5个等级:debug、info、warning、error、critical

# logging.debug('debug日志')  # 10
# logging.info('info日志')  # 20
# logging.warning('warning日志')  # 30
# logging.error('error日志')  # 40
# logging.critical('critical日志')  # 50

需解决的问题:

"""
1.乱码
2.日志格式
3.如何既打印到终端又写到文件中
"""

logging 模块4个对象:

"""
1.logger对象:负责产生日志

2.filter对象:过滤日志(了解)

3.handler对象:控制日志输出的位置(文件/终端)

4.formmater对象:规定日志内容的格式
"""

logging 模块自定义配置的8个步骤:

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.addHandler(hd1)
logger.addHandler(hd2)
logger.addHandler(hd3)

# 6.给handler绑定formmate对象
hd1.setFormatter(fm1)
hd2.setFormatter(fm2)
hd3.setFormatter(fm1)

# 7.设置日志等级
logger.setLevel(20)

# 8.记录日志
logger.debug('写了半天 好累啊 好热啊 好想释放')
日志配置的8个步骤

日志的配置参数:

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用户输出的消息
View Code

以上日志配置的过程过去繁琐,因此引入日志配置文件(日志配置字典):

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配置
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },  # 当键不存在的情况下 默认都会使用该k:v配置
    },
}


# 使用日志字典配置
logging.config.dictConfig(LOGGING_DIC)  # 自动加载字典中的配置
logger1 = logging.getLogger('asajdjdskaj')
logger1.debug('好好的 不要浮躁 努力就有收获')
完整的日志配置文件

logging 在项目中的使用:

 

 

三、hashlib 模块

hashlib模块就是加密模块。

import hashlib  # 加密模块
md = hashlib.md5()  # 生成一个造密文的对象
# md.update()方法需要自己记住,里面的参数必须是二进制数据
md.update('大碗宽面!'.encode('utf-8'))  # 往对象里传明文数据,update只能接受二进制数据
print(md.hexdigest())  # 获取明文对应的密文

'''
注意:不同的加密算法使用方法相同,密文对应的字符串越长对应的加密算法越复杂
    但是:加密算法月复杂意味着,时间消耗越长,占用的空间越大
    通常情况下使用md5就足够了

'''

hashlib模块应用场景:

  1、密码的密文存储

  2、检验文件内容是否一致

注意:明文的传入内容可以分多次传入,但只要内容相同生成的密文就相同。

'''
传入的明文内容可以分多次传入,但只要内容一样的生成的密文就相同!
'''
import hashlib
md = hashlib.md5()  # 造加密的对象
md.update('大碗宽面!吴亦凡'.encode('utf-8'))

md.update('大碗'.encode('utf-8'))
md.update('宽面!'.encode('utf-8'))
md.update('吴亦凡'.encode('utf-8'))
print(md.hexdigest())  # 585b9ec0968c2d95fa9afdd68d860e58

虽然有了密文对密码进行加密但是黑客可以通过拿到密码对应的密文到数据库中进行对比,可能会对比出真正的密码,

因此引出了进一步的加密操作—— ‘加盐处理’。

加盐处理:

  就是在我们输入真正的密码之前自己手动的添加一些内容,就算别人获得我们密文对应的明文密码也不知道哪些是真正的密码

# 加盐处理:就是在我们传入明文密码的前一步自己动手去加一些内容,这样黑客通过数据对比拿到密码也不能知道准确的密码

import hashlib
md = hashlib.md5()  # 造加密的对象
md.update('进一步加密'.encode('utf-8'))  # 在输入真正的密码明文之前自己动手加一些内容
md.update('大碗宽面!吴亦凡'.encode('utf-8'))
print(md.hexdigest())  # 0dde467fb48ade931ce953d8c87f5c7d

动态加盐处理:我们自己随机生成动态的内容去添加到真正明文密码的前面。

 小程序

  对用户输入的密码进行加盐处理加密

# 小程序:对用户输入的密码进行加盐处理
import hashlib
def userpwd(password):
    md = hashlib.md5()
    md.update('加盐处理'.encode('UTF-8'))
    md.update(password.encode('UTF-8'))
    res = md.hexdigest()
    return res
pwd = input('输入密码:').strip()
dd = userpwd(pwd)
print(dd)
# >>>:输入密码:CQUPT
# >>>:96625af5a7c3ffb0321981d56ab79e7e

 

四、openpyxl 模块

openpyxl 是比较火的操作Excel的模块!

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

xlwd  写excel
xlrt  读excel

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

 openpyxl 模块中的“写”操作:

from openpyxl import Workbook  # 引入工作簿模块

# 生成工作簿对象
wb = Workbook()  

# 创建工作表sheet 后面跟两个参数,一个是工作表名一个是索引
wb1 = wb.create_sheet('index1',0) 

# 按位置添加数据
wb1['A1'] = 666
wb1['A2'] = 999

# 写Excel形式函数求和
wb1['A3'] = '=sum(A1:A2)'

# 点title 改变sheet表的名字
# wb1.title('星空1号')  # 错误格式
wb1.title = '星空1号'

# 指定行、列添加指定数值
wb1.cell(row=5,column=5,value=6969)

# 为工作簿添加表头,和表内容
wb1.append(['name','age','hobby'])
wb1.append(['jason',19,'sing song'])
wb1.append(['tank',29,'run'])

# 保存新建的Excel文件
wb.save('op创建.xlsx')

openpyxl模块中的“读”操作:

from openpyxl import load_workbook  #读文件

wb = load_workbook('op创建.xlsx',read_only=True)
print(wb)  # 结果是一个对象

# 打印Excel文件中所有的sheet表
print(wb.sheetnames)  # ['星空1号', 'Sheet']

# 打印指定sheet表中特定区域的值
print(wb['星空1号']['E5'].value)  # 6969

# 'A3'是我们通过代码操作求和的数值,打印看会发生什么
print(wb['星空1号']['A3'].value)  # =SUM(A1:A2)结果返回一个公式
'''
通过打印'A3'可知# 通过代码产生的excel表格必须经过人为操作之后才能读取出函数计算出来的结果值
'''

 

五、深浅拷贝

1、赋值运算

l1 = [1,2,3,['jason','egon']]
l2 = l1

l1[0] = 111
print(l1)  # [111, 2, 3, ['jason', 'egon']]
print(l2)  # [111, 2, 3, ['jason', 'egon']]

l1[3][0] = 'kevin'
print(l1)  # [111, 2, 3, ['kevin', 'egon']]
print(l2)  # [111, 2, 3, ['kevin', 'egon']]

以上代码图解:

2、浅拷贝

# 示例
l1 = [1,'q',[1,2]]
l2 = l1.copy()

浅拷贝图解:

 总结:

  浅拷贝就是在内存重新创建了一块空间来存储新的列表,但新列表里面的元素和旧列表里面的元素是公用的。

3、深拷贝

# 示例
l1 = [1,'q',[1,2]]
l2 = l1.copy()

图解:

总结:

  深拷贝也是在内存空间中新开辟一块空间创建一个列表,列表里面的元素可变类型是新创建的,不可变类型是公用的,

也就是说不可变类型是指向同一个内存地址。