# 常用模块

  • 什么是模块:模块就是一组功能的集合,我们的程序可以导入模块来复用模块里的功能

    python 模块分为3种:

    1. 内置模块:python安装时自带的

    2. 扩展模块: 比如第三方模块

    3. 自定义模块: 自己写的模块

# collections模块

在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:CounterdequedefaultdictnamedtupleOrderedDict

	#1.namedtuple: 生成可以使用名字来访问元素内容的tuple
from collections import namedtuple
Point = namedtuple('Point',['x','y'])
p=Point(1,2)
print(p.x,p.y)   # 1,2

	#2.deque: 双端队列,可以快速的从另外一侧追加和推出对象
from collections import deque
q=deque(['a','b','c'])
q.append('x')
q.appendleft('y')
print(q)   # deque(['y', 'a', 'b', 'c', 'x'])

	#3.Counter: 计数器,主要用来计数
c = Counter('abcdeabcdabcaba')
print(c)    # Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

	#4.OrderedDict: 有序字典
	#5.defaultdict: 带有默认值的字典
from collections import defaultdict
values = [11, 22, 33,44,55,66,77,88,99,90]
my_dict = defaultdict(list)   # 设置字典的默认值
for value in  values:
    if value>66:
        my_dict['k1'].append(value)
    else:
        my_dict['k2'].append(value)
print(my_dict)  # defaultdict(<class 'list'>, {'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99, 90]})   # print(list(my_dict.values()))
        
from collections import defaultdict
dd = defaultdict(lambda: 'N/A')
dd['key1'] = 'abc'
print(dd['key1']) # key1存在    'abc'
print(dd['key2']) # key2不存在,返回默认值   'N/A'  

from collections import defaultdict
dic = defaultdict(list)  # 如果没有给一个key赋值,就会返回一个list
dic['name'] ='wang'
dic['list'].append([1,3,4])
print(dic)  #defaultdict(<class 'list'>, {'name': 'wang', 'list': [[1, 3, 4]]})
print(dict(dic))  #{'name': 'wang', 'list': [[1, 3, 4]]}

# dic1=dict()
# print(dic1)  #{}
  1. 使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低,deque是为了高效插入和删除

# decimal模块

内置的精确计算的饿模块

import decimal
d1 =decimal.Decimal('0.1')  #注意必须是字符串
d2=decimal.Decimal('0.01')
print(d1*d2)  #0.001
print(decimal.Decimal(0.1))   #0.100000000000000005555……

# time、datetime

时间模块

  1. 表示时间的三种方式表示时间:时间戳、元组(结构化时间)、格式化时间(字符串):

    ​ 时间戳:通常来说,时间戳表示的是从1970.1.1开始的计算偏移量

    ​ 格式化时间字符串:‘1999-12-06‘

    ​ 结构化时间元组:struct_time元组共有9个元素共九个元素:(年月日时分秒,一年中第几周,一年中 第几天等)

    #时间戳
    >>>time.time()
    1500875844.800804
    
    #时间字符串
    >>>time.strftime("%Y-%m-%d %X")
    '2017-07-24 13:54:37'
    >>>time.strftime("%Y-%m-%d %H-%M-%S")
    '2017-07-24 13-55-04'
    
    #时间元组:localtime将一个时间戳转换为当前时区的struct_time
    time.localtime()
    time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24,
              tm_hour=13, tm_min=59, tm_sec=37, 
                     tm_wday=0, tm_yday=205, tm_isdst=0)
    
    # 时间戳时间-localtime/gmtime-> 结构化时间 -strftime-> 格式化时间
    # 时间戳时间<-mktime- 结构化时间 <-strptime- 格式化时间
    time.strptime("2017-03-16","%Y-%m-%d")  #返回一个元组结构化时间
    time.strftime("%Y-%m-%d",time.localtime(1500000000)) # 转化为格式化时间
    time.mktime(time.localtime(1500000000))  # 结构化转时间戳
    time.localtime(1500000000)  # 时间戳转结构化
    

    img

# random模块

随机数模块

