2010-01-29 10 views
9

Ho un pezzo di codice che sto usando per cercare gli eseguibili dei file di gioco e restituire le directory. Mi piacerebbe davvero ottenere una sorta di indicatore di avanzamento per quanto è lungo lo os.walk. Come potrei realizzare una cosa del genere?Come ottenere progressi su os.walk in python?

Ho provato a fare startpt = root.count(os.sep) e misurarlo, ma ciò dimostra quanto sia profondo os.walk in un albero di directory.

def locate(filelist, root=os.curdir): #Find a list of files, return directories. 
    for path, dirs, files in os.walk(os.path.abspath(root)): 
     for filename in returnMatches(filelist, [k.lower() for k in files]): 
      yield path + "\\" 
+0

la vera domanda è perché il tuo 'os.walk' impiega così tanto tempo? quanti file stai confondendo? qual è il rendimento dei 'returnMatches'? – SilentGhost

+0

def returnMatches (a, b): elenco di ritorno (set (a) e set (b)) # Ripristina un elenco di corrispondenze tra elenchi dati. Questo è tutto returnMatches è ... questo richiede solo un paio di secondi per completare, ma sto aggiungendo il polacco al programma, così alle persone non sembra che il mio programma stia semplicemente facendo niente per un paio di secondi. Sulla macchina * MY * l'intera operazione richiede circa 10 secondi. Ma questo sarà impacchettato e funzionante su qualsiasi numero di macchine/ambienti Windows – ThantiK

+0

Nota sulla mia macchina: Ancora in esecuzione un'unità IDE molto lenta. ;) – ThantiK

risposta

3

L'ho capito.

ho usato os.listdir per ottenere una lista di directory di primo livello, e quindi utilizzato la funzione .split sul sentiero che os.walk restituito, restituendo il primo elenco di livello che era attualmente in.

che ha lasciato io con una lista di directory toplevel, che ho trovato l'indice della directory corrente di os.walk e confrontare l'indice restituito con la lunghezza della lista, dandomi una percentuale completa. ;)

Questo non mi dà un liscio progresso, perché il livello di lavoro svolto in ciascuna directory può variare ma l'appianamento dell'indicatore di avanzamento non mi interessa. Ma potrebbe essere facilmente realizzato estendendo il controllo del percorso più in profondità nella struttura delle directory.

Ecco il codice finale di ottenere i miei progressi:

def locateGameDirs(filelist, root=os.curdir): #Find a list of files, return directories. 
    toplevel = [folder for folder in os.listdir(root) if os.path.isdir(os.path.join(root, folder))] #List of top-level directories 
    fileset = set(filelist) 

    for path, dirs, files in os.walk(os.path.abspath(root)): 

     curdir = path.split('\\')[1] #The directory os.walk is currently in. 

     try: #Thrown here because there's a nonexistant(?) first entry. 
      youarehere = toplevel.index(curdir) 
      progress = int(((youarehere)/len(toplevel))*100) 
     except: 
      pass 

     for filename in returnMatches(filelist, [k.lower() for k in files]): 
      yield filename, path + "\\", progress 

E proprio ora per scopi che sto facendo questa ulteriore nel codice di debug:

for wow in locateGameDirs(["wow.exe", "firefox.exe", "vlc.exe"], "C:\\"): 
    print wow 

Esiste un modo poco piacevole sbarazzarsi di quel tentativo/eccetto ?; sembra che la prima iterazione del percorso non mi dia nulla ...

+0

La prima iterazione fornisce la radice. Prova ad aggiungere "percorso di stampa" per vedere cosa intendo. –

2

farlo in due passaggi: in primo luogo contare quanti file totale/cartelle sono nella struttura, e poi durante il secondo passaggio fare l'elaborazione vera e propria.

+0

Non ci vorrà più del doppio? –

+1

Questo è utile solo se l'elaborazione richiede molto più tempo di quanto non faccia l'albero. Se l'OP sta aprendo ogni file, probabilmente lo fa. Se l'OP sta guardando solo alcuni dettagli del nome, quasi certamente non lo fa. – Omnifarious

+0

@Omnifarious: quindi non è chiaro perché avrebbe voluto conoscere i progressi, dal momento che costerà più dell'elaborazione effettiva. – SilentGhost

0

È necessario conoscere il numero totale di file per fare un indicatore di progresso significativo.
È possibile ottenere il numero di file come questo

len(list(os.walk(os.path.abspath(root)))) 

ma che sta andando a prendere un po 'di tempo e probabilmente bisogno di un indicatore di progresso per questo ...

Per trovare il numero di file molto velocemente avresti bisogno di un filesystem che tenga traccia del numero di file per te.

forse si può salvare il totale da una precedente esecuzione e l'uso che come una stima

+1

Non mi interessa il numero di file. Onestamente sarei felice solo sapendo quale directory superiore è in tutte le directory migliori. Ad esempio, ho le directory principali denominate C: \\ 1, C: \\ 2 e così via ... Solo dire "La tua directory di primo livello x su x" andrebbe bene, io non lo faccio sapere come tirarlo fuori. – ThantiK

+0

Ho lavorato per ottenere almeno le mie directory di primo livello: [cartella per cartella in os.listdir ('C: \\') se os.path.isdir (os.path.join ('C: \\ ', cartella))] Ora, come potrei fare per capire dove si trova os.walk? – ThantiK

5

Dipende!

Se i file e le directory sono distribuiti in modo più o meno uniforme, è possibile mostrare un processo approssimativo assumendo che ogni directory di livello occupazionale richieda lo stesso tempo. Ma se non vengono distribuiti in modo uniforme non è possibile scoprirlo a buon mercato. Devi sapere approssimativamente come è popolata ogni directory in anticipo, oppure devi os.walkare l'intera cosa due volte (ma ciò è utile solo se l'elaborazione effettiva impiega molto più tempo rispetto a os.walk stesso).

