首页 > 技术文章 > 模块补充

mayugang 2018-12-09 22:05 原文

本次补充的模块,包括logging模块、hashlib模块、datetime模块、shutil模块、xml模块、subprocess模块、struct模块、colections模块,共计8个模块.

1.logging模块

'''
整体说明:
01 日志就是记录一些信息,方便查询或者辅助开发。
02 日志有两种形式,一种是记录到文件中,一种是显示屏幕(控制台)中。
03 日志有3个版本,低配版、标配版、高配版。
04 日志的5种等级,由低到高的顺序如下:
    (1)logging.debug('调试模式'),默认值为10
    (2)logging.info('正常运行') ,默认值为20
    (3)logging.warning('警告'),默认值为30
    (4)logging.error('错误'),默认值为40
    (5)logging.critical('系统崩了'),默认值为50
05 默认的日志级别设置为warning。
'''

# 低配版本:只能写入文件或者是屏幕显示。
import logging

logging.basicConfig(
    # level= 10,
    level=logging.DEBUG,  # 设置显示的级别
    format='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s ',  # 设置日志显示格式
    datefmt="%Y-%m-%d",  # 设置日期,与astime对应
    filename='a.log',  # 默认我a模式,使用的是gbk形式编码。
    filemode="a"  # 可以修改模式,但是一般不用修改
)

logging.debug("调试模式")
logging.info("正常运行")
logging.warning("警告")
logging.error("错误")
logging.critical("系统崩溃了")

# 标配版本:既能在文件中写入,又在屏幕显示。
import logging
# 创建logging对象
logger = logging.getLogger()
# 创建文件对象
fh1 = logging.FileHandler("a1,log",encoding="utf8")  # 一定要指定编码方式,否则程序会报错。
fh2 = logging.FileHandler("a2.log",encoding="utf8")

# 创建屏幕对象
sh =logging.StreamHandler()

# 定义显示格式
formater1 = logging.Formatter(
    fmt='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s', # fmt变量名是固定的。
    datefmt= "%Y-%m-%d %H:%M:%S",
)

formater2 = logging.Formatter(
    fmt='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s', # fmt变量名是固定的。
    datefmt= "%Y-%m-%d %H:%M:%S",
)
formater3 = logging.Formatter(
    fmt='%(asctime)s %(filename)s %(message)s', # fmt变量名是固定的。
    datefmt= "%Y-%m-%d %H:%M:%S",
)

# 给对象绑定格式
fh1.setFormatter(formater1)
fh2.setFormatter(formater2)
sh.setFormatter(formater3)

# 给logger对象添加其他对象
logger.addHandler(fh1)
logger.addHandler(fh2)
logger.addHandler(sh)

# 设置logger级别
logger.setLevel(30)
sh.setLevel(40)
fh1.setLevel(40)
fh2.setLevel(50)

logging.debug('调试模式')  # 10
logging.info('正常运行')  # 20
logging.warning('警告')  # 30
logging.error('错误')  # 40
logging.critical('系统崩了')  # 50

# 高配版本:通过导入文件(导入字典的方式)写日志Django

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'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

# 定义日志输出格式 结束

# print(__file__)
logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目录

logfile_name = '高配版.log'  # log文件名

# # 如果不存在定义的日志目录就创建一个
# if not os.path.isdir(logfile_dir):
#     os.mkdir(logfile_dir)

# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)


# log配置字典
# 第一层键值对的键固定的关键字不能改变,即version、disable_existing_loggers、formatters、filters、handlers、loggers不能改变。

LOGGING_DIC = {
    'version': 1, # 版本
    'disable_existing_loggers': False,  #
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
        'id_simple_format':{
                'format': id_simple_format
        }
    },
    'filters': {},
    'handlers': {
        #打印到终端的日志
        'console': {
            'level': 'ERROR',  # 设置在终端显示的等级
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',  # 设置在文件打印的等级
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,  # 日志文件
            'maxBytes': 300,  # 日志大小 300bytes
            'backupCount': 5, # 最多只有5个文件
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
    },
}



logging.config.dictConfig(LOGGING_DIC)  # 导入上面定义的logging配置
# # logging.config  # 将你写好的logging 字典 在导入logging.config后,传到logging模块中
logger = logging.getLogger()  # 生成一个log实例  通过字典自己设置的个性化的log对象


