파이썬에서 중첩 된 try / except 블록이 좋은 프로그래밍 습관입니까?


201

속성 호출로 사전에 액세스 해야하는 자체 컨테이너를 작성하고 있습니다. 컨테이너의 일반적인 사용법은 다음과 같습니다.

dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo

이런 식으로 쓰는 것은 어리석은 일이지만 그것이 제공해야 할 기능입니다. 나는 이것을 다음과 같은 방식으로 구현하려고 생각했다.

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        try:
            return self.dict[item]
        except KeyError:
            print "The object doesn't have such attribute"

난인지 확실하지 중첩 된 시도 / 블록은 다른 방법을 사용하는 것입니다, 그래서 좋은 연습입니다 제외 hasattr()하고 has_key():

def __getattribute__(self, item):
        if hasattr(self, item):
            return object.__getattribute__(item)
        else:
            if self.dict.has_key(item):
                return self.dict[item]
            else:
                raise AttributeError("some customised error")

또는 다음 중 하나를 사용하고 다음과 같이 하나의 catch catch 블록을 사용하십시오.

def __getattribute__(self, item):
    if hasattr(self, item):
        return object.__getattribute__(item)
    else:
        try:
            return self.dict[item]
        except KeyError:
            raise AttributeError("some customised error")

가장 파이썬적이고 우아한 옵션은 무엇입니까?


파이썬 신들이 제공하는 것에 감사드립니다 if 'foo' in dict_container:. 아멘.
gseattle 2019

답변:


181

첫 번째 예는 완벽합니다. 공식 파이썬 문서조차도 EAFP로 알려진이 스타일을 권장합니다 .

개인적으로 필요하지 않은 경우 중첩을 피하는 것이 좋습니다.

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        pass  # fallback to dict
    try:
        return self.dict[item]
    except KeyError:
        raise AttributeError("The object doesn't have such attribute") from None

추신. has_key()파이썬 2에서는 오랫동안 사용되지 않습니다 item in self.dict. 대신 사용하십시오 .


2
return object.__getattribute__(item)TypeError잘못된 수의 인수가 전달 되어 올바르지 않습니다 . 대신이어야 return object.__getattribute__(self, item)합니다.
martineau 2018 년

13
PEP 20 : 평평한 것이 중첩 된 것보다 낫습니다.
Ioannis Filippidis

7
from None마지막 줄 의 의미는 무엇입니까 ?
niklas

2
@niklas 기본적으로 예외 컨텍스트를 억제합니다 ( "이 예외를 처리하는 동안 또 다른 예외가 발생했습니다"). 여기를
Kade

파이썬 문서가 중첩 시도를 권장한다는 사실은 미친 것입니다. 분명히 끔찍한 스타일입니다. 실패 할 수있는 일련의 연산을 처리하는 올바른 방법은 파이썬이 지원하지 않는 일종의 모나 딕 구조를 사용하는 것입니다.
Henry Henrinson

19

Java에서는 실제로 흐름 제어에 예외를 사용하는 것은 좋지 않은 관행이지만 (주로 예외로 인해 jvm이 자원을 수집하도록 강요하기 때문에 ) 여기에는 더 중요한 두 가지 원칙이 있습니다 .Dy TypingEAFP . 이것은 기본적으로 객체가 작동한다고 생각하는 방식으로 객체를 사용하고 상황이 그렇지 않은 경우 처리하는 것이 좋습니다.

요약하면 유일한 문제는 코드가 너무 많이 들여 쓰기되는 것입니다. 느낌이 좋으면 lqc 제안과 같은 일부 중첩을 단순화하십시오.


10

구체적인 예를 들어 실제로 중첩 할 필요는 없습니다. try블록 의 표현식이 성공하면 함수가 반환되므로 전체 시도 / 제외 블록 이후의 모든 코드는 첫 번째 시도가 실패한 경우에만 실행됩니다. 그래서 당신은 할 수 있습니다 :

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        pass
    # execution only reaches here when try block raised AttributeError
    try:
        return self.dict[item]
    except KeyError:
        print "The object doesn't have such attribute"

