zoukankan      html  css  js  c++  java
  • python:序列与模块

    一,序列化模块

    什么叫序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化

    比如,我们在python代码中计算的一个数据需要给另外一段程序使用,那我们怎么给?
    现在我们能想到的方法就是存在文件里,然后另一个python程序再从文件里读出来。
    但是我们都知道,对于文件来说是没有字典这个概念的,所以我们只能将数据转换成字典放到文件中。
    你一定会问,将字典转换成一个字符串很简单,就是str(dic)就可以办到了,为什么我们还要学习序列化模块呢?
    没错序列化的过程就是从dic 变成str(dic)的过程。现在你可以通过str(dic)的方法,将一个名为dic的字典转换成一个字符串,
    但是你要怎么把一个字符串转换成字典呢?
    聪明的你肯定想到了eval(),如果我们将一个字符串类型的字典str_dic传给eval,就会得到一个返回的字典类型了。
    eval()函数十分强大,但是eval是做什么的?eval官方demo解释为:将字符串str当成有效的表达式来求值并返回计算结果。
    BUT!强大的函数有代价。安全性是其最大的缺点。
    想象一下,如果我们从文件中读出的不是一个数据结构,而是一句"删除文件"类似的破坏性语句,那么后果实在不堪设设想。
    而使用eval就要担这个风险。
    所以,我们并不推荐用eval方法来进行反序列化操作(将str转换成python中的数据结构)
    为什么要进行序列化

    序列化的目的

    1、以某种存储形式使自定义对象持久性
    2、将对象从一个地方传递到另一个地方。
    3、使程序更具维护性。

    1,json模块

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

    1)dumps和loads

    #json dumps序列化方法 loads反序列化方法
    dic = {1:"a",2:'b'}
    print(type(dic),dic)
    import json
    str_d = json.dumps(dic)   # 序列化
    print(type(str_d),str_d)
    # '{"kkk":"v"}'#注意:kkk用" "(双引号引起来了)
    dic_d = json.loads(str_d) # 反序列化
    print(type(dic_d),dic_d)
    '''
    <class 'dict'> {1: 'a', 2: 'b'}
    <class 'str'> {"1": "a", "2": "b"}
    <class 'dict'> {'1': 'a', '2': 'b'}
    '''
    dumps和loads

    dumps是序列化方法,loads反序列化方法

    2)dump与load

    import json
    # json dump load
    dic = {1:"a",2:'b'}
    f = open('fff','w',encoding='utf-8')
    json.dump(dic,f)
    f.close()
    f = open('fff')
    res = json.load(f)
    f.close()
    print(type(res),res)
    '''
    <class 'dict'> {'2': 'b', '1': 'a'}
    '''
    dump和load

    3)dumps、loads与dump、load的区别

    有s的直接在内存操作数据类型,没有s的方法是直接在文件里读写数据类型。 

    dump需要需要有文件句柄,load不能进行多次load。

    4)ensure_ascii关键字参数

    import json
    f = open('file','w')
    json.dump({'国籍':'中国'},f)
    ret = json.dumps({'国籍':'中国'})
    f.write(ret+'
    ')
    json.dump({'国籍':'美国'},f,ensure_ascii=False)
    ret = json.dumps({'国籍':'美国'},ensure_ascii=False)
    f.write(ret+'
    ')
    f.close()
    ensure_ascii关键字参数

    5)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)
    json格式化输出

    2,pickle

        #所有的python中的数据类型都可以转化成字符串形式
        #pickle序列化的内容只有python能理解
        #且部分反序列化依赖python代码
        #可以分步dump和分步load
     #序列化与反序列话需要相同的环境
    pickle模块提供了四个功能:dumps、dump、loads、load
    1)pickle 的dumps和loads
    import pickle
    dic = {'k1':'v1','k2':'v2','k3':'v3'}
    str_dic = pickle.dumps(dic)
    print(str_dic)  #一串二进制内容
    
    dic2 = pickle.loads(str_dic)
    print(dic2)    #字典

    2)分步dump与load

    dump和load必须用wb和rb打开

    import time
    struct_time1  = time.localtime(1000000000)
    struct_time2  = time.localtime(2000000000)
    f = open('pickle_file','wb')
    pickle.dump(struct_time1,f)
    pickle.dump(struct_time2,f)
    f.close()
    f = open('pickle_file','rb')
    struct_time1 = pickle.load(f)
    struct_time2 = pickle.load(f)
    print(struct_time1.tm_year)
    print(struct_time2.tm_year)
    f.close()
    分步dump和分步load

    3,shelve

        #序列化句柄
        #使用句柄直接操作,非常方便
    shelve也是python提供给我们的序列化工具,比pickle用起来更简单一些
    shelve只提供给我们一个open方法,open方法获取一个文件句柄,操作和字典类似。是用key来访问的,使用起来和字典类似。
    import shelve
    f = shelve.open('shelve_file')
    f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'}  #直接对文件句柄操作,就可以存入数据
    f.close()
    import shelve
    f1 = shelve.open('shelve_file')
    existing = f1['key']  #取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
    f1.close()
    print(existing)

    二、模块

    1,什么是模块?

    一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。

    2,import加载的模块分为四个通用类别:

      1 使用python编写的代码(.py文件)

      2 已被编译为共享库或DLL的C或C++扩展

      3 包好一组模块的包

      4 使用C编写并链接到python解释器的内置模块

    3,import

    1)示例文件:自定义模块my_module.py,文件名my_module.py,模块名my_module

    #my_module.py
    print('from the my_module.py')
    
    money=1000
    
    def read1():
        print('my_module->read1->money',money)
    
    def read2():
        print('my_module->read2 calling read1')
        read1()
    
    def change():
        global money
        money=0
    View Code

    模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载大内存中的模块对象增加了一次引用,不会重新执行模块内的语句)

    #demo.py
    import my_module #只在第一次导入时才执行my_module.py内代码,此处的显式效果是只打印一次'from the my_module.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
    import my_module
    import my_module
    import my_module
    
    '''
    执行结果:
    from the my_module.py
    '''
    #调用了多次但是只打印了一个结果

    我们可以从sys.modules中找到当前已经加载的模块,sys.modules是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入。

    2)每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突

    #测试一:money与my_module.money不冲突
    #demo.py
    #my_module中:from the my_module.py
    import my_module
    money=10
    print(my_module.money)
    
    '''
    执行结果:
    from the my_module.py
    '''

    3)测试二:read1与my_module.read1不冲突

    #测试二:read1与my_module.read1不冲突
    #demo.py
    #my_module:
        #print('from the my_module.py')
        #def read1():
            # print('money 1000')   
    import my_module
    def read1():
        print('========')
    my_module.read1()
    
    '''
    执行结果:
    from the my_module.py
    my_module->read1->money 1000
    '''                        

    4)测试三:执行my_module.change()操作的全局变量money仍然是my_module中的

    #测试三:执行my_module.change()操作的全局变量money仍然是my_module中的
    #demo.py
    import my_module
    money=1
    my_module.change()
    print(money)
    
    '''
    执行结果:
    from the my_module.py
    '''

    5)

    总结:首次导入模块my_module时会做三件事:

    1.为源文件(my_module模块)创建新的名称空间,在my_module中定义的函数和方法若是使用到了global时访问的就是这个名称空间。

    2.在新创建的命名空间中执行模块中包含的代码,见初始导入import my_module

    导入模块时到底执行了什么?
    
    事实上函数定义也是“被执行”的语句,模块级别函数定义的执行将函数名放入模块全局名称空间表,用globals()可以查看

    3.创建名字my_module来引用该命名空间

    1 这个名字和变量名没什么区别,都是‘第一类的’,且使用my_module.名字的方式可以访问my_module.py文件中定义的名字,my_module.名字与test.py中的名字来自两个完全不同的地方。

    6)为模块名起别名,相当于m1=1;m2=m1

    import my_module as sm
    print(sm.money)

    示范用法一:

    有两中sql模块mysql和oracle,根据用户的输入,选择不同的sql功能

    #mysql.py
    def sqlparse():
        print('from mysql sqlparse')
    #oracle.py
    def sqlparse():
        print('from oracle sqlparse')
    
    #test.py
    db_type=input('>>: ')
    if db_type == 'mysql':
        import mysql as db
    elif db_type == 'oracle':
        import oracle as db
    
    db.sqlparse() 
    复制代码

    4,from ... import...

    from my_module import read1,read2
    这样在当前位置直接使用read1和read2就好了,执行时,仍然以my_module.py文件全局名称空间
    from demo import money,read
    print(money)
    read()
    money = 200
    read()
    #测试一:导入的函数read1,执行时仍然回到my_module.py中寻找全局变量money
    #demo.py
    from my_module import read1
    money=1000
    read1()
    '''
    执行结果:
    from the my_module.py
    spam->read1->money 1000
    '''
    
    #测试二:导入的函数read2,执行时需要调用read1(),仍然回到my_module.py中找read1()
    #demo.py
    from my_module import read2
    def read1():
        print('==========')
    read2()
    
    '''
    执行结果:
    from the my_module.py
    my_module->read2 calling read1
    my_module->read1->money 1000
    '''
    这样在当前位置直接使用read1和read2就好了,执行时,仍然以my_module.py文件全局名称空间

    如果当前有重名read1或者read2,那么会有覆盖效果

    #测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
    #demo.py
    from my_module import read1
    def read1():
        print('==========')
    read1()
    '''
    执行结果:
    from the my_module.py
    ==========
    '''

    需要特别强调的一点是:python中的变量赋值不是一种存储操作,而只是一种绑定关系,如下:

    from my_module import money,read1
    money=100 #将当前位置的名字money绑定到了100
    print(money) #打印当前的名字
    read1() #读取my_module.py中的名字money,仍然为1000
    
    '''
    from the my_module.py
    my_module->read1->money 1000
    '''

    1)as

    也支持as

    from my_module import read1 as read

    2)也支持多行输入

    from my_module import (read1,
                      read2,
                     money)

    5,把模块当做脚本执行

    我们可以通过模块的全局变量__name__来查看模块名:
    当做脚本运行:
    __name__ 等于'__main__'

    当做模块导入:
    __name__= 模块名

    作用:用来控制.py文件在不同的应用场景下执行不同的逻辑
    if __name__ == '__main__':

    def fib(n):   
        a, b = 0, 1
        while b < n:
            print(b, end=' ')
            a, b = b, a+b
        print()
    
    if __name__ == "__main__":
        print(__name__)
        num = input('num :')
        fib(int(num))

    6模块搜索路径

    python解释器在启动时会自动加载一些模块,可以使用sys.modules查看

    在第一次导入某个模块时(比如my_module),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用

    如果没有,解释器则会查找同名的内建模块,如果还没有找到就从sys.path给出的目录列表中依次寻找my_module.py文件。

    所以总结模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块

    需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错。

    模块总结:

    1,import和from import都支持as重命名,都支持多名字的导入

    2,一个模块被import导入之后,并不会再次被导入。因为sys.mouldes记录了所有被导入的模块

    3,sys.path记录了导入模块的时候寻找的所有路径

     4,模块就是一个py文件

     5,__all__必须与*连用

  • 相关阅读:
    HGOI 20191029am 题解
    『ZJOI2019 D2T2』语言
    『NOIP 2019Day2 T3』 保卫王国(defense)
    『HGOI 20190917』Lefkaritika 题解 (DP)
    『HGOI 20190917』Cruise 题解 (计算几何+DP)
    HGOI 20190830 题解
    『Codeforces 1186E 』Vus the Cossack and a Field (性质+大力讨论)
    HGOI 20190828 题解
    BZOJ 1934 善意的投票
    BZOJ 2763 飞行路线 BFS分层
  • 原文地址:https://www.cnblogs.com/kakawith/p/8269249.html
Copyright © 2011-2022 走看看