문제

코 루틴과 자원 획득의 조합은 의도하지 않은 (또는 직관적이지 않은) 결과를 초래할 수있는 것처럼 보입니다.

기본적인 질문은 이와 같은 것이 효과가 있는지 여부입니다.

def coroutine():
    with open(path, 'r') as fh:
        for line in fh:
            yield line

그 일. (테스트 할 수 있습니다!)

더 깊은 관심사는 그 것입니다 with 대안이어야합니다 finally, 블록 끝에 리소스가 릴리스되도록합니다. 코 루틴은 실행을 중단하고 재개 할 수 있습니다 이내에 그만큼 with 블록 갈등은 어떻게 해결됩니까?

예를 들어, 코 루틴이 아직 반환되지 않은 동안 코 루틴 내부 및 외부에서 읽기/쓰기가있는 파일을 열면 다음과 같습니다.

def coroutine():
    with open('test.txt', 'rw+') as fh:
        for line in fh:
            yield line

a = coroutine()
assert a.next() # Open the filehandle inside the coroutine first.
with open('test.txt', 'rw+') as fh: # Then open it outside.
    for line in fh:
        print 'Outside coroutine: %r' % repr(line)
assert a.next() # Can we still use it?

업데이트

이전 예제에서 쓰기 잠금 파일 핸들 경합을하려고했지만 대부분의 OS는 프로세스 당 파일 핸들을 할당하기 때문에 경합이 없습니다. (예제를 지적하기 위해 @miles에 대한 kudos는 너무 의미가 없었습니다.) 여기에 실질적인 교착 상태를 보여주는 수정 된 예입니다.

import threading

lock = threading.Lock()

def coroutine():
    with lock:
        yield 'spam'
        yield 'eggs'

generator = coroutine()
assert generator.next()
with lock: # Deadlock!
    print 'Outside the coroutine got the lock'
assert generator.next()
도움이 되었습니까?

해결책

나는 당신이 어떤 갈등을 요구하는지, 예제의 문제를 실제로 이해하지 못합니다. 같은 파일에 두 개의 공존하는 독립적 인 핸들을 갖는 것은 괜찮습니다.

내가 당신의 질문에 응답하여 배웠다는 것을 알지 못하는 한 가지는 발전기에 새로운 Close () 메소드가 있다는 것을 알았습니다.

close() 새로운 것을 키 웁니다 GeneratorExit 발전기 내부의 예외 반복을 종료합니다. 이 예외를 받으면 생성기의 코드는 GeneratorExit 또는 StopIteration.

close() 발전기가 쓰레기 수집 될 때 호출되므로 발전기의 코드가 발전기가 파괴되기 전에 마지막으로 실행할 수있는 기회가 하나를 얻는다는 것을 의미합니다. 이 마지막 기회는 그것을 의미합니다 try...finally 발전기의 진술은 이제 작동하도록 보장 될 수 있습니다. 그만큼 finally 절은 이제 항상 실행할 기회를 얻게됩니다. 이것은 사소한 언어 퀴즈처럼 보이지만 발전기를 사용하고 try...finally 실제로 구현하기 위해서는 필요합니다 with PEP 343에 의해 설명 된 진술.

http://docs.python.org/whatsnew/2.5.html#pep-342-new-generator-features

그래서 상황을 처리합니다 with 진술은 발전기에 사용되지만 중간에 생성되지만 컨텍스트 관리자의 __exit__ 발전기가 쓰레기 수집 될 때 방법이 호출됩니다.


편집하다:

파일 핸들 문제와 관련하여 : 때로는 posix와 유사하지 않은 플랫폼이 존재한다는 것을 잊어 버립니다. :)

자물쇠가가는 한, 나는 Rafał Dowgird가 "발전기가 자원을 보유하는 다른 물체와 똑같다는 것을 알고 있어야한다"고 말할 때 손톱에 머리를 때린다고 생각합니다. 나는 생각하지 않는다 with 이 기능은 동일한 교착 상태 문제로 어려움을 겪기 때문에 진술은 실제로 여기에 관련이 있습니다.

def coroutine():
    lock.acquire()
    yield 'spam'
    yield 'eggs'
    lock.release()

generator = coroutine()
generator.next()
lock.acquire() # whoops!

다른 팁

