development

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

big-blog 2020. 2. 23. 11:57
반응형

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


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

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

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

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

파이썬 핵심 개발자조차도 흐름 제어에 예외를 사용하며 해당 스타일은 언어에 크게 영향을 미칩니다 (즉, 반복자 프로토콜은 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를 가장 많이 사용하는 것은 약간의 미화를위한 것입니다. 이 사용은 항상 선택 사항이며 반드시 필요한 것은 아닙니다.


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

예외가 없으면 실행될 코드 대신 블록에 블록에 코드를 포함 시킬 수 있다는 것이 사실 이지만, 그 코드 자체가 우리가 잡는 종류의 예외를 발생 시키면 어떻게 될까요? 블록에 그대로두면 버그가 숨겨집니다.elsetrytry

우리는 코드에서 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

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


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

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

처리해야 할 사용자 입력이 있지만 이미 처리 된 기본값이있는 상황이 있다고 가정하십시오. 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

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

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


파이썬에서 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)을 읽고, 긴장하고있는 원리가 있다는 것을 이해하고, 그 문장 중 하나에 너무 의존하는 교리에주의하십시오.


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.

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"
   ....:

당신이 이것을 볼 때마다 :

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

이것은 파이썬에서 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

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 년 동안 작성된 많은 코드를 정리해야합니다. 나중에 읽고 수정하려고합니다. 나는 매일 가독성에 투표하고 일요일에는 두 번 투표합니다.

참고 URL : https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python



반응형