Vale a dire: si dispone di 4 directory di livello superiore e ognuna contiene 4 file. Se si assume che ogni directory di primo livello abbia il 25% di progresso e che ogni file impieghi un altro 25% del progresso per quella directory, è possibile mostrare un indicatore di progresso piacevole. Ma se l'ultimo sottodir risulta contenere molti più file dei primi, l'indicatore di avanzamento avrà raggiunto il 75% prima di scoprirlo. Non si può davvero risolvere il problema se l'os.walk stesso è il collo di bottiglia (non l'elaborazione) e si tratta di un albero di directory arbitrario (non uno in cui si conosce in anticipo circa la durata di ogni sottostruttura).

E, naturalmente, che sta assumendo il costo qui è circa lo stesso per ogni file ...

4

solo mostrare una barra di avanzamento indeterminata (vale a dire quelli che mostrano un blob che rimbalza avanti e indietro o l'effetto Palo del barbiere) . In questo modo gli utenti sanno che il programma sta facendo qualcosa di utile ma non li induce in errore per quanto riguarda il tempo di completamento e così via.

+0

Anche se ho capito il mio problema, dal momento che l'operazione è così breve probabilmente è proprio su questo. Grazie;) – ThantiK

0

Suggerisco di evitare di camminare nella directory. Utilizza invece un'applicazione basata su indicizzazione per trovare rapidamente i file. È possibile utilizzare l'interfaccia della riga di comando dell'app tramite sottoprocesso e trovare i file quasi istantaneamente.

Su Windows, vedere Everything. Su UNIX, controlla individuare. Non sono sicuro di Mac, ma sono sicuro che c'è anche un'opzione.

+0

Questo sarà un file eseguibile che viene distribuito alle persone. Non per uso personale. Non posso usare cose come questa. – ThantiK

+0

Non potresti spedire l'app di ricerca insieme al tuo programma? Forse aiutato da un installatore? Se vuoi davvero camminare, le uniche opzioni che vedo sono già state suggerite: fare due passi (uno per il conteggio, uno per l'operazione effettiva) o una barra di progresso indeterminato che si spunta dopo ogni x numero di iterazioni. –

+0

No. Il programma è in pratica solo un eseguibile da 4mb pieno di py2exe, nessun motivo per installare un programma che cerca solo un elenco di giochi installati e carica i file di salvataggio su un server. – ThantiK

0

come ho detto nel commento, il collo della bottiglia delle prestazioni risiede probabilmente al di fuori della funzione locate. il tuo returnMatches è una funzione abbastanza costosa.Penso che sarebbe meglio sostituirlo con il seguente codice:

def locate(filelist, root=os.curdir) 
    fileset = set(filelist)   # if possible, pass the set instead of the list as a first argument 
    for path, dirs, files in os.walk(os.path.abspath(root)): 
      if any(file.lower() in fileset for file in files): 
       yield path + '\\' 

In questo modo si riduce il numero di operazioni dispendiose, resa una volta per ogni file nella directory (che credo sia quello che effettivamente bugnata per fare) e puoi dimenticare i progressi allo stesso tempo. Non credo che il progresso sarebbe comunque una caratteristica atteso dell'interfaccia.

+0

def returnMatches (a, b): lista di ritorno (set (a) e set (b)) E ho provato il tuo metodo pubblicato qui. Non ha funzionato più velocemente. – ThantiK

+0

@ThantiK: significa solo che la maggior parte del tempo viene spesa da 'os.walk' stesso. Non rende il tuo approccio efficiente. – SilentGhost

0

Pensare fuori dalla scatola qui ... e se l'avete fatto sulla base dimensioni:

  • Usa sottoprocesso per eseguire 'du -sb' e ottenere il TOTAL_SIZE di root directory
  • Mentre si cammina, controllare la dimensione di ogni file e decremento dal TOTAL_SIZE (dando remaining_size)
  • pct_complete = (TOTAL_SIZE - remaining_size)/TOTAL_SIZE

Pensieri?

-aj

0

Un'ottimizzazione che si potrebbe fare: si converte l'elenco dei file in un set su ogni chiamata per restituire i Ticket, anche se non cambia mai. sposta la conversione all'inizio della funzione 'locate' e passa l'insieme ad ogni iterazione.

+0

Grazie - In realtà l'ho preso dal post di SilentGhosts, anche se era preoccupato più delle prestazioni che del compito in corso;) – ThantiK

0

Bene, questo è stato divertente. Ecco un altro modo sciocco di farlo, ma come tutto il resto, calcola solo il progresso giusto per percorsi uniformi.

import os, sys, time 

def calc_progress(progress, root, dirs): 
    prog_start, prog_end, prog_slice = 0.0, 1.0, 1.0 

    current_progress = 0.0 
    parent_path, current_name = os.path.split(root) 
    data = progress.get(parent_path) 
    if data: 
     prog_start, prog_end, subdirs = data 
     i = subdirs.index(current_name) 
     prog_slice = (prog_end - prog_start)/len(subdirs) 
     current_progress = prog_slice * i + prog_start 

     if i == (len(subdirs) - 1): 
      del progress[parent_path] 

    if dirs: 
     progress[root] = (current_progress, current_progress+prog_slice, dirs) 

    return current_progress 

def walk(start_root): 
    progress = {} 
    print 'Starting with {start_root}'.format(**locals()) 

    for root, dirs, files in os.walk(start_root): 
     print '{0}: {1:%}'.format(root[len(start_root)+1:], calc_progress(progress, root, dirs)) 
Problemi correlati