我发现自己经常会遇到这样的问题。我有一个函数
def compute(input):
result = two_hour_computation(input)
result = post_processing(result)
return result
并且post_processing(result)
失败。现在要做的显而易见的事情是将函数更改为
import pickle
def compute(input):
result = two_hour_computation(input)
pickle.dump(result, open('intermediate_result.pickle', 'wb'))
result = post_processing(result)
return result
但是我通常不记得我所有的函数都是这样写的。我希望我有一个像这样的装饰器:
@return_intermediate_results_if_something_goes_wrong
def compute(input):
result = two_hour_computation(input)
result = post_processing(result)
return result
有这样的东西吗?我在谷歌上找不到。
1条答案
按热度按时间4ioopgfo1#
函数的"外部"在运行时无论如何都不能访问函数内部局部变量的状态,所以这不能用装饰器来解决。
无论如何,我认为捕捉错误和保存有价值的中间结果的责任应该由程序员显式地完成,如果你"忘记"做这件事,那对你来说一定不是那么重要。
也就是说,类似于 "在A、B或C引发异常的情况下执行X" 的情况是上下文管理器的典型用例。您可以编写自己的上下文管理器,充当中间结果的桶(代替变量),并在异常退出时执行一些
save
操作。大概是这样的
显然,您可以这样做,而不是将
print(f"saved {self.value}!")
放在save
中:现在,您需要记住的是将这些操作 Package 在
with
-语句中,并将中间结果赋给上下文管理器的value
属性。输出:
正如您所看到的,中间计算值
2.0
被"保存"了,即使下一个函数引发了异常。值得注意的是,在这个例子中,上下文管理器只在遇到异常时才调用
save
,而不是在上下文"和平"退出时调用。当然,如果你愿意,你可以将此设置为无条件的。这可能不像仅仅在函数上添加装饰器那么方便,但是它完成了工作。而且IMO事实上,你仍然必须有意识地在这个上下文中 Package 你的重要行动是一件好事,因为它教会你特别注意这些事情。
顺便说一句,这是在Python中实现数据库事务之类的典型方法(例如,在SQLAlchemy中)。
PS
公平地说,我可能应该稍微限定一下我的初始语句。当然,你 * 可以 * 在你的函数中只使用non-localstate,尽管通常有充分的理由不鼓励这样做。简单地说,如果在你的例子中
result
是一个全局变量(并且您在函数中声明了global result
),事实上,这个问题可以通过一个装饰器来解决,但是我不推荐这种方法,因为全局状态是一个反模式。(而且它仍然要求您记住每次使用为该作业指定的任何全局变量。)