Python FTP“;“块”;迭代器(无需将整个文件加载到内存中)

Python FTP“;“块”;迭代器(无需将整个文件加载到内存中),python,ftp,Python,Ftp,关于堆栈溢出,有几个答案是关于检索FTP文件并将其写入流(如字符串缓冲区或可在其上迭代的文件)的 例如: 但是,这些解决方案涉及在开始处理内容之前将整个文件加载到内存或下载到磁盘 我没有足够的内存来缓冲整个文件,也无法访问磁盘。这可以通过处理回调函数中的数据来实现,但是我想知道是否有可能将ftp代码包装成某种魔术,返回一个迭代器,而不是在代码中添加回调 即,而不是: def get_ftp_data(handle_chunk): ... ftp.login('uesr', 'pa

关于堆栈溢出,有几个答案是关于检索FTP文件并将其写入流(如字符串缓冲区或可在其上迭代的文件)的

例如:

但是,这些解决方案涉及在开始处理内容之前将整个文件加载到内存或下载到磁盘

我没有足够的内存来缓冲整个文件,也无法访问磁盘。这可以通过处理回调函数中的数据来实现,但是我想知道是否有可能将ftp代码包装成某种魔术,返回一个迭代器,而不是在代码中添加回调

即,而不是:

def get_ftp_data(handle_chunk):
    ...
    ftp.login('uesr', 'password') # authentication required
    ftp.retrbinary('RETR etc', handle_chunk)
    ...

get_ftp_data(do_stuff_to_chunk)
我想要:

for chunk in get_ftp_data():
    do_stuff_to_chunk(chunk)

(与现有答案不同)我希望在迭代之前不将整个ftp文件写入磁盘或内存。

您必须将
retrbinary
调用放入另一个线程,并将回调提要块放入迭代器:

import threading, Queue

def ftp_chunk_iterator(FTP, command):
    # Set maxsize to limit the number of chunks kept in memory at once.
    queue = Queue.Queue(maxsize=some_appropriate_size)

    def ftp_thread_target():
        FTP.retrbinary(command, callback=queue.put)
        queue.put(None)

    ftp_thread = threading.Thread(target=ftp_thread_target)
    ftp_thread.start()

    while True:
        chunk = queue.get()
        if chunk is not None:
            yield chunk
        else:
            return
如果不能使用线程,最好将回调编写为协同程序:

from contextlib import closing


def process_chunks():
    while True:
        try:
            chunk = yield
        except GeneratorExit:
            finish_up()
            return
        else:
            do_whatever_with(chunk)

with closing(process_chunks()) as coroutine:

    # Get the coroutine to the first yield
    coroutine.next()

    FTP.retrbinary(command, callback=coroutine.send)
# coroutine.close() #  called by exiting the block

我很害怕。直观地说,它似乎不是绝对需要线程的东西。另外,虽然我在最初的问题中没有明确说明这一点,但我的执行环境没有线程。我希望有更好的方法。@natb1:不幸的是,它确实需要线程。如果您不能使用线程,那么最好将回调编写为协同程序,这样灵活性会降低,而且会更混乱。感谢您向我介绍协同程序。不幸的是,在我看来,这个例子像是说
FTP.retrbinary(command,callback=do_which_with)的一种冗长的方式。
@natb1:如果
do_which_with
是一个简单的函数,但是您可以根据协同程序的状态将任意代码块放在那里。如果它确实减少到
FTP.retrbinary(command,callback=do_which_with)
,则迭代器也会不必要地膨胀。@user2357112我喜欢线程版本。coroutine one乍一看是一个简单的calback解决方案,但有一个显著的区别——在
process\u chunks
generator中,所有处理(对于所有chunks)都写在一段代码中,直到close()才会返回。非常好。建议:将协同程序创建和关闭放入
块怎么样?有类似的问题