util_logger.py 2022-09-13 笔记,技巧 暂无评论 1032 次阅读 ``` import logging import os import atexit import traceback import time import json import pickle #=============================================================# # 接口 # #-------------------------------------------------------------# # path_this 当前绝对路径 # # namespace 当前文件夹名称 # # getLogger(name) 获取一个Logger # #-------------------------------------------------------------# path_this = os.path.dirname(os.path.abspath(__file__)) namespace = path_this[len(os.path.dirname(path_this))+1:] def getLogger(name): logging.getLogger(name).addHandler(file_handler) return logging.getLogger(name) disable = lambda name: logging.getLogger(name).setLevel(logging.WARNING) #=============================================================# # 配置Logger #-------------------------------------------------------------# logging.basicConfig(level=logging.DEBUG, format='%(name)-12s: %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S') file_handler = logging.FileHandler(filename=f'{namespace}.log',mode='a',encoding='utf-8') formatter = logging.Formatter('%(asctime)s|%(name)-12s|%(levelname)-8s|%(message)s') file_handler.setFormatter(formatter) logger = getLogger('util_logger') #=============================================================# # 自动设置窗口标题(Windows)或进程名(Linux) # #-------------------------------------------------------------# # 提示一:Windows下,关闭指定窗口 # # taskkill /FI "WINDOWTITLE eq <窗口标题>" # # # # 提示二:Linux下,关闭指定进程 # # pkill <进程名> # #-------------------------------------------------------------# if os.name=='nt': os.system(f"title {namespace}") else: try: import setproctitle setproctitle.setproctitle(namespace) except Exception as e: logger.warning(e) #=============================================================# # 日志记录程序的启动与退出 # #-------------------------------------------------------------# logger.info(" Program Start. ".center(32,'-')) @atexit.register def program_exit(): logger.info(" Program Exit. ".center(32,'-')) #=============================================================# # 修饰器:函数异常返回默认值,不影响程序继续执行,保存错误日志# #-------------------------------------------------------------# # 例:让divide(a,b)函数不会因异常导致整个程序退出 # # # # @util_logger.go_on_with(float('inf')) # # def divide(a,b): # # return a/b # # # #-------------------------------------------------------------# from functools import wraps def go_on_with(default=None,debug=True): """修饰一个函数,当被修饰的函数出现异常,则替它返回默认值,并记录错误日志。这样不会因它的异常而导致整个程序退出。 Args: default (any, optional): 出现异常时返回的默认值. Defaults to None. debug (bool, optional): 是否记录错误日志. Defaults to True. """ def _go_on(func): @wraps(func) def wrapper(*args,**kwargs): try: return func(*args,**kwargs) except Exception as e: trace = f"\n{traceback.format_exc()}" if debug else "" logger.warning(f"Exception occured by: {func.__name__}({','.join([str(x) for x in args]+[f'{k}={v}' for k,v in kwargs.items()])}) (default return {default}){trace}") return default return wrapper return _go_on #=============================================================# # 时间工具 #-------------------------------------------------------------# num2time = lambda t=None: time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(t if t else time.time()))) time2num = lambda t=None: int(time.mktime(time.strptime(t, "%Y-%m-%d %H:%M:%S")) if t else time.time()) #=============================================================# # 路径操作 #-------------------------------------------------------------# # safe_filename(filename):去除文件名的非法符号\\/:*?"<>| # # path_dup(path):获得一个“不会覆盖原有文件”的绝对路径(DUPlicate) # 例:path_dup('data.csv') # 如果该目录下没有`data.csv`,则会返回`.../data.csv` # 如果该目录下已有`data.csv`,则会返回`.../data_000.csv` # 如果该目录下已有`data_000.csv`,则会返回`.../data_001.csv` # 如果该目录下已有`data_001.csv`,则会返回`.../data_002.csv` # 以此类推,最高到`data_9999.csv`。输入也可以是文件夹。 # 这是用Lambda+Iterator实现的,比函数+循环实现的效率高。 # # path_tsp(path):获得该路径文件(夹)的修改时间(TimeStamP) # 例:path_tsp('util_logger.py') # 返回`20220813-114738`,格式是`年月日-时分秒` # 如果路径文件不存在,返回相同格式的当前时间。 # # path_tbk(path):获得该路径文件夹的版本戳 # 例:path_tbk('data.csv') # 返回`data_20220812-151244.csv` # 如果`data_20220812-151244.csv`已存在,则会返回`data_20220812-151244_000.csv` # # To_Backup(path):给文件(夹)重命名,以防止被覆盖。(To BackUp) # 例:path_tbk('data.csv') # 将`data.csv`重命名成了`data_20220812-151244.csv` # 如果`data.csv`不存在,无操作。两种情况都返回None。 # # path_de_stamp(path):去除文件的版本戳 #-------------------------------------------------------------# safe_filename = lambda filename,r='_': filename.translate(str.maketrans('\\/:*?"<>|',r*9)).strip() path_dup = lambda p: os.path.abspath(p) if not os.path.exists(p) else next(filter(lambda i:not os.path.exists(i),map(lambda i:f'{os.path.splitext(os.path.abspath(p))[0]}_{i:03}{os.path.splitext(p)[1]}',range(10000)))) path_tsp = lambda p=None: time.strftime("%Y%m%d-%H%M%S", time.localtime(os.path.getmtime(p) if p!=None and os.path.exists(p) else time.time())) path_tbk = lambda p: path_dup(f'{os.path.splitext(p)[0]}_{path_tsp(p)}{os.path.splitext(p)[1]}') To_Backup = lambda p: os.rename(p,path_tbk(p)) if os.path.exists(p) else None def path_de_stamp(p): """路径去版本戳(把形如_20220812-151244_001的版本戳去掉)""" l,e = p.rfind('_'),p.rfind('.') if e<0: e=len(p) r = l if p[l+1:e].isdigit() else e if r>16 and p[r-16]=='_' and p[r-7]=='-' and p[r-6:r].isdigit() and p[r-15:r-8].isdigit(): return p[:r-16]+p[e:] return p #=============================================================# # 读写对象(默认JSON,如果后缀是.pkl则保存pickle对象) #-------------------------------------------------------------# def saveobj(path_file,obj,indent=4): """保存对象(默认JSON,如果后缀是.pkl则保存pickle对象) Args: obj (dict): 字典对象 path_file (str): 保存文件路径 indent (int, optional): 缩进几个空格. Defaults to 4. """ if path_file.endswith('.pkl'): with open(path_file,'wb') as f: pickle.dump(obj,f) else: with open(path_file,'w',encoding='utf-8') as f: json.dump(obj,f,ensure_ascii=False,indent=indent) def loadobj(path_file): """读取对象(默认JSON,如果后缀是.pkl则保存pickle对象) Args: path_file (str): 要读取的对象文件路径 Returns: dict: 读取到的对象。如果文件不存在,返回空字典。 """ try: with open(path_file,'rb') as f: return pickle.load(f) if path_file.endswith('.pkl') else json.load(f) except Exception: pass return {} import collections class openobj(collections.UserDict): """带with和save的字典(dict)类,可以代替loadobj使用。 Example: (最佳用法) with openobj('test.json') as obj: # 像打开文件一样打开对象 print(obj) # 离开with时自动保存 obj['a']="中文" # 读写对象 注意: 不能直接给obj赋值,如obj={...},必须obj[...]={...} 或者obj.update({...}),不然保存不了。 Args: path_file (str): 保存路径,后缀可以是.json或.pkl。 如果要保存json不支持的内容(如Python对象,后缀就必须是.pkl """ def __init__(self,path_file): self.path_file = path_file self.data = loadobj(path_file) def __enter__(self): return self.data def __exit__(self,exc_type,exc_value,traceback): saveobj(self.path_file + ('.unsaved.pkl' if exc_type else ''),self.data) def save(self): saveobj(self.path_file,self.data) if __name__=='__main__': with openobj('test.json') as obj: # 像打开文件一样打开对象 print(obj) # 离开with时自动保存 obj['a']="中文" # 读写对象 ``` 标签: none 本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