• python常用模块整理(干货满满)


    random 模块

      随机模块,在某个范围内取到每一个值的概率是相同的

    import random
    # 随机小数
    print(random.random())          # 0-1之内的随机小数
    print(random.uniform(1, 5))     # 任意范围之内的随机小数
    
    
    # 随机整数
    print(random.randint(1, 2))     # [1,2] 包含2在内的范围内随机取整数
    print(random.randrange(1, 2))   # [1,2) 不包含2在内的范围内随机取整数
    print(random.randrange(1, 10, 2))   # [1,10) 不包含10在内的范围内随机取奇数
    
    
    # 随机抽取
    lst = [1, 2, 3, 'abc', ('wahaha', 'qqxing')]
    ret = random.choice(lst)      # 随机抽取一个值
    print(ret)
    
    ret1 = random.sample(lst, 2)    # 随机抽取两个值
    print(ret1)
    
    
    # 打乱顺序  在原列表的基础上做乱序
    lst = [1, 2, 3, 'abc', ('wahaha', 'qqxing')]
    random.shuffle(lst)
    print(lst)

    随机生成验证码

    # (1)4位数字的验证码
    # 基础版本
    lis = ''
    for i in range(4):
        num = random.randint(0, 9)
        lis += str(num)
    print(lis)
    
    # 函数版本
    def rand_code(n=4):
        lis = ''
        for i in range(n):
            num = random.randint(0, 9)
            lis += str(num)
        return lis
    print(rand_code(6))
    
    
    # (2)6位 数字+字母
    def rand_code(n):
        code = ''
        for i in range(n):
            rand_num = str(random.randint(0, 9))
            rand_alph = chr(random.randint(97, 122))
            rand_alph_upper = chr(random.randint(65, 90))
            rand_num = random.choice([rand_num, rand_alph, rand_alph_upper])
            code += rand_num
        return code
    ret = rand_code(6)
    print(ret)
    
    
    # (3)可控制验证码  数字 / 数字+字母
    def rand_code(num, DefaultAlph=True):   # 当DefaultAlph=True时生成字母+数字的验证码, 为False时生成纯数字验证码
        code = ''
        for i in range(num):
            rand_num = str(random.randint(0, 9))
            if DefaultAlph:
                rand_alph = chr(random.randint(97, 122))
                rand_alph_upper = chr(random.randint(65, 90))
                rand_num = random.choice([rand_num, rand_alph, rand_alph_upper])
            code += rand_num
        return code
    ret = rand_code(4, DefaultAlph=False)
    print(ret)

    time 模块

    #常用方法
    import time
    
    time.sleep(secs)
    #(线程)推迟指定的时间运行。单位为秒
    
    time.time()
    #获取当前时间戳

    表示时间的三种方式

    在python中,通常用这三种方式表示时间:时间戳、格式化的时间字符串、元组(struct_time)

    # 时间模块
    import time
    # 时间戳
    print(time.time())      # 返回当前时间的时间戳
    # 结果>>> 1543743462.3950245
    
    # 时间字符串
    print(time.strftime('%Y-%m-%d %X'))
    # 结果>>> 2018-12-02 17:39:58
    print(time.strftime('%Y-%m-%d %H-%M-%S'))
    # 结果>>> 2018-12-02 17-39-58
    
    # 时间元组:localtime将一个时间戳转换为当前时区的struct_time
    print(time.localtime())
    # 结果>>> time.struct_time(tm_year=2018, tm_mon=12, tm_mday=2, tm_hour=17, tm_min=43, tm_sec=44, tm_wday=6, tm_yday=336, tm_isdst=0)

    time模块相关方法:

    time.localtime([secs]):将一个时间戳转换为当前时区的struct_time;secs参数未提供,则以当前时间为准。
    time.gmtime([secs]):和 localtime()类似;gmtime()方法是将一个时间戳转换为UTC时区(0 时区)的struct_time。
    time.time():返回当前时间戳
    time.mktime(t):将一个time.struct_time转为时间戳
    time.sleep(secs):线程推迟指定的时间运行,单位为秒
    time.asctime([t]):把一个表示时间的元组或者struct_time表示为这种形式:'Sun Dec  2 17:52:36 2018'。如果没有参数,默认将time.localtime()作为参数传入
    time.ctime([t]):把一个时间戳(按秒计算的浮点数)转为time.asctime()的形式。如果参数未给或者为None的时候,默认将time.time()作为参数,相当于time.asctime(time.localtime(secs))
    time.strftime(format[, t]):把一个代表时间的元组或者struct_time(如由time.localtime()和time.gmtime()返回)转为格式化的时间字符串,如果t未指定,默认传入time.localtime()
    time.strptime(string[, format]):把一个格式化时间字符串转化为struct_time。实际上它和strftime()是逆操作

    时间格式之间的转换


    # 时间戳——>结构化时间
    # time.gmtime(时间戳)      #UTC时间,与英国伦敦当地时间一致
    # time.localtime(时间戳)   #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间
    print(time.gmtime(1510000000))
    #结果>>> time.struct_time(tm_year=2017, tm_mon=11, tm_mday=6, tm_hour=20, tm_min=26, tm_sec=40, tm_wday=0, tm_yday=310, tm_isdst=0)
    print(time.localtime())
    #结果>>> time.struct_time(tm_year=2018, tm_mon=12, tm_mday=2, tm_hour=18, tm_min=4, tm_sec=23, tm_wday=6, tm_yday=336, tm_isdst=0)
    
    # 结构化时间——>时间戳
    # time.mktime(结构化时间)
    time_tuple = time.localtime(1510000000)
    print(time.mktime(time_tuple))
    #结果>>> 1510000000.0
    # 结构化时间——>字符串时间
    # time.strftime("格式定义","结构化时间")  结构化时间参数若不传,则显示当前时间
    print(time.strftime("%Y-%m-%d %X"))
    #结果>>> 2018-12-02 18:07:47
    print(time.strftime("%Y-%m-%d", time.localtime(1510000000)))
    #结果>>> 2017-11-07
    
    # 字符串时间——>结构化时间
    # time.strptime(时间字符串,字符串对应格式)
    print(time.strptime("2018-02-22", "%Y-%m-%d"))
    #结果>>> time.struct_time(tm_year=2018, tm_mon=2, tm_mday=22, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=53, tm_isdst=-1)
    print(time.strptime("2018/03/01", "%Y/%m/%d"))
    #结果>>> time.struct_time(tm_year=2018, tm_mon=3, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=60, tm_isdst=-1)
    # 结构化时间 ——> %a %b %d %H:%M:%S %Y串
    # time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
    print(time.asctime(time.localtime(1510000000)))
    #结果>>> Tue Nov  7 04:26:40 2017
    print(time.asctime())
    #结果>>> Sun Dec  2 18:12:19 2018
    
    # 时间戳 ——> %a %b %d %H:%M:%S %Y串
    # time.ctime(时间戳)  如果不传参数,直接返回当前时间的格式化串
    print(time.ctime())
    #结果>>> Sun Dec  2 18:13:14 2018
    print(time.ctime(1510000000))
    #结果>>> Tue Nov  7 04:26:40 2017

    获取当月一号的时间戳格式

    # 结构化时间
    struct_time = time.localtime()
    struct_time = time.strptime('%s-%s-1'%(struct_time.tm_year,struct_time.tm_mon),'%Y-%m-%d')
    print(time.mktime(struct_time))
    # 格式化时间
    ret = time.strftime('%Y-%m-1')
    struct_time = time.strptime(ret,'%Y-%m-%d')
    print(time.mktime(struct_time))

    datetime 模块

    相比于time模块,datetime模块的接口则更直观,更容易调用

    • datetime模块定义了下面这几个类:
    • datetime.date:表示日期的类;常用的属性有year, month, day;
    • datetime.time:表示时间的类;常用的属性有hour, minute, second, microsecond;
    • datetime.datetime:表示日期时间。
    • datetime.timedelta:表示时间间隔,即两个时间点之间的长度。
    • datetime.tzinfo:与时区有关的相关信息。
    import datetime
    print(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

    sys 模块

    常用方法:

    sys.argv           命令行参数List,第一个元素是程序本身路径,(类似shell中调用脚本后面传入的$1,$2,$3)
    sys.exit(n)        退出程序,正常退出时exit(0),错误退出sys.exit(1)
    sys.version        获取Python解释程序的版本信息
    sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
    sys.platform       返回操作系统平台名称

    sys.argv用法

    name = sys.argv[1]
    pwd = sys.argv[2]
    if name == 'xiaobai' and pwd == 'a123456':
        print('执行以下代码')
    else:
        exit()

    os 模块

    #当前执行这个python文件的工作目录相关的工作路径
    os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
    os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
    os.curdir  返回当前目录: ('.')
    os.pardir  获取当前目录的父目录字符串名:('..')
    
    #和文件夹相关
    os.makedirs('dirname1/dirname2')    可生成多层递归目录
    os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
    os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
    os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
    os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
    
    # 和文件相关
    os.remove()  删除一个文件
    os.rename("oldname","newname")  重命名文件/目录
    os.stat('path/filename')  获取文件/目录信息
    
    # 和操作系统差异相关
    os.sep      输出操作系统特定的路径分隔符,win下为"\",Linux下为"/"
    os.linesep  输出当前平台使用的行终止符,win下为"	
    ",Linux下为"
    "
    os.pathsep  输出用于分割文件路径的字符串 win下为;,Linux下为:
    os.name     输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
    
    # 和执行系统命令相关
    os.system("bash command")  运行shell命令,直接显示
    os.popen("bash command).read()  运行shell命令,获取执行结果
    os.environ  获取系统环境变量
    
    #path系列,和路径相关
    os.path.abspath(path) 返回path规范化的绝对路径 
    os.path.split(path)   将path分割成目录和文件名二元组返回 
    os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 
    os.path.basename(path)返回path最后的文件名。如何path以/或结尾,那么就会返回空值,即os.path.split(path)的第二个元素。
    os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
    os.path.isabs(path)   如果path是绝对路径,返回True
    os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
    os.path.isdir(path)   如果path是一个存在的目录,则返回True。否则返回False
    os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
    os.path.getatime(path)  返回path所指向的文件或者目录的最后访问时间
    os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
    os.path.getsize(path)   返回path的大小

    注意:os.stat('path/filename')  获取文件/目录信息 的结构说明

    stat结构:

    st_mode: inode 保护模式
    st_ino: inode 节点号。
    st_dev: inode 驻留的设备。
    st_nlink: inode 的链接数。
    st_uid: 所有者的用户ID。
    st_gid: 所有者的组ID。
    st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
    st_atime: 上次访问的时间。
    st_mtime: 最后一次修改的时间。
    st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。

    统计目录大小:

    import os, sys
    
    SIZE = 0
    def countDirSize(path):
        global SIZE
        pathDirList = os.listdir(path)
        for fileName in pathDirList:
            newAbsPath = os.path.join(path, fileName)
            if os.path.isdir(newAbsPath):
                SIZE += os.path.getsize(newAbsPath)
                countDirSize(newAbsPath)
            else:
                SIZE += os.path.getsize(newAbsPath)
    def win():
        path = input('请输入需要统计的目录>>> ')
        if os.path.exists(path):
            countDirSize(path)
        else:
            print("请输入正确的路径...")
            exit()
        return SIZE
    
    def linux():
        path = sys.argv[1]
        if os.path.exists(path):
            countDirSize(path)
        else:
            print("请输入正确的路径...")
            exit()
        return SIZE
    if __name__ == '__main__':
        if os.name == "nt":
            print(win())
        elif os.name == "posix":
            print(linux())

    统计当前文件大小:

    import os
    import sys
    import datetime
    import time
    
    
    now_date = datetime.datetime.now().strftime('%Y%m%d')
    
    def get_size(file_path):
        size = os.path.getsize(file_path)
        mod_date = time.strftime('%Y%m%d',time.localtime(os.path.getatime(file_path)))
        if mod_date == now_date:
            fnl_size=round(round(size,4)/1024,2)
            return (fnl_size)
        else:
            return 1
    
    if __name__ == "__main__":
        file_path=sys.argv[1]
        size=get_size(file_path)
        print(size)

    查找空目录:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    #*******查询指定目录中的空目录*********
    #执行方法:python3 04-1.py + 目录名称
    
    import sys
    import os, os.path
    
    def find(p):
        fs = os.listdir(p)
        if len(fs) == 0:
            print(os.path.abspath(p))
            return
        for f in fs:
            pf = os.path.join(p, f)
            if not os.path.isdir(pf):
                continue
            find(pf)
    
    
    if __name__ == '__main__':
        find(sys.argv[1])

    删除空目录:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import os
    import sys
    
    def del_emp_dir(path):
        for (root, dirs, files) in os.walk(path):
            # print('path-->',path)
            # print('root-->',root)
            # print('dirs-->',dirs)
            for item in dirs:
                dir = os.path.join(root, item)
                try:
                    os.removedirs(dir)  #os.rmdir() 方法用于删除指定路径的目录。仅当这文件夹是空的才可以, 否则, 抛出OSError。
                    print(dir)
                except Exception as e:
                    pass
                    # print('Exception',e)
    if __name__ == '__main__':
        del_emp_dir(sys.argv[1])

    查看目录文件/文件夹是否更新

    #!usr/bin/python
    # -*- encoding: utf-8 -*-
    # =============================================================================
    #       Author: wu
    
    import os
    import datetime
    import time
    
    
    now_date = datetime.datetime.now().strftime('%Y%m%d%H%M')
    # 获取文件夹日期
    files_date = datetime.datetime.now().strftime('%Y-%m-%d')
    # print(files_date,type(files_date))
    
    def files_report(report):
        try:
            start_time = time.time()
            new_report = os.path.join(report,files_date)
            # print(new_report)
            # 列出目录的下所有文件和文件夹保存到lists
            lists = os.listdir(new_report)
            # 按照时间进行排序
            lists.sort(key=lambda fn: os.path.getmtime(new_report + "/" + fn))
            # 获取最新的文件保存到file_new中
            file_new = os.path.join(new_report, lists[-1])
            mod_date = time.strftime('%Y%m%d%H%M', time.localtime(os.path.getatime(file_new)))
            s = '文件时间:【%s】'% mod_date
            print('最新文件:【%s】 
    %s'%(file_new,s))
            end_time =time.time()
            # print(mod_date,type(mod_date))
            old_time =  end_time - start_time
            if mod_date == now_date:
                print('查询耗时:【%s】'% old_time)
                return '状态:【((o(^_ ^)o))文件正常】'
            else:
                print('查询耗时:【%s】' %old_time)
                return '状态:【ლ(ٱ٥ٱლ)没有最新文件夹,请检查!】'
        except Exception as e:
            return '【ლ(ٱ٥ٱლ)单日文件夹不存在,请检查!】'
    
    
    if __name__ == '__main__':
        report = r"path"  # 目录地址
        res = files_report(report)
        print(res)

    序列化模块(json模块、pickle)

    序列化:

      将一个对象从内存中转换为可存储(字符串类型)或者可传输(bytes)类型的过程,就叫做序列化。在python中叫做pickling,通俗讲:序列化就是将其他数据类型转换为字符串/bytes类型的过程。

    为什么要使用序列化:

      (1)持久化数据类型

      (2)跨平台进行交互。不同的编程语言都用协商好的序列化格式,那么便能打破平台/语言之间的限制,实现跨平台数据交互。

      (3)使程序更具维护性

    Json

    json格式在各个语言之间都是通用的序列化格式。在json中,所有的字符串都必须是" "双引号。

    json的优点:

      所有的数据类型都是各个语言通用的。在各个编程语言中都支持。

    json的缺点:

      1、json只是支持非常少的数据类型

      2、对数据类型的约束十分严格

        (1)字典中的key必须是字符串。

        (2)json只支持列表,字典,数值,字符串,布尔值。

     json模块提供了四个功能:dumps、dump、loads、load

    # dumps 与 loads
    import json
    
    # 序列化
    dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4}
    str_dic = json.dumps(dic)   # 序列化:将一个字典转换为字符串
    print(str_dic, type(str_dic))
    #结果>>> {"name": "xiaobai", "age": 20, "sex": "nan", "2": 4} <class 'str'>
    
    # 注意:json转换完的字符串类型的字典中的字符串是有""表示; 如果数字为key那么dump之后会强行转换为字符串数据类型
    
    
    # 反序列化
    dic2 = json.loads(str_dic)  # 将一个字符串格式的字典转换成一个字典
    print(dic2, type(dic2))
    #结果>>> {'name': 'xiaobai', 'age': 20, 'sex': 'nan', '2': 4} <class 'dict'>
    
    # 注意:要用json的loads功能处理的字符串类型的字典中的字符串必须有""表示
    
    
    # json是否支持元组,对元组做value的字典会把元组强制转换为列表
    dic = {'a': (1, 2, 3)}
    str_dic = json.dumps(dic)
    print(str_dic)  # {"a": [1, 2, 3]}
    new_dic = json.loads(str_dic)
    print(new_dic)  # {'a': [1, 2, 3]}
    
    # json是否支持元组做key?  不支持,会报错
    # dic = {(1, 2, 3): "a"}
    # str_dic = json.dumps(dic)   # TypeError: keys must be a string
    
    # 处理嵌套的数据类型
    list_dic = [1, ['a', 'b', 'c'], 2, {'k1': 'k2', 'k3': 'k4'}]
    str_dic = json.dumps(list_dic)
    print(str_dic, type(str_dic))
    #结果:[1, ["a", "b", "c"], 2, {"k1": "k2", "k3": "k4"}] <class 'str'>
    new_dic = json.loads(str_dic)
    print(new_dic, type(new_dic))
    #结果:[1, ['a', 'b', 'c'], 2, {'k1': 'k2', 'k3': 'k4'}] <class 'list'>

    如果想把数据类型直接序列化到一个文件中,那么就要使用到dump和load方法

    # dump 与 load
    # 系列化进文件
    import json
    
    dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4}
    with open('dump_json', 'w') as f:
        json.dump(dic, f)   # dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
    
    with open('dump_json') as f:
        ret = json.load(f)   # load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
        print(type(ret), ret)
        # <class 'dict'> {'name': 'xiaobai', 'age': 20, 'sex': 'nan', '2': 4}
    
    # 能不能dump多个数据进入文件, dump可以多个数据进去,但是load不出来了,会报错
    dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4}
    dic2 = {'k1': 'v1', 'k2': 'v2'}
    with open('dump_json', 'w') as f:
        json.dump(dic, f)
        json.dump(dic2, f)
    
    # with open('dump_json') as f:
    #     json.load(f)    # json.decoder.JSONDecodeError
    
    
    # 如果非要使用json dump多个数据到文件里面,那么就要用到dumps
    dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4}
    dic2 = {'k1': 'v1', 'k2': 'v2'}
    with open('dump_json', 'w') as f:
        str_dic1 = json.dumps(dic)
        str_dic2 = json.dumps(dic2)
        f.write(str_dic1 + '
    ')
        f.write(str_dic2 + '
    ')
    
    with open('dump_json') as f:
        for line in f:
            ret = json.loads(line)
            print(ret)

    写入中文乱码,需要使用ensure_ascii关键字参数

    # 中文格式的
    import json
    dic = {'中国':'北京', '美国':'华盛顿'}
    new_dic = json.dumps(dic)
    print(new_dic)  # {"u4e2du56fd": "u5317u4eac", "u7f8eu56fd": "u534eu76dbu987f"}
    new2_dic = json.dumps(dic, ensure_ascii=False)
    print(new2_dic) # {"中国": "北京", "美国": "华盛顿"}
    
    with open('dump_json', 'w') as f:
        json.dump(dic, f)   # 写入文件的内容:{"u4e2du56fd": "u5317u4eac", "u7f8eu56fd": "u534eu76dbu987f"}
    
    with open('dump_json', 'w', encoding='utf-8') as f:
        json.dump(dic, f, ensure_ascii=False)   # 写入文件的内容:{"中国": "北京", "美国": "华盛顿"}

    json格式化输出

    import json
    data = {'username':['李华','二愣子'],'sex':'male','age':16}
    json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
    print(json_dic2)
    # 结果:
    '''
    {
      "age":16,
      "sex":"male",
      "username":[
        "小明",
        "小李"
      ]
    }
    '''

    pickle

    由于json格式对python数据类型的支持不是那么完美,如果只是在python程序之间交互,使用pickle模块的支持性会更好。但是不足之处就是,pickle只是适用于python语言。

    pickle的优点:

      (1)pickle支持python中的几乎所有数据类型

      (2)pickle会把数据类型序列化为bytes类型

    pickle的缺点:

      (1)pickle只适用于python

    pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load  (不仅可以序列化字典,列表...可以把python中任意的数据类型序列化)

    import pickle
    # dumps 与 loads
    dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4}
    b_dic = pickle.dumps(dic)
    print(type(b_dic))  # <class 'bytes'>
    d_dic = pickle.loads(b_dic)
    print(type(d_dic))  #<class 'dict'>
    
    # dump 与 load
    dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4}
    with open('pickle_dump', 'wb') as f:
        pickle.dump(dic, f)
    
    with open('pickle_dump', 'rb') as f:
        ret = pickle.load(f)
        print(ret)  # {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4}
    
    # 可以发现pickle和json用法其实是完全一样,只是dump写和读的时候注意,因为pickle转换为bytes类型,所以写读时候都要以wb 和rb的形式

    hashlib模块

    算法介绍:

      Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

      什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。

      摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

      摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

    hashlib介绍:

      hashlib模块是一个内部有摘要算法的模块,而且内部可以给我们提供不止一种摘要算法。能够把 一个 字符串 数据类型的变量,转换成一个 定长的 密文的 字符串,字符串里的每一个字符都是一个十六进制数字

    为什么需要hashlib?

      对于同一个字符串,用相同的算法,相同的手段去进行摘要,获得的值总是相同的

    hashlib模块的使用:

      hashlib模块提供了多种摘要算法:

        md5算法: 定长32位  16进制(应用最广发的摘要算法)

        sha系统算法: 定长40位

        sha算法要比MD5算法更加复杂,且sha N数值越大,算法越复杂,耗时越久,结果越长,但也更安全

    import hashlib
    # hashlib模块md5摘要算法的使用
    name = "xiaobai"
    password = 'xiaobai123'
    
    # 1.首先,需要先实例化一个md5的对象,一个对象只加密一个数据
    md5_obj = hashlib.md5()
    # 2. update()方法,把需要进行md5的对象放入
    md5_obj.update(password.encode('utf-8'))
    # 3. 通过hexdigest(),得到摘要算法之后的密文
    md5_password = md5_obj.hexdigest()
    print(md5_password)     # 21b3a6792936ba9c2ecbcbe0da8ba961
    print(len(md5_password))    # md5算法,定长32位
    
    # hashlib模块sha摘要算法的使用
    
    # 操作和md5如出一辙,先创建对象,通过update加密,再通过hexdigest取值
    name = "xiaobai"
    password = 'xiaobai123'
    sha_obj = hashlib.sha1()
    sha_obj.update(password.encode('utf-8'))
    sha_password = sha_obj.hexdigest()
    print(sha_password)     # 6e96c5250d4d9c3b1ea9b5815d41aa0343a3c691
    print(len(sha_password))    # sha1算法,定长40位

    hashlib的应用

    用户登录的验证

    name   | password
    -------+--------
    xiaobai|xiaobai123

    有一个用户小白,密码为xiaobai123,如果密码就这样明文存储,如果数据库被黑,那么密码就毫无保留的暴露给了黑客。所以这时候就需要用到摘要,在数据库中,存储密码的摘要信息,每次登陆的时候,再做摘要信息的对比

    name   | password
    -------+--------
    xiaobai|21b3a6792936ba9c2ecbcbe0da8ba961

    所以每次登陆的时候,便需要进行密码信息的摘要对比

    import hashlib      # 密码加密
    
    def get_md5_pwd(s):
        md5_obj = hashlib.md5()
        md5_obj.update(s.encode('utf-8'))
        ret = md5_obj.hexdigest()
        return ret
    
    
    username = input("username>>>: ").strip()
    password = input("password>>>: ").strip()
    
    with open('userinfo', encoding='utf-8') as f:
        for line in f:
            user, pwd = line.strip().split('|')
            if username == user and get_md5_pwd(password) == pwd:
                print("登录成功")
                break
        else:
            print("登录失败")

     通过摘要算法的手段,虽然密码是用密文的形式存储了,但是在现在的攻击手段中,有一种叫做"撞库"的手段,就是通过一个存储着大量密码与md5后的摘要对应的关系,再一一进行匹配,如果摘要信息一致,便能够反推出密码,因为同一种算法的同一个字符串,结果总是不变的。那么,有什么方法能够防止撞库?那就通过加盐值得手段(1.固定盐值  2.更好的方法:动态加盐)

      何为盐值(salt),其实就是给原数据+一段指定的字符串,这样得到的MD5值就会发生变化。只要颜值不被黑客知道,那么就很难反向推出原数据。

    # 加盐的md5算法,采用固定盐值(盐值:static)
    username = "xiaobai"
    password = "xiaobai123"
    
    md5_obj = hashlib.md5()
    md5_obj.update('static'.encode('utf-8'))    # 加油
    md5_obj.update(password.encode('utf-8'))
    ret = md5_obj.hexdigest()
    print(ret)
    
    # 动态加盐,通过把用户的唯一标识作为盐值,例如每个用户的用户名都是唯一
    username = "xiaobai"
    password = "xiaobai123"
    
    md5_obj = hashlib.md5()
    md5_obj.update(username.encode('utf-8'))    # 动态加盐
    md5_obj.update(password.encode('utf-8'))
    ret = md5_obj.hexdigest()
    print(ret)

    文件一致性的校验

    给一个文件中的所有内容进行摘要算法,得到一个md5结果。此时,我们可以体验到md5摘要算法的神奇的地方,对于同一个字符串,不管把他拆开多少段,最终得到的md5值都是一样。

    # 同一个字符串,不管拆开多少段,最终的md5都是一样的。
    s = 'hello world'
    md5_obj = hashlib.md5()
    md5_obj.update(s.encode('utf-8'))
    ret = md5_obj.hexdigest()
    print(ret)  # 5eb63bbbe01eeed093cb22bb8f5acdc3
    
    md5_obj = hashlib.md5()
    md5_obj.update('hello '.encode('utf-8'))
    md5_obj.update('world'.encode('utf-8'))
    ret = md5_obj.hexdigest()
    print(ret)  # 5eb63bbbe01eeed093cb22bb8f5acdc3

    所以对文件进行一致性校验

    def get_file_md5(file_path):
        file_md5_obj = hashlib.md5()
        with open(file_path, encoding='utf-8') as f:
            for line in f:
                file_md5_obj.update(line.encode('utf-8'))
        ret = file_md5_obj.hexdigest()
        return ret

    总结:两个文件MD5对比

    # 文件校验, 两个文件对比
    import os, sys, hashlib
    
    
    def get_file_md5(file_path):
        file_md5_obj = hashlib.md5()
        with open(file_path, encoding='utf-8') as f:
            for line in f:
                file_md5_obj.update(line.encode('utf-8'))
        ret = file_md5_obj.hexdigest()
        return ret
    
    
    def file_Contrast(file_one_path, file_tow_path):
        file_one_md5 = get_file_md5(file_one_path)
        file_tow_md5 = get_file_md5(file_tow_path)
        if file_one_md5 == file_tow_md5:
            print("%s 与 %s 一致" % (file_one_path, file_tow_path))
        else:
            print("两个文件不一致")
    
    if __name__ == '__main__':
        if os.name == 'posix':
            if len(sys.argv) < 3:
                print("33[1;36;40mUSAGE: python %s  <file1>  <file2>33[0m" % sys.argv[0])
                exit(-1)
            file_one_path = sys.argv[1]
            file_tow_path = sys.argv[2]
            if os.path.exists(file_one_path) and os.path.exists(file_tow_path):
                file_Contrast(file_one_path, file_tow_path)
            else:
                print("请输入正确的路径")
        elif os.name == 'nt':
            file_one_path = input('输入需要对比的第一个文件路径>>>: ')
            file_tow_path = input('输入需要对比的第一个文件路径>>>: ')
            if os.path.exists(file_one_path) and os.path.exists(file_tow_path):
                file_Contrast(file_one_path, file_tow_path)
            else:
                print("请输入正确的路径")

    对视频文件进行一致性校验

    一般是视频格式的文件/网络传输的文件,都是二进制的bytes类型。此时没有行的概念,该怎么做?此时,可以设置一个buffer,每次都读取相同长度的buffer.

    #设置一个buffer,每次都通过f.read(buffer)读取定长的数据。如果电脑配置比较高,调整相应的buffer即可
    import os, sys, hashlib
    def get_vedio_md5(file_path, buffer=1024):
        file_size = os.path.getsize(file_path)
        md5_obj = hashlib.md5()
        with open(file_path, 'rb') as f:
            while file_size:
                content = f.read(buffer)
                md5_obj.update(content)
                file_size -= len(content)
        ret = md5_obj.hexdigest()
        return ret

    logging模块

    logging模块是用来操作日志的。

    logging模块分为两种配置方式:(1)函数式简单配置。(2)logger对象配置

    函数式简单配置

    import logging   # 日志模块
    logging.debug(
    'debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical')

    默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。此时如果想改变显示,就需要在logging.basicConfig()中,把level级别调低

    import logging
    logging.basicConfig(level=logging.DEBUG)     # 日志级别调到debug
    logging.debug('debug message')
    logging.info('info message')
    logging.warning('warning message')
    logging.error('error message')
    logging.critical('critical')

    此时,如果不想日志默认输出到标准输出,想重定向输出到指定的日志文件中,那么也可以通过修改logging.basicconfig()中filename属性,指定重定向的文件。

    import logging
    # 通过修改logging.basicConfig(filename=‘file_path’),进行输出重定向 logging.basicConfig(level=logging.DEBUG, filename='loging.log') logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical')

    basicConfig常用的参数及输出格式

    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用户输出的消息
    
    
    
    # 一般常用的配置
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                        datefmt='%a, %d %b %Y %H:%M:%S',
                        filename='loging.log',
                        filemode='w')

    总结:

      通过logging模块的简单配置项可以完成对日志的基本操作,但是依然有几点痛点:

      (1)basicConifg不支持输出中文

      (2)日志的输出只能够文件/屏幕二选一,不能做到同时。

    logger 对象配置

    既然logging模块自带的basicConfig配置不能够解决中文问题,那么只能通过自己创建对象的方式来更加灵活的操作,解决固有的痛点:1.不能支持中文  2.同时向文件和屏幕输出内容

      事前应该有几个准备事项:

      (1)先实例化一个日志对象

      (2)创建一个控制文件输出的文件操作符

      (3)创建一个控制屏幕输出的屏幕操作符

      (4)指定日志输出的格式(可以指定多个,文件输出和屏幕输出格式可以不同)

      (5)文件操作符绑定一个日志格式

      (6)屏幕操作符绑定一个日志格式

      (7)日志对象绑定文件操作符以及屏幕操作符

    import logging
    
    # (1) 创建一个log对象
    logger = logging.getLogger()
    # (2) 创建一个控制文件输出的文件操作符,encoding='utf-8‘’,解决中文问题
    file_handler = logging.FileHandler('test.log', encoding='utf-8')
    # (3) 创建一个控制屏幕输出的屏幕操作符
    screen_handler = logging.StreamHandler()
    # (4) 设置日志输出的格式
    log_fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    # (5) 给文件操作符绑定一个日志格式
    file_handler.setFormatter(log_fmt)
    # (6) 给屏幕操作符绑定一个日志格式
    screen_handler.setFormatter(log_fmt)
    # (7) 日志对象绑定文件操作符和屏幕操作符
    logger.addHandler(file_handler)
    logger.addHandler(screen_handler)
    # (8) 设置日志输出的级别
    logger.setLevel(logging.DEBUG)
    # (9) 打印日志
    logger.debug('这是debug的消息')
    logger.info('这是info的消息')
    logger.warning('这是warning的消息')
    logger.error('这是error的消息')

    实际应用

    import logging
    
    # (1) 创建一个log对象
    logger = logging.getLogger()
    # (2) 创建一个控制文件输出的文件操作符,encoding='utf-8‘’,解决中文问题
    file_handler = logging.FileHandler('test.log', encoding='utf-8')
    # (3) 创建一个控制屏幕输出的屏幕操作符
    screen_handler = logging.StreamHandler()
    # (4) 设置日志输出的格式
    log_fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    # (5) 给文件操作符绑定一个日志格式
    file_handler.setFormatter(log_fmt)
    # (6) 给屏幕操作符绑定一个日志格式
    screen_handler.setFormatter(log_fmt)
    # (7) 日志对象绑定文件操作符和屏幕操作符
    logger.addHandler(file_handler)
    logger.addHandler(screen_handler)
    # (8) 设置日志输出的级别
    logger.setLevel(logging.DEBUG)
    # (9) 打印日志
    
    
    def login():
        username = input("username>>>: ")
        password = input("password>>>: ")
        if username == "xiaobai" and password == "xiaobai123":
            logger.info("%s 登录成功 " % username)
            print("登录成功")
        else:
            logger.error("%s 登录失败,密码:%s " % (username, password))
            print("登录失败")
    login()

    不同级别的logging 日志信息写入到不同文件

    # -*- coding: utf-8 -*-
    import os
    import time
    import logging
    import inspect
    from logging.handlers import RotatingFileHandler
    
    
    dir = os.path.dirname(__file__)
    dir_time = time.strftime('%Y-%m-%d', time.localtime())
    
    handlers = {logging.NOTSET: os.path.join(dir, 'notset_%s.log'%dir_time),
    
                logging.DEBUG: os.path.join(dir, 'debug_%s.log'%dir_time),
    
                logging.INFO: os.path.join(dir, 'info_%s.log'%dir_time),
    
                logging.WARNING: os.path.join(dir, 'warning_%s.log'%dir_time),
    
                logging.ERROR: os.path.join(dir, 'error_%s.log'%dir_time),
    
                logging.CRITICAL: os.path.join(dir, 'critical_%s.log'%dir_time),
                }
    
    
    def createHandlers():
        logLevels = handlers.keys()
    
        for level in logLevels:
            path = os.path.abspath(handlers[level])
            handlers[level] = RotatingFileHandler(path, maxBytes=10000, backupCount=2, encoding='utf-8')
    
    # 加载模块时创建全局变量
    
    createHandlers()
    
    
    class TNLog(object):
    
        def printfNow(self):
            return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    
        def __init__(self, level=logging.NOTSET):
            self.__loggers = {}
    
            logLevels = handlers.keys()
    
            for level in logLevels:
                logger = logging.getLogger(str(level))
    
                # 如果不指定level,获得的handler似乎是同一个handler?
    
                logger.addHandler(handlers[level])
    
                logger.setLevel(level)
    
                self.__loggers.update({level: logger})
    
        def getLogMessage(self, level, message):
            frame, filename, lineNo, functionName, code, unknowField = inspect.stack()[2]
    
            '''日志格式:[时间] [类型] [记录代码] 信息'''
    
            return "[%s] [%s] [%s - %s - %s] %s" % (self.printfNow(), level, filename, lineNo, functionName, message)
    
        def info(self, message):
            message = self.getLogMessage("info", message)
    
            self.__loggers[logging.INFO].info(message)
    
        def error(self, message):
            message = self.getLogMessage("error", message)
    
            self.__loggers[logging.ERROR].error(message)
    
        def warning(self, message):
            message = self.getLogMessage("warning", message)
    
            self.__loggers[logging.WARNING].warning(message)
    
        def debug(self, message):
            message = self.getLogMessage("debug", message)
    
            self.__loggers[logging.DEBUG].debug(message)
    
        def critical(self, message):
            message = self.getLogMessage("critical", message)
    
            self.__loggers[logging.CRITICAL].critical(message)
    
    
    if __name__ == "__main__":
        logger = TNLog()
    
        logger.debug("debug")
        logger.info("info")
        logger.warning("warning")
        logger.error("error")
        logger.critical("critical")

    参考:https://www.cnblogs.com/yanjieli/p/10179626.html

  • 相关阅读:
    python 数据类型 转换
    python 爬虫简单优化
    三种方式简单爬取图片
    爬虫简单入门:第一个简单爬虫
    python 数据结构 容器(字典,列表,元组,集合)
    蓝桥杯带分数(难)
    有几个水洼(DFS)
    蓝桥杯横向打印二叉树(不会,好难啊)
    输入不确定数量的数字
    闰年
  • 原文地址:https://www.cnblogs.com/Tang-Yuan/p/14325440.html
Copyright © 2020-2023  润新知