编辑
自从首次发布这个答案后,我制作了一个pypi包https://pypi.org/project/jupyter-save-load-vars。可以通过以下方式进行安装
pip install jupyter-save-load-vars
由于dill.dump_session在无法pickle化的对象上失败,并且显然无法配置为简单地忽略这些对象,因此我编写了一对静态函数savevars(file)和loadvars(),它们位于this ne1 library中,用于我们的神经形态工程jupyter笔记本类芯片练习。
这种方法基于有用的帖子Get locals from calling namespace in Python
使用示例:
from ne1 import savevars,loadvars
a=1
b=[2,3]
c='string'
o=(i for i in [])
savevars('testvars')
del a,b,c
loadvars('testvars')
print(a)
print(b)
print(c)
输出:
[INFO]: 2023-10-02 08:28:32,878 - NE1 - saved to testvars.dill variables [ a b c ] (File "/home/ne1/CoACH-labs/ne1.py", line 132, in savevars)
[WARNING]: 2023-10-02 08:28:32,879 - NE1 - could not pickle: ['o'] (File "/home/ne1/CoACH-labs/ne1.py", line 134, in savevars)
[INFO]: 2023-10-02 08:28:32,881 - NE1 - from testvars.dill loaded variables ['a', 'b', 'c'] (File "/home/ne1/CoACH-labs/ne1.py", line 158, in loadvars)
1
[2, 3]
string
为了完整地使用下面的代码,我还提供了一个自定义的日志记录器,用于格式化日志消息。如果你不想要这个日志输出,可以将
log.XXX(替换为
print(。
savevars(filename)函数:
def savevars(filename):
"""
saves all local variables to a file with dill
:param filename: the name of the file. The suffix .dill is added if there is not a suffix already.
"""
if filename is None:
log.error('you must supply a filename')
return
from pathlib import Path
p=Path(filename)
if p.suffix=='':
p = p.parent / (p.name + _DILL)
import inspect,dill
locals=None
frame = inspect.currentframe().f_back
try:
locals = frame.f_locals
finally:
del frame
if locals is None: return
data={}
could_not_pickle=[]
from types import ModuleType
s=f'saved to {p} variables [ '
for k,v in locals.items():
if k.startswith('_') or k=='tmp' or k=='In' or k=='Out' or hasattr(v, '__call__') \
or isinstance(v,ModuleType) or isinstance(v,logging.Logger):
continue
try:
if not dill.pickles(v):
could_not_pickle.append(k)
continue
except:
could_not_pickle.append(k)
continue
s=s+k+' '
data[k]=v
s=s+']'
try:
with open(p,'wb') as f:
try:
dill.dump(data,f)
log.info(f'{s}')
if len(could_not_pickle)>0:
log.warning(f'could not pickle: {could_not_pickle}')
except TypeError as e:
log.error(f'\n Error: {e}')
except Exception as e:
log.error(f'could not save data to {p}')
loadvars() 函数
def loadvars(filename):
""" Loads variables from file into the current workspace
:param filename: the dill file to load from, e.g. lab1. The suffix .dill is added automatically unless there is already a suffix.
This function loads the variables found in filename into the parent workspace.
"""
import dill
from pathlib import Path
p=Path(filename)
if p.suffix=='':
p = p.parent / (p.name + _DILL)
if not p.exists:
log.error(f'{p} does not exist')
return
try:
with open(p,'rb') as f:
data=dill.load(f)
log.info(f'from {p} loaded variables {list(data.keys())}')
import inspect
try:
frame = inspect.currentframe().f_back
locals = frame.f_locals
for k in data:
try:
locals[k] = data[k]
except Exception as e:
log.error(f'could not set variable {k}')
finally:
del frame
except Exception as e:
log.error(f'could not load; got {e}')
自定义的日志记录器,具有格式化和代码行超链接,在PyCharm中可正常工作。
import logging
_LOGGING_LEVEL = logging.DEBUG
class CustomFormatter(logging.Formatter):
"""Logging Formatter to add colors and count warning / errors"""
grey = "\x1b[2;37m"
yellow = "\x1b[33;21m"
cyan = "\x1b[0;36m"
green = "\x1b[31;21m"
red = "\x1b[31;21m"
bold_red = "\x1b[31;1m"
light_blue = "\x1b[1;36m"
blue = "\x1b[1;34m"
reset = "\x1b[0m"
format = '[%(levelname)s]: %(asctime)s - %(name)s - %(message)s (File "%(pathname)s", line %(lineno)d, in %(funcName)s)'
FORMATS = {
logging.DEBUG: grey + format + reset,
logging.INFO: cyan + format + reset,
logging.WARNING: red + format + reset,
logging.ERROR: bold_red + format + reset,
logging.CRITICAL: bold_red + format + reset
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record).replace("\\", "/")
def get_logger():
""" Use get_logger to define a logger with useful color output and info and warning turned on according to the global LOGGING_LEVEL.
:returns: the logger.
"""
logger = logging.getLogger('NE1')
logger.setLevel(_LOGGING_LEVEL)
if len(logger.handlers)==0:
ch = logging.StreamHandler()
ch.setFormatter(CustomFormatter())
logger.addHandler(ch)
return logger
log=get_logger()