random.random()   # 大于0且小于1之间的小数
random.uniform(13)  # 大于1小于3的小数
random.randint(1,5)  # 大于1小于等于5
random.randrange(1,10,2)  # 大于等于1且小于10之间的奇数
random.choice([1,'23',[4,5]])   # 随机返回一个
random.sample([1,'23',[4,5]],2)  #随机不重复匹配2个
random.shuffle([1,3,4,5]) # 打乱次序,洗牌
random.choices([1,'23',[4,5],'3','4'],k=3)  # 随机选3个

# 验证码随机
import random
def make_code(n):
    res = ''
    for i in range(n):
        s1 = chr(random.randint(65,90))  # 生成字母chr
        s2 = str(random.randint(0,9))
        res += random.choice([s1,s2])
    return res
print(make_code(9))   # H53FVG8K4

 #红包代码,几率相等    
import random
def func(money,pople):
    lst=[0,2000]  # 为了去小数,首尾
    lst.extend(random.sample(list(range(1,money*10)),pople-1))
    lst.sort()
    lst =list(map(lambda x:x/10,lst))  # 除以10
    g=(round(lst[i+1]-lst[i],3) for i in range(len(lst)-1))
    return g
ret=list(func(200,10))
print(ret)

# os模块

与操作系统交互的一个接口

os.makedirs('dirname1/dirname2')    #可生成多层递归目录
os.removedirs('dirname1')  #若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    #生成单级目录;相当于shell中mkdir dirname
os.listdir('dirname')  #列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.rename("oldname","newname")  #重命名文件/目录
os.stat('path/filename')  #获取文件/目录信息
os.system("bash command")  #运行shell命令,直接显示
os.getcwd()  #获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  #改变当前脚本工作目录;相当于shell下cd

os.path.split(path)  #将path分割成目录和文件名二元组返回
os.path.dirname(path) #返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) #返回path最后的文件名。如何path以/或\结尾,那么就会返回空值
os.path.exists(path)  #如果path存在,返回True;如果path不存在,返回False

os.path.isfile(path)  #如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  #如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  #将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getsize(path) #返回path的大小

# sys模块

是与Python解释器交互的一个接口

sys.argv           # 命令行参数List,第一个元素是程序本身路径
sys.exit(n)        #退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version        #获取Python解释程序的版本信息
sys.path           #返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       #返回操作系统平台名称
  • 序列化模块: 将原本的字典、列表等数据类型内容转化成一个字符串的过程。反序列化概念相反。

    img

# json模块

提供四个功能:dumps、dump、loads、load

    #dump和load是直接将对象序列化之后写入文件,且依赖一个文件句柄。而dumps和loads是直接在文件(内存)中进行操作,当然可以可以通过文件的‘w'模式写入。
    #内存中操作
    import json
    dic = {'k1':'v1','k2':'v2','k3':'v3'}
    str_dic = json.dumps(dic)  #序列化:将一个字典转换成一个字符串,
    print(type(str_dic),str_dic) #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}
    
    dic2 = json.loads(str_dic)  #反序列化:将一个字符串格式的字典转换成一个字典
    #注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示,''都不行
    print(type(dic2),dic2)  #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
    
    #文件中操作
    import json
    f = open('json_file','w')
    dic = {'k1':'v1','k2':'v2','k3':'v3'}
    json.dump(dic,f)  #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
    f.close()
    
    f = open('json_file')
    dic2 = json.load(f)  #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
    f.close()
    print(type(dic2),dic2) 

# pickle模块

用于python特有的数据类型和python数据类型进行转换

