존재하지 않는 속성을 처리하기위한 hasattr () 대 try-except 블록


답변:


82

hasattr내부적으로 그리고 신속하게 try/except블록 과 동일한 작업을 수행합니다 . 매우 구체적이고 최적화 된 단일 작업 도구이므로 적용 가능한 경우 매우 범용적인 대안보다 선호되어야합니다.


8
경쟁 조건을 처리하기 위해 여전히 try / catch 블록이 필요하다는 점을 제외하고 (스레드를 사용하는 경우).
Douglas Leeder

1
또는 방금 만난 특별한 경우 : 값이없는 django OneToOneField : hasattr (obj, field_name)은 False를 반환하지만 field_name이있는 속성이 있습니다. 이는 DoesNotExist 오류를 발생시킵니다.
Matthew Schinckel

3
그 참고 hasattr합니다 모든 예외 잡기 파이썬 2.x 또는 3.0에서을 예제와 간단한 해결 방법 은 내 대답 을 참조하십시오 .
Martin Geisler 2013

5
흥미로운 설명 : try작업 작동 해야 함을 전달할 수 있습니다 . 비록 try그것이 더 읽기 고려 될 수 있도록의 의도는, 그것은 일반적이다, 항상 그런 아니다.
Ioannis Filippidis 2014 년

88

성능 차이를 보여주는 벤치가 있습니까?

시간이 너의 친구 야

$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "nonexistent")'
1000000 loops, best of 3: 1.87 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "a")'
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
 c.a
except:
 pass'
1000000 loops, best of 3: 0.247 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
 c.nonexistent
except:
 pass'
100000 loops, best of 3: 3.13 usec per loop
$

       |positive|negative
hasattr|  0.446 |  1.87 
try    |  0.247 |  3.13

16
흥미롭고 가시적 인 숫자를 제공하는 +1. 사실, "try"는 일반적인 경우 (즉, 파이썬 예외가 정말 예외적 인 경우)를 포함 할 때 효율적입니다.
Eric O Lebigot

이 결과를 해석하는 방법을 모르겠습니다. 여기서 어느 것이 더 빠르며 얼마나 빠릅니까?
Stevoisiak

2
@ StevenM.Vascellaro : 속성이 존재하는 경우, try빠른 속도의 두 배입니다 hasattr(). 그렇지 않은 경우 try보다 약 1.5 배 느립니다 hasattr()(둘 다 속성이 존재하는 경우보다 상당히 느립니다). 이것은 아마도 행복한 길에서 try거의 아무것도하지 않지만 (Python은 사용 여부에 관계없이 이미 예외 오버 헤드를 지불하고 있음) hasattr()이름 조회 및 함수 호출이 필요 하기 때문일 것입니다 . 불행한 경로에서 둘 다 예외 처리와 a를 수행해야 goto하지만 hasattr()Python 바이트 코드가 아닌 C에서 수행합니다.
케빈

24

세 번째, 종종 더 나은 대안이 있습니다.

attr = getattr(obj, 'attribute', None)
if attr is not None:
     print attr

장점 :

  1. getattr나쁜없는 예외 삼키는 동작 마틴 GEISER 지적 - 옛날 비단뱀에, hasattr심지어 삼키는 것이다 KeyboardInterrupt.

  2. 객체에 속성이 있는지 확인하는 일반적인 이유는 속성을 사용할 수 있도록하기위한 것이며, 이는 당연히 속성으로 이어집니다.

  3. 속성은 원자 적으로 읽히고 객체를 변경하는 다른 스레드로부터 안전합니다. (하지만 이것이 중요한 문제라면 객체에 액세스하기 전에 잠그는 것이 좋습니다.)

  4. 보다 짧고 try/finally종종보다 짧습니다 hasattr.

  5. 넓은 except AttributeError블록 AttributeErrors은 예상 한 블록이 아닌 다른 블록을 잡을 수 있으며, 이로 인해 혼란스러운 동작이 발생할 수 있습니다.

  6. 속성에 액세스하는 것은 지역 변수에 액세스하는 것보다 느립니다 (특히 일반 인스턴스 속성이 아닌 경우). (솔직히 말해서 Python의 마이크로 최적화는 종종 바보의 심부름입니다.)

