我目前正在做一些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')
这样您可以逐行执行(因此您的回溯仍然是有帮助的),并且您只抑制您明确想要的异常。其他异常照常传播。
异常从不"无缘无故"发生。总有一个原因,这个原因需要解决。否则,您的程序将开始生成"随机"数据,而"随机"将受您隐藏的错误的支配。
当然,你需要一个解决问题的方法。以下是我的建议:
-
创建一个包装器类,实现你需要的所有方法,并将它们委托给真正的Excel实例。
-
在每个方法之前添加一个装饰器,将该方法包装在
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次)。我不建议这样做