如何正确处理Python3中的异常

2024-10-06 10:34:09 发布

您现在位置:Python中文网/ 问答频道 /正文

我不明白“此时此地”应该处理什么样的异常,我应该在这里重新提出什么样的异常,或者不处理什么样的异常,以及以后(在更高层)如何处理它们。例如:我使用python3和ssl通信编写了客户机/服务器应用程序。客户端应该验证文件上的任何差异,如果存在差异,则应将此“更新”文件发送到服务器。


class BasicConnection:
    #blablabla
    def sendMessage(self, sock, url, port, fileToSend, buffSize):
        try:
            sock.connect((url, port))
            while True:
                data = fileToSend.read(buffSize)
                if not data: break
                sock.send(data)
            return True
        except socket.timeout as toErr:
            raise ConnectionError("TimeOutError trying to send File to remote socket: %s:%d"
                                  % (url,port)) from toErr
        except socket.error as sErr:
            raise ConnectionError("Error trying to send File to remote socket: %s:%d"
                                  % (url,port)) from sErr
        except ssl.SSLError as sslErr:
            raise ConnectionError("SSLError trying to send File to remote socket: %s:%d"
                                  % (url,port)) from sslErr
        finally:
            sock.close()

在python中使用异常是否正确?问题是:如果file.read()抛出IOError怎么办?我应该在这里处理,还是什么都不做,等会儿再抓?还有其他可能的例外?

  1. 客户端使用此类(BasicConnection)将更新的文件发送到服务器:

class PClient():
    def __init__(self, DATA):
        '''DATA = { 'sendTo'      : {'host':'','port':''},
                    'use_ssl'     : {'use_ssl':'', 'fileKey':'', 'fileCert':'', 'fileCaCert':''},
                    'dirToCheck'  : '',
                    'localStorage': '',
                    'timeToCheck' : '',
                    'buffSize'    : '',
                    'logFile'     : ''}   '''
        self._DATA = DATA
        self._running = False
        self.configureLogging()


    def configureLogging(self):
        #blablabla

    def isRun(self):
        return self._running

    def initPClient(self):
        try:
            #blablabla

            return True
        except ConnectionError as conErr:
            self._mainLogger.exception(conErr)
            return False
        except FileCheckingError as fcErr:
            self._mainLogger.exception(fcErr)
            return False
        except IOError as ioErr:
            self._mainLogger.exception(ioErr)
            return False
        except OSError as osErr:
            self._mainLogger.exception(osErr)
            return False


    def startPClient(self):
        try:
            self._running = True
            while self.isRun():
                try :
                    self._mainLogger.debug("Checking differences")
                    diffFiles = FileChecker().checkDictionary(self._dict)

                    if len(diffFiles) != 0:
                        for fileName in diffFiles:
                            try:
                                self._mainLogger.info("Sending updated file: %s to remote socket: %s:%d"
                                    % (fileName,self._DATA['sendTo']['host'],self._DATA['sendTo']['port']))
                                fileToSend = io.open(fileName, "rb")
                                result = False
                                result = BasicConnection().sendMessage(self._sock, self._DATA['sendTo']['host'],
                                                                       self._DATA['sendTo']['port'], fileToSend, self._DATA['buffSize'])
                                if result:
                                    self._mainLogger.info("Updated file: %s was successfully delivered  to remote socket: %s:%d"
                                    % (fileName,self._DATA['sendTo']['host'],self._DATA['sendTo']['port']))
                            except ConnectionError as conErr:
                                self._mainLogger.exception(conErr)
                            except IOError as ioErr:
                                self._mainLogger.exception(ioErr)
                            except OSError as osErr:
                                self._mainLogger.exception(osErr)

                        self._mainLogger.debug("Updating localStorage %s from %s " %(self._DATA['localStorage'], self._DATA['dirToCheck']))
                        FileChecker().updateLocalStorage(self._DATA['dirToCheck'],
                                                         self._DATA['localStorage'])
                    self._mainLogger.info("Directory %s were checked" %(self._DATA['dirToCheck']))
                    time.sleep(self._DATA['timeToCheck'])
                except FileCheckingError as fcErr:
                    self._mainLogger.exception(fcErr)
                except IOError as ioErr:
                    self._mainLogger.exception(ioErr)
                except OSError as osErr:
                    self._mainLogger.exception(osErr)
        except keyboardInterrupt:
            self._mainLogger.info("Shutting down...")
            self.stopPClient()
        except Exception as exc:
            self._mainLogger.exception(exc)
            self.stopPClient()
            raise RuntimeError("Something goes wrong...") from exc

    def stopPClient(self):
        self._running = False