logging.debug('调试模式')  # 10
logging.info('正常运行')  # 20
logging.warning('警告')  # 30
logging.error('错误')  # 40
logging.critical('系统崩了')  # 50

2.hashlib模块

'''
整体说明:
01 hash:对不可变的数据(int bool str tuple)进行哈希算法,将数据str tuple转化成为等长度的一串数字。
02 python中的haslib提供了常见的摘要算法,如MD5、SHA1.
03 摘要算法:又称哈希算法、散列算法,它通过一个函数,把任意长度的数据转换成为一个长度固定的数据串(通常用16机制的字符串表示)。
04 摘要算法的作用:通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。摘要算法
   之所以能指出数据是否被篡改过,就是因为摘要函数是一个单项函数,计算(data)很容易,但是通过摘要digest反对data却非常困难。而且,对
   原始数据做一个bit的修改,都会导致计算出的摘要完全不同。
05 加密原则说明:
    (1)加密类型必须是str
    (2)无论这个str多长,加密之后都转化为成等长度的数字。
    (3)无论在任何机器上,对相同的字符串进行加密,加密的密文一致。
    (4)不同的字符串加密之后的密文一定不同。
06 hashlib的用途:
    (1)对密码进行加密
        a)普通加密
        b)加固定盐
        c)动态加盐
    (2)文件的校验


'''
# 01 hashlib的的简单举例
import hashlib
ret = hashlib.md5()
ret.update("马玉刚".encode("utf8"))
print(ret.hexdigest())

# 02 hashlib的应用
# 0201 对密码进行加密
# md5加密

# a,普通加密
import hashlib

def encryption(password):  # 定义对password加密的函数
    ret = hashlib.md5()
    ret.update(password.encode('utf-8'))
    return ret.hexdigest()


def register():
    username = input('>>>')
    password = input('>>>')
    password = encryption(password) # 引用加密函数,对密码进行加密
    with open('register', encoding='utf-8', mode='a') as f1:
        f1.write('{}|{}'.format(username, password)) # 各式化写入文件、。

register()

# b, 加固定盐。
'''
dsaflsak@123213OHJ123
#简单密码普遍存在:
123456
111111
666666
....
'''
import hashlib
ret = hashlib.md5('马玉刚'.encode('utf-8'))
ret.update('alex3714'.encode('utf-8'))
print(ret.hexdigest())

# c 动态加盐。

import hashlib
username = '马玉刚'
password = '马玉刚123456'
ret = hashlib.md5(username[::2].encode('utf-8')) # 动态加盐
ret.update('alex3714'.encode('utf-8'))
print(ret.hexdigest())

# sha 加密
import hashlib
ret = hashlib.sha1()
ret.update('马玉刚'.encode('utf-8'))
print(ret.hexdigest())

import hashlib
ret = hashlib.sha512()
ret.update('fdsklafdsjafklsdajflksdfjsadf'.encode('utf-8'))
print(ret.hexdigest())

# 固定盐动态盐与上面一致。

# 2,文件的校验。
import hashlib

s1 = '狗哥,好黑呀,现场怼呀'
ret = hashlib.md5()
ret.update(s1.encode('utf-8'))
print(ret.hexdigest()) # 7bc77d0bc1281428e5c63e628fa40c49

ret = hashlib.md5()
ret.update('狗哥'.encode('utf-8'))
ret.update(',好黑呀'.encode('utf-8'))
ret.update(',现场'.encode('utf-8'))
ret.update(''.encode('utf-8'))
ret.update(''.encode('utf-8'))
print(ret.hexdigest())  # 7bc77d0bc1281428e5c63e628fa40c49


#  小文件:可以采用一次性全部读出
import hashlib
def file_hashlib(file):
    ret = hashlib.md5()
    with open(file, mode='rb') as f1:
        ret.update(f1.read())
    return ret.hexdigest()

print(file_hashlib('文件校验1'))
print(file_hashlib('文件校验2'))


# 大文件:用for循环可以逐行读取大文件,避免读大文件导致程序崩溃。

import hashlib
def file_hashlib(file):
    ret = hashlib.md5()

    with open(file, mode='rb') as f1:
        for i in f1:
            ret.update(i)
    return ret.hexdigest()

