项目开发规范、time模块、日志
一. 昨日内容回顾
自定义模块
import tbjx
1.创建一个以 tbjx 命名的名称空间
2.执行此模块的代码,并将所有内容加载到内存
3.调用此模块的代码要通过 tbjx. 的方式
改名:
import time as t
1.使代码更简洁
2.优化代码
import a
import b
import c
from tbjx import name,read1
1.创建一个以 tbjx 命名的名称空间
2.执行此模块的代码,并将所有内容加载到内存
3.直接将 name read1 加载到当前文件的全局中
好处:使用方便
缺点:容易与本文件同名的变量,函数冲突
from tbjx import functools as func
from tbjx import *
1.创建一个以 tbjx 命名的名称空间
2.执行此模块的代码,并将所有内容加载到内存
3.直接将 tbjx 模块中所有的内容全部复制一份到当前文件中
__all__ = ["name", "read1"]
文件的使用:
1.当脚本或者代码块,运行本文件的所有代码
2.当作一个模块,被其他模块调用
__name__
本文件使用,相当于 __main__
其他文件,__name__ == "被调用的模块名"
__name__ == "__main__":
1.可以在本模块下,测试自己的代码
2.项目的执行文件使用
解释器是按照一定的顺序和位置去寻找模块
搜索路径:内存--->内置模块--->sys.path[此列表的第一个元素是当前文件的路径]
想在不同的文件夹下调用模块?
将模块的路径添加到sys.path中
import sys
print(sys.modules)
程序一运行,解释器会把所有这些 modules 先加载到内存中
二.项目开发规范
代码不可能只写在一个文件中,否则调整和修改都很不方便,所以要将代码规范化:
目录规范化,代码规范化(PEP8)
目录规范化的好处?
1.项目结构清晰,便于查找
2.对项目修改时,易于调试
# 三.time模块,datetime模块 import time print(time.time()) print(time.sleep(1)) # time模块提供的时间有三种模式 # 第一种形式 # time.time() 时间戳——从1970.1.1 0:00距今的时间,供计算机使用 import time print(time.time()) # 第二种形式(格式化时间) # 2019/1/8 10:39 strftime() import time ft = time.strftime("%Y-%m-%d") ft = time.strftime("%Y/%m/%d") ft = time.strftime("%Y/%m/%d %I%M%S") ft = time.strftime("%Y{}%m{}%d %I:%M:%S").format("年", "月", "日") print(ft) # 2019年01月08 10:55:19 # 第三种形式:结构化时间以元组的形式存在 # 作为格式化时间与时间戳进行转化的中转 import time struct_time = time.localtime() print(struct_time) # time.struct_time(tm_year=2019, tm_mon=1, tm_mday=8, tm_hour=10, # tm_min=56, tm_sec=56, tm_wday=1, tm_yday=8, tm_isdst=0) print(struct_time[0]) # 2019 print(struct_time.tm_year) # 2019 # 三种格式之间的转化 # 时间戳 ---> 格式化时间 import time timestamp = time.time() # 先将时间戳转化成结构化时间 st = time.localtime(timestamp) ft = time.strftime("%Y{}%m{}%d %I:%M:%S", st).format("年", "月", "日") print(ft) # 2019年01月08 11:00:59 # 格式化时间 ---> 时间戳 import time ft = time.strftime("%Y/%m/%d %I:%M:%S") # 先将格式化时间转化成结构化时间 st = time.strptime(ft, "%Y/%m/%d %I:%M:%S") print(st) # time.struct_time(tm_year=2019, tm_mon=1, tm_mday=8, tm_hour=11, tm_min=3, tm_sec=58, tm_wday=1, tm_yday=8, tm_isdst=-1) # 另一种转化方式 # 结构化时间 ---> 格式化时间 格式固定 import time strut_time = time.localtime() st = time.asctime(strut_time) print(st) # Tue Jan 8 11:10:57 2019 # 时间戳 ---> 格式化时间 格式固定 import time print(time.ctime(15000000000)) # Mon May 1 10:40:00 2445
# 时间差的计算 # 2019 1 8 11 # 2016 9 16 12 # 第一步,将这两个格式化时间转化成时间戳 import time now_time = time.strftime("%Y/%m/%d %H:%M:%S") now_timestamp = time.mktime(time.strptime(now_time, "%Y/%m/%d %H:%M:%S")) old_time = time.strftime("2016/9/16 12:00:00") old_timestamp = time.mktime(time.strptime(old_time, "%Y/%m/%d %H:%M:%S")) dif_time = now_timestamp - old_timestamp # print(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(dif_time))) struct_time = time.gmtime(dif_time) msg = "过去了%d年%d月%d天%d小时%d分钟%d秒" % (struct_time.tm_year-1970,struct_time.tm_mon-1, struct_time.tm_mday-1,struct_time.tm_hour, struct_time.tm_min,struct_time.tm_sec) print(msg) # 过去了2年3月22天23小时29分钟55秒
# datetime import datetime now_time = datetime.datetime.now() print(now_time) # 2019-01-08 11:30:56.074414 print(now_time + datetime.timedelta(weeks=3)) # 三周前 print(now_time + datetime.timedelta(weeks=3)) # 三周后 # print(now_time + datetime.timedelta(years=3)) # 年没有 print(datetime.datetime.now() + datetime.timedelta(days=-3)) # 三天前 print(datetime.datetime.now() + datetime.timedelta(days=3)) # 三天后 print(datetime.datetime.now() + datetime.timedelta(hours=5)) # 5小时后 print(datetime.datetime.now() + datetime.timedelta(hours=-5)) # 5小时前 print(datetime.datetime.now() + datetime.timedelta(minutes=-15)) # 15分钟前 print(datetime.datetime.now() + datetime.timedelta(minutes=15)) # 15分钟后 print(datetime.datetime.now() + datetime.timedelta(seconds=-70)) # 70秒前 print(datetime.datetime.now() + datetime.timedelta(seconds=70)) # 70秒后 # 可以直接调整 print(now_time.replace(year=2010)) # 2010-01-08 11:34:20.476402 print(now_time.replace(month=10)) # 2019-10-08 11:35:10.009449 print(now_time.replace(year=2010, month=10, day=23)) # 2010-10-23 11:36:25.877321 # 将时间戳转化成时间 print(datetime.date.fromtimestamp(1232132131)) # 2009-01-17
四.日志
个性化推荐 —— 淘宝,京东,知乎,网易云音乐
只要做了记录,就可以当作广义的日志
开发中的日志
1.帮助调试代码 一个py文件中,使用print()太多,耗费性能大
2.代码警告,危险提示作用
3.对服务器的操作命令
4.重要的节点,需要日志提示,比如转账
# 日志分三种配置: # 低配——不能同时显示和写入 import logging logging.debug('debug message') # 调试模式 logging.info('info message') # 正常运转模式 logging.warning('warning message') # 警告模式 logging.error('error message') # 错误模式 logging.critical('critical message') # 致命模式 # WARNING:root:warning message # ERROR:root:error message # CRITICAL:root:critical message import logging while 1: try: num = input(">>>") int(num) except ValueError: logging.warning("输入了非数字元素,警告!") # 指定显示信息格式 import logging logging.basicConfig( level=logging.DEBUG, # 可以设置级别 把上面的注释掉,然后写下面的 # level=20, 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') # Tue, 08 Jan 2019 12:14:58 abc.py[line:214] DEBUG debug message # Tue, 08 Jan 2019 12:14:58 abc.py[line:215] INFO info message # Tue, 08 Jan 2019 12:14:58 abc.py[line:216] WARNING warning message # Tue, 08 Jan 2019 12:14:58 abc.py[line:217] ERROR error message # Tue, 08 Jan 2019 12:14:58 abc.py[line:218] CRITICAL critical message
默认情况下Python的logging模块将日志打印到了标准输出中
且只显示了大于等于WARNING级别的日志
这说明默认的日志级别设置为WARNING
日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG
默认的日志格式为 日志级别: Logger名称: 用户输出消息
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="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()中,显示是在Logger所属 filename 中
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为
1. level: 设置rootlogger的日志级别
2. format: 指定handler使用的日志显示格式
format参数中可能用到的格式化信息有以下内容:
%(name)s——Logger的名字
这里如果加上的话,显示是root
%(levelno)s——数字形式的日志级别
这里加上的话逐行显示10,20,30,40,50
%(levelname)s——文本形式的日志级别
%(pathname)s——调用日志输出函数的模块的完整路径名,可能没有
显示本文件的绝对路径
这里加上的话,会有乱码,因为路径中有中文字符
%(filename)s——调用日志输出函数的模块的文件名
这里是本文件名,123asda.py
%(module)s——调用日志输出函数的模块名
这里加上的话,是 123asda
%(funcName)s——调用日志输出函数的函数名
这里加上的话,因为从上面到这里都还没函数
所以显示是 <module>
%(lineno)d——调用日志输出函数的语句所在的代码行
即比如下面的 logging.debug("debug message")
显示是在本文件的第几行,这五个都会显示
%(created)f——当前时间,用Unix标准的表示时间的浮点数表示
这里加上的话,显示 1547727721.904610
%(relativeCreated)d——输出日志信息时间,自Logger创建以来的毫秒数
%(asctime)s——字符串形式的当前时间
比如这里加上显示:Thu, 17, Jan 2019 20:27:49
%(thread)d——线程ID,可能没有
这里加上显示:7536
%(threadName)s——线程名,可能没有
这里加上显示:MainThread
%(process)d——进程ID,可能没有
这里加上显示:7692
%(messages)s——用户输出的消息
3. datefmt: 指定 format 中 asctime 的显示格式
4. filename: 用指定的文件名创建FileHandler,这样日志会被存储在指定的文件中
5. filemode: 文件打开方式,在指定了 filename 时使用这个参数,默认为"a"
6. stream: 用指定的stream创建StreamHandler, 可以指定输出到sys.stderr,
sys.stdout或者文件(f=open("test.log","w")), 默认为sys.stderr.
如果同时列出了filename和stream两个参数,stream会被忽略
import logging # 创建一个logger对象 logger = logging.getLogger() # 创建一个handler,用于写入日志文件 fh = logging.FileHandler("test.log", encoding="utf-8") # 设置级别 fh.setLevel(logging.DEBUG) # 这里 DEBUG 改为 INFO 在 test.log中也只显示 WARNING 以上级别的信息 # 再创建一个handler, 用于输出到控制台 ch = logging.StreamHandler() # 设置显示格式,并将此格式传到两个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.debug("logger debug message") logger.info("logger info message") logger.warning("logger warning message") logger.error("logger error message") logger.critical("logger critical message") # 与本文件同一个目录下,还会创建一个 test.log文件,输出信息和下面的一样 # 2019-01-17 21:01:52,674 - root - WARNING - logger warning message # 2019-01-17 21:01:52,674 - root - ERROR - logger error message # 2019-01-17 21:01:52,674 - root - CRITICAL - logger critical message
# 标配 import logging # 1.产生 logger 对象 logger = logging.getLogger() # 2.产生其他对象(屏幕对象,文件对象) sh = logging.StreamHandler() fh1 = logging.FileHandler("staff.log", encoding="utf-8") fh2 = logging.FileHandler("boss.log", encoding="utf-8") # 3.设置显示格式 formater = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s') formater1 = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s') formater2 = logging.Formatter('%(asctime)s-%(message)s') # 4.给对象绑定格式 sh.setFormatter(formater) fh1.setFormatter(formater1) fh2.setFormatter(formater2) # 5.给 logger 对象绑定其他对象 logger.addHandler(sh) logger.addHandler(fh1) logger.addHandler(fh2) # 6.设置显示级别 # 其他对象的级别要高于 logger 的级别 logger.setLevel(40) sh.setLevel(20) fh1.setLevel(30) fh2.setLevel(50) logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') # 2019-01-08 12:40:42,240-root-ERROR-error message # 2019-01-08 12:40:42,240-root-CRITICAL-critical message
logging库提供了多个组件:Logger, Handler, Filter, Formatter
Logger对象提供应用程序可直接使用的接口
Handler发送日志到适当的目的地
Filter提供了过滤日志信息的方法
logger的配置文件
上面方式是通过logger对象配置完成日志功能,需要创建各种对象
下面是以字典方式创建logger配置文件,也是实际工作中的做法,相当于一个模板
import os import logging.config # 定义日志输出格式 standard_format = "[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s]" \ "[%(filename)s:%(lineno)d][%(levelname)s][%(message)s]" simple_format = "[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d][%(message)s]" id_simple_format = "[%(levelname)s][%(asctime)s][%(message)s]" # 设置 log 文件路径 logfile_dir = os.path.dirname(os.path.abspath(__file__)) # 设置 log 文件名 logfile_name = "test.log" # 如果不存在定义的日志目录就创建一个 if not os.path.isdir(logfile_dir): os.mkdir(logfile_dir) # log配置字典 LOGGING_DIC = { "version": 1, "disable_existing_loggers": False, "formatters": { "standard": { "format": standard_format }, "simple": { "format": simple_format } }, "filters": {}, # 打印到屏幕的日志 "screen": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "simple" }, # 打印到文件的日志,收集 INFO 及以上的日志 "file":{ "level": "DEBUG", "class": "logging.handlers.RotatingFileHandler", # 保存到文件 "formatter": "standard", "filename": logfile_path, # 日志文件 "maxBytes": 1024*1024*5, # 日志大小 5M "backupCount": 5, "encoding": "utf-8", # 日志文件的编码,不用担心中文乱码的问题 }, "loggers": { # logging.getLogger(__name__)拿到的logger配置 "": { # 把上面定义的两个handler都加上,即log数据即写入文件又打印到屏幕 "handlers": ["screen", "file"], # 总级别,一定要设置最低的,即 DEBUG "level": "DEBUG", # 向上(更高level的logger)传递 "propagate": True, }, }, } def load_my_logging_cfg(): # 导入上面定义的logging配置 logging.config.dictConfig(LOGGING_DIC) logger = logging.getLogger("转账业务") logger.info("It works!") # 记录该文件的运行状态 if __name__ == "__main__": load_my_logging_cfg()