2013-01-19 12 views
9

Powiel możliwe:
Python FAQ: “How fast are exceptions?”Python - sprawność złowionych wyjątkami

pamiętam czytanie że Python implementuje „Lepiej prosić o przebaczenie niż prosić o pozwolenie” filozofię w odniesieniu do wyjątków . Według autora oznaczało to, że kod Pythona powinien używać wielu klauzul try - except, zamiast próbować określić z wyprzedzeniem, czy miałeś zamiar zrobić coś, co spowodowałoby wyjątek.

Właśnie napisałem kilka prób - z wyjątkiem klauzul w mojej aplikacji internetowej, w której wyjątek zostanie podniesiony przez większość czasu, gdy kod jest uruchamiany. Tak więc w tym przypadku podniesienie i złapanie wyjątku będzie normą. Czy to jest złe z punktu widzenia wydajności? Pamiętam też, jak ktoś mi mówił, że wychwycenie podniesionego wyjątku ma duży wpływ na wydajność.

Czy korzystanie z klauzul try - except jest niepotrzebnie nieefektywne, ponieważ oczekuje się, że wyjątek zostanie podniesiony i przechwycony przez prawie cały czas?

Oto kod - używa ORD Django do sprawdzania obiektów, które kojarzą użytkowników z różnymi dostawcami usług społecznościowych.

try: 
    fb_social_auth = UserSocialAuth.objects.get(user=self, provider='facebook') 
    user_dict['facebook_id'] = fb_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['facebook_id'] = None 

try: 
    fs_social_auth = UserSocialAuth.objects.get(user=self, provider='foursquare') 
    user_dict['foursquare_id'] = fs_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['foursquare_id'] = None 

try: 
    tw_social_auth = UserSocialAuth.objects.get(user=self, provider='twitter') 
    user_dict['twitter_id'] = tw_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['twitter_id'] = None 

Pierwszy z nich rzadko się wyjątek, ponieważ teraz jesteśmy egzekwowania „Zaloguj się z Facebook” jako podstawowej metody dla nowych użytkowników do przyłączenia się do witryny. Ale Twitter i Foursquare są opcjonalne, na wypadek, gdyby chcieli zaimportować znajomych lub obserwujących i oczekuję, że większość ludzi tego nie zrobi.

Jestem otwarty na lepsze sposoby kodowania tej logiki.

+1

Jaki jest konkretny przykład kodu? –

+1

Stosowanie "Lepiej starać się o przebaczenie, niż prosić o zgodę" wszędzie tam, gdzie ma to zastosowanie, zdecydowanie nie jest najlepszym pomysłem. Rozważmy to: jeśli wiesz, że w 95% wywołań do kodu wewnątrz 'try..catch' zostanie zgłoszony wyjątek, lepiej przepisz go. –

+1

Jedno powiedzenie, które brzmi "wyjątki są dla wyjątkowych przypadków". Sądzę, że jeśli spodziewasz się wyjątku przez większość czasu, być może używasz wyjątków w niewłaściwy sposób. –

Odpowiedz

6

Za każdym razem, gdy kodujesz, możesz zrównoważyć obawy: wydajność, czytelność, poprawność, możliwość rozbudowy, łatwość obsługi itd. Niestety, często nie jest możliwe poprawienie kodu w każdym z tych kierunków jednocześnie. To, co jest szybkie, może na przykład nie być tak czytelne.

Jednym z powodów, dla których w Pythonie zachęca się try..except, jest to, że często nie można przewidzieć wszystkich sposobów użycia kodu, więc zamiast sprawdzać, czy istnieje określony warunek, lepiej jest po prostu uchwycić dowolny z pewna klasa błędów, które mogą się pojawić. Tak więc try..except może sprawić, że Twój kod będzie bardziej przydatny do ponownego użycia.

Jednak jest również prawdą, że try..except jest powolny, jeśli często osiąga się klauzulę except.

Czy istnieje sposób na zakodowanie tego bloku, aby wyjątek nie został podniesiony i użyj polecenia try..except, aby złapać rzadszy warunek?

Lub jeśli nie, ze względu na wydajność, możesz nie używać try..except. W programowaniu jest kilka twardych i szybkich reguł. Musisz wybrać sposób na podstawie twojego salda obaw.

+0

+1 for Istnieje kilka trudnych i szybkich reguł w programowaniu. :) –

+0

+1 punkt na IMO. – jldupont

2

Jeśli próbujesz zoptymalizować tę funkcję dla prędkości, powinieneś skupić się na tym, co może być rzeczywistym wąskim gardłem. Twoje trzy zapytania do bazy danych, z których każda spowoduje przejście systemu operacyjnego do kontekstu, prawie na pewno będą miały rząd wielkości dłuższy niż wyjątek.Jeśli chcesz, aby kod tak szybko, jak to możliwe, rozpocząć łącząc wszystkie trzy zapytania do bazy danych w jednym:

auth_objects = UserSocialAuth.objects.filter(user=self, provider__in=('facebook', 'foursquare', 'twitter')) 

a następnie pętli obiektów. Filtr provider__in może być niepotrzebny, jeśli tylko ci trzej dostawcy są jedynymi użytkownikami w bazie danych.

2

To prawda, że ​​złapanie wyjątku jest umiarkowanie kosztowne (patrz poniżej dla niektórych czasów) i nie chciałbyś tego w wąskim gardle programu, ale w przykładach, które dajesz, wyłapanie wyjątku będzie bardzo mała część środowiska wykonawczego w porównaniu do wywołania Model.objects.get, które musi zbudować zapytanie SQL, przesłać je do serwera bazy danych i poczekać, aż baza danych zgłosi, że nie ma takiego obiektu.

Kilka przykładowych czasów. Funkcja f2 rzuca i przechwytuje wyjątek, podczas gdy f1 implementuje tę samą funkcjonalność bez używania wyjątków.

d = dict() 

def f1(): 
    if 0 in d: return d[0] 
    else: return None 

def f2(): 
    try: return d[0] 
    except KeyError: return None 

>>> timeit(f1) 
0.25134801864624023 
>>> timeit(f2) 
2.4589600563049316 

I f3 próbuje dostać się do nieistniejącego obiektu z bazy danych (który jest uruchomiony na tym samym komputerze) za pośrednictwem ORM Django:

def f3(): 
    try: 
     MyModel.objects.get(id=999999) 
    except MyModel.DoesNotExist: 
     pass 

Trwa to około 400 razy dłużej niż f2 (tak długo, że nie chcą czekać na domyślny number=1000000 iteracji do Complete):

>>> timeit(f3, number=1000) 
1.0703678131103516 
Powiązane problemy