[python] ‘finally’는 항상 Python에서 실행됩니까?

Python에서 가능한 모든 try-finally 블록에 대해 finally 블록이 항상 실행 됩니까?

예를 들어, except블록 에있는 동안 돌아 왔다고 가정 해 보겠습니다 .

try:
    1/0
except ZeroDivisionError:
    return
finally:
    print("Does this code run?")

아니면 다시 올릴 수도 있습니다 Exception.

try:
    1/0
except ZeroDivisionError:
    raise
finally:
    print("What about this code?")

테스트 결과 finally 위의 예에서 실행되는 것으로 나타 났지만 생각하지 못한 다른 시나리오가 있다고 생각합니다.

finally블록이 Python에서 실행되지 않을 수 있는 시나리오가 있습니까?



답변

“Guaranteed”는 어떤 구현에 해당하는 것보다 훨씬 강력한 단어 finally입니다. 무엇을 보장하는 것은 실행이 전체의 흘러 경우이다 tryfinally구조, 그것이 통과하게 finally그렇게 할 수 있습니다. 보장되지 않는 것은 실행이 tryfinally.

  • finally생성기 또는 비동기 코 루틴의 A 는 객체가 결론에 도달하지 않으면 절대 실행되지 않을 수 있습니다 . 일어날 수있는 방법은 많습니다. 여기에 하나가 있습니다.

    def gen(text):
        try:
            for line in text:
                try:
                    yield int(line)
                except:
                    # Ignore blank lines - but catch too much!
                    pass
        finally:
            print('Doing important cleanup')
    
    text = ['1', '', '2', '', '3']
    
    if any(n > 1 for n in gen(text)):
        print('Found a number')
    
    print('Oops, no cleanup.')

    이 예제는 약간 까다 롭습니다. 생성기가 가비지 수집되면 Python finallyGeneratorExit예외 를 던져 블록 을 실행하려고 시도 하지만 여기서 예외를 포착 한 다음 yield다시 한 번 Python이 경고를 출력합니다 ( “generator ignore GeneratorExit “) 포기합니다. 자세한 내용은 PEP 342 (향상된 생성기를 통한 코 루틴) 를 참조하십시오.

    생성기 또는 코 루틴이 결론을 내리기 위해 실행되지 않을 수있는 다른 방법으로는 객체가 GC 처리되지 않은 경우 (예, CPython에서도 가능) 또는 async with awaits가 __aexit__인 경우 또는 객체 가 블록 에서 awaits 또는 yields 인 경우가 finally있습니다. 이 목록은 완전한 것이 아닙니다.

  • finally데몬이 아닌 모든 스레드가 먼저 종료되면 데몬 스레드의 A 가 실행되지 않을 수 있습니다 .

  • os._exitfinally블록 을 실행하지 않고 즉시 프로세스를 중지합니다 .

  • os.forkfinally블록이 두 번 실행될 수 있습니다 . 두 번 발생하는 상황에서 예상 할 수있는 일반적인 문제뿐만 아니라 공유 리소스에 대한 액세스가 올바르게 동기화 되지 않은 경우 동시 액세스 충돌 (충돌, 중단 등)이 발생할 수 있습니다 .

    때문에 multiprocessing사용이 포크-없이-간부 작업자 프로세스를 생성하기 위해 사용하는 경우 포크의 시작 방법 다음 (유닉스의 기본)를 호출합니다 os._exit작업자의 작업이 완료되면 작업자에, finally그리고 multiprocessing상호 작용 문제 (수 있습니다 ).

  • C 레벨 분할 오류는 finally블록 실행 을 방해 합니다.
  • kill -SIGKILLfinally블록이 실행되는 것을 방지 합니다. SIGTERMSIGHUP 또한 방지 할 수 finally당신이 종료 자신을 제어 할 수있는 처리기를 설치하지 않으면 실행 블록; 기본적으로 Python은 SIGTERM또는 SIGHUP.
  • 의 예외로 finally인해 정리가 완료되지 않을 수 있습니다. 사용자 히트 제어-C을 경우 하나 특히 주목할만한 경우는 단지 우리가 실행을 시작하고 같은 finally블록을. 파이썬은KeyboardInterruptfinally 블록 내용의 모든 줄을 건너 뜁니다 . ( KeyboardInterrupt-안전한 코드는 작성하기가 매우 어렵습니다).
  • 컴퓨터의 전원이 꺼 지거나 최대 절전 모드에서 깨어나지 않으면 finally블록이 실행되지 않습니다.

finally블록은 거래 시스템이 아닙니다; 원 자성 보장이나 그 어떤 것도 제공하지 않습니다. 이러한 예 중 일부는 분명해 보일 수 있지만 이러한 일이 발생할 수 있다는 사실을 잊고 finally너무 많이 의존하기 쉽습니다 .


