【问题标题】:Cleaning up nested Try/Excepts清理嵌套的 Try/Excepts
【发布时间】:2009-07-13 01:51:59
【问题描述】:

我刚刚编写了一段代码,让我觉得嵌套比最优代码要多得多。我想请教一下如何改进它的风格,特别是让它更符合“平面比嵌套更好”。

for app in apps:
    if app.split('.', 1)[0] == 'zc': #only look for cron in zc apps
        try:
            a = app + '.cron'
            __import__(a)
            m = sys.modules[a]

            try:
                min = m.cron_minute()
                for job in min:
                    k.add_interval_task(job[0], 'minute task', r(M_LB, M_UB),
                                        60*job[1], 
                                        kronos.method.threaded, (), ())
            except AttributeError: #no minute tasks
                pass

            try:
                hour = m.cron_hour()
                for job in hour:
                    k.add_daytime_task(job[0], 'day task', range(1, 8), None,
                                       (job[1], r(H_LB, H_UB)), 
                                       kronos.method.threaded, (), ())
            except AttributeError: #no hour tasks
                pass

        except ImportError: #no cron jobs for this module
            pass

编辑: 结合下面的建议,这是我重写的表格。

for app in apps:
    if app.split('.', 1)[0] != 'zc': #only look for cron in zc apps
        continue

    try:
        a = app + '.cron'
        __import__(a)
    except ImportError: #no cron jobs for this module, continue to next one
        continue

    m = sys.modules[a]
    if hasattr(m, 'cron_minute'):
        min = m.cron_minute()
        for job in min:
            k.add_interval_task(job[0], 'minute task', r(M_LB, M_UB),
                                60*job[1], 
                                kronos.method.threaded, (), ())

    if hasattr(m, 'cron_hour'):
        hour = m.cron_hour()
        for job in hour:
            k.add_daytime_task(job[0], 'day task', range(1, 8), None,
                               (job[1], r(H_LB, H_UB)), 
                               kronos.method.threaded, (), ())

【问题讨论】:

    标签: python


    【解决方案1】:

    主要问题是你的 try 子句太宽泛了,尤其是最外面的那个:有了这种习惯,你迟早会遇到一个神秘的错误,因为你的一个 try/except 不小心隐藏了一个意外的异常“从你正在调用的其他函数中冒泡”。

    所以我建议:

    for app in apps:
        if app.split('.', 1)[0] != 'zc': #only look for cron in zc apps
            continue
    
        try:
            a = app + '.cron'
            __import__(a)
        except ImportError: #no cron jobs for this module
            continue
    
        # etc etc
    

    顺便说一句,我还以另一种方式(不依赖于任何尝试/除外)应用“平面优于嵌套”,即“如果我在循环的这一腿上没有更多事情可做,请继续 [即进入循环的下一段]而不是“如果我有事要做:”然后是大量的嵌套代码。我一直更喜欢这种风格(if/continue 或 if/return)而不是嵌套if 使用提供功能的语言,例如 continue(基本上所有现代语言,因为 C 有它;-)。

    但这是一个简单的“扁平 vs 嵌套”样式偏好,问题的核心是:保持你的 try 子句小!最坏的情况是,当您不能简单地继续或在 except 子句中返回时,您可以使用 try/except/else:仅在 try 子句中放入绝对必须存在的内容——可能和预期的一小段代码raise -- 并将以下代码的其余部分(不应该也不预期会提高的部分)放在 else 子句中。这不会改变嵌套,但在降低意外隐藏意外异常的风险方面确实有很大的不同!

    【讨论】:

    • 谢谢,这正是我需要的。我忘记了以这种方式使用 continue 。您关于保持尝试尽可能小的建议是有道理的。使用 continue 允许我这样做。重写后的形式更加明确。
    【解决方案2】:

    我想知道,如果每个时间单位的作业实际上都被破坏了,它们会引发 AttibuteError 或其他一些异常吗?尤其是,如果某项工作确实被破坏了,你可能不会抓住它们。

    另一个有用的选择是只用 try-catch 包装有问题的代码,使异常处理程序尽可能靠近异常。这是一个刺:

    for app in apps:
        if app.split('.', 1)[0] == 'zc': #only look for cron in zc apps
            try:
                a = app + '.cron'
                __import__(a)
                m = sys.modules[a]
            except ImportError: #no cron jobs for this module
                                #exception is silently ignored
                                #since no jobs is not an error
                continue
            if hasattr(m, "cron_minute"):
                min = m.cron_minute()
                for job in min:
                    k.add_interval_task(job[0], 'minute task', r(M_LB, M_UB),
                                        60*job[1], 
                                        kronos.method.threaded, (), ())
    
            if hasattr(m, "cron_hour"):
                hour = m.cron_hour()
                for job in hour:
                    k.add_daytime_task(job[0], 'day task', range(1, 8), None,
                                       (job[1], r(H_LB, H_UB)), 
                                       kronos.method.threaded, (), ())
    

    注意这里只有一个异常处理程序,我们通过正确忽略来处理它。 因为我们可以预测不存在一个或另一个属性的可能性,我们 明确地检查它,这有助于使代码更清晰。否则,它不是 很明显为什么你会捕捉到 AttributeError,或者是什么引发了它。

    【讨论】:

    • 啊,谢谢!在这里使用 hasattr() 比 try/except 更简洁。
    【解决方案3】:

    您可以创建一个执行主要逻辑的函数,以及另一个调用该函数的函数,并用 try...except 语句包装它。然后,在主应用程序中,您可以调用那些已经处理异常的函数。 (基于“清洁代码”一书的建议)。

    【讨论】:

    • 此代码只被调用一次。我可以编写几个函数来完成它,但这实际上只是生成意大利面条代码,而不是真正改进布局。
    【解决方案4】:

    嗯,这里的诀窍是弄清楚它们是否坏了。这是异常处理处理部分。我的意思是,至少打印一条说明评论假设的警​​告。在实际处理之前担心过度嵌套似乎是你超前了。在时尚之前担心是正确的。

    【讨论】:

    • 您误解了代码的作用。如果异常被捕获,它应该什么都不做——如果正在检查的东西不存在(事实上,在大多数情况下它们不会存在),这不是错误。我不希望为找不到可选函数的每个实例打印警告。
    猜你喜欢
    • 1970-01-01
    • 2020-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多