python-3.x 有更好的方法吗?通过for循环和map计算文件和目录

bpzcxfmw  于 2023-01-03  发布在  Python
关注(0)|答案(2)|浏览(149)

各位,
我正在优化它以加快进程...
我正在做的是创建一个scandir条目的字典...例如。

fs_data = {}
for item in Path(fqpn).iterdir():
    # snipped out a bunch of normalization code
    fs_data[item.name.title().strip()] = item

{'file1': <file1 scandisk data>, etc}

然后稍后使用函数来收集数据中的文件和目录的计数。
现在我怀疑使用map的新代码可以优化为比旧代码更快,我怀疑必须运行两次列表解析,一次用于文件,一次用于目录。
但我想不出一种方法来优化它,使其只需运行一次。
有谁能建议一种方法来在新版本中同时对文件和目录求和吗?(如果需要,我可以回退到旧代码)
但我可能在这一点上过度优化了?
欢迎任何反馈。

def new_fs_counts(fs_entries) -> (int, int):
    """
    Quickly count the files vs directories in a list of scandir entries
    Used primary by sync_database_disk to count a path's files & directories

    Parameters
    ----------
    fs_entries (list) - list of scandir entries

    Returns
    -------
    tuple - (# of files, # of dirs)

    """
    def counter(fs_entry):
        return (fs_entry.is_file(), not fs_entry.is_file())

    mapdata = list(map(counter, fs_entries.values()))
    files = sum(files for files, _ in mapdata)
    dirs = sum(dirs for _, dirs in mapdata)
    return (files, dirs)

对比

def old_fs_counts(fs_entries) -> (int, int):
    """
    Quickly count the files vs directories in a list of scandir entries
    Used primary by sync_database_disk to count a path's files & directories

    Parameters
    ----------
    fs_entries (list) - list of scandir entries

    Returns
    -------
    tuple - (# of files, # of dirs)

    """
    files = 0
    dirs = 0
    for fs_item in fs_entries:
        is_file = fs_entries[fs_item].is_file()
        files += is_file
        dirs += not is_file
    return (files, dirs)
jhdbpxl9

jhdbpxl91#

如果直接Mapis_file函数,map * 在这里是 * 快速的:

files = sum(map(os.DirEntry.is_file, fs_entries.values()))
dirs = len(fs_entries) - files

(使用filter可能会更快,至少在大多数条目 * 不是 * 文件的情况下。或者使用filteris_dir,如果这对你有用,而且大多数条目不是目录的话。或者使用itertools.filterfalseis_file。或者使用itertools.compress。另外,* 用list.countoperator.countOf对 * True进行计数,而不是 * 对 * bool进行求和,可能会更快一些,但所有这些想法都需要更多的代码(有些还需要内存),我更喜欢上面的方法。)

gcuhipw9

gcuhipw92#

好吧,Map绝对不是正确答案。
今天早上我起床用timeit创建了一个测试...它有点现实溅到脸上。
如果不进行优化,新的与旧的,新的Map代码大约是2倍的时间。
新版本:0.023185124970041215旧版本:0.011841499945148826
我最终真的上了一点点击诱饵的当,并认为用MAP重写会获得一些更好的效率。
为了完整起见。

from timeit import timeit
import os

new = '''
def counter(fs_entry):
    files = fs_entry.is_file()

    return (files, not files)

mapdata = list(map(counter, fs_entries.values()))
files = sum(files for files, _ in mapdata)
dirs = sum(dirs for _, dirs in mapdata)
#dirs = len(fs_entries)-files
'''
#dirs = sum(dirs for _, dirs in mapdata)

old = '''
files = 0
dirs = 0
for fs_item in fs_entries:
   is_file = fs_entries[fs_item].is_file()
   files += is_file
   dirs += not is_file
'''

fs_location = '/Volumes/4TB_Drive/gallery/albums/collection1'
fs_data = {}
for item in os.scandir(fs_location):
    fs_data[item.name] = item

print("New : ", timeit(stmt=new, number=1000, globals={'fs_entries':fs_data}))
print("old : ", timeit(stmt=old, number=1000, globals={'fs_entries':fs_data}))

虽然我能够缩小差距与一些优化..(谢谢李的建议)
新版本:0.10864979098550975旧版本:0.08246175001841038
很明显,for循环解决方案更容易阅读、更快,而且更简单。
新老之间的速度差异,似乎没有具体的Map。
重复的sum语句增加了.021,并且最大的减速来自第二个fs_entry.is_file,它增加了.06x的计时...

相关问题