`if key in dict` vs.`try / except`-어느 것이 더 읽기 쉬운 관용구입니까?


93

관용구와 가독성에 대한 질문이 있는데,이 경우에 파이썬 철학이 충돌하는 것 같습니다.

사전 B에서 사전 A를 만들고 싶습니다. 특정 키가 B에 없으면 아무 작업도 수행하지 않고 계속합니다.

어느 쪽이 더 낫습니까?

try:
    A["blah"] = B["blah"]
except KeyError:
    pass

또는

if "blah" in B:
    A["blah"] = B["blah"]

"용서를 구하고 구하라"대 "단순함과 명료 함".

어느 것이 더 낫고 그 이유는 무엇입니까?


1
두 번째 예는 if "blah" in B.keys(), 또는 로 작성하는 것이 더 좋습니다 if B.has_key("blah").
girasquid 2010

2
A.update(B)당신을 위해 작동 하지 않습니까?
SilentGhost 2010

21
@Luke : O (1) 작업을 O (n) 작업으로 변경 하고 확인하기 has_key위해 더 이상 사용되지 않습니다 . inB.keys()
kindall 2010

4
@Luke : 아니에요. .has_key더 이상 사용되지 않으며 keyspy2k에 불필요한 목록을 생성하고 py3k에서 중복됩니다.
SilentGhost

2
'빌드'A,에서와 같이 A는 시작하기 위해 비어 있습니까? 그리고 우리는 특정 키만 원합니까? 이해력 사용 : A = dict((k, v) for (k, v) in B if we_want_to_include(k)).
Karl Knechtel 2010

답변:


76

예외는 조건부가 아닙니다.

조건부 버전이 더 명확합니다. 그것은 당연합니다. 이것은 단순한 흐름 제어이며, 예외가 아닌 조건문을 위해 설계된 것입니다.

예외 버전은 주로 루프에서 이러한 조회를 수행 할 때 최적화로 사용됩니다. 일부 알고리즘의 경우 내부 루프에서 테스트를 제거 할 수 있습니다. 여기에는 그 혜택이 없습니다. "blah"두 번 말하지 않아도된다는 작은 장점이 있지만, 이런 일을 많이한다면 move_key어차피 도우미 기능 이있을 것 입니다.

일반적으로 특별한 이유가없는 한 기본적으로 조건부 버전을 사용하는 것이 좋습니다. 조건문은 이를 수행 하는 명백한 방법이며 일반적으로 한 솔루션을 다른 솔루션보다 선호하는 강력한 권장 사항입니다.


3
동의하지 않습니다. "X를하고, 작동하지 않으면 Y를하십시오"라고 말하면. 여기서 조건부 솔루션에 대한 주된 이유는 "blah"더 자주 작성해야하므로 오류가 발생하기 쉬운 상황이됩니다.
glglgl jul.

6
특히 Python에서 EAFP는 매우 널리 사용됩니다.
glglgl jul.

8
이 대답은 Python을 제외하고 내가 아는 모든 언어에 맞을 것입니다.
Tomáš Zato-Monica 복원

3
파이썬에서 조건문 인 것처럼 예외를 사용한다면, 아무도 그것을 읽을 필요가 없기를 바랍니다.
Glenn Maynard

그래서 최종 평결은 무엇입니까? :)
floatingpurr

60

예외와 이중 조회를 모두 피하는 세 번째 방법도 있습니다. 조회 비용이 많이 드는 경우 중요 할 수 있습니다.

value = B.get("blah", None)
if value is not None: 
    A["blah"] = value

경우에는 사전에 포함 할 것으로 예상 None당신이 좋아 좀 더 난해한 상수를 사용하여 값을 NotImplemented, Ellipsis또는 새로운 하나를 만들 :

MyConst = object()
def update_key(A, B, key):
    value = B.get(key, MyConst)
    if value is not MyConst: 
        A[key] = value

어쨌든 사용 update()은 나에게 가장 읽기 쉬운 옵션입니다.

a.update((k, b[k]) for k in ("foo", "bar", "blah") if k in b)

14

내가 이해하는 바에 따르면 dict B의 키, 값 쌍으로 dict A를 업데이트하고 싶습니다.

update 더 나은 선택입니다.

A.update(B)

예:

>>> A = {'a':1, 'b': 2, 'c':3}
>>> B = {'d': 2, 'b':5, 'c': 4}
>>> A.update(B)
>>> A
{'a': 1, 'c': 4, 'b': 5, 'd': 2}
>>> 

"B에 특정 키가없는 경우"죄송합니다. 더 명확해야했지만 B에 특정 키가있는 경우에만 값을 복사하고 싶습니다. B에서 모두가 아닙니다.
LeeMobile 2010

1
@LeeMobile -A.update({k: v for k, v in B.iteritems() if k in specificset})
갖가지 잡다한

8

Python 성능 위키에서 직접 인용 :

처음을 제외하고는 단어를 볼 때마다 if 문의 테스트가 실패합니다. 많은 수의 단어를 세는 경우 많은 단어가 여러 번 나올 것입니다. 값의 초기화가 한 번만 발생하고 해당 값의 증가가 여러 번 발생하는 상황에서는 try 문을 사용하는 것이 더 저렴합니다.

따라서 상황에 따라 두 가지 옵션이 모두 실행 가능한 것 같습니다. 자세한 내용은이 링크를 확인하시기 바랍니다 : Try-except-performance


흥미로운 글이지만 다소 불완전하다고 생각합니다. 사용 된 dict에는 1 개의 요소 만 있으며 더 큰 dict는 성능에 상당한 영향을 미칠 것이라고 생각합니다.
user2682863

