파이썬에서 try-except-else를 사용하는 것이 좋은 습관입니까?


439

파이썬에서 때때로, 나는 블록을 본다 :

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

try-except-else가 존재하는 이유는 무엇입니까?

흐름 제어를 수행하기 위해 예외를 사용하고 있기 때문에 그런 종류의 프로그래밍을 좋아하지 않습니다. 그러나 언어에 포함되어 있다면 그럴만한 이유가 있어야합니까?

예외는 오류가 아니며 예외적 인 조건 (예 : 파일을 디스크에 쓰려고 시도하고 더 이상 공간이 없거나 권한이 없음)에만 사용해야하며 흐름이 아니라는 것을 이해합니다. 제어.

일반적으로 예외를 다음과 같이 처리합니다.

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

또는 예외가 발생했을 때 아무것도 반환하지 않으려면 다음을 수행하십시오.

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

답변:


666

"무지가 없는지 모르겠지만 흐름 제어를 수행하기 위해 예외를 사용하고 있기 때문에 그런 종류의 프로그래밍은 마음에 들지 않습니다."

파이썬 세계에서는 흐름 제어에 예외를 사용하는 것이 일반적이며 일반적입니다.

파이썬 핵심 개발자조차도 흐름 제어에 예외를 사용하며 해당 스타일은 언어에 크게 영향을 미칩니다 (즉, 반복자 프로토콜은 StopIteration 을 사용 하여 루프 종료 신호를 보냅니다).

또한 try-except-style은 일부 "윤리적 인 모양" 구성에 내재 된 경쟁 조건을 방지하는 데 사용됩니다 . 예를 들어, os.path.exists 를 테스트 하면 사용 시점에 따라 정보가 최신 정보가 아닐 수 있습니다. 마찬가지로 Queue.full 은 오래된 정보를 반환합니다. 이 경우에는 try-except-else 스타일 이보다 안정적인 코드를 생성합니다.

"예외가 오류가 아니라는 것을 이해합니다. 예외적 인 조건에서만 사용해야합니다."

다른 언어에서는이 규칙이 라이브러리에 반영된 문화적 규범을 반영합니다. "규칙"은 부분적으로 해당 언어의 성능 고려 사항을 기반으로합니다.

파이썬 문화 규범은 다소 다릅니다. 대부분의 경우 제어 흐름에 예외를 사용해야합니다 . 또한 파이썬에서 예외를 사용하더라도 일부 컴파일 언어에서와 같이 주변 코드와 호출 코드가 느려지지 않습니다 (예 : CPython 은 실제로 예외 사용 여부에 관계없이 모든 단계에서 예외 검사 코드를 이미 구현합니다).

다시 말해, "예외가 예외적이라는 것"에 대한 이해는 다른 언어에서는 의미가 있지만, 파이썬에는 해당되지 않는 규칙입니다.

"그러나 언어 자체에 언어가 포함되어 있다면 그럴만한 이유가 있어야합니까?"

경쟁 조건을 피하는 것 외에도 예외는 루프 외부에서 오류 처리를 가져 오는 데 매우 유용합니다. 이것은 자동 루프 불변 코드 모션 을 갖지 않는 해석 언어에서 필요한 최적화입니다 .

또한 예외는 문제를 처리하는 기능이 문제가 발생한 위치에서 멀리 떨어져있는 일반적인 상황에서 코드를 약간 단순화 할 수 있습니다. 예를 들어, 비즈니스 로직을위한 최상위 수준의 사용자 인터페이스 코드 호출 코드를 사용하여 하위 수준의 루틴을 호출하는 것이 일반적입니다. 하위 수준 루틴 (예 : 데이터베이스 액세스의 고유 키에 대한 중복 레코드)에서 발생하는 상황은 최상위 코드에서만 처리 할 수 ​​있습니다 (예 : 기존 키와 충돌하지 않는 새 키를 사용자에게 요청). 이런 종류의 제어 흐름에 예외를 사용하면 중간 수준의 루틴이 문제를 완전히 무시하고 흐름 제어의 측면과 완벽하게 분리 될 수 있습니다.

예외의 불가 결성에 대한 멋진 블로그 게시물이 여기에 있습니다 .

또한이 스택 오버플로 답변 : 예외는 예외적 인 예외입니까?