주의 obj.attribute해야 할 사항은 None으로 설정된 경우에 신경을 쓰면 다른 센티넬 값을 사용해야한다는 것입니다.


1
+1-이것은 dict.get ( 'my_key', 'default_value')와 리그에 있으며 더 널리 알려 져야합니다

1
존재 여부를 확인하고 기본값으로 속성을 사용하려는 일반적인 사용 사례에 적합합니다.
dsalaj 2015

18

나는 거의 항상 사용합니다 hasattr: 대부분의 경우에 올바른 선택입니다.

문제가있는 경우는 클래스가 오버라이드 (override) 할 때이다 __getattr__: hasattr합니다 모든 예외를 잡는 대신 잡기의 AttributeError예상처럼. 즉, 예외 b: False를 보는 것이 더 적절하더라도 아래 코드는 인쇄됩니다 ValueError.

class X(object):
    def __getattr__(self, attr):
        if attr == 'a':
            return 123
        if attr == 'b':
            raise ValueError('important error from your database')
        raise AttributeError

x = X()
print 'a:', hasattr(x, 'a')
print 'b:', hasattr(x, 'b')
print 'c:', hasattr(x, 'c')

따라서 중요한 오류가 사라졌습니다. 이 문제는 Python 3.2 ( issue9666 )에서 수정되었으며hasattr 현재 AttributeError.

쉬운 해결 방법은 다음과 같은 유틸리티 함수를 작성하는 것입니다.

_notset = object()

def safehasattr(thing, attr):
    return getattr(thing, attr, _notset) is not _notset

이것은 getattr상황을 처리하고 적절한 예외를 발생시킬 수 있습니다.


2
이것은 또한 Python2.6 에서 약간 개선hasattr 되어 적어도 잡히지 않을 것입니다 KeyboardInterrupt.
poolie

또는보다는 safehasattr, 단지 사용 getattr당신은 거의 항상있는, 그것을 사용하려고하는 경우 로컬 변수의 값을 복사합니다.
poolie 2013 년

@poolie 멋지네요. 이렇게 hasattr개선 된 줄은 몰랐 습니다.
Martin Geisler 2013 년

네, 좋습니다. 나는 누군가에게 피하라고 말 하려던 오늘까지도 그것을 몰랐고 hasattr확인하러 갔다. hasattr이 방금 ^ C를 삼킨 재미있는 bzr 버그가있었습니다.
poolie 2013 년

2.7을 3.6으로 업그레이드하는 동안 문제가 발생했습니다. 이 답변은 문제를 이해하고 해결하는 데 도움이됩니다.
Kamesh Jungi

13

예를 들어 함수에 대한 두 명의 호출자가있는 경우 하나는 속성이있는 객체를 제공하고 다른 하나는 속성이없는 객체를 제공하는 경우와 같이 함수가 의도적으로 속성 없는 객체를 허용할지 여부에 따라 달라집니다 .

속성이없는 객체를 얻을 수있는 유일한 경우가 오류 때문인 경우 더 느리더라도 예외 메커니즘을 사용하는 것이 좋습니다. 더 깔끔한 디자인이라고 믿기 때문입니다.

결론 : 효율성 문제 라기보다는 디자인과 가독성 문제라고 생각합니다.


1
코드를 읽는 사람들에게 "시도"가 의미를 갖는 이유를 주장하는 +1. :)
Eric O Lebigot

5

속성이 없는 것이 오류 조건 이 아닌 경우 예외 처리 변형에 문제가 있습니다. obj.attribute에 액세스 할 때 내부적으로 올 수있는 AttributeErrors도 catch합니다 (예를 들어 속성은 속성이므로 액세스하면 일부 코드가 호출 됨).


제 생각에는 이것은 대체로 무시 된 주요 문제입니다.
릭 모니카 지원

5

이 주제는 Sebastian Witowski가 EuroPython 2016 Talk Writing faster Python 에서 다루었습니다 . 다음은 성능 요약과 함께 그의 슬라이드를 재현 한 것입니다. 그는 또한 이 토론을 시작하기 전에 용어 Look을 사용 하며 여기에서 해당 키워드에 태그를 지정할 가치가 있습니다.