그것들을 중첩시키는 것은 나쁘지 않지만 평평하게두면 구조가 더 명확 해집니다. 순차적으로 일련의 일을 시도하고 작동하는 첫 번째 항목을 반환합니다.

또한 여기 __getattribute__대신 실제로 사용하고 싶은지 생각할 수도 __getattr__있습니다. __getattr__일반 속성 조회 프로세스가 이미 실패했음을 알기 때문에 사용 하면 작업이 단순화됩니다.


10

조심하십시오-이 경우 먼저 finally만지지 만 건너 뜁니다.

def a(z):
    try:
        100/z
    except ZeroDivisionError:
        try:
            print('x')
        finally:
            return 42
    finally:
        return 1


In [1]: a(0)
x
Out[1]: 1

와우 내 마음이 아프다 ...이 동작을 설명하는 문서 조각을 알려 주시겠습니까?
Michal

1
@Michal : fyi :에 대해 두 finally블록이 모두 실행 a(0)되지만 부모 만 finally-return반환됩니다.
Sławomir Lenart

7

제 생각에는 이것이 파이썬을 다루는 가장 파이썬적인 방법 일 것입니다. 이는 내부 사전에 유지되는 "특수"속성 만 처리하면된다는 것을 의미하기 때문에 __getattr__()대신 정의합니다 __getattribute__().

def __getattr__(self, name):
    """only called when an attribute lookup in the usual places has failed"""
    try:
        return self.my_dict[name]
    except KeyError:
        raise AttributeError("some customized error message")

2
참고에 예외를 발생시키는 것을 except차단하는 파이썬 3에 혼란 출력을 줄 수 있음으로 인해의 (PEP 당 3134) 파이썬 3 트랙 제 예외합니다 ( KeyError제 예외 (의 "컨텍스트"등) AttributeError), 그리고이 도달하면 최상위 레벨에서는 두 예외를 모두 포함하는 역 추적을 인쇄합니다. 이는 두 번째 예외가 예상되지 않은 경우에 도움이 될 수 있지만 의도적으로 두 번째 예외를 제기하는 경우 바람직하지 않습니다. Python 3.3의 경우 PEP 415는을 사용하여 컨텍스트를 억제하는 기능을 추가했습니다 raise AttributeError("whatever") from None.
Blckknght

3
@Blckknght :이 경우 두 예외를 모두 포함하는 트레이스 백을 인쇄하는 것이 좋습니다. 다시 말해, 나는 항상 바람직하지 않다는 담요 진술이 사실이라고 생각하지 않습니다. 여기서 사용법은 KeyError로 바뀌고 AttributeError트레이스 백에서 발생한 것이 유용하고 적절하다는 것을 보여줍니다.
martineau

더 복잡한 상황에서는 옳을 지 모르지만 예외 유형간에 변환 할 때 첫 번째 예외의 세부 사항이 외부 사용자에게 중요하지 않다는 것을 알고 있습니다. 즉, __getattr__예외가 발생하면 버그는 현재 클래스 코드의 구현 버그가 아닌 속성 액세스의 오타 일 수 있습니다. 컨텍스트로 이전 예외를 표시하면 문제를 해결할 수 있습니다. 를 사용하여 컨텍스트를 억제 raise Whatever from None하더라도 필요한 경우를 통해 이전 예외를 계속 얻을 수 있습니다 ex.__context__.
Blckknght

1
귀하의 답변을 수락하고 싶었지만 질문에 중첩 된 try / catch 블록을 사용하는 것이 좋은 방법인지 궁금합니다. 반면에 그것은 가장 우아한 솔루션이며 내 코드에서 사용할 것입니다. 많은 감사합니다 Martin.
Michal

Michal : 천만에요. 를 사용하는 것보다 빠릅니다 __getattribute__().
martineau 2016 년

4

파이썬에서는 허가보다 용서를 구하는 것이 더 쉽습니다. 중첩 된 예외 처리를 땀 흘리지 마십시오.

( has*어쨌든 거의 항상 표지 아래에서 예외를 사용합니다.)


4

documentation 에 따르면 튜플이나 다음과 같은 여러 예외를 처리하는 것이 좋습니다.

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as e:
    print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