"시도 제외 다른 존재 이유는 무엇입니까?"

else 절 자체가 흥미 롭습니다. finally 절 전에 예외가 없을 때 실행됩니다. 그것이 주된 목적입니다.

else-clause가 없으면 종료 전에 추가 코드를 실행하는 유일한 옵션은 try-clause에 코드를 추가하는 서투른 연습입니다. try-block으로 보호되지 않는 코드에서 예외가 발생할 위험이 있기 때문에 서투른 것입니다.

마무리하기 전에 보호되지 않은 추가 코드를 실행하는 사용 사례는 자주 발생하지 않습니다. 따라서 게시 된 코드에서 많은 예제를 볼 것으로 기대하지 마십시오. 다소 드물다.

else-clause의 또 다른 유스 케이스는 예외가 발생하지 않을 때 발생해야하고 예외가 처리 될 때 발생하지 않는 동작을 수행하는 것입니다. 예를 들면 다음과 같습니다.

recip = float('Inf')
try:
    recip = 1 / f(x)
except ZeroDivisionError:
    logging.info('Infinite result')
else:
    logging.info('Finite result')

unittest 러너에서 또 다른 예가 발생합니다.

try:
    tests_run += 1
    run_testcase(case)
except Exception:
    tests_failed += 1
    logging.exception('Failing test case: %r', case)
    print('F', end='')
else:
    logging.info('Successful test case: %r', case)
    print('.', end='')

마지막으로 try-block에서 else-clause를 가장 일반적으로 사용하는 것은 약간의 미화를위한 것입니다. 이 사용은 항상 선택 사항이며 반드시 필요한 것은 아닙니다.


28
"그것은 try-block에 의해 보호되지 않는 코드에서 예외를 발생시킬 위험이 있기 때문에 서투른 것입니다." 이것이 가장 중요한 학습입니다
Felix Dombek

2
답변 해주셔서 감사합니다. try-except-else 사용법의 예를 찾고있는 독자는 shutil의 copyfile 메소드를 살펴보십시오. github.com/python/cpython/blob/master/Lib/shutil.py#L244
suripoori

2
요점은 else 절은 try 절이 성공할 때만 실행된다는 것입니다.
Jonathan

172

try-except-else가 존재하는 이유는 무엇입니까?

try블록은 당신이 예상되는 오류를 처리 할 수 있습니다. except블록 만이 처리 할 준비가되어 예외를 포착해야한다. 예기치 않은 오류를 처리하면 코드가 잘못된 일을하고 버그를 숨길 수 있습니다.

else오류가 없다면 절은 실행됩니다, 그리고에 그 코드를 실행하지 않음으로써 try블록, 당신은 예기치 않은 오류가 끼지 않도록. 다시 말하지만 예기치 않은 오류를 발견하면 버그가 숨겨 질 수 있습니다.

예를 들면 다음과 같습니다.

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

스위트 룸은 "제외 시도는,"이 개 옵션 조항을 가지고 elsefinally. 실제로 try-except-else-finally입니다.

elsetry블록 에서 예외가없는 경우에만 평가됩니다 . 아래에서보다 복잡한 코드를 단순화 할 수 있습니다.

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

따라서 else(버그를 생성 할 수있는) 대안 과를 비교하면 코드 줄이 줄어들고 더 읽기 쉽고 유지 관리가 쉽고 버그가 적은 코드베이스를 가질 수 있습니다.

finally

finally return 문을 사용하여 다른 행을 평가하는 경우에도 실행됩니다.

의사 코드로 분류

주석과 함께 모든 기능을 보여주는 가능한 가장 작은 형태로 이것을 분류하는 것이 도움이 될 수 있습니다. 의사 코드가 함수 내에 있다고 구문 상으로는 정확하지만 이름을 정의하지 않으면 실행할 수 없다고 가정합니다.

예를 들면 다음과 같습니다.

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

우리가 사실입니다 의 코드 포함 else에 블록 try은 예외가 없다면 실행할 수 있지만 것입니다 경우, 대신 블록 무슨 코드 자체는 우리가 잡고있는 종류의 예외가 발생하는 경우? try블록에 그대로두면 버그가 숨겨집니다.

