从列表推导中返回两个列表的性能

0 投票
2 回答
1409 浏览
提问于 2025-04-18 03:43

在我的程序中,我会获取所有的文件夹和文件(用walk),然后把它们写入一个字典,字典的键是文件名,值是文件路径。接着,我从界面上获取一个关键词(用tk.Entry),然后把所有匹配的结果放到两个列表里。我会把这些结果显示出来(用tk.Listbox),并且可以打开选中的文件(用win32shell)。

我使用了这个方法来用一个表达式创建两个列表。在评论中,有人说“单独运行两个列表表达式会更简单,也可能更快。”这让我有点困惑,不知道该用哪种方法。因为我的程序需要处理大约3TB的数据,而我现在没有这些数据,所以无法运行测试看看哪种方法更快。

这是我简化后的代码,我去掉了界面部分,并用keywrdfolder这两个变量分别定义了关键词和路径。

import os
import sqlite3

audio_ext = [".mp3",".mp4","etc..."]
folder = "C:\\Users\\Lafexlos\\Music"
keywrd = "mo"  ##searching keyword which I normally get from user by Entry

conn = sqlite3.connect(":memory:")
data  = conn.cursor()
data.execute(" create table if not exists audio(path text,\
                filename text UNIQUE) ")

for roots ,dirs ,files in os.walk(folder):
    for item in os.listdir(roots):
        if "."+item.split(".")[-1].lower() in audio_ext:
        #Above line is not eye-friendly but is only checks file's extension
            data.execute(" INSERT OR IGNORE into audio \
                (path, filename) VALUES (?,?)",(roots,item))

lines = {}
musics = data.execute("select * from audio")
[lines.update({row[1]:row[0]}) for row in musics]


# This is the option 1. Using zip to create two lists
results,paths = zip(*[(k,v) for k,v in lines.items() if keywrd in k])

# This is option 2. Running same list comprehension twice
results = [k for (k,v) in lines.items() if keywrd in k]
paths = [v for (k,v) in lines.items() if keywrd in k]

print ("Results: ", results)
print ("\n\nPaths: ", paths)

正如我上面提到的,我的问题是,在处理大量数据时,哪种方法会更快呢?

2 个回答

1

使用for循环会更快:

results = []; add_result = result.append
paths = []; add_path = path.append
for k,v in lines.items():
    if keywrd in k:
        add_result(k)
        add_path(v)

最快的方法是用你内存中的sqlite数据库来进行筛选。

3

使用 zip()

results, paths = zip(*((k, v) for k, v in lines.items() if keywrd in k))

这样可以一步就把两个列表合在一起。另一种方法是用一个 for 循环

results = []
paths = []
for (k,v) in lines.items():
    if keywrd in k:
        results.append(k)
        paths.append(v)

列表推导式很适合用来创建 一个 列表;如果你需要从同一个循环中得到多个列表,那就直接用循环吧。

不过,由于这些数据是从 SQLite 查询中来的,最好的办法是让 SQLite 限制只返回匹配的行:

data.execute("select * from audio if filename LIKE ?", ('%{}%'.format(keywrd),))

你的 lines 字典用字典推导式来构建会更高效:

musics = data.execute("select * from audio")
lines = {row[1]: row[0] for row in musics}

或者使用更具体的查询,并直接在游标上循环:

data.execute("SELECT path, filename FROM audio WHERE filename LIKE ?",
             ('%{}%'.format(keywrd),))
paths, results = zip(*data)

LIKE 用于字符串时,如果两边都有 % 通配符,效果和 Python 中的 in 测试是一样的;只要 keywrd 包含在 filename 中,这一行就匹配。

现在也不需要再创建一个中间字典了。

撰写回答