print(file_hashlib('文件校验1'))
print(file_hashlib('文件校验2'))

3.datetime模块

import datetime
now_time = datetime.datetime.now()  # 现在的时间
# 只能调整的字段:weeks days hours minutes seconds
print(datetime.datetime.now() + datetime.timedelta(weeks=3)) # 三周后
print(datetime.datetime.now() + datetime.timedelta(weeks=-3)) # 三周前
print(datetime.datetime.now() + datetime.timedelta(days=-3)) # 三天前
print(datetime.datetime.now() + datetime.timedelta(days=3)) # 三天后
print(datetime.datetime.now() + datetime.timedelta(hours=5)) # 5小时后
print(datetime.datetime.now() + datetime.timedelta(hours=-5)) # 5小时前
print(datetime.datetime.now() + datetime.timedelta(minutes=-15)) # 15分钟前
print(datetime.datetime.now() + datetime.timedelta(minutes=15)) # 15分钟后
print(datetime.datetime.now() + datetime.timedelta(seconds=-70)) # 70秒前
print(datetime.datetime.now() + datetime.timedelta(seconds=70)) # 70秒后

current_time = datetime.datetime.now()
# 可直接调整到指定的 年 月 日 时 分 秒 等

print(current_time.replace(year=1977))  # 直接调整到1977年
print(current_time.replace(month=1))  # 直接调整到1月份
print(current_time.replace(year=1989,month=4,day=25))  # 1989-04-25 18:49:05.898601

# 将时间戳转化成时间
print(datetime.date.fromtimestamp(1232132131))  # 2009-01-17

4.shutil模块

'''
整体说明:
01 高级的文件、文件夹、压缩包处理模块
'''

# 将文件内容拷贝到另一个文件中
import shutil
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

# 拷贝文件
shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在

# 仅拷贝权限。内容、组、用户均不变
shutil.copymode('f1.log', 'f2.log') #目标文件必须存在

# 仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
shutil.copystat('f1.log', 'f2.log') #目标文件必须存在

# 拷贝文件和权限
shutil.copy('f1.log', 'f2.log')

# 拷贝文件和状态信息
shutil.copy2('f1.log', 'f2.log')

# 递归的去拷贝文件夹
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除

# 递归的去删除文件
shutil.rmtree('folder1')

# 递归的去移动文件,它类似mv命令,其实就是重命名。
shutil.move('folder1', 'folder3')

# shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的
import zipfile

# 压缩
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()

# 解压
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall(path='.')
z.close()

# zipfile压缩解压缩

import tarfile

# 压缩
t=tarfile.open('/tmp/egon.tar','w')
t.add('/test1/a.py',arcname='a.bak')
t.add('/test1/b.py',arcname='b.bak')
t.close()


# 解压
t=tarfile.open('/tmp/egon.tar','r')
t.extractall('/egon')
t.close()

# tarfile压缩解压缩

5.xml模块

'''
XML模块整体说明:
    xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,
大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。现在这种格式的文件比较少了,但是还是存在的所以大家
简单了解一下,以备不时之需。
'''

# 01 对XML的增删改查简单操作。
# 增删改查
# 在进行操作之前,都应该进行这两步:

import xml.etree.ElementTree as ET
tree = ET.parse('a.xml')  # 形成树形结构
root = tree.getroot()  # 得到树的根系
print(root)
# 循环打印:
for i in root:
    print(i)
'''
输出结果:'
<Element 'country' at 0x00000196B51191D8>
<Element 'country' at 0x00000196B5124B88>
<Element 'country' at 0x00000196B5124D18>
'''
# 所有的增删改查都是基于这个root根系去操作

# 查:
# 1,全文搜索 year 将所有的year标签全部找
print(root.iter('year'))
print([i for i in root.iter('year')])
# 2,只找第一个,找到就返回
print(root.find('country'))
# 3,在root的子节点找,找所有的
print(root.findall('country'))

# 练习
# 找到标签也可以找到标签相应的内容:tag,attrib,text