우리는 코드에서 try실패하면 큰 실패를 원한다는 원칙 하에서 예상치 못한 예외를 피하기 위해 블록 의 코드 줄을 최소화 하려고합니다. 이것이 가장 좋은 방법 입니다.

예외는 오류가 아니라는 것을 이해합니다.

파이썬에서 대부분의 예외는 오류입니다.

pydoc을 사용하여 예외 계층을 볼 수 있습니다. 예를 들어, 파이썬 2에서 :

$ python -m pydoc exceptions

또는 파이썬 3 :

$ python -m pydoc builtins

우리에게 계층 구조를 제공합니다. Exception파이썬은 엔딩 for루프 ( StopIteration) 와 같은 것들에 대해 일부를 사용하지만 대부분의 종류의 오류가 있음을 알 수 있습니다 . 이것은 파이썬 3의 계층입니다 :

BaseException
    Exception
        ArithmeticError
            FloatingPointError
            OverflowError
            ZeroDivisionError
        AssertionError
        AttributeError
        BufferError
        EOFError
        ImportError
            ModuleNotFoundError
        LookupError
            IndexError
            KeyError
        MemoryError
        NameError
            UnboundLocalError
        OSError
            BlockingIOError
            ChildProcessError
            ConnectionError
                BrokenPipeError
                ConnectionAbortedError
                ConnectionRefusedError
                ConnectionResetError
            FileExistsError
            FileNotFoundError
            InterruptedError
            IsADirectoryError
            NotADirectoryError
            PermissionError
            ProcessLookupError
            TimeoutError
        ReferenceError
        RuntimeError
            NotImplementedError
            RecursionError
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

논평자가 물었다 :

외부 API를 핑하는 메소드가 있고 API 랩퍼 외부의 클래스에서 예외를 처리하려고한다고 가정하십시오. e가 예외 오브젝트 인 경우 except 절의 메소드에서 e를 간단히 리턴합니까?

아니요, 예외를 반환하지 않고 raise스택 추적을 유지하기 위해 예외를 다시 발생 시킵니다.

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

또는 Python 3에서는 예외 체인을 사용하여 새 예외를 발생시키고 역 추적을 유지할 수 있습니다.

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

나는 여기내 대답을 자세히 설명 합니다 .


공감! 핸들 부분에서 일반적으로 무엇을합니까? 외부 API를 핑하는 메소드가 있고 API 랩퍼 외부의 클래스에서 예외를 처리하려고한다고 가정하십시오. e가 예외 오브젝트 인 경우 except 절 아래의 메소드에서 단순히 e를 리턴합니까?
PirateApp

1
@PirateApp 감사합니다! 아니, 그것을 반환하지 않습니다, 당신은 베어 아마 리 레이즈로해야 raise또는 예외 체인을 -하지만 더 주제에 대한 그의는 여기에서 다루지 : stackoverflow.com/q/2052390/541136는 - 나는했습니다 후 나는 아마이 주석을 제거합니다 당신이 그들을 본 것을 보았다.
아론 홀

자세한 내용 감사합니다! 지금 게시물을 통해
PirateApp

36

파이썬은 예외가 예외적 인 경우에만 사용되어야한다는 생각에 동의하지 않습니다. 실제로이 관용구는 '허용이 아닌 용서를 구합니다' . 이는 예외를 흐름 제어의 일상적인 부분으로 사용하는 것이 완벽하게 수용 가능하고 실제로 권장됨을 의미합니다.

이 방법으로 작업하면 일부 문제를 피하는 데 도움이되고 (경쟁 조건을 피하는 경우가 많음) 코드를 좀 더 읽기 쉽게 만드는 경향이 있으므로 일반적으로 좋은 방법입니다.

처리해야 할 사용자 입력이 있지만 이미 처리 된 기본값이있는 상황이 있다고 가정하십시오. 이 try: ... except: ... else: ...구조는 매우 읽기 쉬운 코드를 만듭니다.

try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

다른 언어로 작동하는 방식과 비교하십시오.

raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

장점을 주목하십시오. 값이 유효한지 확인할 필요가 없으며 별도로 구문 분석하면 한 번 수행됩니다. 코드는 또한 더 논리적 인 진행을 따르고 기본 코드 경로가 먼저오고 '작동하지 않으면이 작업을 수행하십시오'.

이 예는 당연히 약간 구성되었지만이 구조에 대한 사례가 있음을 보여줍니다.


