【问题标题】:Print number of occurrences of any items in a list in paths打印路径列表中任何项目的出现次数
【发布时间】:2022-10-16 19:49:23
【问题描述】:

我正在使用os.walk 来识别通用源目录 (SRC) 中包含my_list 中的任何字符串的路径:

SRC = '/User/dir_1/'

my_list = ["dog", "cat", "mouse", "bird"]

for dirpath, dirnames, filenames in os.walk(SRC):
    for folders in dirnames:
        for x in my_list:
            if x in folders:
                source_path = os.path.join(dirpath, folders)

假设print(source_path) 给出以下内容:

/User/dir_1/cat_test/
/User/dir_1/cat_test/bird_results/
/User/dir_1/dir_2/dog_test/
/User/dir_1/dir_2/dog_test/cat_results/
/User/dir_1/mouse_test/
/User/dir_1/mouse_test/mouse_results/
/User/dir_1/unknown_test/dog_results/
/User/dir_1/bird_files/
/User/dir_1/bird_files/bird_a_files/
/User/dir_1/bird_files/bird_b_files/

我的目标是 shutil.move 我的 source_path 的,但是因为,例如,移动 /User/dir_1/bird_files/ 然后尝试移动 /User/dir_1/bird_files/bird_a_files/ 将导致 FileNotFound 错误,我想过滤我的 source_path 以包括那些只有 1 次出现的任何my_list 中的字符串,这样我的 source_path 是:

/User/dir_1/cat_test/
/User/dir_1/dir_2/dog_test/
/User/dir_1/mouse_test/
/User/dir_1/unknown_test/dog_results/
/User/dir_1/bird_files/

我已经尝试过source_path.count(x) == 1,但它迭代了my_list,而不是计数any x in my_list,这样我的输出是(例如):

/User/dir_1/dir_2/dog_test/cat_results/ count == 1 (for dog)
/User/dir_1/dir_2/dog_test/cat_results/ count == 1 (for cat)
/User/dir_1/dir_2/dog_test/cat_results/ count == 0 (for mouse)
/User/dir_1/dir_2/dog_test/cat_results/ count == 0 (for bird)

但我想看看(例如):

/User/dir_1/dir_2/dog_test/cat_results/ count == 2 (for any x in my_list)

这将允许我用count != 1 过滤掉任何source_path

【问题讨论】:

  • 你就不能做for dirpath, dirnames, filenames in os.walk(SRC): print(dirpath.count("dog")),目标有点模糊……
  • @Ferret我的目标是排除在 my_list 中包含多次出现的任何项目的任何 source_path (包括同一项目的多次出现)。所以,例如/dir/dog/cat 和 dir/dog/dog 将被排除,但 /dir/dog 将被保留
  • 您应该创建列表以保留您已经移动的source_path,并使用此列表检查下一个source_path。最终你应该使用os.path.exists(source_path) 来检查source_path 是否仍然存在。或者干脆使用try/except 捕获错误FileNotFound

标签: python list count shutil os.walk


【解决方案1】:

使用推导按计数过滤,然后对结果求和(True 被强制转换为 1)以获得“任何”行为。

paths = """/User/dir_1/cat_test/
/User/dir_1/cat_test/bird_results/
/User/dir_1/dir_2/dog_test/
/User/dir_1/dir_2/dog_test/cat_results/
/User/dir_1/mouse_test/
/User/dir_1/mouse_test/mouse_results/
/User/dir_1/unknown_test/dog_results/
/User/dir_1/bird_files/
/User/dir_1/bird_files/bird_a_files/
/User/dir_1/bird_files/bird_b_files/""".split()


my_list = ["dog", "cat", "mouse", "bird"]

out = []
for path in paths:
    if sum(True for term in my_list if path.count(term) == 1) == 1:
        out.append(path)

print(*out, sep='
')

输出

/User/dir_1/cat_test/
/User/dir_1/dir_2/dog_test/
/User/dir_1/mouse_test/
/User/dir_1/unknown_test/dog_results/
/User/dir_1/bird_files/

编辑:从评论中,os.walk 方法。

想法:从dirnames 参数中删除术语

备注:我用作过滤条件(见代码中的注释)的方法子字符串包含在字符串中这是很差的。在这种特殊情况下,更健壮的可能是d.startswith(c)。为了获得更大的灵活性,请使用正则表达式- 类似的解决方案。

import os


constraints = 'dog', 'cat', 'mouse', 'bird'

wdir = './User' # your reference directory
res = []
for path, dirs, _ in os.walk(wdir, topdown=True):
    # local to each directory's content
    counter = dict.fromkeys(constraints, False)
    dirs_to_skip = []
    
    # filter by constraint
    for c in constraints:
        for d in dirs:
            if c in d: # <-- filter condition!
                if not counter[c]: # 1st match
                    counter[c] = True
                    res.append(os.path.join(path, d))

                dirs_to_skip.append(d)
    
    # remove matched paths          
    for d in dirs_to_skip:
        dirs.remove(d)

print(*res, sep='
')

【讨论】:

  • for a in out: 将允许您使用 shutil.move 中的“输出路径”
  • 有没有办法避免不需要的路径,而不是找到它们并在以后过滤掉它们?例如,如果脚本找到 /User/dir_1/bird_files/ 它将停止在该路径中查找“my_list”子目录(因此,在该示例中,/User/dir_1/bird_files/bird_a_files/ 和 /User/dir_1/bird_files/bird_b_files / 不会被捕获)。我在这里看到了另一种选择(请参阅接受的答案):*.com/questions/43618746/… 这与我正在寻找的内容相似,但是一旦在目录级别找到单个匹配项,它就会停止查找
  • 我认为os.walk 不适合这种情况(如果您共享的链接非常好!)os.listdir 的递归方法会更容易。我会在接下来的几天里试一试:)
  • @Bot75 我添加了一个新的实现,让我知道它是否有意义
最近更新 更多