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)
例子

.

posted @ 2018-05-22 17:52  H-JIACHENG  阅读(248)  评论(0编辑  收藏  举报