Python干净的方式来包装单个语句在一个try块除外

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

在我正在做的VB的等价代码中,这个问题显然被认为是正常的,并用一个On Error Resume Next语句贴满。 当然,Python没有这样的说法。

try except循环try except ,我不能在整个集合中包装整个集合,因为它可能在一半以内“失败”,并且不能正确完成。 那么,将几个独立的语句封装到除了block之外的尝试中是怎样的一种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 

更新

也许需要对错误位进行一些解释。 我的testing机器上有两个相同的代码运行在相同的文件上,产生相同的结果。 一次运行会为每个xlInsideVertical行引发exception。 另一个抛出每个xlInsideHorizontalexception。 最后,第三次运行完成,没有任何例外。

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

它将与每一行包装在一起,除了块之外,我只是想要更短,更明显的东西,因为在自己的try catch循环中包装的20行只是在稍后问一些问题。

Update2

这是一个用于testing的清理CSV文件: gist文件

结论

Vsekhar提供的答案是完美的。 它抽象出exception抑制,所以后来,如果我有时间的话,我可以处理exception。 它还允许loggingexception,使其不会消失, 不会阻止其他exception ,而且足够小,可以在六个月后轻松pipe理。

考虑抽象化压制。 对于亚伦的观点,一般不要吞下例外。

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

然后,注意当前代码的回溯,确切地说是引发了什么exception,并为此exception创build一个Suppressor:

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

这样你可以逐行执行(所以你的回溯将仍然有帮助),而且你只能抑制你明确想要的exception。 其他exception像往常一样传播。

例外从来没有发生“没有明显的原因”。 总有一个理由需要修正。 否则,你的程序将开始产生“随机”的数据,其中“随机”是在你隐藏的bug的摆布。

但是,当然,你需要解决你的问题。 这是我的build议:

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

  2. 在包含方法的每个方法之前添加一个装饰器, try excepttry except并loggingexception。 永远不要吞噬exception

现在,代码适用于您的客户,他们会花一些时间来找出问题的原因。 我的猜测是,a)Excel不会产生有用的错误信息,或b)包装代码吞下真正的exception,使您在黑暗中或c)Excel方法返回错误代码(如“失败”为“false”)您需要调用另一个Excel方法来确定问题的原因是什么。

[编辑]根据下面的评论,归结为“我的老板不在乎,也没有我可以做的”:你错过了一个关键点:这是你的老板的责任做出的决定,但它是你的责任给她的选项列表以及利弊,以便她能做出正确的决定。 只是坐在那里说“我什么都不能做”就会让你陷入麻烦,你试图避免。

例:

解决scheme1:忽略错误

Pro:最less的工作量Con:结果数据有可能是错误的或随机的。 如果重要的商业决策是以此为基础的,那么这些决策就是错误的。

解决scheme2:logging错误

Pro:工作量不大,用户可以快速开始使用结果,花费时间找出问题的根源。Con:“如果你今天不能修复,明天你会觉得你有时间去修复它?” 另外,由于您不是专家,因此可能需要很长时间才能find问题的根源

解决scheme3:询问专家

find该领域的专家,并帮助他/她看看/改进解决scheme。

Pro:比自己学习COM的内容要快得多解决schemeCon:昂贵但成功的机会很高。 也会发现我们甚至不知道的问题。

我想你看到的模式。 老板做错了决定,因为我们(愿意)让他们。 世界上的任何一个老板都很乐意在做出决定的时候提出难以理解的事实和意见(好吧,那些不应该成为老板的人,所以这是一个知道什么时候开始找工作的绝好办法) 。

如果你select解决scheme#2,去包装的方法。 请参阅文档如何编写装饰器( 来自IBM的示例 )。 这只是几分钟的工作来包装所有的方法,它会给你一些工作。

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

[编辑2]下面是一些代码,将“危险的”部分包装在助手类中,使得更新风格变得更加简单:

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

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

在你的情况下只吞下一些特定的exceptiontypes可能是明智的(如@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次尝试)。 我不推荐这个。