# 与json模块类似 需要注意的是在写入文件的时候,要用'wb','rb'
  # 内存中操作
  import pickle
  dic = {'k1':'v1','k2':'v2','k3':'v3'}
  str_dic = pickle.dumps(dic)
  print(str_dic)  #一串二进制内容
  
  dic2 = pickle.loads(str_dic)
  print(dic2)    #字典
  
  #文件中操作
  import time
  struct_time  = time.localtime(1000000000)
  print(struct_time)
  f = open('pickle_file','wb')
  pickle.dump(struct_time,f)
  f.close()
  
  f = open('pickle_file','rb')
  struct_time2 = pickle.load(f)
  print(struct_time2.tm_year)
  
  #补充:读取多s个文件内容时,需要使用while True
  while True:
      try:
          obj =pickle.load(f2)  # 注意这个load是一个一个的加载需要用循环
          li.append(obj)
      except:break
   else:return (True,None,None) 

# shelve模块

是对象持久化保存,同时生成三个文件,可以把生成的对象当成字典操作。

  #shelve模块中的key键必须是字符串形式,value可以是任意值
  # 1.创建一个shelf对象,直接使用open函数即可
  import shelve
  s = shelve.open('test_shelf.db')         #
  try:
      s['kk'] = {'int': 10, 'float': 9.5, 'String': 'Sample data'}
      s['MM'] = [1, 2, 3]
  finally:
      s.close()
  
  #2.对shelf对象,增、删、改操作
  s = shelve.open('test_shelf.db', flag='w', writeback=True)
  try:
      # 增加
      s['QQQ'] = 2333
      # 删除
      del s['MM']
      # 修改
      s['kk'] = {'String': 'day day up'}
  finally:
      s.close()   
    
  # 3,回写
  d=shelve.open(r'a.txt',writeback=True)    #writeback=True,对子字典修改完后要写回,否则不会看到修改后的结果
  d['egon']={'age':18,'sex':'male'}         #存的时候会生成三个文件,不用管,是python的一种处理机制
  d['egon']['age']=20                       #将年龄修改为20
  print(d['egon']['age'])                   #此时拿到的是修改后的年龄
  print(d['egon']['sex'])
  d.close()

# hashlib模块

提供常见的摘要算法 1. 摘要算法: 又称哈希算法,散列算法,通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制表示),目的是为了发现原始数据是否被人篡改过。原因是单向函数,反推很难但是现在已经有人反推成功。 2. 两种加密方式:可在md5() 中加密;还可以再update中提前加密。

     import hashlib 
     md5 = hashlib.md5('a'.encode('utf-8'))  # 生成加密对象,这里可以加盐
     md5.update('how to use md5 in python hashlib?'.encode('utf-8'))     #上传加密数据
     print md5.hexdigest() # 生成密文
     
     # 如果数据量大时,比如校验视频文件时,可以多次update(),最后生成密文比较
     #MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似
     
     #校验两个文件时候一致
     import os
     import hashlib
     
     def file_md5(path):
         filesize = os.path.getsize(path)   # 先获取文件大小
         md5 = hashlib.md5()      # 获取对象
         with open(path,'rb') as f:
             while filesize >= 4096:   # 判断大小,以便相减
                 content = f.read(4096)
                 md5.update(content)  # 每次更新对象内容
                 filesize -= 4096
             else:
                 content = f.read(filesize)
                 if content:
                     md5.update(content)
         return md5.hexdigest()  # 生成密文
     
     def cmp_file(path1,path2):
         return file_md5(path1) == file_md5(path2)
     
     path1 = r'D:\s20\day18\视频\4.面向对象整理.mp4'
     path2 = r'D:\s20\day18\视频\tmp.mp4'
     ret = cmp_file(path1,path2)
     print(ret)

