파이썬에서 try vs if 사용


145

변수가 값을 갖는지 테스트 할 때 어느 것을 사용 할지 try또는 결정해야 할 근거가 if있습니까?

예를 들어, 목록을 반환하거나 값을 반환하지 않는 함수가 있습니다. 처리하기 전에 결과를 확인하고 싶습니다. 다음 중 더 바람직한 이유는 무엇입니까?

result = function();
if (result):
    for r in result:
        #process items

또는

result = function();
try:
    for r in result:
        #process items
except TypeError:
    pass;

관련 토론 :

파이썬에서 멤버 존재 확인


#process items 스탠자가 TypeError를 던지기 쉬운 경우에는 다시 시도해야합니다 : except : block. 이 특정 예제의 경우 다음과 같이 if를 사용하면됩니다.
richo

답변:


237

파이썬은 LBYL 스타일 ( " 도약 전에보기 ")보다 EAFP 스타일 ( " 권한보다 용서를 구하는 것이 더 쉽다")을 권장한다고 들었습니다 . 나에게 그것은 효율성과 가독성의 문제입니다.

None99 %의 시간이 예상되는 경우 (예 : 목록 또는 빈 문자열을 반환하는 대신 함수가 목록을 반환하거나result 실제로 반복 가능한 내용이 포함될try/except 접근법을 사용합니다 . 예외가 실제로 예외적 인 경우 더 빠릅니다. 경우 result입니다 None후 사용 시간의 50 % 이상이 if아마 더 좋다.

몇 가지 측정으로이를 지원하려면 다음을 수행하십시오.

>>> import timeit
>>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
0.06379691968322732
>>> timeit.timeit(setup="a=1;b=1", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.0829463709378615
>>> timeit.timeit(setup="a=1;b=0", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.5070195056614466
>>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:\n a/b")
0.11940114974277094
>>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:\n a/b")
0.051202772912802175

따라서, if성명서는 항상 비용이 들지만,try/except 블록 . 그러나 Exception실제로 발생하면 비용이 훨씬 높습니다.

사기:

  • 사용하기에 완벽하게 (그리고 "pythonic") try/except흐름 제어 하는 .
  • 그러나 가장 의미가있는 경우 Exception 실제로 예외적 인 가 있습니다.

파이썬 문서에서 :

EAFP

허가보다 용서를 구하는 것이 더 쉽습니다. 이 일반적인 Python 코딩 스타일은 유효한 키 또는 속성이 있다고 가정하고 가정이 거짓으로 판명되면 예외를 포착합니다. 이 깨끗하고 빠른 스타일은 많은 사람들의 존재에 의해 특징 tryexcept문. 이 기술 은 C와 같은 다른 많은 언어에 공통적 인 LBYL 스타일 과 대조 됩니다.


1
감사합니다. 이 경우 이론적 근거가 결과를 기대할 수 있음을 알았습니다.
artdanil 2009

6
.... 그리고 그것은 파이썬을 위해 실제로 최적화 된 JIT를하기가 너무 어려운 이유 중 하나입니다. 최근 베타 LuaJIT 2에서 입증 된 바와 같이, 동적 언어 실제로 매우 빠릅니다. 그러나 그것은 초기 언어 디자인과 그것이 권장하는 스타일에 크게 의존합니다. (관련 노트에서, 언어 설계는 최고의 JavaScirpt JIT조차도 LuaJIT 1과 비교할 수없는 2보다 훨씬 적은 이유입니다)
Javier

1
@ 2rs2ts : 방금 비슷한 타이밍을 보냈습니다. Python 3에서는 키가 사전에있는 경우 try/except보다 25 % 빠릅니다 if key in d:. 예상대로 키가 사전에 없을 때 속도가 훨씬 느리고이 대답과 일치합니다.
Tim Pietzcker

5
그러나 이것이 오래된 대답이라는 것을 알고 1/1있습니다 dis.dis('1/1').
Andrea Corbellini

1
또한 수행하려는 작업에 따라 다릅니다. 단일 부서는 빠르며 오류를 잡는 데 다소 비쌉니다. 그러나 예외 처리 비용이 수용 가능한 경우에도 컴퓨터가 요구하는 사항을 신중하게 생각하십시오. 작업 자체가 결과에 관계없이 매우 비싸고 성공 여부를 알 수있는 저렴한 방법이있는 경우 : 사용하십시오. 예 : 공간이 충분하지 않다는 것을 알기 위해 파일의 절반을 복사하지 마십시오.
Bachsau

14

함수는 혼합 유형 (예 : 목록 또는 빈 문자열)을 반환해서는 안됩니다. 값 목록 또는 빈 목록 만 반환해야합니다. 그런 다음 아무것도 테스트 할 필요가 없습니다. 즉 코드가 다음과 같이 축소됩니다.

for r in function():
    # process items

2
나는 그 시점에서 당신과 완전히 동의합니다. 그러나 기능은 내 것이 아니며 그냥 사용하고 있습니다.
artdanil 2009

2
@artdanil : 브랜든 코프만 (Brandon Corfman)이 생각하는 것과 같은 방식으로 작성하는 기능으로 랩핑 할 수 있습니다.
quamrana 2009

24
질문은 다음과 같습니다. 래퍼는 if 또는 iterable 한 경우를 처리하려고 시도해야합니까?
jcdyer 2009

@quamrana 정확히 내가하려고하는 것입니다. @jcd가 지적했듯이 래퍼 함수에서 상황을 처리하는 방법에 대한 질문입니다.
artdanil 2009

12

내가 제공 한 코드가 언뜻 명확하지 않고 코드 샘플 다음에 설명을 읽어야하는 경우 내 솔루션을 무시하십시오.

"값이 리턴되지 않음"이 리턴 값이 없음을 의미한다고 가정 할 수 있습니까? 예인 경우 또는 "값 없음"이 False boolean-wise 인 경우 코드에서 기본적으로 "값 없음"을 "반복하지 않음"으로 처리하므로 다음을 수행 할 수 있습니다.

for r in function() or ():
    # process items

경우 function()사실이 아니다 반환 무엇인가, 당신으로 반복 빈 튜플 이상은, 즉 어떤 반복 실행되지 않습니다. 이것은 본질적으로 LBYL입니다.


4

두 번째 예제가 깨졌습니다. 문자열과 목록을 모두 반복 할 수 있으므로 코드에서 TypeError 예외가 발생하지 않습니다. 빈 문자열 또는 목록을 반복하는 것도 유효합니다. 루프 본문을 0 번 실행합니다.


4

다음 중 더 바람직한 이유는 무엇입니까?

이 경우에는 도약하기 전에 확인하는 것이 좋습니다. 예외 접근 방식을 사용하면 TypeError가 루프 본문의 어느 곳에서나 발생할 수 있으며 잡히고 버려집니다. 원하는 것이 아니며 디버깅이 까다로워집니다.

(하지만 Brandon Corfman에 동의합니다 : 빈 목록 대신 'no items'에 대해 None을 반환하지 않습니다. Python 또는 Java에서 볼 수없는 Java 코더의 불쾌한 습관입니다.)


4

일반적으로 내가 얻은 인상은 예외적 인 상황을 위해 예외를 예약해야한다는 것입니다. result가 비어 있지 않을 것으로 예상되는 경우 (예를 들어, 디스크가 충돌 한 경우 등) 두 번째 방법이 적합합니다. 반면에, 빈 상태 result가 정상적인 조건에서 완벽하게 합리적이라면, if명세서로 테스트하는 것이 더 합리적입니다.

나는 (보다 일반적인) 시나리오를 염두에 두었습니다.

# keep access counts for different files
file_counts={}
...
# got a filename somehow
if filename not in file_counts:
    file_counts[filename]=0
file_counts[filename]+=1

동등한 대신 :

...
try:
    file_counts[filename]+=1
except KeyError:
    file_counts[filename]=1

이것은 Tim Pietzcker가 언급 한 접근 방식의 차이점의 예입니다. 첫 번째는 LBYL이고 두 번째는 EAFP
Managu 2009

++파이썬에서 작동하지 않는 간단한 메모 += 1대신 대신 사용하십시오.
tgray

3

bobince는 현명하게 두 번째 사례를 줄 바꿈하면 루프에서 TypeErrors를 잡을 수 있다고 지적합니다. 실제로 시도를 사용하려면 루프 전에 반복 가능한지 테스트 할 수 있습니다

result = function();
try:
    it = iter(result)
except TypeError:
    pass
else:
    for r in it:
        #process items

보시다시피, 그것은 추악합니다. 나는 그것을 제안하지는 않지만 완전성을 위해 언급해야합니다.


내 눈에, 의도적으로 형식 오류가 발생하는 것은 항상 나쁜 코딩 스타일입니다. 개발자는 예외없이 해당 값을 처리하기 위해 기대할 사항을 알고 준비해야합니다. 작업이 프로그래머의 관점에서 수행해야 할 작업을 수행하고 예상 결과가 목록 또는 인 None경우 항상 is None또는로 결과를 확인하십시오 is not None. 다른 한편으로는 합법적 인 결과에 대한 예외를 제기하는 것은 나쁜 스타일입니다. 예상치 못한 사항은 예외입니다. 예 : str.find()검색 자체가 오류없이 완료되었으므로 아무것도 발견되지 않으면 -1을 리턴합니다.
Bachsau

1

성능과 관련하여 일반적으로 예외를 발생시키지 않는 코드에 try 블록을 사용하면 매번 if 문을 사용하는 것보다 빠릅니다. 따라서 결정은 흥분 사례의 확률에 달려 있습니다.


-5

일반적으로 try / catch 또는 예외 처리 항목을 사용하여 흐름을 제어 해서는 안됩니다 . 배후에서는 StopIteration예외 발생을 통해 반복이 제어되지만 첫 번째 코드 스 니펫을 두 번째 코드보다 선호해야합니다.


7
이것은 Java에서 마찬가지입니다. 파이썬은 EAFP를 상당히 많이 수용합니다.
fengb

3
여전히 이상한 연습입니다. 대부분의 프로그래머의 두뇌는 내 경험에서 EAFP를 건너 뛸 수 있도록 연결되어 있습니다. 그들은 왜 특정 경로가 선택되었는지 궁금해합니다. 이것은 모든 규칙을 어기는 시간과 장소가 있다는 것입니다.
ilowe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.