day15-包和logging模块的使用

昨天我们讲述了模块的使用,我们知道了模块就是一个功能的集合体,讲的通俗一点,模块就是一个文件.既然我们功能多了,知道给功能根据属性放到不同的模块中,那么当我们的模块越来越多时呢?

我们在windows图像界面文件多了的时候,我们都知道用一个文件夹去将这些文件区分,那么在python中去包含模块的文件夹呢?这个文件夹就就叫做包

在讲包之前,我们来分析一下,文件的俩种用途??

文件的俩种用途

从我们昨天学的模块中,我们发现文件可以被直接执行,也可以被当作一个模块被导入到执行文件中?

那么现在我有一个要求? 如何区分一个文件是执行文件还是被导入的模块?或者说如何实现我这个文件,当被导入时执行对应代码,当是执行文件时,又执行另外的代码?

这时候,我们应该都会想到,欸,执行文件?那不是有一个环境变量 sys.path 和它有关吗?当是执行文件时,里面有它的直属文件夹,当是被导入时,环境变量这个列表中则没有它的直属文件夹?

判断在没在环境变量中

import os
import sys
dir = os.path.dirname(__file__)
print(dir)  # Z:/线下培训/04-第四周/02-day02/01-区分py文件的俩种用途  这里是/ 右斜杠
print(sys.path)  # ['Z:\\线下培训\\04-第四周\\02-day02\\01-区分py文件的俩种用途',...]  这里是左斜杠
if dir not in sys.path:  # 因为这是在windows中,所有 我 取 反了
    s = """这是一个执行文件xxx"""
    print(s)
else:
    y = """这是在被导入时执行的文件xxx"""
    print(y)

你一看?我靠?这么麻烦?这一搞,文件内的代码,不是看起来很乱?

其实python内置了一个 内置属性就可以做到这样,甚至有过之而不不及.

__name__属性

那就是 _name_, 当 __name__ 是一个被执行的文件时,它返回的值为 __main__ ,当是被当作模块导入时,返回的是它本身即模块名.

所以我们可以这样做

def f1():
    print('xxxxxxx')


# print(__name__)

if __name__ == '__main__':
    f1()

这时,条件判断之外的为模块时执行的,条件判断之内的为执行文件时执行的

但,一般一个项目只有一个执行文件,但是碍于好区分,可以在执行文件中加上 这个条件判断.

下面开始介绍包

概念:文件夹内包含一个_init_.py

功能:将导入模块语句放到一个包内,通过改变_init_.py文件来使执行者不改变使用语法的情况下,改变了模块的位置.

什么意思??这么抽象???

别急,让我们来看看具体实例

包的由来以及实例

本来所有的模块和功能都来自一个模块中,即使用者的使用方法为:aaa.功能名

但为了解决设计者的维护困难,因此设计者将其分成了下面的目录结构.

要求,不改变使用方法,原使用方法为 aaa.f1(),aaa.f2(),aaa.f3().

目录结构为:

包的使用  (这是一个文件夹) 
	- aaa
		- __init__py  (为aaa下的包)
		- bbb
			- __init__.py  (为bbb下的包)
			- m3.py  (为bbb这个文件夹下的模块)
		- m1.py  (为aaa这个文件夹下的模块)
		- m2.py  (为aaa这个文件夹下的模块) 
	- run.py  (为执行文件)

其中 m1 模块下.有一个功能 叫做 f1. m2 模块下也有一个功能叫做 f2 ,m3 模块中 也有一个功能 叫做 f3

即m1,m2,m3的文件内容为

# m1.py
def f1():
    print('m1.f1')

# m2.py
def f2():
    print('m2.f2')

# m3.py
def f3():
    print('m3.f3')

问,我如何可以使用到f1,f2,f3这些功能

方案1:昨天的模块

# run.py文件
from aaa import m1,m2
from aaa.bbb import m3

m1.f1()
m2.f2()
m3.f3()

但是这样做并没有解决,使用方法不改变的情况.我们就想实现,用aaa.f1(),aaa.f2(),aaa.f3().

所以这时候我们就可以使用包的一些特性,那就是包的_init_.py文件了

方案2:包来解决

# aaa下的__init__.py文件
from .m1 import f1
from .m2 import f2
from .bbb.m3 import f3

# run.py的文件
import aaa

aaa.f1()
aaa.f2()
aaa.f3()

欸,这就满足了,我使用者没改变使用方法,而,设计者又分成了若干个包,利于维护的需求.

其实以上就是包的由来

