我应该如何正确处理Python3中的异常

rau*_*uch 12 python logging exception

我无法理解我应该处理什么样的异常'此处和现在',以及我应该重新提出或者在这里不处理的异常,以及稍后如何处理它们(在更高层).例如:我使用python3和ssl通信编写了客户端/服务器应用程序.客户端应该验证有关它们的任何差异的文件,如果diff存在,那么它应该将此"更新"文件发送到服务器.


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()
Run Code Online (Sandbox Code Playgroud)

在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
Run Code Online (Sandbox Code Playgroud)

这是对的吗?可能有人花费自己的时间,只是帮助我理解处理异常的pythonic风格?我无法理解如何处理NameError,TypeError,KeyError,ValueError等等异常.......他们可以随时抛出任何声明......以及怎样做和他们一起,如果我想记录一切.

  1. 人们通常会记录哪些信息?如果发生错误,我应该记录哪些信息呢?所有追溯,或只是关于它或其他东西的相关消息?

  2. 我希望有人帮助我.非常感谢.

Ale*_*lli 24

通常,您应该"捕获"您期望发生的异常(因为它们可能是由用户错误或程序控制之外的其他环境问题引起的),特别是如果您知道您的代码可能对它们执行的操作.只是在错误报告中提供更多细节是一个边缘问题,尽管某些程序的规范可能需要这样做(例如,由于这些问题而不应该崩溃的长期服务器,而是记录大量状态信息,给出用户摘要说明,并继续为将来的查询工作).

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

如果你的错误出现,而最终用户正在运行的程序,你应该记录了大量的状态信息,并给用户一个总结的解释和道歉(或许还有一个请求,发送给您的日志信息,如果你不能自动化 - 或者,至少在您从用户的机器向您的机器发送任何内容之前询问权限).到目前为止,您可以保存一些用户的工作,但通常(在一个已知有错误的程序中)可能无法正常工作.

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

有时像这样的一些例外只是因为"请求宽恕比允许更容易"(EAFP) - 一种完全可以接受的Python编程技术.在那种情况下,你当然应该立即处理它们.例如:

try:
    return mylist[theindex]
except IndexError:
    return None
Run Code Online (Sandbox Code Playgroud)

在这里,你可能会认为theindex通常是有效的索引mylist,但偶尔外mylist的界限-而后者情况下,通过假设的应用程序中,该片段所属的语义,是不是一个错误,只是有点异常是通过考虑列表在两侧具有无限数量的概念扩展来确定None.尝试/除了正确检查索引的正值和负值(和更快,如果超出界限是非常罕见的情况)更容易.

由于内置和dicts方法(允许您提供默认值)等,类似的适用情况KeyErrorAttributeError发生频率较低; 但是列表没有直接等价的那些,所以try/except更常见.getattrgetcollections.defaultdictIndexError

试图抓住语法错误,类型错误,错误值,名称错误等等,有点罕见,更多的争议 - 尽管它肯定会是适当的,如果错误被诊断为"插电式",第三方代码之外您的框架/应用程序正在尝试加载和动态执行的控件(事实上,在您提供库等的情况下,需要与您的控件之外的代码和平共存,这可能是错误的).类型和价值的错误可能的EAFP模式中,有时会发生 - 例如,当您尝试重载函数接受一个字符串或一个数字,表现略有不同在每一种情况下,捕捉这样的错误可能会比试图检查类型更好 - 但是因此超载的功能概念往往是非常可疑的.

返回"用户和环境的错误",用户难免会犯错误,当他们给你的输入,显示文件名,这不是真正各地(或您没有权限来读取或写入,如果这就是你应该什么正在做的事情,等等:所有这些错误当然应该被抓住,并向用户清楚地解释出错的原因,以及另一个获得正确输入的机会.网络有时走下来,数据库或其他外部服务器可能没有反应不如预期,等等 - 有时它是值得抓住这样的问题和重试(后也许有点等待 - 也许有指示,什么是错的用户,例如,它们可能不小心拔掉了电缆,你想让他们有机会解决问题并告诉你什么时候再试一次),

所以,简而言之,你的Q标题的答案是"它取决于";-).我希望我能够列出它可以依赖的许多情况和方面,并建议对这些问题采取最有用的态度.