我正在编写一个包含许多步骤和大量数据的数据分析程序。有时我想在途中保存泡菜,有时候不是。我将把这些保存称为“检查点”。
如果pickle文件是可读的,并且全局var PICKLE
是True
,我可以跳过一些分析步骤。布局代码的一种愚蠢但冗长的方式是这样的:
if PICKLE:
try:
with open('pickle1.pkl', 'rb') as f:
data1 = pickle.load(f)
except:
# do things to generate data1
temp = step1()
data1 = step2(temp)
with open('pickle1.pkl', 'wb') as f:
pickle.dump(data1, f)
else:
# do things to generate data1
temp = step1()
data1 = step2(temp)
这只是我分析中许多人的“检查点”,而进入这些“检查点”通常需要的不仅仅是两个步骤。因此,如上所述布置我的代码会产生大量重复的代码。
我可以通过将功能放入功能中来略微改进,但为了强调丑陋,我将展示2个检查点:
def generateData1():
# do things
return data1
def generateData2():
# do things
return data2
if PICKLE:
try:
with open('pickle1.pkl', 'rb') as f:
data1 = pickle.load(f)
except:
data1 = generateData1()
with open('pickle1.pkl', 'wb') as f:
pickle.dump(data1, f)
else:
data1 = generateData1()
if PICKLE:
try:
with open('pickle2.pkl', 'rb') as f:
data2 = pickle.load(f)
except:
data2 = generateData2()
with open('pickle2.pkl', 'wb') as f:
pickle.dump(data2, f)
else:
data2 = generateData2()
现在每个“检查点”都会重复更少的代码,但是这个问题非常难看,通过将所有函数放在最顶层,并将所有流控制和检查点结构代码放在底部,阅读代码需要很多上下跳跃。此外,这些示例中的所有代码都会针对我想要创建的每个检查点重复,并且它们都具有完全相同的结构。
我不禁想到这里有一个优雅的解决方案,只需要少量的重复代码,而且大部分都是可读的。
答案 0 :(得分:5)
为什么不将它进一步提取到函数中以避免所有重复代码?
def pickle_function(pickle_filename, data_function):
with open(pickle_filename, 'wb') as f:
try:
data = pickle.load(f)
except:
data = data_function()
pickle.dump(data, f)
if PICKLE:
pickle_function('pickle1.pkl', generateData1)
# Some intermediate logic before next 'checkpoint'
if PICKLE:
pickle_function('pickle2.pkl', generateData2)
此外,我不确定您在打开文件时遇到的Exception
是什么,因此如果文件可能不存在,您可能需要重新组织。捕获特定的异常(例如except FileNotFoundError:
)总是一个好主意,以便大声提出任何意外行为。
答案 1 :(得分:1)
装饰师怎么样:
import os
import pickle
import functools
PICKLE = False
PICKLE_PATH = '/tmp'
def checkpoint(f):
if not PICKLE:
return f
save_path = os.path.join(PICKLE_PATH, '%s.pickle' % f.__name__)
@functools.wraps(f)
def wrapper(*args, **kwargs):
if os.path.exists(save_path):
with open(save_path, 'rb') as f:
return pickle.load(f)
rv = f(*args, **kwargs)
with open(save_path, 'wb') as f:
pickle.dump(rv, f)
return rv
return wrapper
用法:
@checkpoint
def step1():
return do_stuff_here()
def intermediate_step():
return some_operation(step1())
@checkpoint
def step2():
return do_stuff_with(intermediate_step())
# ... and so on
答案 2 :(得分:0)
您也可以使用while
语法而不是重复if/else
来摆脱代码重复。
因此,作为一个不一定打算通知您工作流程的基本示例,您可以使用一个函数来处理有关数据的操作。
def change_data(previousdata, iteration):
if iteration == 0:
##some change
return new_value
elif iteration == 1:
##some other change
return new_value
…
elif iteration = total_needed ##however many different tests there are
indicate_doneness() ##whatever this means for you
你有那些建议'从pickle加载,或者创建数据并将其转储'功能。
def pickle_or_dont(args):
try: ##the suggested code from other answers
然后设置一个while循环来跟踪已完成的迭代次数以及您所处的“阶段”。这消除了重复代码的需要。
total_needed = 7 ##or however many
data_generated = 0
while data_generated < total_needed:
my_data = change_data(my_data, data_generated)
pickle_or_dont(my_data)
data_generated += 1
我对你的预期操作顺序的感觉可能不正确,你会比我更清楚。但我认为while
循环会让你不再重复代码。