# logging模块

  • 用途:主要用于输出运行日志,可以设置输出日志的等级,日志保存,日志文件回滚等;相当于print,但它是标准输出,不能设置级别,影响开发者查看其他数据,可以把它理解为,设了等级才输出的print。

  • logging 模块的使用:

    1. 简单配置法: 存在编码问题,但代码简单
    2. logger对象法: 使用方便,但配置复杂
  • logging的基本使用方法:

    • 默认情况下Python的logging模块将日志打印到了标准输出中
    • 只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING
    • 日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG
  • 函数式简单配置法

    import logging  
    logging.debug('debug message')  
    logging.info('info message')  
    logging.warning('warning message')  
    logging.error('error message')  
    logging.critical('critical message') 
    
    #配置日志等级,以及日志格式、输出位置:
    import logging  
    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='/tmp/test.log',   #写入文件地址
                        filemode='w')  
      
    logging.debug('debug message')  
    logging.info('info message')  
    logging.warning('warning message')  
    logging.error('error message')  
    logging.critical('critical message')
    
    #配置参数
    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用户输出的消息
    
  • logger对象配置法

    #相应组建介绍:
    1、日志器:logger,提供了应用程序可一致使用的接口
    2、处理器: Handler, 将logger创建的日志记录发送到合适的目的地输出或则记录
    3、过滤器:Filter,提供更细粒读的控制工具决定输出哪条日志记录
    4、格式器:Formater,决定日志记录的最终输出格式
    	日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
    	
    #方法:
    1、Logger.setLevel()  # 设置日志器将处理的日志消息的最低级别
    2、Logger.addHander()
       Logger.removeHander()   #添加或删除一个hander
    3、Logger.addFilter()
       Logger.removeFilter()   #为该logger对象添加 和 移除一个filter对象
         
    # 基本格式:
    import logging
    logger = logging.getLogger()   # 创建个logger对象
    							# 创建一个handler,用于写入日志文件,文件操作符
    fh = logging.FileHandler('test.log',encoding='utf-8') 
     
    						# 再创建一个handler,用于输出到控制台 ,屏幕输出流,操作符
    ch = logging.StreamHandler() 
    							#创建一个格式
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
     
    fh.setLevel(logging.DEBUG)   #设置查看等级
     
    fh.setFormatter(formatter)   # 绑定文件操作符,和格式
    ch.setFormatter(formatter)    #绑定屏幕操作符,和格式
    logger.addHandler(fh)        #logger对象可以添加多个fh和ch对象 
    logger.addHandler(ch)     
     
    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')
    
    #坑点:用Python的logging模块记录日志时,可能会遇到重复记录日志的问题,第一条记录写一次,第二条记录写两次,第三条记录写三次原因:没有移除handler 解决:在日志记录完之后removeHandler,还可以使用单例模式,还可以使用模块导入的形式,因为在一个主程序中相同模块只会导入一次,所以只能初始化一次。
    
    import logging
    def log(msg):
        #创建logger,如果参数为空则返回root logger
        logger = logging.getLogger("nick")
        logger.setLevel(logging.DEBUG)  #设置logger日志等级
    
        #创建handler
        fh = logging.FileHandler("test.log",encoding="utf-8")
        ch = logging.StreamHandler()
    
        #设置输出日志格式
        formatter = logging.Formatter(
            fmt="%(asctime)s %(name)s %(filename)s %(message)s",
            datefmt="%Y/%m/%d %X",
            )
    
        #为handler指定输出格式
        fh.setFormatter(formatter)
        ch.setFormatter(formatter)
    
        #为logger添加的日志处理器
        logger.addHandler(fh)
        logger.addHandler(ch)
    
        # 输出不同级别的log
        logger.info(msg)
    
        #解决方案1,添加removeHandler语句,每次用完之后移除Handler
        logger.removeHandler(fh)
        logger.removeHandler(ch)
    
    log("警告")
    log("提示")
    log("错误")
    
    ------------------------------------------------------------------
    #添加过滤器的例子****
    import logging
    def _create_log_handlers(stream=None):
    
        error_handler = logging.StreamHandler(stream)  # 获取logger对象
        error_handler.setLevel(logging.WARNING)  #设置等级
        formatter = logging.Formatter("%(levelname)s: %(message)s")
        error_handler.setFormatter(formatter)  #绑定格式
    
        non_error_filter = logging.Filter()   #实例个过滤器
        non_error_filter.filter = lambda record: record.levelno < logging.WARNING #实现过滤器
    
        non_error_handler = logging.StreamHandler(stream)
        non_error_handler.addFilter(non_error_filter)
        formatter = logging.Formatter("%(message)s") # 打印错误信息
        non_error_handler.setFormatter(formatter)
     
        return [error_handler, non_error_handler]    # 返回两个处理器
    ret =_create_log_handlers()
    
    def func():
        logger = logging.getLogger()
        logger.addHandler(ret[0])    #ret[0] 会报错后面两个,在ret[1]时报错前一个
        logger.debug('警告')    # 使用ret[0]调用,小于warning
        logger.warning('大于警告')  # 2019-04-04 21:52:55,654 - root - WARNING - 大于警告
        logger.error('错误')      #2019-04-04 21:53:21,054 - root - ERROR - 错误
    func()
    
    #这里的stream,只在streamHander(stream=None),这里默认是None,对应的输出对象是sys.stderr;当然可以设置为sys.stdout(标准输出,不飘红)
    
    -------------------------------------------------------------------
    #拓展
    def _default_handlers(stream):
        def should_log(record):
            if record.name.startswith("webkitpy.thirdparty.autoinstall"):
                return False
            return True
     
        logging_filter = logging.Filter()
        logging_filter.filter = should_log  #调用过滤器
     
        handler = logging.StreamHandler(stream)
        formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
        handler.setFormatter(formatter)
        handler.addFilter(logging_filter)
     
        return [handler]  #返回一个处理器
    
    -------------------------------------------------------------------
    #终极日志(添加单例模式)
    class Log(object):
        __instance = None
        def __new__(cls, *args, **kwargs):
            if not cls.__instance:
                cls.__instance = super().__new__(cls)
            return cls.__instance
    
        def __init__(self,level = logging.DEBUG):
            if 'logger' not in self.__dict__:
                logger = logging.getLogger()
                logger.setLevel(level)
                fh = logging.FileHandler('test.log', encoding='utf-8')
                ch = logging.StreamHandler()
                formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
                fh.setFormatter(formatter)
                ch.setFormatter(formatter)
                logger.addHandler(fh)
                logger.addHandler(ch)
                self.logger = logger
    
    log = Log()
    

