2013-01-19 24 views
9
的效率

可能重复:
Python FAQ: “How fast are exceptions?”Python的 - 捕获的异常

我记得读了Python实现与问候例外“更好,以寻求宽恕比请求允许”的理念。根据作者的说法,这意味着Python代码应该使用很多try - 除了子句,而不是提前确定是否要做某些可能导致异常的事情。

我刚刚在我的web应用程序中编写了一些try - except子句,其中大部分代码运行时会引发异常。因此,在这种情况下,提高和捕捉异常将是常态。从效率的角度来看,这是否糟糕?我还记得有人告诉我,捕捉异常会带来很大的性能开销。

使用try - except语句会不必要地低效,在这种语句中,您期望几乎所有的时间都会引发异常并捕获异常?

下面是代码 - 它使用Django ORM检查将用户与各种第三方社交提供者相关联的对象。

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 

第一个将很少会例外,因为现在我们都在实施“请用facebook”作为新用户加入该网站的主要方法。但是,Twitter和Foursquare是可选的,以防他们想要导入朋友或追随者,我希望大多数人不会。

我打开更好的方式来编写这个逻辑。

+1

代码的具体示例是什么? –

+1

在适用的任何地方应用“更好地寻求宽恕而不是要求允许”绝对不是最好的主意。考虑一下:如果你知道95%的调用'try..catch'内的代码会引发异常,那么最好重写它。 –

+1

一个说法是,“例外情况是例外情况”。我认为,如果您大多数时间都在期待异常,那么您可能会以错误的方式使用异常。 –

回答

6

无论何时你编码都有一个平衡的问题:性能,可读性,正确性,可扩展性,可维护性等。 不幸的是,通常不可能在同一时间在每个方向上改进代码。例如,速度可能不快。

在Python中鼓励try..except的原因之一是因为您经常无法预测您的代码可能被使用的所有方式,所以不是检查是否存在特定的条件,而是更一般地捕获任何可能会出现某种类型的错误。因此try..except可能会使您的代码更加可重用。

但是,如果经常达到except子句,try..except也是如此。

有没有一种方法来编写该块,以便不会引发异常并使用try..except捕获不太常见的条件?

或者,如果不是,为了效率,您可以选择不使用try..except。编程中几乎没有硬性规定。你必须根据你的平衡问题选择你的方式。

+0

+1对于编程中几乎没有硬性规则和快速规则。 :) –

+0

国际海事组织+1点。 – jldupont

2

如果您试图优化此功能的速度,您应该关注可能是实际瓶颈的问题。您的三个数据库查询(每个查询都会导致操作系统进行上下文切换)几乎肯定会比捕获异常要长一个数量级。如果你想使代码尽可能地快,首先将所有三个数据库查询到一个:通过对象

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

,然后循环。如果这三个提供者是数据库中唯一的提供者,那么provider__in过滤器可能是不必要的。

2

确实,捕获一个异常是非常昂贵的(请参阅下面的某些时间点),并且您不希望将其放在程序的瓶颈中,但在您提供的示例中,捕获异常将会是运行时的一小部分与Model.objects.get的调用相比较,该调用必须构建SQL查询,将其传输到数据库服务器,并等待数据库报告没有这样的对象。

一些示例时序。函数f2引发并捕获异常,而f1在不使用异常的情况下实现相同的功能。

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 

而且f3试图从数据库中获取一个不存在的物体通过Django的ORM(这是在同一台机器上运行):

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

这需要比f2长大约400次(这样只要我不想等待默认number=1000000迭代完成):

>>> timeit(f3, number=1000) 
1.0703678131103516