对吗?也许有人会花自己的时间来帮助我理解处理异常的python风格?我不知道该怎么处理诸如NameError、TypeError、KeyError、ValueError…等等的异常……它们可以在任何时候被抛出到任何语句中。。。如果我想把所有东西都记录下来怎么办。

  1. 人们通常应该记录哪些信息?如果发生错误,我应该记录哪些信息?所有的回溯,或只是相关的信息或其他什么?

  2. 我希望有人能帮助我。 谢谢。


Tags: toselffalsedatareturnportdefas
2条回答

首先,您不需要任何主记录器。 如果您想捕获任何异常,可能要记录或通过电子邮件或其他方式发送它们,请在尽可能高的级别执行该操作——当然不能在这个类中执行。

另外,您肯定不想将每个异常都转换为运行时错误。让它出现。stopClient()方法现在没有作用。当它有了,我们会看看它。。

你基本上可以把ConnectionError、IOError和OSError包在一起(比如,像其他东西一样重新提升),但仅此而已。。。

一般来说,您应该“捕获”您希望发生的异常(因为它们可能是由用户错误或程序控制范围之外的其他环境问题引起的),特别是如果您知道代码可以对它们做些什么的话。仅仅在错误报告中提供更多细节是一个无关紧要的问题,尽管有些程序的规范可能需要这样做(例如,一个长时间运行的服务器不应该因为这样的问题而崩溃,而是记录大量的状态信息,给用户一个摘要解释,并为将来的查询继续工作)。

NameErrorTypeErrorKeyErrorValueErrorSyntaxErrorAttributeError等等,都可以认为是由于程序中的错误——错误,而不是程序员控制之外的问题。如果您正在发布库或框架,以便您的代码将被您控制之外的其他代码调用,那么此类错误很可能出现在其他代码中;您通常应该让异常传播以帮助其他程序员调试他们自己的错误。如果你要发布一个应用程序,你就拥有这些bug,你必须选择帮助你找到它们的策略。

如果最终用户运行程序时出现错误,则应记录大量状态信息,并向用户提供简要解释和道歉(如果无法自动执行,则可能请求向您发送日志信息,或者至少在将用户计算机中的任何内容发送到您的计算机之前请求权限)。到目前为止,您可能能够保存用户的一些工作,但通常(在已知有bug的程序中)这些工作可能无论如何都不起作用。

当然,大多数bug都应该在您自己的测试过程中出现;在这种情况下,传播异常非常有用,因为您可以将其连接到调试器并探索bug的详细信息。

有时,像这样的异常出现仅仅是因为“请求宽恕比请求许可更容易”(EAFP)——这是Python中一种完全可以接受的编程技术。在那种情况下,你当然应该马上处理。例如:

try:
    return mylist[theindex]
except IndexError:
    return None

在这里,您可能希望theindex通常是进入mylist的有效索引,但有时会超出mylist的界限——根据此片段所属的假设应用程序的语义,后一种情况不是错误,考虑到列表在概念上用无限个Nones在两边扩展,只需修复一点异常。尝试/排除比正确检查索引的正值和负值更容易(如果超出界限的情况确实很少发生,则速度更快)。

由于dicts的getattr内置和get方法(允许您提供默认值)、collections.defaultdict等,类似地,KeyErrorAttributeError的适当情况发生的频率也较低,但是列表没有这些的直接等价项,因此try/except更常见于IndexError

尝试捕捉语法错误、类型错误、值错误、名称错误等是比较少见和有争议的——尽管如果在“插件”中诊断出错误肯定是合适的,您的框架/应用程序试图动态加载和执行的超出您控制范围的第三方代码(实际上,在这种情况下,您正在提供库等,并且需要与超出您控制范围的代码和平共存,这很可能是错误的)。类型和值错误有时可能发生在EAFP模式中——例如,当您试图重载一个函数以接受字符串或数字,并且在每种情况下的行为略有不同时,捕获此类错误可能比尝试检查类型要好——但这样重载的函数的概念往往并不十分可疑。

回到“用户和环境错误”,用户在给您输入时,不可避免地会犯错误,指出一个实际上不在附近的文件名(或者您没有读取或写入的权限,如果这是您的等等:当然,所有这些错误都应该被捕捉到,并向用户清楚地解释出了什么问题,还有一个让输入正确的机会。网络有时会出现故障,数据库或其他外部服务器可能无法按预期做出响应,等等——有时值得抓住这些问题并重试(可能在稍等之后——可能向用户指出问题所在,e、 g.他们可能不小心拔下了电缆,你想给他们一个机会,让他们修复问题,告诉你什么时候再试一次),有时(特别是在无人值守的长时间运行的程序中),除了命令关机(以及详细记录环境的每一个可能相关方面)之外,你无能为力。

所以,简言之,你的问题的答案是,“这取决于”;—)。我希望我在列出它所依赖的许多情况和方面,并建议对这些问题采取什么样的普遍最有用的态度时,我是有用的。

相关问题 更多 >