格式化,如果尝试除了else代码块

时间:2014-02-11 14:27:42

标签: python layout readability

我正在编写一个包含许多步骤和大量数据的数据分析程序。有时我想在途中保存泡菜,有时候不是。我将把这些保存称为“检查点”。

如果pickle文件是可读的,并且全局var PICKLETrue,我可以跳过一些分析步骤。布局代码的一种愚蠢但冗长的方式是这样的:

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()

现在每个“检查点”都会重复更少的代码,但是这个问题非常难看,通过将所有函数放在最顶层,并将所有流控制和检查点结构代码放在底部,阅读代码需要很多上下跳跃。此外,这些示例中的所有代码都会针对我想要创建的每个检查点重复,并且它们都具有完全相同的结构。

我不禁想到这里有一个优雅的解决方案,只需要少量的重复代码,而且大部分都是可读的。

3 个答案:

答案 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循环会让你不再重复代码。