if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Który powinien być preferowany i dlaczego?
if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Który powinien być preferowany i dlaczego?
Odpowiedzi:
hasattr
wewnętrznie i szybko wykonuje to samo zadanie cotry/except
blok: jest to bardzo specyficzne, zoptymalizowane narzędzie do jednego zadania i dlatego powinno być preferowane, gdy ma to zastosowanie, zamiast bardzo ogólnego przeznaczenia.źródło
hasattr
spowoduje to przechwycenie wszystkich wyjątków w Pythonie 2.x. Zobacz moją odpowiedź na przykład i trywialne obejście.try
może przekazać, że operacja powinna działać. Chociażtry
zamiar nie zawsze jest taki, jest powszechny, więc można go uznać za bardziej czytelny.Jakieś ławki, które ilustrują różnicę w wydajności?
czas to twój przyjaciel
$ 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
źródło
try
jest około dwa razy szybszy niżhasattr()
. Jeśli tak nietry
jest , jest około 1,5 raza wolniejsze niżhasattr()
(i oba są znacznie wolniejsze niż wtedy, gdy atrybut istnieje). Dzieje się tak prawdopodobnie dlatego, że na szczęśliwej ścieżcetry
prawie nic nie robi (Python już płaci za narzut wyjątków, niezależnie od tego, czy ich używasz), alehasattr()
wymaga wyszukiwania nazwy i wywołania funkcji. Na nieszczęśliwej ścieżce obaj muszą wykonać jakąś obsługę wyjątków i agoto
, alehasattr()
robią to w C, a nie w kodzie bajtowym Pythona.Istnieje trzecia i często lepsza alternatywa:
attr = getattr(obj, 'attribute', None) if attr is not None: print attr
Zalety:
getattr
nie ma złego zachowania polegającego na połykaniu wyjątków, wskazanego przez Martina Geisera - w starych Pythonachhasattr
nawet połknie plikKeyboardInterrupt
.Normalnym powodem, dla którego sprawdzasz, czy obiekt ma atrybut, jest to, że możesz użyć atrybutu, a to naturalnie prowadzi do tego.
Atrybut jest odczytywany atomowo i jest bezpieczny dla innych wątków zmieniających obiekt. (Chociaż, jeśli jest to poważny problem, możesz rozważyć zablokowanie obiektu przed uzyskaniem do niego dostępu).
Jest krótszy
try/finally
i często krótszy niżhasattr
.Szeroki
except AttributeError
blok może złapać innyAttributeErrors
niż ten, którego się spodziewasz, co może prowadzić do dezorientującego zachowania.Dostęp do atrybutu jest wolniejszy niż dostęp do zmiennej lokalnej (zwłaszcza jeśli nie jest to zwykły atrybut instancji). (Chociaż, szczerze mówiąc, mikro-optymalizacja w Pythonie jest często głupim zadaniem.)
Jedną rzeczą, na którą należy uważać, jest to, że jeśli obchodzi Cię przypadek, w którym
obj.attribute
jest ustawiona na Brak, musisz użyć innej wartości wartowniczej.źródło
Prawie zawsze używam
hasattr
: to właściwy wybór w większości przypadków.Problematyczny jest przypadek, gdy klasa nadrzędna
__getattr__
:hasattr
będzie złapać wszystkie wyjątki zamiast łapania prostuAttributeError
jak można się spodziewać. Innymi słowy, poniższy kod zostanie wydrukowany,b: False
chociaż lepiej byłoby zobaczyćValueError
wyjątek: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')
W ten sposób zniknął ważny błąd. Zostało to naprawione w Pythonie 3.2 ( problem 9666 ), gdzie
hasattr
teraz występuje tylkoAttributeError
.Łatwym obejściem jest napisanie takiej funkcji narzędzia:
_notset = object() def safehasattr(thing, attr): return getattr(thing, attr, _notset) is not _notset
To pozwoli na
getattr
czynienia z sytuacją i może wtedy podnieść odpowiedni wyjątek.źródło
hasattr
przynajmniej nie złapieKeyboardInterrupt
itp.safehasattr
po prostugetattr
kopiować wartość do zmiennej lokalnej, jeśli zamierzasz jej użyć, czym prawie zawsze jesteś.hasattr
zostało to ulepszone w ten sposób.hasattr
, i poszedłem sprawdzić. Mieliśmy kilka zabawnych błędów bzr, w których hasattr właśnie połknął ^ C.Powiedziałbym, że to zależy od tego, czy funkcja może przyjmować obiekty bez atrybutu w fazie projektowania , na przykład, jeśli masz dwie dzwoniących do funkcji, która zapewnia obiektu z atrybutem, a drugi dostarczenie obiektu bez niego.
Jeśli jedyny przypadek, w którym otrzymasz obiekt bez atrybutu, jest spowodowany jakimś błędem, polecam użycie mechanizmu wyjątków, nawet jeśli może być wolniejszy, ponieważ uważam, że jest to bardziej przejrzysty projekt.
Konkluzja: myślę, że jest to raczej kwestia projektu i czytelności niż kwestia wydajności.
źródło
Jeśli brak atrybutu nie jest stanem błędu, wariant obsługi wyjątków ma problem: przechwytuje również błędy AttributeErrors, które mogą pojawić się wewnętrznie podczas uzyskiwania dostępu do obj.attribute (na przykład, ponieważ atrybut jest właściwością, więc dostęp do niej wywołuje kod).
źródło
Temat ten został omówiony w wykładzie EuroPython 2016 Writing szybciej Python przez Sebastiana Witowskiego. Oto reprodukcja jego slajdu z podsumowaniem wyników. Używa również wyglądu terminologii, zanim przejdziesz do tej dyskusji, o czym warto tutaj wspomnieć, aby oznaczyć to słowo kluczowe.
3 POZWOLENIA CZY PRZEBACZENIE?
# 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
źródło
Jeśli testujesz tylko jeden atrybut, powiedziałbym, że użyj
hasattr
. Jednakże, jeśli robisz kilka dostępów do atrybutów, które mogą istnieć lub nie, użycietry
bloku może zaoszczędzić trochę pisania.źródło
Proponuję opcję 2. Opcja 1 ma stan wyścigu, jeśli jakiś inny wątek dodaje lub usuwa atrybut.
Python ma również idiom , że EAFP („łatwiej prosić o przebaczenie niż pozwolenie”) jest lepsze niż LBYL („patrz, zanim skoczysz”).
źródło
Z praktycznego punktu widzenia, w większości języków użycie warunku zawsze będzie znacznie szybsze niż obsługa wyjątku.
Jeśli chcesz obsłużyć przypadek atrybutu, który nie istnieje gdzieś poza bieżącą funkcją, wyjątek jest lepszym sposobem. Wskaźnikiem, że możesz chcieć użyć wyjątku zamiast warunku, jest to, że warunek po prostu ustawia flagę i przerywa bieżącą operację, a coś w innym miejscu sprawdza tę flagę i na jej podstawie podejmuje działania.
To powiedziawszy, jak wskazuje Rax Olgud, komunikacja z innymi jest jednym z ważnych atrybutów kodu i to, co chcesz powiedzieć, mówiąc „to jest wyjątkowa sytuacja”, a nie „to jest coś, czego oczekuję”, może być ważniejsze .
źródło
Pierwszy.
Krótszy znaczy lepszy. Wyjątki powinny być wyjątkowe.
źródło
for
instrukcji i też gohasattr
używa. Jednak „krótszy jest lepszy” (i „prostszy jest lepszy”!) NALEŻY stosować, więc prostszy, krótszy i bardziej szczegółowy hasattr jest rzeczywiście lepszy.Przynajmniej wtedy, gdy zależy to tylko od tego, co dzieje się w programie, pomijając ludzką część czytelności itp. (Co jest właściwie przez większość czasu ważniejsze niż wydajność (przynajmniej w tym przypadku - z tym zakresem wydajności), jak zauważyli Roee Adler i inni).
Jednak patrząc na to z tej perspektywy, staje się kwestią wyboru między
try: getattr(obj, attr) except: ...
i
try: obj.attr except: ...
ponieważ
hasattr
używa tylko pierwszego przypadku do określenia wyniku. Do przemyślenia ;-)źródło