有没有一种方法可以让这个Python函数更好地从ftp站点提取数据?



我已经创建了从ftp站点提取数据的python函数。效果很好。然而,有很多try/except语句。我读过关于使用python"with"语句来做得更好,但我不清楚这将如何改进函数。下面是代码:

HOST = 'ftp.osuosl.org'
DIRN = 'debian/tools'
FILE = 'loadlin.txt'

def func(HOST, DIRN, FILE):
     import ftplib
     from StringIO import StringIO
     import os
     import socket
     try:
          f = ftplib.FTP(HOST)
     except (socket.error, socket.gaierror), e:
          print 'ERROR: cannot reach "%s"' % HOST
          return "None"
     print '*** Connected to host "%s"' % HOST
     try:
          f.login()
     except ftplib.error_perm:
          print 'ERROR: cannot login anonymously'
          f.quit()
          return "None"
     print '*** Logged in as "anonymous"'
     try:
          f.cwd(DIRN)
     except ftplib.error_perm:
          print 'ERROR: cannot CD to "%s"' % DIRN
          f.quit()
          return "None"
     print '*** Changed to "%s" folder' % DIRN
     try:
          r = StringIO()
          f.retrbinary('RETR %s' % FILE, r.write)
     except ftplib.error_perm:
          print 'ERROR: cannot read file "%s"' % FILE
          return "None"
     else:
          print '*** Downloaded "%s" to CWD' % FILE
     f.quit()
     return r.getvalue()
print func(HOST, DIRN, FILE)

一般来说,这种风格效果很好。每个有趣的代码段都有一个try/except块,因此您可以将有关错误的具体细节传达给调用者。

代码在几个地方都有f.quit()。这很好,但是很容易忘记哪些情况应该有quit,哪些不应该。很容易漏掉一个。

考虑使用finally块的这种样式。如果RETR成功或失败,这个块总是被执行。它是更安全的。

try:
      r = StringIO()
      f.retrbinary('RETR %s' % FILE, r.write)
 except ftplib.error_perm:
      print 'ERROR: cannot read file "%s"' % FILE
      return "None"
 else:
      print '*** Downloaded "%s" to CWD' % FILE
 finally:
      f.quit()

使用上下文管理器有一种稍微更好的方法。因为您在except分支中执行的操作以及在运行命令后立即执行的操作基本上是样板文件,所以您可以将其抽象到上下文管理器的__exit__部分中。唯一的复杂之处在于,您希望在捕获异常后返回,而异常无法隐藏在上下文管理器中。所以我们必须在上下文对象本身上设置一个标志,并在调用代码中检查(因此if h.err:检查):

 import ftplib
 from StringIO import StringIO
 import os
 import socket
class HandleExc(object):
    """ Context manager for exception handling. 

    exc_types is a tuple of exception types we want to handle
    """
    def __init__(self, handle=f, exc_types=(ftplib.error_perm,), msg="", errmsg=""):
        self.err = False
        self.f = f
        self.msg = msg
        self.errmsg = errmsg
    def __enter__(self):
        return self
    def __exit__(exc_type, exc_value, traceback):
        if exc_type in exc_types:
            # Got an exception we want to handle.
            print(self.errmsg)
            if self.f:
                self.f.close()
            self.err = True  # Set the err flag so the caller can check it.
        elif exc_type:
            # Unhandled exception, let it raise (though you may want to call f.close() first).
            return
        else:
            # All good print success message
            print(self.msg)

def func(HOST, DIRN, FILE):
     with HandleExc(exc=(socket.errror, socket.gaierror), 
                    msg='*** Connected to host "%s" ' % HOST,
                    errmsg='ERROR: cannot reach "%s"' % HOST) as h:
          f = ftplib.FTP(HOST)
     if h.err:
         return "None"
     with HandleExc(f, msg='*** Logged in as "anonymous"',
                    errmsg='ERROR: cannot CD to "%s"' % DIRN) as h:
          f.login()
     if h.err:
          return "None"
     with HandleExc(f, msg='*** Changed to "%s" folder' % DIRN,
                    errmsg='ERROR: cannot login anonymously') as h:
          f.cwd(DIRN)
     if h.err:
          return "None"
     with HandleExc(f, msg='*** Downloaded "%s" to CWD' % FILE,
                    errmsg='ERROR: cannot read file "%s"') as h:
          r = StringIO()
          f.retrbinary('RETR %s' % FILE, r.write)
     if h.err:
          return "None"
     f.quit()
     return r.getvalue()
print func(HOST, DIRN, FILE)

这在一定程度上减少了func中的样板代码,尽管不是全部。

相关内容

  • 没有找到相关文章

最新更新