log.logger.warning('选择序号输入的内容有误') ```

  • logging模块的补充

    • logging模块是线程安全的,不需要客户做任何特殊的工作。它通过使用线程锁实现了这一点; 有一个锁来序列化访问模块的共享数据,每个处理程序还创建一个锁来序列化访问其底层 I/O。

    • 日志记录级别:

      1555819360479

    • 日志记录格式化字符串

      1555819431858

    • 使用basicConfig方法配置logging记录格式:1555819463688

  • 使用logging打印进程

    import threading
    import logging
    FORMAT = "%(asctime)s %(thread)d %(message)s"
    logging.basicConfig(level=logging.INFO,format=FORMAT)
     
    def add(x,y):
        logging.warning("{} {}".format(threading.enumerate(),x+y))
     
    t = threading.Timer(1,add,args=(4,5))
    t.start()
     
    运行结果:
    2017-12-17 15:40:34,226 123145367023616 [<_MainThread(MainThread, stopped 4320629568)>, <Timer(Thread-1, started 123145367023616)>] 9
    
  • logger类:使用工厂方法返回一个Logger实例。logging.``getLogger([name=None])

    • 指定name,返回一个名称为name的Logger实例。如果再次使用相同的名字,是实例化一个对象。未指定name,返回Logger实例,名称是root,即根Logger。Logger是层次结构的,使用 '.' 点号分割,如'a'、'a.b'或'a.b.c.d','a'是'a.b'的父parent,a.b是a的子child。对于foo来说,名字为foo.bar、foo.bar.baz、foo.bam都是foo的后代。

    • 举例

      import logging
      DATEFMT ="[%Y-%m-%d %H:%M:%S]"
      FORMAT = "%(asctime)s %(thread)d %(message)s"
      logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT,filename='class_test.log')
       
      root = logging.getLogger()
      print(root.name,type(root),root.parent,id(root)) 
      #root <class 'logging.RootLogger'> None 4367575248
      
      logger = logging.getLogger(__name__)
      print(logger.name, type(logger), id(logger), id((logger.parent)))
       #__main__ <class 'logging.Logger'> 4367575864 4367575248
          
      logger1 = logging.getLogger(__name__ + ".ok")
      print(logger1.name, type(logger1), id(logger1), id((logger1.parent)))
      #__main__.ok <class 'logging.Logger'> 4367575920 4367575864
       
      print(logger1.parent,id(logger1.parent))
      #<logging.Logger object at 0x10453eb38> 4367575864
      
    • 子child的级别设置,不影响父parent的级别:

      import logging
      
      FORMAT = "%(asctime)s %(thread)d %(message)s"
      logging.basicConfig(level=logging.WARNING,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]")
       
      root = logging.getLogger()
      print(1,root,id(root)) #RootLogger,根Logger
      #1 <logging.RootLogger object at 0x104534f28> 4367535912
      
      root.info('my root') #低于定义的WARNING级别,所以不会记录
      loga = logging.getLogger(__name__) #Logger继承自RootLogger
      print(2,loga,id(loga),id(loga.parent))
      #2 <logging.Logger object at 0x1044ef630> 4367250992 4367535912
      
      print(3,loga.getEffectiveLevel()) #数值形式的有效级别
       #3 30
          
      loga.warning('before')
      loga.setLevel(28) #设置级别为28
      print(4,loga.getEffectiveLevel())
      #4 28
      loga.info('after')#
      loga.warning('after1')
      
    • Handler:Handler控制日志信息的输出目的地,可以是控制台、文件。

      • 可以单独设置level;可以单独设置格式;可以设置过滤器

      • StreamHandler #不指定使用sys.strerr;FileHandler #文件;_StderrHandler #标准输出

        NullHandler #什么都不做

    • level的继承

      import logging
       
      FORMAT = "%(asctime)s %(thread)d %(message)s"
      logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]")
       
      root = logging.getLogger() #根Logger级别为INFO 20
      print('root:',root.getEffectiveLevel())
       #root: 20
          
      log1 = logging.getLogger('s')
      log1.setLevel(logging.ERROR) #级别为ERROR 40
      print('log1:',log1.getEffectiveLevel())
      #log1: 40
      log1.error('log1 error')
      log2 = logging.getLogger('s.s1') #继承自log1 40,无法使用warning
      log2.setLevel(logging.WARNING) #设置为WARNING 30,才可以使用warning
      print('log2:',log2.getEffectiveLevel())
      #log2: 30
      log2.warning('log2 warning')  
      #logger实例,如果设置了level,就用它和信息的级别比较,否则,继承最近的祖先的level
      

      同样,handler也可以设置使用logging.Formatter()设置格式和Logging.Filter()设置过滤器

      import logging
       
      FORMAT = "%(asctime)s %(thread)d %(message)s"
      logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]")
       
      root = logging.getLogger()
      print(1,root.getEffectiveLevel()) #RootLogger,根Logger
       
      log1 = logging.getLogger('s')#模块化用__module__,函数化用__name__作为Logger名,Logger同名内存中也只有一个
      print(2,log1.getEffectiveLevel())
       
      h1 = logging.FileHandler('test.log')
      h1.setLevel(logging.WARNING)
      fmt1 = logging.Formatter('[%(asctime)s] %(thread)s %(threadName)s log1-handler1 %(message)s')
      h1.setFormatter(fmt1) #重新个性化定义记录的格式化字符串
      log1.addHandler(h1)
      filter1 = logging.Filter('s') #过滤器 会记录s, s.s2的信息
      log1.addFilter(filter1)
      print(3,log1.getEffectiveLevel())
       
      log2 = logging.getLogger('s.s2')
      print(4,log2.getEffectiveLevel())
       
      h2 = logging.FileHandler('test1.log')
      h2.setLevel(logging.WARNING)
      log1.addHandler(h2)
      filter1 = logging.Filter('s.s2') #过滤器不会记录s.s2的消息,只会记录自己的消息
      log1.addFilter(filter1)
      print(3,log1.getEffectiveLevel())
       
      log1.warning('log1 warning===')
      log2.warning('log2 warning---')