• 常用模块(二)


    一 序列化

    1.什么是序列化与反序列化

    序列化指的是把内存的数据类型转换成一个特定的格式的内容

    内存中的数据类型-------》序列化--------》特定的格式(jso格式或者 pickle格式)

    特定的格式(json 格式或者 pickle 格式)--------》反序列化--------》内存中的数据类型

    2、为何要用序列化

    序列化得到结果===》特定的格式的内容有两种用途

    ​ 1、可用于存储==》用于存档

    ​ 2、传输给其他平台使用==》跨平台数据交互

    强调:

    ​ 针对用途 1 的特定格式:

    ​ 可以是一种专用的格式==》pickle 只有 python 可以识别

    ​ 针对用途 2 的特定格式:

    ​ 应该是一种通用能够被所有语言识别的格式===》json

    3、如何序列化与反序列化

    二 json

    通过 json 序列化得到的都是 json 格式的字符串类型

    1 序列化

    序列化: python数据类型 ---》 json序列化 ---》字符串 ---》 json文件中

    import json
    json.dumps(True)
    print(res,type(res)) #true, <class 'str'>
    
    json.dumps([1, 'aaa', True, False])
    print(res, type(res)) #[1, "aaa", true, false] <class 'str'>
    

    文件操作:

    import json
    json.dumps:res= json.dumps('aaa'), f = open() --> f.write(res)
    with open('a.txt', 'wt', encoding='utf-8')as f:
    		json.dump('haha', f) #内部实现 f.write()
        
    # 序列化的结果写入文件的复杂方法
    json_res=json.dumps([1,'aaa',True, False])
    # print(json_res,type(json_res)) # "[1, "aaa", true, false]"
    with open('test.json',mode='wt',encoding='utf-8') as f:
        f.write(json_res)
    
    # 将序列化的结果写入文件的简单方法
    with open('test.json',mode='wt',encoding='utf-8') as f:
        json.dump([1,'aaa',True,False],f)
    

    2 反序列化

    反序列化: json文件中 --》字符串 ---》 json反序列化 ---》 python或其他语言数据类型

    import json
    
    json.loads:f = open(), str = f.read(), json.loads(str)
    

    案例二:

    import json
    
    #序列化
    with open(r'a.json', 'wt', encoding='utf-8')as f:
        json.dump('haha', f)
        
    #反序列化
    with open(r'a.json', 'rt', encoding='utf-8')as f:
        res = json.load(f)
        print(res,type(res)) #haha <class 'str'>
    

    文件操作

    # 从文件读取json格式的字符串进行反序列化操作的复杂方法
    with open('test.json',mode='rt',encoding='utf-8') as f:
        json_res=f.read()
        l=json.loads(json_res)
        print(l,type(l))
    
    # 从文件读取json格式的字符串进行反序列化操作的简单方法
    with open('test.json',mode='rt',encoding='utf-8') as f:
        l=json.load(f)
        print(l,type(l))
    

    json格式兼容的是所有语言通用的数据类型,但不能序列化 python 的集合数据类型

    json.dumps({1,2,3,4,5}) #报错
    

    json 强调:json 数据内部的字符串是双引号,没有单引号。

    ​ 一定要搞清楚 json 格式,不要与 python 混淆

    l=json.loads('[1, "aaa", true, false]')
    print(l) #[1, 'aaa', True, False]
    
    l=json.loads("[1,1.3,true,'aaa', true, false]")
    print(l) #报错, json格式数据内部的字符串是双引号
    

    python中元组,若将其转换成 json 数据,内部会将元组--->列表

    tuple1 = ('1',2,3, 'aa')
    res =json.dumps(tuple1)
    print(res,type(res)) #["1", 2, 3, "aa"] <class 'str'>
    

    了解:

    在 python3.5 之上的解释器 bytes 类型可以反序列化成 python格式数据,但是 python3.5 及之前不支持

    l = json.loads(b'[1, "aaa", true, false]') #b'json格式数据'
    print(l, type(l)) #[1, 'aaa', True, False] <class 'list'>
    
    with open('test.json',mode='rb') as f:
        l=json.load(f)
        
    #注:在 python3.5 之上的解释器 bytes 类型可以反序列化成 python格式数据,但是 python3.5 之前不支持
    
    res=json.dumps({'name':'哈哈哈'}, ensure_ascii=False) #ensure_ascii 默认为 True,此时中文字符序列化的得到 "u54c8u54c8u54c8"
    print(res,type(res)) #{"name": "哈哈哈"} <class 'str'>
    
    res=json.loads('{"name": "u54c8u54c8u54c8"}')
    print(res,type(res)) #{'name': '哈哈哈'} <class 'dict'>
    

    猴子补丁:一种程序打补丁的思想

    在入口处打猴子补丁

    import json
    import ujson
    
    def monkey_patch_json():
      	json.__name__ = 'ujson'
        json.dumps = ujson.dumps
        json.loads = ujson.loads
        
    monekey_patch_json() #在入口文件处运行
    
    
    
    # 后续代码中的应用
    # json.dumps()
    # json.dumps()
    # json.dumps()
    

    三 pickle

    pickle 模块是 python 提供的一种序列化模块,序列化的结果是 bytes 类型数据

    优点:可以支持 python 中所有的数据类型

    ​ 可以直接存'bytes'类型数据,pickle 存取速度更快

    缺点:

    ​ 只能支持 python 使用,不能跨平台

    序列化与 反序列化

    Pickle 会将python所有数据序列化成 bytes 类型

    import pickle
    res=pickle.dumps({1,2,3,4,5})
    print(res, type(res)) #b'x80x03cbuiltins
    set
    qx00]qx01(Kx01Kx02Kx03Kx04Kx05ex85qx02Rqx03.' <class 'bytes'>
    
    s=pickle.loads(res)# 后续代码中的应用
    print(s,type(s)) #{1, 2, 3, 4, 5} <class 'set'>
    

    python2与python3的pickle兼容性问题

    # coding:utf-8
    import pickle
    
    with open('a.pkl',mode='wb') as f:
        # 一:在python3中执行的序列化操作如何兼容python2
        # python2不支持protocol>2,默认python3中protocol=4
        # 所以在python3中dump操作应该指定protocol=2
        pickle.dump('你好啊',f,protocol=2)
    
    with open('a.pkl', mode='rb') as f:
        # 二:python2中反序列化才能正常使用
        res=pickle.load(f)
        print(res)
    
    

    pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据。

    四 configparser

    configparser:配置模块

    '''aa.ini'''
    
    # 注释1
    ; 注释2
    
    [section1]
    k1 = v1
    k2:v2
    user=egon
    age=18
    is_admin=true
    salary=31
    
    [section2]
    k1 = v1
    
    # coding:utf-8
    '''配置文件'''
    import configparser
    
    #初始化,并读取配置文件
    config=configparser.ConfigParser()
    res = config.read('aa.ini') #  'aa.ini':文件路径
    print(res,type(res)) #['aa.ini'] <class 'list'>
    
    #查看所有的标题 sections
    res=config.sections()
    print(res) #['section1', 'section2']
    
    #查看标题section1下所有key=value的key
    options=config.options('section1')
    print(options) #['k1', 'k2', 'user', 'age', 'is_admin', 'salary']
    
    #查看标题section1下所有key=value的(key,value)格式
    item_list=config.items('section1') #返回值是列表套元组形式
    print(item_list) #[('k1', 'v1'), ('k2', 'v2'), ('user', 'egon'), ('age', '18'), ('is_admin', 'true'), ('salary', '31')]
    
    # #查看标题section1下user的值=>字符串格式
    val=config.get('section1', 'user')
    print(val) #egon
    
    #查看标题section1下age的值=>整数格式
    val1 = config.getint('section1','age')
    print(val1, type(val1)) #18 <class 'int'>
    
    #查看标题section1下is_admin的值=>布尔值格式
    val2=config.getboolean('section1','is_admin')
    print(val2) #True
    
    #查看标题section1下salary的值=>浮点型格式
    val3=config.getfloat('section1','salary')
    print(val3) #31.0
    

    改写

    import configparser
    
    config=configparser.ConfigParser()
    config.read('a.cfg',encoding='utf-8')
    
    
    #删除整个标题section2
    config.remove_section('section2')
    
    #删除标题section1下的某个k1和k2
    config.remove_option('section1','k1')
    config.remove_option('section1','k2')
    
    #判断是否存在某个标题
    print(config.has_section('section1'))
    
    #判断标题section1下是否有user
    print(config.has_option('section1',''))
    
    
    #添加一个标题
    config.add_section('egon')
    
    #在标题egon下添加name=egon,age=18的配置
    config.set('egon','name','egon')
    config.set('egon','age',18) #报错,必须是字符串
    
    
    #最后将修改的内容写入文件,完成最终的修改
    config.write(open('a.cfg','w'))
    

    具体案例:

    项目的配置文件采用configparser进行解析

    # 生成mysql配置信息
    '''
    conf_obj = configparser.ConfigParser()
    
    # conf_obj['配置标题'] = {配置字典}
    conf_obj['MYSQL'] = {'HOST': '127.0.0.1',
                         'PORT': '3306',
                         'USER': 'tank',
                         'PASSWORD': '123456',
                         }
    
    with open('mysql.ini', 'w') as f:
        conf_obj.write(f)
    '''
    
    #结果展示:
    '''
    [MYSQL]
    host = 127.0.0.1
    port = 3306
    user = tank
    password = 123456
    '''
    
    
    # 校验mysql配置信息
    conf_obj = configparser.ConfigParser()
    # 读取mysql.ini配置文件
    conf_obj.read('mysql.ini')
    
    title = conf_obj.sections()
    
    if 'MYSQL' in title:
    
        ini_user = conf_obj['MYSQL']['USER']
        ini_pwd = conf_obj['MYSQL']['PASSWORD']
    
        if ini_user == 'tank' and ini_pwd == '123456':
            print('mysql连接成功!')
    

    五 hashlib

    1、什么是哈希 hash

    hash 一类算法,该算法接收传入的内容,经过运算得到一串 hash 值

    hash 值的特点:

    1、只要传入值一样,得到的hash 值必然一样

    2、不能由 hash 值返解成内容

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

    2、用途

    用途 1:特点 1:用于密码密文传输与校验

    用途2:特点1和特点 3:用于文件完整性校验

    3、如何用

    hashlib 是一个加密模块,内置了很多算法

    1 hashlib使用

    #传入的值必须为 bytes类型
    
    import hashlib
    m = hashlib.md5()
    m.update('hello'.encode('utf-8')) #传入数据必须为 bytes 类型,所以需要将字符串 encode 成 bytes 类型
    res = m.hexdigest()
    print(res) #5d41402abc4b2a76b9719d911017c592
    

    待加密的数据分开多次传和完整一次性传入给加密对象,最后加密的结果都是一样的。验证了只要hash 的特点 1:只要传入值一样,得到的 hash 值必然一样

    import hashlib
    m = hashlib.md5('hello'.encode('utf-8'))
    m.update('world'.encode('utf-8'))
    res =m.hexdigest() #'helloworld'
    print(res)
    
    import hashlib
    m = hashlib.md5()
    m.update('hello'.encode('utf-8'))
    m.update('world'.encode('utf-8'))
    res =m.hexdigest()#'helloworld'
    print(res)
    
    #结果展示
    '''
    fc5e038d38a57032085441e7fe7010b0
    fc5e038d38a57032085441e7fe7010b0
    '''
    

    2 模拟撞库

    import hashlib
    user_pwd = 'beffc46e29d93a4b0ad3765c242d6fc8'
    # 模拟撞库
    # 制作密码字典
    password = [
        'h123an',
        '123han',
        'ha123n',
        '12han3',
        'han123',
        'ha12n3',
    ]
    
    dic = {}
    for str_num in password:
        m = hashlib.md5(str_num.encode('utf-8'))
        dic[str_num] = m.hexdigest() #n拿到密码字典
    
    #模拟撞库得到密码
    for k, v in dic.items():
        if v == user_pwd:
            print('撞库成功, 明文密码为%s'%k)#撞库成功, 明文密码为han123
            break
    
    

    3 加盐

    增加撞库的成本,可以提高加密数据的安全性==》加盐

    #提升撞库成本====》加盐
    import hashlib
    
    m = hashlib.md5()
    m.update('han123'.encode('utf-8'))
    #加盐
    str1 = '天王盖地虎'
    m.update(str1.encode('utf-8'))
    res = m.hexdigest() #'天王盖地虎han123'
    print(res) #5c62dc89fd9440ab7a2b3ecee7e904ad
    

    具体案例:

    def pwd_md5(pwd):
        global res
        md5_obj = hashlib.md5()
        str1 = pwd
        md5_obj.update(str1.encode('utf-8'))  # update 中一定要传入bytes类型数据
        # 创造盐
        sa1 = '哈哈'
        # 加盐
        md5_obj.update(sa1.encode('utf-8'))  # update 中一定要传入bytes类型数据
        # 得到一个加密后的字符串
        res1 = md5_obj.hexdigest()
    
        print(res1)
        return res1
    
    pwd = input('请输入用户密码').strip()
    user_str2 = f'tank:{pwd_md5(pwd)}'
    # def register(*regs, **kwregs):
    #     # # user_str1 = f'tank:1234'
    with open('user.txt', 'w', encoding='utf-8')as f:
            f.write(user_str2)
    

    提高加密程度操作

    可以截取数据中某些位置进行拼接加密(如:1/4 , 2/4, 3/4, 4/ 4等数据大小位置),用来提高校验数据完整性是否一致,一般可用于视频、文本及应用软件等是否被完整未被篡改。

    具体案例

    文件完整性校验

    #具体步骤:
    '''
    1、先打开一个未修改'a.txt'文件,获取该文件的md5值,并保存
    2、再打开修改后的'a.txt'文件,获取该文件的md5值,与未修改前的md值值进行校验
    3、若校验成功,证明文件是完整的
    '''
    

    大文件完整性校验

    '''
    c.txt
    haha
    haha
    haha
    '''
    
    '''
    c1.txt
    haha
    haha
    haha
    '''
    
    '''执行文件'''
    import hashlib
    import os
    
    def get_file_md5(file_path):
        '''
        :param file_path: 文件路径
        :return:
        '''
        # 1.先通过os.path.getsize获取文件的大小(int类型)
        file_size = os.path.getsize(file_path)
        # 2)在文件的四个位置找点一个小点
        # 2.1) 获取文件开头位置
        offset1 = 0
        # 2.2) 获取文件3分之1位置
        offset2 = file_size // 3
        # 2.3) 获取文件3分之2位置
        offset3 = (file_size // 3) * 2
        # 2.4) 获取文件最后位置
        offset4 = file_size - 10
    
        # get_data_list: 里面存放文件中4个位置的值,每个位置获取10个值
        get_data_list = [offset1, offset2, offset3, offset4]
        #加密对象
        md5_obj = hashlib.md5()
        with open(file_path, 'rb') as f:
            # 循环4个位置
            for offset in get_data_list:
                # 光标移动到4个位置中
                f.seek(offset)
                # 读取10个bytes数据
                read_data = f.read(10)
                # 通过md5将4个位置截取的字符做一个MD5加密
                md5_obj.update(read_data)
    
        return md5_obj.hexdigest()
    
    #源文件的加密数据
    file1_md5 = get_file_md5("/Users/tophan/2020_python/day22/c.txt")
    #修改后文件的加密数据
    file2_md5 = get_file_md5("/Users/tophan/2020_python/day22/c1.txt")
    
    #拿到修改后文件的加密数据与未修改文件的加密数据进行对比
    print(file1_md5 == file2_md5) #True
    

    4 hmac模块(了解)

    python 提供的 hmac 模块,它内部对我们创建 key 和 内容 进行进一步的处理然后再加密:

    #要想保证hmac最终结果一致,必须保证:
    #1:hmac.new括号内指定的初始key一样
    #2:无论update多少次,校验的内容累加到一起是一样的内容
    
    # 操作一
    import hmac
    h1=hmac.new('hello'.encode('utf-8'),digestmod='md5')
    h1.update('world'.encode('utf-8'))
    
    print(h1.hexdigest()) # 0e2564b7e100f034341ea477c23f283b
    
    # 操作二
    import hmac
    h2=hmac.new('hello'.encode('utf-8'),digestmod='md5')
    h2.update('w'.encode('utf-8'))
    h2.update('orld'.encode('utf-8'))
    
    print(h1.hexdigest()) # 0e2564b7e100f034341ea477c23f283b
    

    六 subprocess

    subprocess模块:它是一个子进程模块,可以通过python代码给操作系统终端发送命令

    #执行系统命令
    '''
    Pooen(cmd命令, shell =True,stdout = subprocess.PIPE,strderr = subprocess.PIPE)
    调用Popen 就会将用户的终端命令发送给本地操作系统的终端
    得到一个对象,对象中包含着正确或错误的结果。
    '''
    
    obj = subprocess.Popen('echo 123 ; ls / ; ls /root', shell=True,
                           stdout=subprocess.PIPE, #正确的结果被放入该管道
                           stderr=subprocess.PIPE, #错误的结果被放入该管道
                           )
    
    # print(obj) #<subprocess.Popen object at 0x10f7fad68>
    
    #查看正确管道内的内容
    str_true = obj.stdout.read() #读出的是 bytes类型
    print(str_true.decode('utf-8'))
    # 结果展示
    '''
    123
    Applications
    Library
    Network
    System
    Users
    Volumes
    bin
    cores
    dev
    etc
    home
    installer.failurerequests
    net
    private
    sbin
    tmp
    usr
    var
    '''
    
    #查看错误管道内的内容
    str_error = obj.stderr.read()
    res = str_error.decode('utf-8')
    print(res) #ls: /root: No such file or directory
    

    终端输入命令显示:

    image-20200331123241131

    了解:

    # coding:utf-8
    import subprocess
    
    '''
    sh-3.2# ls /Users/egon/Desktop |grep txt$
    mysql.txt
    tt.txt
    事物.txt
    '''
    
    res1=subprocess.Popen('ls /Users/jieli/Desktop',shell=True,stdout=subprocess.PIPE)
    res=subprocess.Popen('grep txt$',shell=True,stdin=res1.stdout,
                     stdout=subprocess.PIPE)
    
    print(res.stdout.read().decode('utf-8'))
    
    
    #等同于上面,但是上面的优势在于,一个数据流可以和另外一个数据流交互,可以通过爬虫得到结果然后交给grep
    res1=subprocess.Popen('ls /Users/jieli/Desktop |grep txt$',shell=True,stdout=subprocess.PIPE)
    print(res1.stdout.read().decode('utf-8'))
    
    
    #windows下:
    # dir | findstr 'test*'
    # dir | findstr 'txt$'
    import subprocess
    res1=subprocess.Popen(r'dir C:UsersAdministratorPycharmProjects	est函数备课',shell=True,stdout=subprocess.PIPE)
    res=subprocess.Popen('findstr test*',shell=True,stdin=res1.stdout,
                     stdout=subprocess.PIPE)
    
    print(res.stdout.read().decode('gbk')) #subprocess使用当前系统默认编码,得到结果为bytes类型,在windows下需要用gbk解码
    

    七 xml 模块(了解)

    xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。

    xml的格式如下,就是通过<>节点来区别数据结构的:

    <?xml version="1.0"?>
    <data>
        <country name="Liechtenstein">
            <rank updated="yes">2</rank>
            <year>2008</year>
            <gdppc>141100</gdppc>
            <neighbor name="Austria" direction="E"/>
            <neighbor name="Switzerland" direction="W"/>
        </country>
        <country name="Singapore">
            <rank updated="yes">5</rank>
            <year>2011</year>
            <gdppc>59900</gdppc>
            <neighbor name="Malaysia" direction="N"/>
        </country>
        <country name="Panama">
            <rank updated="yes">69</rank>
            <year>2011</year>
            <gdppc>13600</gdppc>
            <neighbor name="Costa Rica" direction="W"/>
            <neighbor name="Colombia" direction="E"/>
        </country>
    </data>
    
    xml数据
    

    xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:

    # print(root.iter('year')) #全文搜索
    # print(root.find('country')) #在root的子节点找,只找一个
    # print(root.findall('country')) #在root的子节点找,找所有
    
    import xml.etree.ElementTree as ET
     
    tree = ET.parse("xmltest.xml")
    root = tree.getroot()
    print(root.tag)
     
    #遍历xml文档
    for child in root:
        print('========>',child.tag,child.attrib,child.attrib['name'])
        for i in child:
            print(i.tag,i.attrib,i.text)
     
    #只遍历year 节点
    for node in root.iter('year'):
        print(node.tag,node.text)
    #---------------------------------------
    
    import xml.etree.ElementTree as ET
     
    tree = ET.parse("xmltest.xml")
    root = tree.getroot()
     
    #修改
    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')
     
     
    #删除node
    for country in root.findall('country'):
       rank = int(country.find('rank').text)
       if rank > 50:
         root.remove(country)
     
    tree.write('output.xml')
    
    #在country内添加(append)节点year2
    import xml.etree.ElementTree as ET
    tree = ET.parse("a.xml")
    root=tree.getroot()
    for country in root.findall('country'):
        for year in country.findall('year'):
            if int(year.text) > 2000:
                year2=ET.Element('year2')
                year2.text='新年'
                year2.attrib={'update':'yes'}
                country.append(year2) #往country节点下添加子节点
    
    tree.write('a.xml.swap')
    

    自己创建 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) #打印生成的格式
    

    八 shelve 模块

    shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写;key必须为字符串,而值可以是python所支持的数据类型

    import shelve
    
    f=shelve.open(r'sheve.txt')
    # f['stu1_info']={'name':'egon','age':18,'hobby':['piao','smoking','drinking']}
    # f['stu2_info']={'name':'gangdan','age':53}
    # f['school_info']={'website':'http://www.pypy.org','city':'beijing'}
    
    print(f['stu1_info']['hobby'])
    f.close()
    
  • 相关阅读:
    BIP_开发案例08_BI Publisher图表示例 饼状图/直方图/折线图(案例)
    BIP_开发案例07_将原有Report Builer报表全部转为XML Publisher形式(案例)
    Form_Form Builder Export导出为Excel(案例)
    BIP_开发案例06_以RB.RDF为数据源BIP.RTF为模板的简单例子(案例)
    Form_Form Builder开发基于视图页面和自动代码生成包(案例)
    BIP_开发案例05_BI Pubisher标准做法以BIP.XML为数据源以BIP.RTF为模板的简单例子(案例)
    BIP_开发案例04_通过BI Publisher实现打印报表的二维码(案例)(待整理)
    BIP_开发案例03_将原有Report Builer报表全部转为XML Publisher形式(案例)
    BIP_开发案例02_BI Publisher中复杂案例实现代码(案例)
    BIP_开发案例01_BI Publisher报表手工提交和控制(案例)
  • 原文地址:https://www.cnblogs.com/xy-han/p/12608871.html
Copyright © 2020-2023  润新知