仔细的同学,你可能会发现我们在aaa的__init__.py文件中在导入语句中使用了 .这个 字符?

是的,当我们在包内使用导入语句可以使用 ...,以及...,......, 那 它们代表的都是什么意思呢?

其实在包的导入中.表示的就是当前的文件夹 即这里的 . 表示的就是 aaa这个文件夹,..表示的就是上一级的文件夹,...表示的是上上上一级的文件夹 ....,依次类推.但是,它们有一个大前提,那就是必须在包内使用,而且 不能超出 包的范围. 即 就是该包的最顶级文件夹.

总结一下包

1、导包就是在导包下_init_.py文件
2、包内部的导入应该使用相对导入,相对导入也只能在包内部使用,而且...取上一级不能出包
3、
使用语句中的点代表的是访问属性
m.n.x ----> 向m要n,向n要x
而导入语句中的点代表的是路径分隔符
import a.b.c --> a/b/c,文件夹下a下有子文件夹b,文件夹b下有子文件或文件夹c
所以导入语句中点的左边必须是一个包

到这.我们自己自定义的功能呐,模块啊,包啊.都已经结束了,那么接下来就是一些 别人写好的 模块了.即常用模块

logging 模块

简介

Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。

日志级别

CRITICAL = 50 #FATAL = CRITICAL
ERROR = 40
WARNING = 30 #WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0 #不设置

模块提供logger,handler,filter,formatter。

  • logger:提供日志接口,供应用代码使用。logger最常用的操作有两类:配置和发送日志消息。可以通过logging.getLogger(name)获取logger对象,如果不指定name则返回root对象,多次使用相同的name调用getLogger方法返回同一个logger对象。
  • handler:将日志记录(log record)发送到合适的目的地(destination),比如文件,socket等。一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。
  • filter:提供一种优雅的方式决定一个日志记录是否发送到handler。
  • formatter:指定日志记录输出的具体格式。formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。

logger,handler和日志消息的调用可以有具体的日志级别(Level),只有在日志消息的级别大于logger和handler的级别才放行。

logging用法解析

  1. 初始化 logger = logging.getLogger("endlesscode"),getLogger()方法后面最好加上所要日志记录的模块名字,后面的日志格式中的%(name)s 对应的是这里的模块名字
  2. 设置级别 logger.setLevel(logging.DEBUG),Logging中有NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL这几种级别,日志会记录设置级别以上的日志
  3. Handler,常用的是StreamHandler和FileHandler,windows下你可以简单理解为一个是console和文件日志,一个打印在终端的窗口上,一个记录在一个文件上
  4. formatter,定义了最终log信息的顺序,结构和内容
    %(name)s Logger的名字
    %(levelname)s 文本形式的日志级别
    %(message)s 用户输出的消息
    %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
    %(levelno)s 数字形式的日志级别
    %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
    %(filename)s 调用日志输出函数的模块的文件名
    %(module)s 调用日志输出函数的模块名
    %(funcName)s 调用日志输出函数的函数名
    %(lineno)d 调用日志输出函数的语句所在的代码行
    %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
    %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
    %(thread)d 线程ID。可能没有
    %(threadName)s 线程名。可能没有
    %(process)d 进程ID。可能没有
  5. 记录 使用object.debug(message)来记录日志

介绍来自互联网

我直接上实例吧!让我们写出我们的第一个记日志的小功能

# -*- coding: utf-8 -*-
# @Author  : JKey  
# Timer    : 2021/1/5 14:15
import logging

# 创建一个logger对象
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# 创建一个handler.用于写入日志文件
fh = logging.FileHandler("test.log")
fh.encoding = 'utf-8'
fh.setLevel(logging.DEBUG)

# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(fh)
logger.addHandler(ch)

logger.info('你好啊,第一个log信息')

放入到我们之前写的项目中,即

