我目前正在使用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提供的答案是完美的。它抽象了异常抑制,以便以后,如果我有时间,我实际上可以处理它们发生的异常。它还允许记录异常,使它们不会消失,不会停止其他异常,并且足够小,可以在六个月后轻松管理。
答案 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)
例外从未发生“无缘无故”。总有一个原因,需要修复这个原因。否则,您的程序将开始生成“随机”数据,其中“随机”受您隐藏的错误的支配。
但是,当然,您需要一个解决方案来解决您的问题。这是我的建议:
创建一个包装类,实现您需要的所有方法,并将它们委托给真正的Excel实例。
在每个方法之前添加一个装饰器,将方法包装在try except
块中并记录异常。 永远不会吞下例外
现在代码适用于您的客户,它会花一些时间找出问题的原因。我的猜测是a)Excel不会产生有用的错误信息或b)包装器代码吞下真正的异常,让你在黑暗中或c)Excel方法返回错误代码(如“失败”的“假”)并且您需要调用另一个Excel方法来确定问题的原因。
[编辑] 根据下面的评论归结为“我的老板不关心,我无能为力”:你错过了一个关键点:这是你的老板< em>责任做出决定,但你有责任给她一个选项列表以及优点/缺点,以便她做出明智的决定。只是坐在那里说“我什么都做不了”会让你陷入你想要避免的麻烦之中。
示例:
亲:工作量最少 Con:结果数据有可能是错误的或随机的。如果重要的业务决策以此为基础,则这些决策存在很高的风险。
专业:工作量少,用户可以快速开始使用结果,花时间找出问题的根源 骗子:“如果你今天无法修复它,是什么让你觉得明天你有时间修好它?”此外,您可能需要很长时间才能找到问题的根源,因为您不是专家
找到该领域的专家,帮助他/她查看/改进解决方案。
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次尝试)。我不推荐这个。