有没有办法让这个Python函数更好地从ftp站点提取数据?

时间:2014-08-18 20:33:49

标签: python ftp

我已经创建了python函数来从ftp站点提取数据。它运作良好。但是,有很多try / except语句。我读到了使用python"和#34;声明使这更好,但我不清楚这将如何改善功能。这是代码:

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)

2 个答案:

答案 0 :(得分:0)

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

代码在几个地方有f.quit()。这很好,但很容易忘记哪些案例应该quit哪些不应该。{1}}。很容易错过一个。

使用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()

答案 1 :(得分:0)

使用上下文管理器有一种略微更好的方法。因为您在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中的样板代码,但不完全。