나는 진정한 갈등이 있다고 생각하지 않습니다. 생성기는 자원을 보유하는 다른 객체와 마찬가지로 제작자의 책임이라는 점을 알고 있어야합니다. 따라서 제대로 마무리되도록하는 것은 제작자의 책임입니다 (개체가 보유한 자원과 충돌/교착 상태를 피하기 위해). 여기서 볼 수있는 유일한 (사소한) 문제는 발전기가 컨텍스트 관리 프로토콜 (적어도 Python 2.5의 경우)을 구현하지 않으므로 다음과 같이 할 수는 없습니다.

with coroutine() as cr:
  doSomething(cr)

대신 :

cr = coroutine()
try:
  doSomething(cr)
finally:
  cr.close()

쓰레기 수집가가 close() 어쨌든, 그러나 자원을 자유롭게하는 데 의존하는 것은 나쁜 관행입니다.

왜냐하면 yield 임의의 코드를 실행할 수 있습니다. 수율 명세서에 잠금을 잡는 데 매우주의를 기울입니다. 그러나 재정의되거나 달리 수정 될 수있는 메소드 나 함수를 호출하는 것을 포함하여 다른 많은 방법으로 유사한 효과를 얻을 수 있습니다.

그러나 발전기는 항상 (거의 항상) "폐쇄"이며 명시 적으로 close() 전화를 걸거나 쓰레기가 수집되어 있습니다. 발전기를 닫으면 a GeneratorExit 발전기 내부의 예외와 그에 따라 실행 된 진술 정리 등이 실행됩니다. 예외를 포착 할 수 있지만 기능을 던지거나 종료해야합니다 (즉, StopIteration 예외), 수율보다는. 쓰레기 수집가에 의존하여 작성된 것보다 나중에 발생할 수 있기 때문에 쓰레기 수집가에 의존하여 발전기를 닫는 것은 열악한 관행 일 것입니다. .

그것이 내가 일이 작동하기를 기대하는 방법 일 것입니다. 그렇습니다. 블록은 완료 될 때까지 자원을 해제하지 않으므로 그 의미에서 자원이 어휘 중첩으로 빠져 나갔습니다. 그러나 이것은 블록과 동일한 자원을 사용하려고 시도한 함수 호출을 만드는 것과 다르지 않습니다. 블록에 블록에 도움이되는 것은 없습니다. ~ 아니다 아직 종료되었습니다 무엇이든 이유. 실제로 발전기에 특정한 것은 아닙니다.

걱정할 가치가있는 한 가지는 발전기가 발생하는 경우 행동입니다. 절대 재개. 나는 예상했을 것이다 with a처럼 행동하는 블록 finally 차단하고 전화하십시오 __exit__ 종료시 부분이지만 그렇지 않은 것 같습니다.

TLDR의 경우 이런 식으로보십시오.

with Context():
    yield 1
    pass  # explicitly do nothing *after* yield
# exit context after explicitly doing nothing

그만큼 Context 후에 끝납니다 pass 완료되었습니다 (예 : 아무것도), pass 이후에 실행됩니다 yield 수행됩니다 (즉, 실행 이력서). 그래서 with~ 후에 통제가 재개됩니다 yield.

tldr : a with 맥락은 언제 유지됩니다 yield 제어를 해제합니다.


실제로 여기에는 관련된 두 가지 규칙이 있습니다.

  1. 언제 with 자원을 해제 하시겠습니까?

    그렇게합니다 한 번 그리고 직접 ~ 후에 블록이 완료되었습니다. 전자는 그것이 출시되지 않았다는 것을 의미합니다 ~ 동안yield, 그것은 여러 번 일어날 수 있습니다. 나중에는 그것이 출시된다는 것을 의미합니다 ~ 후에 yield 완료되었습니다.

  2. 언제 yield 완벽한?

    에 대해 생각하다 yield 리버스 호출 : Control은 호출자가 아닌 발신자에게 전달됩니다. 비슷하게, yield 통화가 제어를 반환 할 때와 같이 제어가 다시 전달 될 때 완료됩니다.

둘 다에 유의하십시오 with 그리고 yield 여기서 의도 된대로 일하고 있습니다! a의 요점 with lock 자원을 보호하는 것이며 yield. 이 보호 기능을 항상 명시 적으로 공개 할 수 있습니다.

def safe_generator():
  while True:
    with lock():
      # keep lock for critical operation
      result = protected_operation()
    # release lock before releasing control
    yield result
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top