2017-12-28 3 views
0

ISFTPFile 이상의 파일을 읽으려고하는데이 시나리오에서 @inlinceCallbacks을 사용하지 않으시겠습니까?refactor readChunk에서 SFTPFile의 인라인 콜백 사용을 중단 하시겠습니까?

ISFTPFile에 대해 더 읽고 쓸 수있는 방법이 있습니까?

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

주 (while 루프를 사용하여) 원래 calculate_checksums에서 명시 적 반복은 이제 map을 내부에 숨겨져 있음 :

@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

당신은 효과적으로 파일 청크의 반복자를 통해 sha256.update를 매핑 할 . 기본적으로 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의를 산출 발전기가있다 : 그래서, 첫 번째 단계로, 그 조각을 구현한다. 죄송 합니다만 map과 함께 사용할 수 없습니다. , async_map 여전히 우리가에서 모든 덩어리를 방문하여 확인 할 책임이 async_map 이후

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

    d.addCallback(function) 
    d.addCallback(lambda ignored: async_map(function, iterable)) 
    return d 

map을 대체 할 것입니다 및 map 원래 구현에서 반복을 교체 : 그래서 지금은지도 표시 - 동일 조건 변경 허락이 처리 할 수 ​​구현 반복 가능. 그러나 반복 (for 또는 while)은 Deferred과 잘 섞이지 않습니다 (일반적으로 혼합하는 것은 inlineCallbacks 일 때입니다). 따라서 async_map은 반복하지 않습니다. 재발 - 일반적인 반복 대안. 각각의 재귀 호출은 더 이상 존재하지 않을 때까지 (또는이 경우 EOFError로 인해 발생하는 Deferred이 실패 할 때까지) 반복 가능 프로그램의 다음 요소에서 작동합니다.

재귀는 함수 및 함수 호출에서 작동하기 때문에 Deferred을 사용한 반복보다 잘 작동합니다. Deferred은 함수 및 함수 호출을 처리 할 수 ​​있습니다. 함수를 addCallbackDeferred으로 전달하면 결국 해당 함수가 호출됩니다. 반복은 함수의 작은 조각 ("블록"또는 "스위트"라고도 함)과 Deferred으로 처리 할 수 ​​없습니다. 블록을 addCallback에 보낼 수 없습니다.

는 이제 Deferred을 만들려면 다음 두 가지를 사용하는 화재 다이제스트 계산했을 때 : 또한 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이된다 다음 hasher 클로저를 구비하지 않는 비트 좋네요

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 

한다.

물론이 기능을 다시 작성하여 inlineCallbacks을 피할 수있는 많은 다른 방법이 있습니다. 내가 선택한 방법으로 생성기 함수를 사용하지 않아도되므로 실제로는 도움이되지 못했습니다. 만약 그렇다면, 아마도 여러분이 제가 여기서 한 것처럼 문제를 분해 할 수 있습니다. 그 중 어떤 것도 발전기를 포함하지 않습니다.

+0

고마워요. 이것이 제가 성취하고자했던 것입니다. 좀 더 설명해 주시겠습니까? d.addCallback (lambda ignored : async_map (function, iterable))'에서'async_map' 또는'async_reduce'를 재귀 호출해야하는 이유는 무엇입니까? – heniek

+0

물론! 재귀를 설명하는 답변을 더 편집했습니다. –

관련 문제