# 1,找所有的rank标签,以及 attrib 和 text (这里利用列表推导式比较方便)
print([i for i in root.iter('rank')])
'''
输出结果:
[<Element 'rank' at 0x000001367D0D49F8>, <Element 'rank' at 0x000001367D0D4BD8>, <Element 'rank' at 0x000001367D0D4D68>]
'''
print([i.attrib for i in root.iter('rank')])
'''
输出结果:
[{'updated': 'yes'}, {'updated': 'yes'}, {'updated': 'yes'}]
'''
print([i.text for i in root.iter('rank')])  # ['2', '5', '69']

# 2,找到第二个country的 neighbor标签以及他的属性
print([tag for tag in root.findall('country')][1].find('neighbor').attrib)
'''
输出结果:
{'direction': 'N', 'name': 'Malaysia'}
'''

# 增 append
import xml.etree.ElementTree as ET
tree = ET.parse('a.xml')  # 形成树形结构
root = tree.getroot()  # 得到树的根系

# 给 year 大于2010年的所有标签下面添加一个month标签,属性为name:month 内容为30days

for country in root.findall('country'):
    for year in country.findall('year'):
        if int(year.text) > 2010:
            month = ET.Element('month')
            month.text = '30days'
            month.attrib = {'name': 'month'}
            country.append(month)
tree.write('b.xml')

#

import xml.etree.ElementTree as ET
tree = ET.parse('a.xml')  # 形成树形结构
root = tree.getroot()  # 得到树的根系
# 对所有的year属性以及值进行修改
for node in root.iter('year'):
    new_year=int(node.text)+1
    node.text=str(new_year)
    node.set('updated','yes')
    node.set('version','1.0')
tree.write('test.xml')


#
import xml.etree.ElementTree as ET
tree = ET.parse('a.xml')  # 形成树形结构
root = tree.getroot()  # 得到树的根系

# 将 rank值大于50的country标签删除
for country in root.findall('country'):
   rank = int(country.find('rank').text)
   if rank > 50:
     root.remove(country)

tree.write('output.xml')

# 02 自己创建一个XML文件

import xml.etree.ElementTree as ET

new_xml = ET.Element("namelist")
name = ET.SubElement(new_xml, "name", attrib={"enrolled": "yes"})
age = ET.SubElement(name, "age", attrib={"checked": "no"})
sex = ET.SubElement(name, "sex")
sex.text = '33'
name2 = ET.SubElement(new_xml, "name", attrib={"enrolled": "no"})
age = ET.SubElement(name2, "age")
age.text = '19'

et = ET.ElementTree(new_xml)  # 生成文档对象
et.write("test.xml", encoding="utf-8", xml_declaration=True)

ET.dump(new_xml)  # 打印生成的格式

6.subprocess模块

'''
整体说明:
01 subprocess模块是python从2.4版本开始引入的模块。主要用来取代 一些旧的模块方法,如os.system、os.spawn*、os.popen*、
   commands.*等。subprocess通过子进程来执行外部指令,并通过input/output/error管道,获取子进程的执行的返回信息。
02 可以实现通过python进程执行外部命令(模拟调用cmd来输出结果)
'''
import subprocess

obj = subprocess.Popen('dir',
                       shell=True,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE,
                       )

ret = obj.stdout.read().decode('gbk')  # 正确命令
ret1 = obj.stderr.read().decode('gbk') # 错误命令
print(ret)
print(ret1)

# shell: 命令解释器,相当于调用cmd 执行指定的命令。
# stdout:正确结果丢到管道中。
# stderr:错了丢到另一个管道中。
# windows操作系统的默认编码是gbk编码。

7.struct模块

'''
整体说明:该模块可以把一个类型,如数字,转成固定长度的bytes类型。
'''
import struct
# 将一个数字转化成等长度的bytes类型。
ret = struct.pack('i', 183346)
print(ret, type(ret), len(ret))

# 通过unpack反解回来
ret1 = struct.unpack('i',ret)[0]
print(ret1, type(ret1), len(ret1))


# 但是通过struct 处理不能处理太大

ret = struct.pack('l', 4323241232132324)
print(ret, type(ret), len(ret))  # 报错

8.colections模块

'''
整体说明:
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。

1.namedtuple: 生成可以使用名字来访问元素内容的tuple

2.deque: 双端队列,可以快速的从另外一侧追加和推出对象

3.Counter: 计数器,主要用来计数

4.OrderedDict: 有序字典

5.defaultdict: 带有默认值的字典
'''

 

推荐阅读