Python整洁的方式将单个语句包装在try except块中



我目前正在做一些Python自动化Excel与com。它功能齐全,能做我想做的事,但我发现了一些令人惊讶的事情。有时,我使用的一些Excel命令会异常失败,没有明显的原因。其他时候,它们会起作用。

在我所做的VB等效代码中,这个问题显然被认为是正常的,并被On Error Resume Next语句覆盖。当然,Python没有said语句。

我不能将整个集合包装在try except循环中,因为它可能在中途"失败"并且不能正确完成。那么,将几个独立语句包装到一个try except块中的python方法是什么呢?具体来说,要比:

更干净
try:
   statement
except:
   pass
try:
   statement
except:
   pass

对应代码为excel.Selection.Borders位。

def addGridlines(self, infile, outfile):
    """convert csv to excel, and add gridlines"""
    # set constants for excel
    xlDiagonalDown = 5
    xlDiagonalUp = 6
    xlNone = -4142
    xlContinuous = 1
    xlThin = 2
    xlAutomatic = -4105
    xlEdgeLeft = 7
    xlEdgeTop = 8
    xlEdgeBottom = 9
    xlEdgeRight = 10
    xlInsideVertical = 11
    xlInsideHorizontal = 12
            # open file
    excel = win32com.client.Dispatch('Excel.Application')
    workbook = excel.Workbooks.Open(infile)
    worksheet = workbook.Worksheets(1)
    # select all cells
    worksheet.Range("A1").CurrentRegion.Select()
    # add gridlines, sometimes some of these fail, so we have to wrap each in a try catch block
    excel.Selection.Borders(xlDiagonalDown).LineStyle = xlNone
    excel.Selection.Borders(xlDiagonalUp).LineStyle = xlNone
    excel.Selection.Borders(xlDiagonalUp).LineStyle = xlNone
    excel.Selection.Borders(xlEdgeLeft).LineStyle = xlContinuous
    excel.Selection.Borders(xlEdgeLeft).Weight = xlThin
    excel.Selection.Borders(xlEdgeLeft).ColorIndex = xlAutomatic
    excel.Selection.Borders(xlEdgeTop).LineStyle = xlContinuous
    excel.Selection.Borders(xlEdgeTop).Weight = xlThin
    excel.Selection.Borders(xlEdgeTop).ColorIndex = xlAutomatic
    excel.Selection.Borders(xlEdgeBottom).LineStyle = xlContinuous
    excel.Selection.Borders(xlEdgeBottom).Weight = xlThin
    excel.Selection.Borders(xlEdgeBottom).ColorIndex = xlAutomatic
    excel.Selection.Borders(xlEdgeRight).LineStyle = xlContinuous
    excel.Selection.Borders(xlEdgeRight).Weight = xlThin
    excel.Selection.Borders(xlEdgeRight).ColorIndex = xlAutomatic
    excel.Selection.Borders(xlInsideVertical).LineStyle = xlContinuous
    excel.Selection.Borders(xlInsideVertical).Weight = xlThin
    excel.Selection.Borders(xlInsideVertical).ColorIndex = xlAutomatic
    excel.Selection.Borders(xlInsideHorizontal).LineStyle = xlContinuous
    excel.Selection.Borders(xlInsideHorizontal).Weight = xlThin
    excel.Selection.Borders(xlInsideHorizontal).ColorIndex = xlAutomatic
    # refit data into columns
    excel.Cells.Select()
    excel.Cells.EntireColumn.AutoFit()
    # save new file in excel format
    workbook.SaveAs(outfile, FileFormat=1)
    workbook.Close(False)
    excel.Quit()
    del excel

:

可能需要对错误位进行一些解释。在我的测试机器上,使用相同的代码,在相同的文件上运行两个相同的程序,产生相同的结果。一次运行会为每一行xlInsideVertical抛出异常。另一个为每个xlInsideHorizontal抛出异常。最后,第三次运行完全没有异常。

据我所知,Excel认为这是正常的行为,因为我克隆的是由Excel的宏生成器构建的VB代码,而不是由人生成的VB代码。当然,这可能是一个错误的假设。

除了我想要更短更明显的代码块之外,它将每行都包装在try中,因为20行包装在自己的try catch循环中只是在自找麻烦。

更新2 :

这是一个经过清理的CSV文件,用于测试:结论

:

Vsekhar给出的答案是完美的。它抽象了异常抑制,以便以后,如果有时间,我可以在发生异常时实际处理异常。它还允许记录异常,因此它们不会消失,不会阻止其他异常,并且足够小,可以在六个月后轻松管理。

考虑抽象掉抑制。Aaron的观点是,一般情况下不要吞下异常。

class Suppressor:
    def __init__(self, exception_type):
        self._exception_type = exception_type
    def __call__(self, expression):
        try:
            exec expression
        except self._exception_type as e:
            print 'Suppressor: suppressed exception %s with content '%s'' % (type(self._exception_type), e)
            # or log.msg('...')

然后,在当前代码的回溯中注意所引发的异常,并为该异常创建一个Suppressor:

s = Suppressor(excel.WhateverError) # TODO: put your exception type here
s('excel.Selection.Borders(xlDiagonalDown).LineStyle = xlNone')

这样您可以逐行执行(因此您的回溯仍然是有帮助的),并且您只抑制您明确想要的异常。其他异常照常传播。

异常从不"无缘无故"发生。总有一个原因,这个原因需要解决。否则,您的程序将开始生成"随机"数据,而"随机"将受您隐藏的错误的支配。

当然,你需要一个解决问题的方法。以下是我的建议:

  1. 创建一个包装器类,实现你需要的所有方法,并将它们委托给真正的Excel实例。

  2. 在每个方法之前添加一个装饰器,将该方法包装在try except块中并记录异常。永远不要吞下异常

现在代码为您的客户工作了,这为您赢得了一些时间来找出问题的原因。我的猜测是a) Excel不产生有用的错误信息或b)包装器代码吞噬了真正的异常,让你在黑暗中或c) Excel方法返回一个错误代码(如"false"for"failed"),你需要调用另一个Excel方法来确定问题的原因是什么。

[EDIT]根据下面的评论,归结为"我的老板不在乎,我无能为力":你错过了一个关键点:你的老板有责任做决定,但有责任给她一份包括利弊的选项清单,这样她就能做出一个合理的决定。只是坐在那里说"我无能为力"会让你陷入你想要避免的麻烦。

的例子:

解决方案1:忽略错误

优点:最少的工作量缺点:结果数据有可能是错误的或随机的。如果重要的商业决策是基于它的,那么这些决策出错的风险就很高。

解决方案2:记录错误

优点:工作量小,用户可以快速开始使用结果,为找出问题的根源赢得时间反面观点:"如果你今天都不能把它修好,你凭什么认为明天还有时间把它修好?"而且,你可能要花很长时间才能找到问题的根源,因为你不是专家

解决方案3:咨询专家

找到该领域的专家,帮助他/她看看/改进解决方案。

优点:比自己学习COM的来龙去脉要快得多缺点:昂贵但成功的机会很大。也会发现我们甚至不知道的问题。

我想你看到了模式。老板之所以会做出错误的决定,是因为我们(心甘情愿地)任由他们这么做。世界上任何一个老板在必须做决定的时候,都乐于听取确凿的事实和意见(好吧,那些不愿意做老板的人不应该是老板,所以这是一个确定何时开始寻找新工作的方法)。

如果选择解决方案#2,则使用包装器方法。请参阅如何编写装饰器的文档(来自IBM的示例)。包装所有的方法只需要几分钟的工作,它会给你一些工作。

下一步是创建一个较小的示例,有时会失败,然后在这里发布有关Python, Excel和COM包装器的具体问题,以找出问题的原因。

[EDIT2]下面是一些代码,将"危险"部分包装在helper类中,使更新样式更简单:

class BorderHelper(object):
    def __init__(self, excel):
        self.excel = excel
    def set( type, LineStyle = None, Weight = None, Color = None ):
        border = self.excel.Selection.Borders( type )
        try:
            if LineStyle is not None:
                border.LineStyle = LineStyle
        except:
            pass # Ignore if a style can't be set
        try:
            if Weight is not None:
                border.Weight = Weight
        except:
            pass # Ignore if a style can't be set
        try:
            if Color is not None:
                border.Color = Color
        except:
            pass # Ignore if a style can't be set

用法:

    borders = BorderHelper( excel )
    borders.set( xlDiagonalDown, LineStyle = xlNone )
    borders.set( xlDiagonalUp, LineStyle = xlNone )
    borders.set( xlEdgeLeft, LineStyle = xlContinuous, Weight = xlThin, Color = xlAutomatic )
    ...

这只是包装函数调用,但您可以扩展它来处理属性访问,并代理嵌套属性访问的结果,最后只是包装__setattr__在您的try:except块。

在你的情况下,只吞下一些特定的异常类型可能是明智的(如@vsekhar所说)。

def onErrorResumeNext(wrapped):
    class Proxy(object):
        def __init__(self, fn):
            self.__fn = fn
        def __call__(self, *args, **kwargs):
            try:
                return self.__fn(*args, **kwargs)
            except:
                print "swallowed exception"
    class VBWrapper(object):
        def __init__(self, wrapped):
            self.wrapped = wrapped
        def __getattr__(self, name):
            return Proxy(eval('self.wrapped.'+name))
    return VBWrapper(wrapped)

的例子:

exceptionProofBorders = onErrorResumeNext(excel.Selection.Borders)
exceptionProofBorders(xlDiagonalDown).LineStyle = xlNone
exceptionProofBorders(xlDiagonalup).LineStyle = xlNone

您可以从三个列表中压缩参数,并执行以下操作:

for border, attr, value in myArgs:
    while True:
        i = 0
        try:
            setattr(excel.Selection.Borders(border), attr, value) 
        except:
            if i>100:
                break
        else:
            break

如果你的异常真的是随机的,这将尝试直到成功(限制为100次)。我不建议这样做

最新更新