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用法解析
- 初始化 logger = logging.getLogger("endlesscode"),getLogger()方法后面最好加上所要日志记录的模块名字,后面的日志格式中的%(name)s 对应的是这里的模块名字
- 设置级别 logger.setLevel(logging.DEBUG),Logging中有NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL这几种级别,日志会记录设置级别以上的日志
- Handler,常用的是StreamHandler和FileHandler,windows下你可以简单理解为一个是console和文件日志,一个打印在终端的窗口上,一个记录在一个文件上
- 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。可能没有 - 记录 使用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和属性即可