Python干净的方式来包装try块中的单个语句

时间:2011-09-01 13:45:49

标签: python excel vba com try-catch

我目前正在使用com进行Excel的Python自动化。它功能齐全,可以满足我的需求,但我发现了一些令人惊讶的东西。有时,我使用的某些Excel命令会因为没有明显原因的异常而失败。其他时候,他们会工作。

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

我无法将整个集合包裹在try except循环中,因为它可能会“中途失败”并且无法正常完成。那么,将几个独立语句包装到try除块之外的pythonic方法是什么?具体而言,比以下更清洁:

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中包含的每一行中起作用,除了块我只想要更短更明显的东西,因为在他们自己的try catch循环中包含的20行只会在以后遇到麻烦。

UPDATE2

这是用于测试的已清理CSV文件:gist file

结论

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

4 个答案:

答案 0 :(得分:12)

考虑抽象抑制。而对于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 :(得分:10)

例外从未发生“无缘无故”。总有一个原因,需要修复这个原因。否则,您的程序将开始生成“随机”数据,其中“随机”受您隐藏的错误的支配。

但是,当然,您需要一个解决方案来解决您的问题。这是我的建议:

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

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

  3. 现在代码适用于您的客户,它会花一些时间找出问题的原因。我的猜测是a)Excel不会产生有用的错误信息或b)包装器代码吞下真正的异常,让你在黑暗中或c)Excel方法返回错误代码(如“失败”的“假”)并且您需要调用另一个Excel方法来确定问题的原因。

    [编辑] 根据下面的评论归结为“我的老板不关心,我无能为力”:你错过了一个关键点:这是你的老板< em>责任做出决定,但有责任给她一个选项列表以及优点/缺点,以便她做出明智的决定。只是坐在那里说“我什么都做不了”会让你陷入你想要避免的麻烦之中。

    示例:

    解决方案1:忽略错误

    亲:工作量最少 Con:结果数据有可能是错误的或随机的。如果重要的业务决策以此为基础,则这些决策存在很高的风险。

    解决方案2:记录错误

    专业:工作量少,用户可以快速开始使用结果,花时间找出问题的根源 骗子:“如果你今天无法修复它,是什么让你觉得明天你有时间修好它?”此外,您可能需要很长时间才能找到问题的根源,因为您不是专家

    解决方案3:咨询专家

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

    Pro:比自己学习COM的来龙去快得多 骗局:昂贵但成功的机会很高。还会发现我们甚至不知道的问题。

    ...

    我认为你看到的模式。老板做出错误的决定,因为我们(心甘情愿地)让他们。当他们必须作出决定时,世界上的任何老板都对于事实和投入感到高兴(好吧,那些不应该是老板的人,所以这是一种确定何时开始寻找新工作的绝对方式)

    如果您选择解决方案#2,请选择包装方法。 See the docs如何编写装饰器(example from IBM)。只需几分钟的时间来完成所有方法的包装,它将为您提供一些工作。

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

    [EDIT2] 以下是一些包含帮助类中“危险”部分的代码,并使样式更新更简单:

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

答案 2 :(得分:4)

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

在你的案例中只吞下一些特定的异常类型可能是明智的(正如@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

答案 3 :(得分:0)

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

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次尝试)。我不推荐这个。