2017-12-28 10 views
0

ISFTPFileよりファイルを読み込もうとしていますが、このシナリオで@inlinceCallbacksを使用しないでください。SFTPFileからのリファクタリングreadChunkがinlineCallbacksの使用を停止する方法は?

また、ISFTPFileの読み取り/書き込みにはより良い方法がありますか?

あなたが効果的にファイルのチャンクのイテレータ上sha256.updateをマッピングしたい
@defer.inlineCallbacks 
def calculate_checksum(open_file): 
    hasher = hashlib.sha256() 

    offset = 0 
    try: 
     while True: 
      d = yield open_file.readChunk(offset, chunk_size) 
      offset += chunk_size 
      hasher.update(d) 

    except EOFError: 
     pass 

    target_checksum = hasher.hexdigest() 
    defer.returnValue(target_checksum) 


client_file = client.openFile(
    filename=target, flags=FXF_READ, attrs={}) 
checksum = yield client_file.addCallback(calculate_checksum) 
+0

リファクタリングまたはコードレビューのためにここをクリックしてください:https://codereview.stackexchange.com/ – Joe

答えて

1

hasher = hashlib.sha256() 
chunks = read_those_chunks() 
map(hasher.update, chunks) 
return hasher.hexdigest() 

注意(whileループを使用して)、元calculate_checksumsからの明示的な反復が今mapの内部に隠されていること。基本的には、mapが繰り返しになりました。

read_those_chunksがファイル全体を(おそらく)メモリに読み込むことを避けたいという障害があります。

def read_those_chunks(open_file, chunk_size): 
    offset = 0 
    while True: 
     yield open_file.readChunk(offset, chunk_size) 
     offset += chunk_size 

以降のチャンク(またはEOFError)で火災Deferred Sを生成するジェネレータがあります:だから、最初のステップとして、その部分を実装します。残念ながら、mapでこれを使用することはできません。 、元の実装から反復を置き換えasync_map以来

​​

mapmapを交換しようとしているasync_mapはまだ確認してください、私たちからすべてのチャンクを訪問する責任です。だから今、地図を問わずこれに対処することができます実装繰り返し可能です。ただし、繰り返し(forまたはwhileのいずれか)は、Deferredとよく混合されません(混合するのは通常、inlineCallbacksを取り出すときです)。したがってasync_mapは反復しません。それは繰り返します - 繰り返しの一般的な選択肢。各再帰呼び出しは、それ以上が存在しなくなるまで(または、EOFErrorのためにこの場合に起こるように、Deferredが失敗するまで)繰り返し可能性の次の要素で動作します。

再帰は関数と関数呼び出しで動作するため、Deferredを使用した反復処理よりも優れています。 Deferredは、関数と関数呼び出しを扱うことができます。関数をaddCallbackDeferredに渡すと、最終的にその関数が呼び出されます。反復は機能の小さな断片(「ブロック」または「スイート」と呼ばれることもあります)とDeferredで処理できません。ブロックをaddCallbackに渡すことはできません。

は今Deferredを作成するには、これらの2を使用している火災ダイジェストが計算されている場合:またasync_mapが、それは結果のリストを作成していないことにmap異なることに気付くかもしれ

def calculate_checksum(open_file, chunk_size): 
    hasher = hashlib.sha256() 
    chunks = read_those_chunks(open_file, chunk_size) 
    d = async_map(hasher.update, chunks) 
    d.addErrback(lambda err: err.trap(EOFError)) 
    d.addCallback(lambda ignored: hasher.hexdigest()) 
    return d 

それが作る関数呼び出しです。おそらくそれは、よりreduceようなものだ:

def async_reduce(function, iterable, lhs): 
    try: 
     d = next(iterable) 
    except StopIteration: 
     return lhs 

    d.addCallback(lambda rhs: function(lhs, rhs)) 
    d.addCallback(lambda lhs: async_reduce(function, iterable, lhs)) 
    return d 

それはもちろん、まだ再帰の代わりに反復的です。

そしてhexdigestを計算するための還元関数は次のようである:

def update_hash(hasher, s): 
    hasher.update(s) 
    return hasher 

それでcalculate_checksumは次のようになる。

def calculate_checksum(open_file, chunk_size): 
    chunks = read_those_chunks(open_file, chunk_size) 
    d = async_reduce(update_hash, hashlib.sha256(), "") 
    d.addErrback(lambda err: err.trap(EOFError)) 
    d.addCallback(lambda hasher: hasher.hexdigest()) 
    return d 

hasher閉鎖を持っていないため、ビット進歩しています。

もちろん、inlineCallbacksを避けるためにこの関数を書き換えることができる他にも多くの方法があります。私が選択した方法は、ジェネレータ関数の使用を排除するものではありません。したがって、あなたがエスケープしたいものなら、それは本当に助けにはなりません。そうであれば、ここで私がここで行ったように、問題を分解することができます。

+0

ありがとうございます。これは私が達成したかったものです。もう少し説明してください。d.addCallback(lambda ignored:async_map(function、iterable))に 'async_map'または' async_reduce'の再帰呼び出しが必要なのはなぜですか? – heniek

+0

確かに!再帰を議論する答えに、もう少しのテキストを編集しました。 –

関連する問題