我记得读过Python实现了一个"更好地寻求宽恕而不是要求许可"关于例外的哲学。根据作者的说法,这意味着Python代码应该使用大量的try - except子句,而不是试图提前确定是否要做一些会导致异常的事情。
我刚刚在我的网络应用程序上写了一些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是可选的,如果他们想要导入朋友或粉丝,我希望大多数人不会。
我愿意接受更好的方法来编写这种逻辑。
答案 0 :(得分:6)
无论何时编码都存在一个平衡的问题:性能,可读性,正确性,可扩展性,可维护性等。 遗憾的是,通常无法同时改进这些方向中的每个方向的代码。例如,什么是快速可能不那么可读。
在Python中鼓励try..except
的原因之一是因为您经常无法预测代码的所有使用方式,因此不是检查是否存在特定条件,更常见的是捕获任何可能出现的某类错误。因此try..except
可能会使您的代码更具可重用性。
但是,如果经常达到try..except
子句,except
也很慢。
有没有办法对该块进行编码,以便不会引发异常并使用try..except来捕获较不频繁的条件?
如果不是,为了提高效率,您可以选择不使用try..except。编程中几乎没有硬性和快速的规则。您必须根据您的平衡点选择自己的方式。
答案 1 :(得分:2)
如果您尝试针对速度优化此功能,则应关注可能是实际瓶颈的问题。您的三个数据库查询(每个查询都会导致操作系统进行上下文切换)几乎肯定比捕获异常要长一个数量级。如果要尽可能快地编写代码,请首先将所有三个数据库查询合并为一个:
auth_objects = UserSocialAuth.objects.filter(user=self, provider__in=('facebook', 'foursquare', 'twitter'))
然后遍历对象。如果这三个提供者是数据库中唯一的提供者,则provider__in
过滤器可能是不必要的。
答案 2 :(得分: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