15

파이썬에서 try-except-else를 사용하는 것이 좋은 습관입니까?

이에 대한 답은 상황에 따라 다르다는 것입니다. 이렇게하면 :

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

파이썬을 잘 모른다는 것을 보여줍니다. 이 기능은 다음 dict.get방법으로 캡슐화됩니다 .

item = d.get('item', 'default')

try/except 블록 원자 방법 한줄 효과적으로 수행 할 수있는 기록 훨씬 더 육안으로 클러스터링 된 자세한 방법이다. 이것이 사실 인 다른 경우가 있습니다.

그러나 이것이 모든 예외 처리를 피해야한다는 의미는 아닙니다. 경우에 따라 경쟁 조건을 피하는 것이 좋습니다. 파일이 존재하는지 확인하지 말고 파일을 열어 적절한 IOError를 잡으십시오. 단순성과 가독성을 위해 이것을 캡슐화하거나 제안으로 고려하십시오.

파이썬선 (Zen of Python)을 읽고, 긴장하고있는 원리가 있다는 것을 이해하고, 그 문장 중 하나에 너무 의존하는 교리에주의하십시오.


12

try-except-else-finally에 대한 모든 것을 설명하는 다음 예를 참조하십시오.

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

그것을 구현하고 다음을 수행하십시오.

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.

3
이것은 아주 서두른 사람이 긴 추상 설명을 읽지 않아도 전체 try 절 을 신속하게 보여주는 훌륭하고 간단한 예입니다 . (물론 더 이상 서두르지 않으면 다시 와서 전체 초록을 읽어야합니다.)
GlobalSoftwareSociety

6

finally 블록은 try에서 else 블록을 사용하는 것과 같지 않으므로 finally 블록을 사용할 때는주의해야합니다. finally 블록은 시도의 결과와 상관없이 실행됩니다.

In [10]: dict_ = {"a": 1}

In [11]: try:
   ....:     dict_["b"]
   ....: except KeyError:
   ....:     pass
   ....: finally:
   ....:     print "something"
   ....:     
something

모두 else 블록을 사용하면 코드를 더 읽기 쉽고 예외가 발생하지 않을 때만 실행됩니다

In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print "something"
   ....:

나는 마침내 항상 실행된다는 것을 알고 있으며, 이것이 항상 기본값을 설정하여 우리에게 유리하게 사용될 수있는 이유입니다. 따라서 예외의 경우 반환됩니다. 최종 블록을 제거하기에 충분합니다. Btw, 예외 캐치 패스를 사용하는 것은 내가 절대로하지 않을 것입니다 :)
Juan Antonio Gomez Moriano

@ Juan Antonio Gomez Moriano, 내 코딩 블록은 예를 들기위한 것입니다. 아마 패스를 사용하지 않을 것입니다
Greg

4

당신이 이것을 볼 때마다 :

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

또는 이것조차도 :

try:
    return 1 / x
except ZeroDivisionError:
    return None

대신 이것을 고려하십시오 :

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x

1
그것은 단지 내 친구의 예일 뿐이므로 내 질문에 대답하지 않습니다.
Juan Antonio Gomez Moriano

파이썬에서 예외는 오류가 아닙니다. 그들은 심지어 탁월하지도 않습니다. 파이썬에서는 흐름 제어에 예외를 사용하는 것이 일반적이며 당연합니다. 이는 표준 라이브러리에 contextlib.suppress ()가 포함되어 있음을 증명합니다. 여기 레이몬드 Hettinger의 답변을 참조하십시오 : stackoverflow.com/a/16138864/1197429을 (레이몬드는 핵심 파이썬 기여하고, 모든 것을 파이썬의 권위자입니다!)
라지브 Bakulesh 샤

4

아무도이 의견을 게시하지 않았기 때문에

대부분의 사람들에게 친숙하지 않기 때문에else 절을 피하십시오try/excepts

키워드는 달리 try, exceptfinally,의 의미 else절은 자기 - 분명하지 않다; 읽기 어렵습니다. 자주 사용되지 않기 때문에 코드를 읽는 사람들이 문서를 다시 확인하여 진행 상황을 이해하도록 할 수 있습니다.