2
이 답변은 실제로 원래의 질문을 다루지 않지만 그것을 읽는 사람에게는 끝 부분을 제외하고는 "맨손"이라는 말이 끔찍한 아이디어입니다.
잃었다

코드가 print 문 바로 다음에 동일한 예외를 다시 발생 시킨다는 것을 감안할 때 이것은 실제로 큰 문제입니다. 이 경우 예외를 숨기지 않고 더 많은 컨텍스트를 제공 할 수 있습니다. 그것이 다시 일어나지 않으면 나는 전적으로 동의 할 것이지만, 당신이 의도하지 않은 예외를 숨길 위험이 있다고 생각하지 않습니다.
NimbusScale

4

중첩 된 try / except에 대한 좋고 간단한 예는 다음과 같습니다.

import numpy as np

def divide(x, y):
    try:
        out = x/y
    except:
        try:
            out = np.inf * x / abs(x)
        except:
            out = np.nan
    finally:
        return out

이제 다양한 조합을 시도하면 올바른 결과를 얻을 수 있습니다.

divide(15, 3)
# 5.0

divide(15, 0)
# inf

divide(-15, 0)
# -inf

divide(0, 0)
# nan

[물론 우리는 numpy를 가지고 있으므로이 함수를 만들 필요가 없습니다]


2

내가 피하고 싶은 한 가지는 오래된 예외를 처리하면서 새로운 예외를 발생시키는 것입니다. 오류 메시지를 읽기 어렵게 만듭니다.

예를 들어, 내 코드에서는 원래

try:
    return tuple.__getitem__(self, i)(key)
except IndexError:
    raise KeyError(key)

그리고 나는이 메시지를 받았다.

>>> During handling of above exception, another exception occurred.

내가 원하는 것은 이것입니다.

try:
    return tuple.__getitem__(self, i)(key)
except IndexError:
    pass
raise KeyError(key)

예외 처리 방법에는 영향을 미치지 않습니다. 어느 코드 블록에서나 KeyError가 발생했을 수 있습니다. 이것은 단지 스타일 포인트를 얻는 문제입니다.


인상의 허용 대답의 사용을 참조 from None도 들어 있지만 스타일 포인트. :)
피아노 사우루스

1

try-except-finally가 finally 블록 내부에 중첩 된 경우 "child"의 결과가 최종적으로 유지됩니다. 공식적인 설명을 아직 찾지 못했지만 다음 코드 스 니펫은 Python 3.6 에서이 동작을 보여줍니다.

def f2():
    try:
        a = 4
        raise SyntaxError
    except SyntaxError as se:
        print('log SE')
        raise se from None
    finally:
        try:
            raise ValueError
        except ValueError as ve:
            a = 5
            print('log VE')
            raise ve from None
        finally:
            return 6       
        return a

In [1]: f2()
log SE
log VE
Out[2]: 6

이 동작은 블록을 제외하고 마지막으로 중첩 될 때 @ Sławomir Lenart가 제공 한 예와 다릅니다.
Guanghua Shu

0

나는 그것이 파이썬이나 우아함의 문제라고 생각하지 않습니다. 가능한 한 많은 예외를 방지해야합니다. 예외는 제어 할 수없는 코드 또는 이벤트에서 발생할 수있는 오류를 처리하기위한 것입니다. 이 경우 항목이 속성인지 또는 사전인지 확인할 때 완전히 제어 할 수 있으므로 중첩 된 예외를 피하고 두 번째 시도를 고수하십시오.


문서에서 : 멀티 스레드 환경에서 LBYL (Look Before You Leap) 접근 방식은“looking”과“ leapping ”사이의 경쟁 조건을 유발할 수 있습니다. 예를 들어, 맵핑 후 키 : return mapping [key] 인 경우 코드는 테스트 후 조회 전에 조회에서 다른 스레드가 맵핑에서 키를 제거하면 실패 할 수 있습니다. 이 문제는 잠금으로 또는 EAFP (권한보다 용서를 구하는 것이 더 쉽다)를 사용하여 해결할 수 있습니다 .
Nuno André
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.