무언가를 '시도'하고 예외를 포착하거나 예외를 피하는 것이 가능한지 테스트하는 것이 더 낫습니까?


139

if무언가가 유효한지 테스트해야합니까, 아니면 try예외를 잡아야합니까?

  • 한 가지 방법이 선호된다는 확실한 문서가 있습니까?
  • 한 가지 방법은 더 파이썬 입니까?

예를 들어,

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

또는:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

몇 가지 생각 ...
PEP 20 은 말합니다 :

오류가 자동으로 전달되지 않아야합니다.
명시 적으로 침묵하지 않는 한.

try대신에를 사용하는 것이 if자동으로 전달되는 오류로 해석되어야합니까? 그렇다면, 이런 식으로 그것을 사용하여 명시 적으로 침묵 시키는가?


나는 당신 이 한 가지 일만 할 수있는 상황에 대해서는 언급 하지 않습니다 . 예를 들면 다음과 같습니다.

try:
    import foo
except ImportError:
    import baz

답변:


161

당신은 좋아한다 try/except를 통해 if/else그 결과의 경우

  • 추가 조회를 방지하여 속도 향상
  • 더 깔끔한 코드

종종 이들은 서로 밀접한 관계가 있습니다.


속도 향상

긴 목록에서 요소를 찾으려고 할 경우 :

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

index목록에 있고 IndexError가 발생하지 않는 경우를 제외하고는 try를 사용하는 것이 가장 좋습니다 . 이 방법으로에 의해 추가 조회가 필요하지 않습니다 if index < len(my_list).

파이썬은 예외 사용을 권장합니다. 예외 처리Dive Into Python 의 문구입니다 . 귀하의 예제는 예외를 자동으로 전달 하는 대신 예외를 정상적으로 처리 할뿐만 아니라 예외 를 발견 할 수없는 예외적 인 경우 에만 예외가 발생합니다 (따라서 단어 예외 !).


깔끔한 코드

공식 파이썬 문서는 EAFP : 권한보다 용서를 구하는 것이 더 쉽다고 언급 하고 Rob Knight실수를 피하는 대신 잡는 것이 더 깨끗하고 읽기 쉬운 코드를 만들 수 있다고 지적합니다 . 그의 예는 다음과 같이 말합니다.

더 나빠 (LBYL '도약 전에 봐') :

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit():
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(s)

더 나은 (EAFP : 허가보다 용서를 구하는 것이 더 쉽다) :

try:
    return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

if index in mylist인덱스 테스트는 가능한 인덱스가 아닌 mylist의 요소입니다. if index < len(mylist)대신에 원할 것입니다.
ychaouche

1
이것은 의미가 있지만 int ()에 대해 가능한 예외를 던질 수있는 문서를 어디서 찾을 수 있습니까? docs.python.org/3/library/functions.html#int 여기에서이 정보를 찾을 수 없습니다.
BrutalSimplicity

반대로, 당신은 (에서이 사건을 추가 할 수 있습니다 해제 문서를. ) 당신의 대답에, 당신이 선호하는시기를 if/else보다try/catch
rappongy

20

이 특별한 경우에는 완전히 다른 것을 사용해야합니다.

x = myDict.get("ABC", "NO_ABC")

그러나 일반적으로 : 테스트가 자주 실패 할 것으로 예상되는 경우을 사용하십시오 if. 작업을 시도하고 실패한 경우 예외를 포착하는 것과 비교하여 테스트 비용이 비싸면를 사용하십시오 try. 이러한 조건 중 어느 것도 해당되지 않으면 더 쉽게 읽을 수 있습니다.


1
코드 샘플 아래 설명에 +1이 표시됩니다.
ktdrv

4
나는 이것이 그가 요구 한 것이 아니라는 것이 분명하다고 생각하며, 이제는 게시물을 더 명확하게하기 위해 게시물을 편집했습니다.
agf

9

사용 tryexcept직접하기보다는 내부 if가드한다 항상 경쟁 조건의 가능성이있는 경우 수행 할 수. 예를 들어, 디렉토리가 존재하는지 확인하려면 다음을 수행하지 마십시오.

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

다른 스레드 또는 프로세스가 isdir와 사이에 디렉토리를 생성하면 mkdir종료됩니다. 대신 다음을 수행하십시오.

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