( try/except/else내 코드베이스에서를 발견 하고 wtf 순간을 일으켜서 인터넷 검색을 강요 했기 때문에이 답변을 정확하게 작성 하고 있습니다).

따라서 OP 예제와 같은 코드가있는 곳마다 :

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    # do some more processing in non-exception case
    return something

리팩토링을 선호합니다

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    return  # <1>
# do some more processing in non-exception case  <2>
return something
  • <1> 명백한 귀환은 예외적으로 우리가 작업을 마쳤 음을 분명히 보여줍니다.

  • <2>는 좋은 사소한 부작용으로, else블록에 있던 코드는 한 수준으로 독려됩니다.


1
악마의 주장 반대 주장 : 사람들이 더 많이 사용할수록 더 잘 채택 될 것입니다. 가독성이 중요하다는 데 동의하지만 생각할만한 음식입니다. 즉, 누군가가 try-else를 이해하면 많은 경우 대안보다 훨씬 더 읽기 쉽다고 주장합니다.
bob

2

이것은 파이썬에서 try-except-else-finally 블록을 이해하는 방법에 대한 간단한 스 니펫입니다.

def div(a, b):
    try:
        a/b
    except ZeroDivisionError:
        print("Zero Division Error detected")
    else:
        print("No Zero Division Error")
    finally:
        print("Finally the division of %d/%d is done" % (a, b))

div 1/1을 사용해 봅시다 :

div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done

div 1/0을 사용해 봅시다

div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done

1
나는 이것이 왜 else 코드를 try 안에 넣을 수 없는지를 예시하지 못한다고 생각한다
Mojimi

-4

OP, 당신은 정확합니다. 파이썬에서 try / except 후 else는 추악 합니다. 필요하지 않은 다른 흐름 제어 객체로 이어집니다.

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

완전히 동등한 것은 다음과 같습니다.

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

이것은 else 절보다 훨씬 명확합니다. try / except 후 else는 자주 작성되지 않으므로 의미가 무엇인지 파악하는 데 약간의 시간이 걸립니다.

일을 할 수 있다고해서 일을해야한다는 의미는 아닙니다.

누군가가 유용하다고 생각했기 때문에 많은 기능이 언어에 추가되었습니다. 문제는 더 많은 기능 일수록 사람들이 일반적으로 종과 휘파람을 사용하지 않기 때문에 덜 명확하고 명백한 것입니다.

여기 내 5 센트. 영리하다고 생각하고 그것이 엉망이 될 때 uber-tight, uber- 능률적 인 방식으로 코드를 작성하려는 대학 개발자로부터 1 년 동안 작성된 많은 코드를 정리해야합니다. 나중에 읽고 수정하려고합니다. 나는 매일 가독성에 투표하고 일요일에는 두 번 투표합니다.


15
네가 옳아. 그것은 당신의 print진술이 실패 하지 않는 한 완전히 명확하고 동등한 것 입니다. 을 x = blah()반환 str하지만 인쇄 문은 print 'just succeeded with blah. x == %d' % x? 이제 TypeError처리 할 준비가되지 않은 곳에서 생성되고 있습니다. x = blah()예외의 원인을 찾기 위해 검사 중이며 예외도 없습니다. 나는 else이 실수를 저 지르지 못하게 한 곳 에서이 작업을 두 번 이상 수행했습니다 . 이제는 더 잘 알고 있습니다. :-D
Doug R.

2
... 그렇습니다. 이 else절은 예쁘지 않으며, 익숙해 질 때까지는 직관적이지 않습니다. 그러나 finally처음 사용하기 시작한 것도 아니 었 습니다.
Doug R.

2
더그 R.를 에코하기 위해서는 아니다 의 문 동안 예외 때문에 해당 else절을하는 하지 에 의해 붙 잡았다 except.
alastair

if ... except ... else 가독성이 더 높으면, 그렇지 않으면 "오, try 블록 이후에 예외없이 try 블록 외부의 명령문으로 가십시오"를 읽어야합니다. 따라서 else : 더 나은 imo. 또한 트랩되지 않은 명령문을 초기 try 블록 외부에 두는 것이 좋습니다.
cowbert

1
@DougR. " x = blah()예외의 원인을 찾기 위해 검사 중입니다." traceback왜 잘못된 위치에서 예외 소스를 검사합니까?
nehem
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.