列表理解可以帮助迭代sqlalchemy查询返回吗



这是一个非常慢的循环(使用tqdm测量约1.5[it/s](

在上下文中,对象指的是本地的flask SQLAlchemy管理的postgres数据库的模型。网络传输速度不是速度慢的原因。

for author in tqdm(authors):
new_score = 0
for book in author.maintitles:
new_score = new_score + book.score
author.score = new_score

进一步明确:大约有50万本书,大约有5万作者。每本书都可以由几个作者写。

我不会返回列表,但我相信这是可以改进的——列表理解真的能改进吗?

类似。。。

[[(new_score = new_score + book.score,
author.score = new_score) for book in author.maintitles] for author in tqdm(authors)]

不,不要对副作用使用列表理解。即使要使用列表,理解也只比循环快一点。

但是,您可以使用类似的生成器表达式来改进代码。

步骤1:在结束时分配给author.score,而不是每个循环,并使用增广分配。

for author in tqdm(authors):
new_score = 0
for book in author.maintitles:
new_score += book.score
author.score = new_score

步骤2:现在很明显,new_score是一个简单的求和,所以使用sum

for author in tqdm(authors):
author.score = sum(book.score for book in author.maintitles)

旁注:你也可以用列表理解来写这篇文章,但这会使它构建列表然后求和,而生成器表达式更有效,因为它可以边求边求和。

sum([book.score for book in author.maintitles])

由于提供的重构只是证明了列表理解不是一个解决方案,因此我已经发现了问题的根本原因,因此我添加以下内容作为答案。

上面的代码片段是从返回的querylist进行操作的一部分-如前所述,在最后的操作中,在经过重复消除的authors列表(约50名作者(中迭代是一个15小时的过程,速度为1.5 it/s:

# Make the popular books query
popular_books = 
db.session.query(Book).filter(Book.score > 0).all()

# Make a list of all authors for each book returned in the query
authors = []
for book in popular_books:
authors = authors + book.mainauthors

# Remove duplicates using set()
authors = list(set(authors))

for author in tqdm(authors):
author.score = sum(book.score for book in author.maintitles)
db.session.commit()

只需调整查询以通过joinedload返回作者,并使用.distinct()处理重复数据消除,我们不仅将上面的所有内容简化为几行,而且在查询返回后的秒内即可完成操作。

for popular_author in db.session.query(Author).join(Book, Author.maintitles).options(db.joinedload(Book, Artist.maintitles)).filter(Book.popularity > 0).distinct().all():
popular_author.score = sum(book.score for book in popular_author.maintitles)

然而,我仍然不完全确定这种方法如何比旧版本快几个数量级。两者都以相同的方式迭代authors的列表,并执行相同的简单求和操作。

作为参考,在此过程之后提交会话大约需要2:00小时,而之前的实现要快得多。总的来说仍然有显著的改善(7.5倍(。我的猜测是,从一开始就使用更优化的query,返回的所有ORM对象都放在RAM中,操作速度更快。在query上引入pythonlist方法似乎会破坏内存中的ORM。

最新更新