답변

예. 마지막으로 항상 승리합니다.

이를 무력화하는 유일한 방법은 finally:실행할 기회를 얻기 전에 실행을 중지 하는 것입니다 (예 : 인터프리터 충돌, 컴퓨터 끄기, 생성기 영구 정지).

내가 생각하지 못한 다른 시나리오가 있다고 생각합니다.

생각하지 못했던 몇 가지 더 있습니다.

def foo():
    # finally always wins
    try:
        return 1
    finally:
        return 2

def bar():
    # even if he has to eat an unhandled exception, finally wins
    try:
        raise Exception('boom')
    finally:
        return 'no boom'

인터프리터를 종료하는 방법에 따라 마지막으로 “취소”할 수 있지만 다음과 같지는 않습니다.

>>> import sys
>>> try:
...     sys.exit()
... finally:
...     print('finally wins!')
...
finally wins!
$

불안정한 사용 os._exit(제 생각에는 “통역사 충돌”에 해당) :

>>> import os
>>> try:
...     os._exit(1)
... finally:
...     print('finally!')
...
$

저는 현재이 코드를 실행하여 우주의 열사 후에도 마침내 실행될 것인지 테스트하고 있습니다.

try:
    while True:
       sleep(1)
finally:
    print('done')

하지만 아직 결과를 기다리고 있으니 나중에 여기에서 다시 확인하세요.


답변

Python 문서 에 따르면 :

이전에 무슨 일이 있었는지에 관계없이 코드 블록이 완료되고 발생한 예외가 처리되면 최종 블록이 실행됩니다. 예외 처리기 또는 else- 블록에 오류가 있고 새로운 예외가 발생하더라도 최종 블록의 코드는 계속 실행됩니다.

또한 finally 블록에있는 문을 포함하여 여러 개의 return 문이있는 경우 finally 블록 반환이 실행되는 유일한 문이라는 점에 유의해야합니다.


답변

글쎄, 예 그리고 아니오.

보장되는 것은 Python이 항상 finally 블록을 실행하려고한다는 것입니다. 블록에서 복귀하거나 포착되지 않은 예외를 발생시키는 경우, 실제로 예외를 반환하거나 발생하기 직전에 finally 블록이 실행됩니다.

(귀하의 질문에있는 코드를 실행하여 스스로 제어 할 수 있었던 것)

finally 블록이 실행되지 않는 유일한 경우는 Python 인터프리터 자체가 예를 들어 C 코드 내부 또는 정전으로 인해 충돌하는 경우입니다.


답변

생성기 함수를 사용하지 않고 이것을 찾았습니다.

import multiprocessing
import time

def fun(arg):
  try:
    print("tried " + str(arg))
    time.sleep(arg)
  finally:
    print("finally cleaned up " + str(arg))
  return foo

list = [1, 2, 3]
multiprocessing.Pool().map(fun, list)

수면은 일관성없는 시간 동안 실행될 수있는 코드 일 수 있습니다.

여기서 일어나는 것처럼 보이는 것은 완료하는 첫 번째 병렬 프로세스가 try 블록을 성공적으로 떠나지 만 함수에서 아무데도 정의되지 않은 값 (foo)을 반환하려고 시도하는 것입니다. 이로 인해 예외가 발생합니다. 이 예외는 다른 프로세스가 finally 블록에 도달하는 것을 허용하지 않고 맵을 죽입니다.

또한 bar = bazztry 블록에서 sleep () 호출 바로 뒤에 줄을 추가하면 . 그런 다음 해당 라인에 도달하는 첫 번째 프로세스는 예외 (bazz가 정의되지 않았기 때문에)를 발생시켜 자체 finally 블록이 실행되도록 한 다음 맵을 종료하여 다른 try 블록이 finally 블록에 도달하지 않고 사라지게합니다. 반환 문에 도달하지 않는 첫 번째 프로세스도 마찬가지입니다.

이것이 Python 다중 처리에서 의미하는 것은 프로세스 중 하나라도 예외가있을 수있는 경우 모든 프로세스에서 리소스를 정리하는 예외 처리 메커니즘을 신뢰할 수 없다는 것입니다. 다중 처리 맵 호출 외부에서 추가 신호 처리 또는 리소스 관리가 필요합니다.


답변

몇 가지 예와 함께 작동 방식을 확인하는 데 도움이되는 수락 된 답변에 대한 부록 :

  • 이:

     try:
         1
     except:
         print 'except'
     finally:
         print 'finally'

    출력됩니다

    드디어

  •    try:
           1/0
       except:
           print 'except'
       finally:
           print 'finally'

    출력됩니다

    마지막을 제외하고


답변