'foo'디렉토리를 만들 수없는 경우에만 종료됩니다.


7

실패하기 전에 어떤 일이 실패하는지 확인하는 것이 사소한 경우에는 그 점을 선호해야합니다. 결국 예외를 구성 (관련된 역 추적 포함)하는 데 시간이 걸립니다.

다음에 대한 예외를 사용해야합니다.

  1. 예상치 못한 것들, 또는 ...
  2. 하나 이상의 로직 레벨을 뛰어 넘어야하는 것 (예 : break충분하지 않은 곳 ) 또는 ...
  3. 미리 예외를 처리 할 대상을 정확히 모르거나
  4. 고장에 대해 미리 점검하는 것이 비용이 많이 드는 것 (작업을 시도하는 것에 비해)

예를 들어 첫 번째 예에서 실제로 해야 할 일은 .get()기본값을 제공하기 위해 사용 하는 것입니다.

x = myDict.get('ABC', 'NO_ABC')

그 제외하고 if 'ABC' in myDict: x = myDict['ABC']; else: x = 'NO_ABC'종종 실제로 더 빨리 사용하는 것보다 get불행하게도,. 이것이 가장 중요한 기준이라고 말하지는 않지만 알아야 할 사항입니다.
agf

@agf : 명확하고 간결한 코드를 작성하는 것이 좋습니다. 나중에 최적화해야 할 경우 다시 와서 쉽게 다시 작성할 수 있지만 c2.com/cgi/wiki?PrematureOptimization
Amber

나는 알고 있다. 내 포인트는 것이었다 if / elsetry / except자신의 자리를 가질 수 있습니다 경우 특정 대안이있는 경우에도 서로 다른 성능 특성을 가지고 있기 때문에.
agf

@agf, 향후 버전에서 get () 메소드가 명시 적으로 조회하는 것보다 (최소한) 빨라질 지 여부를 알고 있습니까? 그건 그렇고, 두 번 찾을 때 (d : '[ABC']의 'ABC'와 마찬가지로) 시도는 다음과 같습니다.
Remi

2
@Remi 느린 부분은 .get()Python 수준에서 속성 조회 및 함수 호출 오버 헤드입니다. 기본 제공 키워드를 사용하면 기본적으로 C로 넘어갑니다. 조만간 더 빨리 커질 것이라고는 생각하지 않습니다. 지금까지 한 iftry, 읽기 dict.get () 메소드가 반환 포인터 일부 성능 정보를 가지고 있습니다. try사전 크기와 마찬가지로 적중률이 중요합니다 ( 키가 거의 항상 존재하는 경우 더 빠를 수 있음).
agf

5

다른 게시물에서 언급했듯이 상황에 따라 다릅니다. 데이터의 유효성을 미리 확인하는 대신, 특히 대규모 프로젝트에서 데이터를 사용할 때 try / except를 사용하면 몇 가지 위험이 있습니다.

  • try 블록의 코드는 예외가 발생하기 전에 모든 종류의 혼란을 일으킬 수 있습니다. if 문을 사전에 미리 확인하면 이것을 피할 수 있습니다.
  • try 블록에서 호출 된 코드가 TypeError 또는 ValueError와 같은 일반적인 예외 유형을 발생시키는 경우 실제로 예상했던 것과 동일한 예외를 catch하지 못할 수도 있습니다. 예외가 제기 될 수있는 라인.

예를 들어,

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

IndexError는 index_list 또는 my_list의 요소를 가져 오려고 할 때 발생했는지에 대해 아무 것도 말하지 않습니다.


4

if 대신 try를 사용하는 것이 자동으로 전달되는 오류로 해석되어야합니까? 그렇다면, 이런 식으로 사용하여 명시 적으로 침묵시키는 것입니까?

사용 try은 오류가 전달 될 수 있음을 인식하고 있습니다. 이는 오류를 자동으로 전달하는 것과 반대입니다. 사용 except하면 전혀 전달되지 않습니다.

논리가 더 복잡한 try: except:경우에 사용하는 것이 좋습니다 if: else:. 단순한 것이 복잡한 것보다 낫습니다. 복잡한 것이 복잡한 것보다 낫습니다. 허락보다 용서를 구하는 것이 더 쉽습니다.

