如何在速度方面提高"for"循环性能?



我有一个for循环,迭代6907行几乎需要20秒。该循环完成了在给定查询集中生成唯一区域名称列表的工作。我在代码的不同位置放置了时间戳来记录时间。需要更多时间的"for"循环位于变量"t3"one_answers"t4"之间。

时间戳

t = 12:27:22:169533
t2 = 12:27:22:173535
t3 = 12:27:22:793567
6907
t4 = 12:27:42:907362
t5 = 12:27:43:242596
t6 = 12:27:43:242596

6907是我的查询集sales_data 的长度

views.py

class MSZoneProduct(generic.TemplateView):
template_name = 'sales/MSZoneProduct.html'
form_class = MSZoneProductForm
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
form = self.form_class(request.GET)
context = {'form': form}
if form.is_valid():
zone_code_ = form.cleaned_data['zone_name']
product_code_ = form.cleaned_data['product_name']
t = datetime.now().strftime('%H:%M:%S:%f')
print("t = " + t)
product = Product.objects.get(product_code=product_code_)
t2 = datetime.now().strftime('%H:%M:%S:%f')
print("t2 = " + t2)
sales_data = Sales.objects.filter(zone_code=zone_code_, product_code=product).select_related()
t3 = datetime.now().strftime('%H:%M:%S:%f')
print("t3 = " + t3)
print(len(sales_data))
regions = []
message = ""
regions_dict = {}
for x in sales_data:
if x.region_name not in regions:
regions.append(x.region_name)
else:
continue
t4 = datetime.now().strftime('%H:%M:%S:%f')
print("t4 = " + t4)
for x in regions:
sum_ = 0
for y in sales_data:
if y.region_name == x:
sum_ = sum_ + y.quantity
regions_dict[x] = sum_
t5 = datetime.now().strftime('%H:%M:%S:%f')
print("t5 = " + t5)
if len(regions) == 0:
message = "There is no data available for this product in this particular region."
context = {'form': form, 'message': message, 'data': regions_dict}
t6 = datetime.now().strftime('%H:%M:%S:%f')
print("t6 = " + t6)
return render(request, 'sales/MSZoneProduct.html', context)
return render(request, 'sales/MSZoneProduct.html', context)
else:
return redirect('/sales/')

根据您的视图,您需要汇总每个区域的数量。我们可以将所有这些逻辑移动到数据库查询中。这不仅会使它更高效,而且也更优雅:

from django.db.models import Sum
sales_data = Sales.objects.filter(
zone_code=zone_code_, product_code=product
).values('region_name').annotate(
total_quantity=Sum('quantity')
).order_by('region_name')

这将产生一个包含两个元素的字典的QuerySet:映射到区域名称的'region_name',和汇总该区域数量的total_quantity

接下来,我们可以用将其转换为字典regions_dict

regions_dict = { r['region_name']: r['total_quantity'] for r in sales_data }

使用dict理解和itertools.groupby:可以更有效地完成从sales_data = ...t5 = ...的整个代码

from itertools import groupby
from operator import itemgetter
regions_dict = {k: sum(map(itemgetter('quantity'), g)) for k, g in groupby(Sales.objects.filter(zone_code=zone_code_, product_code=product).order_by('region_name').values('region_name', 'quantity'), itemgetter('region_name'))}

使用用列表综合构建的集合?

regions = set(x.region_name for x in sales_data)

唯一的问题是你是否需要这些物品。它们可以在事实之后进行排序,您可以将它们附加到列表中,这取决于它们是否成功添加到集合中。

编辑:我同意在数据库中这样做是更好的方法(@Willem Van Onsem的答案(,但这个答案对任何不使用Django的人来说都是相关的。

改进可以直接将第一次求和存储在复杂度为O(N(的字典中

regions_dict_sums = {x.region_name:0 for x in sales_data}
for x in sales_data:
regions_dict_sums[x.region_name]+=x.quantity

因此,销售额大于零的总地区将在字典中作为关键字进行散列,它们的值是该地区的总销售额,可以随时访问。

最新更新