3

나는 여기에 일반적인 규칙이 A["blah"]일반적으로 존재할 것이라고 생각합니다. 그러면 try-except가 좋지 않으면 사용하십시오.if "blah" in b:

나는 "시도"가 시간적으로 저렴하다고 생각하지만 "예외"는 더 비싸다.


10
기본적으로 최적화 관점에서 코드에 접근하지 마십시오. 가독성 및 유지 관리 측면에서 접근합니다. 목표가 특별히 최적화가 아니라면 이것은 잘못된 기준입니다 (최적화라면 대답은 추측이 아니라 벤치마킹입니다).
Glenn Maynard

나는 아마도 마지막 포인트를 괄호로 묶거나 다소 모호하게 말 했어야했다. 내 요점은 첫 번째 포인트 였고 두 번째 포인트 의 추가적인 장점이 있다고 생각 한다.
neil

3

두 번째 예제는이 코드가 이해가되지 않는 한 당신이 가야한다고 생각합니다.

try:
    A["foo"] = B["foo"]
    A["bar"] = B["bar"]
    A["baz"] = B["baz"]
except KeyError:
    pass

.NET Framework에없는 키가있는 즉시 코드가 중단됩니다 B. 이 코드가 의미가 있다면 예외 메서드를 사용해야하고 그렇지 않으면 테스트 메서드를 사용해야합니다. 제 생각에는 짧고 의도를 명확하게 표현하기 때문에 예외 메서드보다 읽기가 훨씬 쉽습니다.

물론 사용하라고 말하는 사람들 update이 맞습니다. 사전 이해를 지원하는 Python 버전을 사용하는 경우 다음 코드를 강력히 선호합니다.

updateset = {'foo', 'bar', 'baz'}
A.update({k: B[k] for k in updateset if k in B})

"B에없는 키가있는 즉시 코드가 중단된다는 점을 명심하십시오." -이것이 try : 블록에 절대 최소값 만 입력하는 것이 가장 좋은 이유입니다. 일반적으로 이것은 한 줄입니다. 첫 번째 예는 다음과 같은 루프의 일부로 더 좋습니다.for key in ["foo", "bar", "baz"]: try: A[key] = B[key]
Zim

2

다른 언어의 규칙은 예외적 인 조건, 즉 정기적으로 사용하지 않는 오류에 대한 예외를 예약하는 것입니다. 해당 규칙에 따라 StopIteration이 존재하지 않아야하므로 해당 규칙이 Python에 어떻게 적용되는지 모릅니다.


이 밤나무는 예외 처리가 비용이 많이 들고 성능에 큰 영향을 미칠 수있는 언어에서 비롯되었다고 생각합니다. 나는 그 뒤에 진정한 정당화 나 추론을 본 적이 없습니다.
John La Rooy

@JohnLaRooy 아니요, 성능이 실제로 이유가 아닙니다. 예외는 일부 사람들이 코드의 가독성을 방해한다고 생각 하는 일종의 비 로컬 goto 입니다. 그러나 이런 식으로 예외를 사용하는 것은 파이썬에서 관용적 인 것으로 간주되므로 위의 내용은 적용되지 않습니다.
이안 Goldby

조건부 반환은 또한 "non-local goto"이며 많은 사람들이 코드 블록의 끝에서 센티넬을 검사하는 대신 해당 스타일을 선호합니다.
cowbert

1

개인적으로 두 번째 방법을 사용합니다 (하지만 사용 has_key).

if B.has_key("blah"):
  A["blah"] = B["blah"]

이렇게하면 각 할당 작업은 (try / except를 사용하는 4 개 대신) 두 줄 뿐이며 throw되는 모든 예외는 실제 오류 또는 놓친 것입니다 (그냥 거기에없는 키에 액세스하려고하는 대신). .

밝혀 졌을 때 (귀하의 질문에 대한 의견 참조) has_key더 이상 사용되지 않으므로 다음과 같이 작성하는 것이 더 좋습니다.

if "blah" in B:
  A["blah"] = B["blah"]

1

시작 Python 3.8할당 표현식 (PEP 572) ( :=연산자) 의 도입으로 dictB.get('hello', None)변수 의 조건 값 을 캡처하여 value그렇지 않은지 None( dict.get('hello', None)연결된 값 또는 을 반환 함) 확인한 None다음 본문 내에서 사용할 수 있습니다. 조건:

# dictB = {'hello': 5, 'world': 42}
# dictA = {}
if value := dictB.get('hello', None):
  dictA["hello"] = value
# dictA is now {'hello': 5}

값 == 0 인 경우 실패합니다
Eric

0

"도약하기 전에 살펴보기"원칙에 대한 수용된 답변의 강조가 대부분의 언어에 적용될 수 있지만, 파이썬 원칙에 기반한 첫 번째 접근 방식은 더 파이썬적일 수 있습니다. 말할 것도없이 파이썬에서 합법적 인 코딩 스타일입니다. 중요한 것은 올바른 컨텍스트에서 try except 블록을 사용하고 모범 사례를 따르고 있는지 확인하는 것입니다. 예 : try 블록에서 너무 많은 일을하거나, 매우 광범위한 예외를 포착하거나, 더 나쁜 경우는 예외입니다.

허락보다 용서를 구하기가 더 쉽습니다. (EAFP)

여기 에서 파이썬 문서 참조를 참조 하십시오 .

또한 핵심 개발자 중 한 명인 Brett 의이 블로그 에서는이 대부분을 간략하게 다룹니다.

여기에 또 다른 SO 토론을 참조 하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.