속성이 실제로 누락 된 경우 용서를 구하는 것이 권한을 요청하는 것보다 느립니다. 따라서 경험적으로 속성이 누락되거나 예측할 수있는 다른 문제가있을 가능성이 매우 높다는 것을 알고있는 경우 권한 요청 방식을 사용할 수 있습니다. 그렇지 않으면 코드가 대부분의 경우 코드를 읽을 수 있다고 예상하면

3 권한 또는 용서?

# CASE 1 -- Attribute Exists
class Foo(object):
    hello = 'world'
foo = Foo()

if hasatter(foo, 'hello'):
    foo.hello
## 149ns ##

try:
    foo.hello
except AttributeError:
    pass
## 43.1 ns ##
## 3.5 times faster


# CASE 2 -- Attribute Absent
class Bar(object):
    pass
bar = Bar()

if hasattr(bar, 'hello'):
    bar.hello
## 428 ns ##

try:
    bar.hello
except AttributeError :
    pass
## 536 ns ##
## 25% slower

4

테스트하는 속성 중 하나 인 경우 use라고 말하고 싶습니다 hasattr. 그러나 존재하거나 존재하지 않을 수있는 속성에 대한 여러 액세스를 수행하는 경우 try블록 을 사용하면 입력을 줄일 수 있습니다.


3

옵션 2를 제안합니다. 다른 스레드가 속성을 추가하거나 제거하는 경우 옵션 1에 경쟁 조건이 있습니다.

또한 파이썬은이 관용구를 EAFP가 ( '쉽게 허락보다 용서를 물어')가 LBYL ( '보기 전에 도약')보다 더 낫다는 것을.


2

실용적인 관점에서 보면 대부분의 언어에서 조건을 사용하는 것은 예외를 처리하는 것보다 항상 훨씬 빠릅니다.

현재 함수 외부에 존재하지 않는 속성의 경우를 처리하려면 예외가 더 좋은 방법입니다. 조건부 대신 예외를 사용하고 싶을 수있는 표시기는 조건부에서 플래그를 설정하고 현재 작업을 중단하고 다른 곳에서이 플래그를 확인하고 이에 따라 조치를 취한다는 것입니다.

즉, Rax Olgud가 지적했듯이 다른 사람과의 의사 소통은 코드의 중요한 속성 중 하나이며 "이것은 제가 예상하는 일입니다."라기보다는 "이것은 예외적 인 상황입니다"라고 말하고 싶은 것이 더 중요 할 수 있습니다. .


조건부 테스트와 비교하여 "시도"가 "이는 예외적 인 상황"으로 해석 될 수 있다는 사실을 주장하는 +1입니다. :)
Eric O Lebigot

0

첫번째.

짧을수록 좋습니다. 예외는 예외적이어야합니다.


5
예외는 파이썬에서 매우 흔합니다. 모든 for문장 끝에 하나가 있고 hasattr하나도 사용합니다. 그러나 "짧을수록 좋습니다"(그리고 "단순할수록 좋습니다"!) 적용하십시오. 따라서 더 간단하고 짧고 구체적인 hasattr가 실제로 바람직합니다.
Alex Martelli

@Alex는 Python 파서가 해당 문을 1로 변환한다고해서 그다지 흔한 것은 아닙니다. 그들이 그 구문을 설탕으로 만든 이유가 있습니다. 그래서 당신은 try except 블록을 타이핑하는 까다 로움에 얽매이지 않습니다.
Unknown

예외가 예외 인 경우 "노골적인 것이 더 낫다"이고 원래 포스터의 두 번째 옵션이 더 낫다고 말하고 싶습니다.
Eric O Lebigot

0

적어도 프로그램에서 일어나는 일에 달려있을 때, 가독성의 인간 부분을 배제하는 등 (실제로 대부분의 경우 성능보다 중요합니다 (적어도이 경우에는-성능 범위에서)), Roee Adler와 다른 사람들이 지적했듯이).

그럼에도 불구하고 그런 관점에서 보면

try: getattr(obj, attr)
except: ...

try: obj.attr
except: ...

이후 hasattr바로 첫 번째 경우를 사용하여 결과를 확인합니다. 생각할 거리 ;-)

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