- conf
	- settings.py
    	# 文件内容
        # -*- coding: utf-8 -*-
        # @Author  : JKey
        # Timer    : 2021/1/5 12:19
        import os

        FUNC_DICT = {}
        BASE_DIR = os.path.dirname(os.path.dirname(__file__))
        A1_LOG = f'{BASE_DIR}/log/a1.log'
        A2_LOG = f'{BASE_DIR}/log/a2.log'

        standard_format = '%(asctime)s %(filename)s:%(lineno)d %(name)s %(levelname)s %(message)s'
        simple_format = '%(asctime)s %(message)s'
        LOGGING_DIC = {
            'version': 1,
            'disable_existing_loggers': False,
            'formatters': {
                'standard': {
                    'format': standard_format
                },
                'simple': {
                    'format': simple_format
                },
            },
            'filters': {},
            'handlers': {
                #打印到文件的日志,收集info及以上的日志
                'file1': {
                    'level': 'DEBUG',
                    'class': 'logging.FileHandler',  # 保存到文件
                    'formatter': 'standard',
                    'filename': A1_LOG,
                    'encoding': 'utf-8',
                },
                'file2': {
                    'level': 'DEBUG',
                    'class': 'logging.FileHandler',  # 保存到文件
                    'formatter': 'standard',
                    'filename': A2_LOG,
                    'encoding': 'utf-8',
                },
                #打印到终端的日志
                'stream': {
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',  # 打印到屏幕
                    'formatter': 'simple'
                },
            },
            'loggers': {
                #logging.getLogger(__name__)拿到的logger配置
                # 其中''表示的是默认的,即不管什么名称,只要没匹配上,其他的key,就都使用该''key里面的配置
                '': {
                    'handlers': ['file1','file2','stream'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
                    'level': 'ERROR',  # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
                    'propagate': False,  # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
                },
                '提示日志': {
                    'handlers': ['stream'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
                    'level': 'ERROR',  # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
                    'propagate': False,  # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
                },
            },
        }

- core
	- src.py
        # -*- coding: utf-8 -*-
        # @Author  : JKey
        # Timer    : 2021/1/5 8:51
        from lib import common
        from conf import settings


        @common.add_dic(1, "注册")
        def register():
            """注册"""
            print('注册中')


        @common.add_dic(2, "登录")
        def login():
            """登录"""
            print('登陆')


        @common.add_dic(3, "取款")
        def withdraw():
            """取款"""
            print('取款')
            logger1 = common.get_logger("用户交易")
            logger1.error("lxx取了1个亿")


        @common.add_dic(4, "转账")
        def transfer():
            """转账"""
            print('转账')


        def run():
            while True:
                for k, v in settings.FUNC_DICT.items():
                    print(k, v[0])

                chios = input('请输入你的选择>>>:').strip()
                if chios in settings.FUNC_DICT:
                    settings.FUNC_DICT[chios][1]()
                else:
                    print('error')

- lib
	- common.py
    	# -*- coding: utf-8 -*-
        # @Author  : JKey
        # Timer    : 2021/1/5 8:51
        from lib import common
        from conf import settings


        @common.add_dic(1, "注册")
        def register():
            """注册"""
            print('注册中')


        @common.add_dic(2, "登录")
        def login():
            """登录"""
            print('登陆')


        @common.add_dic(3, "取款")
        def withdraw():
            """取款"""
            print('取款')
            logger1 = common.get_logger("用户交易")
            logger1.error("lxx取了1个亿")


        @common.add_dic(4, "转账")
        def transfer():
            """转账"""
            print('转账')


        def run():
            while True:
                for k, v in settings.FUNC_DICT.items():
                    print(k, v[0])

                chios = input('请输入你的选择>>>:').strip()
                if chios in settings.FUNC_DICT:
                    settings.FUNC_DICT[chios][1]()
                else:
                    print('error')

- log
	- a1.log
    - a2.log
-start.py
	# -*- coding: utf-8 -*-
    # @Author  : JKey  
    # Timer    : 2021/1/5 14:25


    from core import src


    if __name__ == '__main__':
        src.run()

其中的 settings中的日志字典LOGGING_DIC是本模块的重点.建议,copy到pycharm中进行,分析.其实也就是针对了 logging 模块内的 logger,handle, 和 formatter,以及filter 这几个 对象进行 配置以及 搭配即可.

总结:

1.文件的俩种类型

  • 1.1:文件为被执行文件

  • 1.2:文件为被导入文件,即模块

  • 有一个内置方法__name__,被执行时返回__mian__,被当作模块导入时返回模块名

  • 可以得到一个条件判断语句

    if __name__ == '__main__':
       	pass
    

2.包的介绍

  • 2.1包其实就是一个文件夹,内置有__init__.py的.
  • ps:在python2中这个py文件必须要有,在python3中可以没有

3.包的使用

  • 3.1:导入包其实就是相当于导入包内的__init__.py文件

4.使用包的注意事项

  • 4.1:使用相对导入时,不能超过包的范围

5.logging 模块的 使用

  • 5.1:这里,只需要你掌握怎么使用即可,即把那个LOGGING_DIC研究怎么改里面的key和属性即可
posted on 2021-01-05 16:43  Jkeykey  阅读(177)  评论(0编辑  收藏  举报