"자동으로 오류를 전달해서는 안된다"는 경고는 코드에서 사용자가 알고있는 예외를 제기 할 수 있고 설계에서 가능성을 인정하는 경우이지만 예외를 처리하는 방식으로 설계되지 않은 경우입니다. 내 관점에서 볼 때 오류를 명시 적 pass으로 except차단 하는 것은 블록 에서 와 같은 작업을 수행하는 것입니다. 이는 "아무것도하지 않음"이 실제로 특정 상황에서 올바른 오류 처리라는 것을 이해해야합니다. (이것은 잘 작성된 코드에서 주석처럼 느껴질 수있는 몇 번 중 하나입니다.)

그러나 특정 예에서 어느 것도 적절하지 않습니다.

x = myDict.get('ABC', 'NO_ABC')

모든 사람들이 이것을 지적하는 이유는 일반적으로 이해하려는 욕구를 인정하고 더 나은 예를 제시 할 수 없다는 것입니다. 실제로 많은 경우에 동등한 부수적 인 단계가 실제로 존재하기 때문입니다. 문제 해결의 첫 번째 단계.


3

try/except제어 흐름에 사용할 때마다 다음을 자문하십시오.

  1. 언제 try블록이 성공하고 언제 실패 하는지 쉽게 알 수 있습니까?
  2. 블록 내부 의 모든 부작용 을 알고 try있습니까?
  3. 블록이 예외를 발생 시키는 모든 경우 를 알고 try있습니까?
  4. try블록 의 구현이 변경되면 제어 흐름이 여전히 예상대로 작동합니까?

이러한 질문 중 하나 이상에 대한 대답이 '아니요'인 경우 요청해야 할 많은 용서가있을 수 있습니다. 미래의 자기 자신으로부터


예입니다. 최근에 다음과 같은 큰 프로젝트에서 코드를 보았습니다.

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

프로그래머와 대화하면서 의도 한 제어 흐름은 다음과 같습니다.

x가 정수이면 y = foo (x)를 수행하십시오.

x가 정수 목록 인 경우 y = bar (x)를 수행하십시오.

이것은 foo데이터베이스 쿼리를 만들었 기 때문에 작동했으며 x정수 이면 쿼리가 성공 하고 ProgrammingErrorif x는 목록이었습니다.

사용 try/except은 잘못된 선택입니다.

  1. 예외 이름은 ProgrammingError실제 문제 ( x정수가 아님)를 나타내지 않으므로 진행 상황을 확인하기가 어렵습니다.
  2. ProgrammingError데이터베이스 호출, 시간을 낭비하는 동안 발생합니다. foo예외가 발생하기 전에 데이터베이스에 무언가를 쓰거나 다른 시스템의 상태를 변경하는 것이 밝혀지면 정말 끔찍할 것입니다 .
  3. 정수 목록 일 ProgrammingError때만 발생 하는지 확실하지 않습니다 x. 예를 들어 foo의 데이터베이스 쿼리에 오타가 있다고 가정하십시오 . 이 또한 발생할 수 있습니다 ProgrammingError. 결과는 정수일 bar(x)때 호출됩니다 x. 이로 인해 암호 예외가 발생하거나 예측할 수없는 결과가 발생할 수 있습니다.
  4. try/except블록은의 모든 향후 구현에 요구 사항을 추가합니다 foo. 우리가 변경할 때마다 foo, 우리는 지금이 목록을 처리하는 방법에 대해 생각하고 그것이 발생되었는지 확인해야 ProgrammingError하지, 말, AttributeError전혀 없거나 전혀없는 오류가 발생했습니다.

0

일반적인 의미를 위해 Python : Exceptions에서 숙어와 안티 이디엄을 읽는 것을 고려할 수 있습니다 .

특별한 경우 다른 사람들이 언급했듯이 다음을 사용해야합니다 dict.get().

get (키 [, 기본값])

키가 사전에 있으면 키의 값을 리턴하고 그렇지 않으면 기본값을 리턴하십시오. 기본값이 지정되지 않은 경우 기본값은 없음으로 설정되므로이 메소드는 KeyError를 발생시키지 않습니다.


나는 링크 가이 상황을 전혀 다루지 않는다고 생각합니다. 그 예는 예상되는 상황에서 예외 처리를 사용할지 여부가 아니라 실제 오류를 나타내는 것을 처리하는 것 입니다 .
agf

첫 번째 링크가 오래되었습니다.
Timofei Bondarev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.