我正在处理一个处理大型数据库的Web应用程序(Python / Django),我需要优化这个循环以获得更好的执行时间。
我有一个条目列表,每个条目都有一个yes_count属性,一个no_count属性和一个tid属性。
我需要创建两个新列表,具体取决于比率= yes_count /(yes_count + no_count)
使用内置功能(或更快的功能)更好的方式吗?
yes_entries = []
no_entries = []
for e in entries:
if e.tid in tids:
if e.yes_count > 0 or e.no_count > 0:
ratio = e.yes_count / (e.yes_count + e.no_count)
if ratio > 0.75:
yes_entries.append(e.tid)
elif ratio < 0.25:
no_entries.append(e.tid)
答案 0 :(得分:1)
我建议将tids
放入O(1)摊销查询速度的集合中(而不是列表的O(N)):
set_tids = set(tids)
在for
循环之前,然后
if e.tid in set_tids
否则你给出的其余代码看起来非常优化
答案 1 :(得分:0)
您还可以只访问e.tid
,e.yes_count
和e.no_count
一次,并将其存储在变量中,从而节省一些时间:
for e in entries:
tid = e.tid
if tid in tids:
yes_count = e.yes_count
no_count = e.no_count
if yes_count > 0 or no_count > 0:
ratio = yes_count / (yes_count + no_count)
if ratio > 0.75:
yes_entries.append(tid)
elif ratio < 0.25:
no_entries.append(tid)
您也可以通过缓存no_entries.append和yes_entries.append来节省时间:
yes_entries_append = yes_entries.append
no_entries_append = no_entries.append
for e in entries:
tid = e.tid
if tid in tids:
yes_count = e.yes_count
no_count = e.no_count
if yes_count > 0 or no_count > 0:
ratio = yes_count / (yes_count + no_count)
if ratio > 0.75:
yes_entries_append(tid)
elif ratio < 0.25:
no_entries_append(tid)
但那时,你可能开始变得愚蠢。
要尝试的另一个,甚至更愚蠢的事情,是看看使用过滤器是否更快。在python2中,filter返回一个列表,这意味着你要迭代两次,这不太理想。但是,我们有itertools来帮助我们:
def filterfunc(e):
return (e.tid in tids) and (yes_count > 0 or no_count > 0)
for e in itertools.ifilter(filterfunc, entries):
tid = e.tid
yes_count = e.yes_count
no_count = e.no_count
ratio = yes_count / (yes_count + no_count)
if ratio > 0.75:
yes_entries_append(tid)
elif ratio < 0.25:
no_entries_append(tid)
下一个问题是我们再次访问e上的字段两次。让我们用一些迭代器魔法解决这个问题:
def filterfunc(t):
tid, yes_count, no_count = t
return (tid in tids) and (yes_count > 0 or no_count > 0)
for tid, yes_count, no_count in itertools.ifilter(filterfunc, itertools.imap(attrgetter(["tid", "yes_count", "no_count"]), entries)):
ratio = yes_count / (yes_count + no_count)
if ratio > 0.75:
yes_entries_append(tid)
elif ratio < 0.25:
no_entries_append(tid)
由您和您的探查者决定从我建议的所有选项中确定最佳方法。
另外,如果您使用的是python3,请使用filter
而不是itertools.ifilter
,因为它返回生成器而不是python2版本的列表。
答案 2 :(得分:0)
注意:以下是尝试更紧凑的解决方案,可能不一定更有效。一些分析可能是有序的。
我假设您正在检查(e.yes_count > 0 or e.no_count > 0)
,这样您就不会以零除的方式结束。假设这是一个非常罕见的事件,我只需将比率计算包装为处理ZeroDivisonError
异常的函数。在这种情况下,我们为该边缘情况返回零。
def get_ratio(y, n):
try:
return y / (y + n)
except ZeroDivisionError:
return 0
接下来,我们创建一个迭代条目的生成器,并返回候选值的比率和tid。
tidset = set(tids) # assuming tids is not yet a set()
ratios = ((get_ratio(e.yes_count, e.no_count), e.tid)
for e in entries if e.tid in tidset)
最后,我们遍历生成器并将它们附加到适当的列表:
yes_entries, no_entries = [], []
for ratio, tid in ratios:
(yes_entries, no_entries)[ratio < 0.75].append(tid)