ver rutas recorrer navegar manejo lista explorador entre directorios directorio con carpetas archivos actual abrir python ruby vbscript benchmarking

recorrer - rutas de archivos en python



puntos de referencia: ¿Python tiene una forma más rápida de recorrer una carpeta de red? (2)

Necesito recorrer una carpeta con aproximadamente diez mil archivos. Mi viejo vbscript es muy lento en el manejo de esto. Desde que comencé a usar Ruby y Python desde entonces, hice un punto de referencia entre los tres lenguajes de scripting para ver cuál sería la mejor opción para este trabajo.

Los resultados de las pruebas a continuación en un subconjunto de 4500 archivos en una red compartida son

Python: 106 seconds Ruby: 5 seconds Vbscript: 124 seconds

Que Vbscript sería más lento no fue una sorpresa, pero no puedo explicar la diferencia entre Ruby y Python. ¿Mi prueba para Python no es óptima? ¿Hay una manera más rápida de hacer esto en Python?

La prueba para thumbs.db es solo para la prueba, en realidad hay más pruebas para hacer.

Necesitaba algo que verifica cada archivo en la ruta y no produce demasiada salida para no perturbar el tiempo. Los resultados son un poco diferentes en cada ejecución pero no mucho.

#python2.7.0 import os def recurse(path): for (path, dirs, files) in os.walk(path): for file in files: if file.lower() == "thumbs.db": print (path+''/''+file) if __name__ == ''__main__'': import timeit path = ''//server/share/folder/'' print(timeit.timeit(''recurse("''+path+''")'', setup="from __main__ import recurse", number=1))

''vbscript5.7 set oFso = CreateObject("Scripting.FileSystemObject") const path = "//server/share/folder" start = Timer myLCfilename="thumbs.db" sub recurse(folder) for each file in folder.Files if lCase(file.name) = myLCfilename then wscript.echo file end if next for each subfolder in folder.SubFolders call Recurse(subfolder) next end Sub set folder = oFso.getFolder(path) recurse(folder) wscript.echo Timer-start

#ruby1.9.3 require ''benchmark'' def recursive(path, bench) bench.report(path) do Dir["#{path}/**/**"].each{|file| puts file if File.basename(file).downcase == "thumbs.db"} end end path = ''//server/share/folder/'' Benchmark.bm {|bench| recursive(path, bench)}

EDITAR: como sospechaba que la impresión causó un retraso, probé las secuencias de comandos con la impresión de todos los 4500 archivos y tampoco imprimí ninguna, la diferencia permanece, R: 5 P: 107 en el primer caso y R: 4.5 P: 107 en el segundo

EDIT2: basado en las respuestas y comentarios aquí una versión de Python que en algunos casos podría ejecutarse más rápido omitiendo carpetas

import os def recurse(path): for (path, dirs, files) in os.walk(path): for file in files: if file.lower() == "thumbs.db": print (path+''/''+file) def recurse2(path): for (path, dirs, files) in os.walk(path): for dir in dirs: if dir in (''comics''): dirs.remove(dir) for file in files: if file.lower() == "thumbs.db": print (path+''/''+file) if __name__ == ''__main__'': import timeit path = ''f:/'' print(timeit.timeit(''recurse("''+path+''")'', setup="from __main__ import recurse", number=1)) #6.20102692 print(timeit.timeit(''recurse2("''+path+''")'', setup="from __main__ import recurse2", number=1)) #2.73848228 #ruby 5.7


Configuré la estructura del directorio con lo siguiente de forma local:

for i in $(seq 1 4500); do if [[ $i -lt 100 ]]; then dir="$(for j in $(seq 1 $i); do echo -n $i/;done)" mkdir -p "$dir" touch ${dir}$i else touch $i fi done

Esto crea 99 archivos con rutas que tienen 1-99 niveles de profundidad y 4401 archivos en la raíz de la estructura del directorio.

Usé el siguiente script de ruby:

#!/usr/bin/env ruby require ''benchmark'' def recursive(path, bench) bench.report(path) do Dir["#{path}/**/**"] end end path = ''files'' Benchmark.bm {|bench| recursive(path, bench)}

Obtuve el siguiente resultado:

user system total real files/ 0.030000 0.090000 0.120000 ( 0.108562)

Utilizo el siguiente script de Python usando os.walk:

#!/usr/bin/env python import os import timeit def path_recurse(path): for (path, dirs, files) in os.walk(path): for folder in dirs: yield ''{}/{}''.format(path, folder) for filename in files: yield ''{}/{}''.format(path, filename) if __name__ == ''__main__'': path = ''files'' print(timeit.timeit(''[i for i in path_recurse("''+path+''")]'', setup="from __main__ import path_recurse", number=1))

Obtuve el siguiente resultado:

0.250478029251

Por lo tanto, parece que Ruby todavía está funcionando mejor. Sería interesante ver cómo funciona esto en su conjunto de archivos en el recurso compartido de red.

Probablemente también sea interesante ver este script ejecutarse en python3 y con jython y tal vez incluso con pypy.


La implementación de Ruby para Dir está en C (el archivo dir.c , según esta documentación ). Sin embargo, el equivalente de Python se implementa en Python .

No es sorprendente que Python sea menos eficiente que C, pero el enfoque utilizado en Python le da un poco más de flexibilidad, por ejemplo, puede omitir subárboles enteros nombrados, por ejemplo, ''.svn'' , ''.git'' , ''.hg'' al atravesar un jerarquía de directorio.

La mayoría de las veces, la implementación de Python es lo suficientemente rápida.

Actualización: La omisión de archivos / subdirectorios no afecta en absoluto la velocidad de cruce, pero el tiempo total que lleva procesar un árbol de directorios puede reducirse sin duda porque evita tener que atravesar subárboles potencialmente grandes del árbol principal. El tiempo ahorrado es, por supuesto, proporcional a la cantidad que omite. En su caso, que parece carpetas de imágenes, es poco probable que ahorre mucho tiempo (a menos que las imágenes estuvieran bajo control de revisión, cuando omitir subárboles propiedad del sistema de control de revisiones podría tener algún impacto).

Actualización adicional: omitir carpetas se hace cambiando el valor de los dirs en su lugar:

for root, dirs, files in os.walk(path): for skip in (''.hg'', ''.git'', ''.svn'', ''.bzr''): if skip in dirs: dirs.remove(skip) # Now process other stuff at this level, i.e. # in directory "root". The skipped folders # won''t be recursed into.