• Python之常用模块(2)


    1、logging 日志模块

    默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET),默认的日志格式为日志级别:Logger名称:用户输出消息。

    import logging
    logging.debug('debug message')
    logging.info('info message')
    logging.warning('warning message')
    logging.error('error message')
    logging.critical('critical message')
    
    默认只打印:waring、error、critical
    WARNING:root:warning message1
    ERROR:root:error message1
    CRITICAL:root:critical message1

    在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有

    filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。

    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='test.log',
                        filemode='w')
    # level:更改日志的默认等级为DEBUG
    # format:定义日志的格式,可以自定义
        #   asctime 是一个变量,下面的 datefmt 决定变量的内容:Mon, 13 Nov 2017 10:36:49
        #   filename 运行程序的名字:logging 模块.py
        #   lineno 表示程序里面的第几行记录日志:[line:23]
        #   levelname 级别名字:warning、error、critical
        #   message 记录的内容:message1
    结果:
    #Mon, 13 Nov 2017 10:36:49 logging 模块.py[line:23] DEBUG debug message1
    
    # datefmt:日志里面的时间是什么格式:'%a, %d %b %Y %H:%M:%S'
    # filename:表示日志记录到哪个文件里面:'test.log'
    # filemode 文件模式:w、a
    # stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open('test.log','w')),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
    # 注意:如果不指定 filename 和 filemode 的话,会输出到屏幕上
    # 其优势就是简单易操作,但缺点很多,比如无法屏幕和文件一起输出等,所以才有了第二种方式:logge
    说明
    %(name)s Logger的名字
    %(levelno)s 数字形式的日志级别
    %(levelname)s 文本形式的日志级别
    %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
    %(filename)s 调用日志输出函数的模块的文件名
    %(module)s 调用日志输出函数的模块名
    %(funcName)s 调用日志输出函数的函数名
    %(lineno)d 调用日志输出函数的语句所在的代码行
    %(created)f 当前时间,用UNIX标准的表示时间的浮点数表示,用 datefmt 即可
    %(relativeCreated)d 输出日志信息时的,自Logger创建以来的毫秒数
    %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
    %(thread)d 线程ID。可能没有
    %(threadName)s 线程名。可能没有
    %(process)d 进程ID。可能没有
    %(message)s用户输出的消息
    format 参数中可能用到的格式化串
    CRITICAL = 50 #FATAL = CRITICAL
    ERROR = 40
    WARNING = 30 #WARN = WARNING  默认级别
    INFO = 20
    DEBUG = 10
    NOTSET = 0 #不设置
    日志级别

    clip_image001

    应用 logging 模块正确姿势

    import logging
    
    
    logger = logging.getLogger()    # 实例化一个 logger 对象
    logger.setLevel(logging.DEBUG) # 设定 logger 的输出等级,全局级别
    
    # 创建一个handler 对象(文件输出流对象),用于写入日志文件
    fh = logging.FileHandler('test1.log') # 文件要指定一个路径
    fh.setLevel(logging.WARNING)    # 给不同的 handler 对象设置不同的日志级别
    
    fh = handlers.RotatingFileHandler( "chat.log",maxBytes=10,backupCount=3)
    # 按文件大小切割日志;格式:文件名,文件字节大小,保留的文件个数
    比如日志文件是chat.log。当chat.log达到指定的大小之后,自动把文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log.1,1为最新
    
    fh = handlers.TimedRotatingFileHandler( "web.log",when="S",interval=5,backupCount=3)
    # 按时间切割日志;格式:文件名,时间类型,间隔多少时间,文件保留个数;新的文件是当前时间。
    S 秒
    M 分
    H 小时
    D 天
    W 每星期(interval==0时代表星期一)
    midnight 每天凌晨
    
    
    
    # 再创建一个handler(屏幕输出流对象),用于输出到屏幕
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG) # 给不同的 handler 对象设置不同的日志级别
    
    # 创建一个格式化对象
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    # fh 和 ch 把格式化对象 formatter 的功能吸过来
    fh.setFormatter(formatter) # 文件输出需要一个格式,fh 对象调用 setformatter 方法指定格式
    ch.setFormatter(formatter) # 屏幕输出需要一个格式,ch 对象调用 setformatter 方法指定格式
    
    logger.addHandler(fh) # logger对象把 fh 和 ch 对象里有的功能都吸过来,同时有他们的功能
    logger.addHandler(ch) # logger 决定使用哪个对象作为输出,这里同时使用两个,文件输出和屏幕输出
    # logger 执行日志输出
    # logger.debug('logger debug message')
    # logger.info('logger info message')
    # logger.warning('logger warning message')
    # logger.error('logger error message')
    # logger.critical('logger critical message')
    import os,sys
    with open('i.cfg') as r:
        ex=os.path.isfile('i1.cfg')  # 判断文件是否存在
        print(ex)
        flag=os.path.getsize('i.cfg') # 判断文件是否为空
        print(flag)
    View Code
    用Python的logging模块记录日志时,遇到了重复记录日志的问题,第一条记录写一次,第二条记录写两次,第三条记录写三次。。。很头疼,这样记日志可不行。网上搜索到了原因与解决方案:
    原因:没有移除handler 
    解决:在日志记录完之后removeHandler
    
    修改前示例代码:
    import logging
    
    
    def log(message):
        logger = logging.getLogger('testlog')
    
        streamhandler = logging.StreamHandler()
        streamhandler.setLevel(logging.ERROR)
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
        streamhandler.setFormatter(formatter)
    
        logger.addHandler(streamhandler)
        logger.error(message)
    
    if __name__ == '__main__':
        log('hi')
        log('hi too')
        log('hi three')
    
    修改前输出结果:
    2016-07-08 09:17:29,740 - ERROR - testlog - hi 
    2016-07-08 09:17:29,740 - ERROR - testlog - hi too 
    2016-07-08 09:17:29,740 - ERROR - testlog - hi too 
    2016-07-08 09:17:29,740 - ERROR - testlog - hi three 
    2016-07-08 09:17:29,740 - ERROR - testlog - hi three 
    2016-07-08 09:17:29,740 - ERROR - testlog - hi three
    修改后示例代码:
    import logging
    
    
    def log(message):
        logger = logging.getLogger('testlog')
    
        streamhandler = logging.StreamHandler()
        streamhandler.setLevel(logging.ERROR)
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
        streamhandler.setFormatter(formatter)
    
        logger.addHandler(streamhandler)
        logger.error(message)
    
        #  添加下面一句,在记录日志之后移除句柄
        logger.removeHandler(streamhandler)
    
    if __name__ == '__main__':
        log('hi')
        log('hi too')
        log('hi three')
    
    修改后输出结果:
    2016-07-08 09:32:28,206 - ERROR - testlog - hi 
    2016-07-08 09:32:28,206 - ERROR - testlog - hi too 
    2016-07-08 09:32:28,206 - ERROR - testlog - hi three
    深度解析:
    Google之后,大概搞明白了,就是你第二次调用log的时候,根据getLogger(name)里的name获取同一个logger,而这个logger里已经有了第一次你添加的handler,第二次调用又添加了一个handler,所以,这个logger里有了两个同样的handler,以此类推,调用几次就会有几个handler。。
    
    所以这里有以下几个解决办法:
    
    每次创建不同name的logger,每次都是新logger,不会有添加多个handler的问题。(ps:这个办法太笨,不过我之前就是这么干的。。)
    像上面一样每次记录完日志之后,调用removeHandler()把这个logger里的handler移除掉。
    在log方法里做判断,如果这个logger已有handler,则不再添加handler。
    与方法2一样,不过把用pop把logger的handler列表中的handler移除。
    下面是方法3与方法4的代码示例:
    方法3:
    import logging
    
    
    def log(message):
        logger = logging.getLogger('testlog')
    
        #  这里进行判断,如果logger.handlers列表为空,则添加,否则,直接去写日志
        if not logger.handlers:
            streamhandler = logging.StreamHandler()
            streamhandler.setLevel(logging.ERROR)
            formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
            streamhandler.setFormatter(formatter)
            logger.addHandler(streamhandler)
    
        logger.error(message)
    
    
    if __name__ == '__main__':
        log('hi')
        log('hi too')
        log('hi three')
    方法4:
    import logging
    
    
    def log(message):
        logger = logging.getLogger('testlog')
    
        streamhandler = logging.StreamHandler()
        streamhandler.setLevel(logging.ERROR)
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
        streamhandler.setFormatter(formatter)
    
        logger.addHandler(streamhandler)
    
        logger.error(message)
    
        #  用pop方法把logger.handlers列表中的handler移除,注意如果你add了多个handler,这里需多次pop,或者可以直接为handlers列表赋空值
        logger.handlers.pop()
        # logger.handler = []
    
    
    if __name__ == '__main__':
        log('hi')
        log('hi too')
        log('hi three')
    logging 重复写日志问题



    2、configparser 模块

    该模块适用于配置文件的格式与windows  ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)。

    import configparser
    
    config = configparser.ConfigParser()  # 生成一个文件配置的操作句柄,可以对文件 read,write
    config['DEFAULT'] = {'ServerAliveInterval': '45',  # 往配置文件里添加内容,以字典的形式添加
                         'Compression': 'yes',
                         'CompressionLevel': '9'}
    
    config['bitbucket.org'] = {'User': 'hg'}
    config['topsecret.server.com'] = {'Host Port': '50022',
                                      'ForwardX11': 'no'}
    config['DEFAULT']['ForwardX11'] = 'yes'
    
    with open('example.ini', 'w', encoding='utf8') as f:
        # configfile.write(config)
        config.write(f)   # 调用 config的 write 方法,把 config 对象写到文件里面去
    新建
    config.read('example.ini')  # 需要读文件,就先关联
    print(config.sections())  # 读取默认下有几个模块,列表形式,不包含 default 模块
    # ['bitbucket.org', 'topsecret.server.com']
    
    print(config.defaults())  # 默认的模块要单独拿出来,打印键值对
    '''
    OrderedDict([('serveraliveinterval', '45'), ('compression', 'yes'), ('compressionlevel', '9'), ('forwardx11', 'yes')])
    '''
    
    
    print(config.default_section)  # 只拿 default 这个模块名字符串   DEFAULT
    print(config['bitbucket.org'])  # 模块对象   <Section: bitbucket.org>
    print(config['bitbucket.org'].keys())  # bitbucket.org模块键的对象    KeysView(<Section: bitbucket.org>)
    print(config['bitbucket.org'].values())  # bitbucket.org模块值的对象  ValuesView(<Section: bitbucket.org>)
    print(config['bitbucket.org'].items())  # bitbucket.org模块键值对的对象  ItemsView(<Section: bitbucket.org>),需要用了两个变量接受
    print(config.items('bitbucket.org'))  # 打印当前模块的所有键值对 [('serveraliveinterval', '45'), ('compression', 'yes'), ('compressionlevel', '9'), ('forwardx11', 'yes'), ('user', 'alex')]
    print(config['bitbucket.org']['User'])  # 取某个模块下的某个键的值:hg
    
    for key in config['bitbucket.org']:   # for 循环里面的键
        print(key)  # 默认default的模块的键值都会赋给下面的单独模块,打印单独的模块下的键值,都会包含默认default模块下的键值
    '''
    user     # bitbucket.org 模块的键
    serveraliveinterval   # default 的键
    compression   #default 的键
    compressionlevel    # default 的键
    forwardx11   # default 的键
    '''
    
    print(config.options('bitbucket.org'))  # 同 for 循环结果,以列表形式
    #['user', 'serveraliveinterval', 'compression', 'compressionlevel', 'forwardx11']
    
    print('user' in config['bitbucket.org'])  # 判断某个键是否在模块里面,True
    查询
    config.read('example.ini')  #需要删除文件内容,就先关联
    config.remove_section('topsecret.server.com')  # 删除模块
    print(config.has_section('topsecret.server.com'))  # 判断有没有这个模块,返回 true、false
    config.remove_option('bitbucket.org','User')  #删除某个 session 下的某个键
    
    config.write(open('example.ini','w')) #无法直接修改文件内容,直接重写一遍一遍再覆盖源文件
    删除
    config.read('example.ini')  #需要改写文件内容,就先关联
    config.set('bitbucket.org', 'User', 'alex')  # 改变键值,格式:session模块,键,新值
    config.write(open('example.ini', 'w'))  # 无法直接修改文件内容,这里是直接重写一遍再覆盖源文件
    修改
    config.read('example.ini')  #需要增加文件内容,就先关联
    config['baidu.org'] = {'jd': 'lqd'}   # 方式1,这种是对整个模块进行重新赋值,会删掉原有
    
    config.add_section('alexsb')        # 方式2,先增加模块名
    config.set('alexsb', 'k2', 'v2')    # 对模块的某个键进行赋值,键不存在会新建一个
    
    config.write(open('example.ini', 'w'))  # 写入文件方式1
    with open('example.ini', 'w', encoding='utf8') as f:  # 写入文件方式2
        config.write(f)
    增加


    3、json、pickle 序列化模块

    什么是序列化?

    序列化是指把内存里的数据类型转变成字符串,以使其能存储到硬盘或通过网络传输到远程,因为硬盘或网络传输时只能接受bytes,在Python中叫pickling

    在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。

    反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。


    json

    如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

    JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:

    # json:dump 方法
    d={'name':'egon'}
    with open('new1','w') as f:
        json.dump(d,f)   # dump(需要序列化的对象,文件对象),1:转成json字符串 2:将json字符串写入 f 里
    
    # json:dumps 方法
    with open('new2','w') as f1:
        data=json.dumps(d)  # 先用 dumps 把对象转换成 json 字符串,进行序列化
        f1.write(data) # 再把序列化后的内容写进文件
    
    # json:loads 方法
    with open('new1') as r:
        d=r.read()
        data=json.loads(d)  # loads 接的是文件内容
        print(data['name'])
    
    
    # json:load 方法
    with open('new2') as r1:
        data=json.load(r1)  # load 接的是文件对象
        print(data['name'])
    
    
    # json load 的时候,不管你是以什么样的形式传过来的,只要符合 json 语法格式就给你 load
    # new3 手动新建的内容,不经过 dump:{"name1":"egon1"} 必须是双引号,单引号不符合 json 语法格式
    with open('new3') as r2:
        data=json.load(r2)
        print(data['name1'])  # 不用dump数据,只要文件内容格式符合json ,就可以 load 出来
    i=10
    s='hello'
    t=(1,4,6)
    l=[3,5,7]
    d={'name':"yuan"}
    
    json_str1=json.dumps(i)
    json_str2=json.dumps(s)
    json_str3=json.dumps(t)
    json_str4=json.dumps(l)
    json_str5=json.dumps(d)
    
    # 屏幕输出的时候,两边单引号隐藏
    print(json_str1)   #'10'
    print(json_str2)   #'"hello"'
    print(json_str3)   #'[1, 4, 6]'
    print(json_str4)   #'[3, 5, 7]'
    print(json_str5)   #'{"name": "yuan"}'
    json的四种方法
    import json
    #dct="{'1':111}" #json 不认单引号,dump 进去单引号,load 出来还是双引号
    #dct=str({"1":111}) #报错,因为生成的数据还是单引号:{'one': 1}
    
    dct='{"1":"111"}'
    print(json.loads(dct))
    
    #conclusion:
    #   无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads
    注意点


    pickle

    pickle 与  json 的区别

    相同点:

    都是通过 dump、dumps、load、loads 数据转换

    不同点:

    json 可以直接看文件内容

    pickle 不能直接看文内容,因为存进去的是以pickle的数据类型,字节格式存进去的

    json 不支持时间格式的数据类型,但是可以在多语言之间进行转换,跨语言,体积小

    pickle 支持任何数据类型,包括类和函数,但是只能在 python 之间流通,存储数据占空间大

    读写方式不一样:

    json 是:r、w、a

    pickle 是:rb、wb、ab

    #时间格式
    import datetime
    import json
    import pickle
    
    t=datetime.datetime.now()
    
    d={"data":t}
    
    json.dump(d,open("new4","w"))   # 直接报错,不是 json 支持的格式
    d1={"name":"alvin"}   # pickle 可以写,但是直接看文件,是乱码,字节格式
    
    s=pickle.dumps(d)
    s1=pickle.dumps(d1)
    f=open('new5',"wb")
    
    f.write(s)
    f.write(s1)
    f.close()
    
    
    f=open("new5","rb")   # 读取数据
    
    data=pickle.loads(f.read())
    
    print(data)
    例子

    .

  • 相关阅读:
    CString to char*
    修改mfc中的图标的问题
    MFC Class Wizard要到这里来找
    多文档情形下,窗口的重绘
    64位的ubuntu跑不了32位下编译出来的代码,可是我就是想跑啊
    ubuntu不能执行某个执行文件,这个叫权限不够
    碰到了在ubuntu下不能读windows盘符的问题——ubuntu使用心得
    画个多边形来,CDC
    如果要在mFC客户区添加控件怎么办
    饿汉单例模式实例——取快递
  • 原文地址:https://www.cnblogs.com/tootooman/p/9072926.html
Copyright © 2020-2023  润新知