verzeichnis löschen - nur wenns leer ist

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
limepix
User
Beiträge: 37
Registriert: Dienstag 11. November 2008, 16:54

ich hatte folgende aufgabe: "lösche alle unterverzeichnisse vom ordner xy, wenn sich darin keine dateien mehr befinden".
soweit so gut. auf die schnelle bratzt man das als nicht so erfahrener python programmierer dann folgendermaßen hin:

Code: Alles auswählen

import os
from dircache import listdir as ls

path = 'c:\\temp\\'

def hasFiles(x):
    has = False
    for e in ls(x):
        if os.path.isfile(x + os.sep + e):
            has = True
            break
        else:
            has =  hasFiles(x + os.sep + e)
            if has: break
    return has

if __name__ == '__main__':        
    map(lambda x: os.system('rmdir /S /Q "' + x + '"'), [path + l \
        for l in ls(path) if not hasFiles(path + l)])
das dingens hat auch soweit funktioniert, allerdings find ich das teil könnte noch bisschen eleganter, freakiger, geekiger ausschaun.zum einen.
und zum anderen - ich hab das auf ein verzeichnis losgelassen in dem ca. 600.000 unterverzeichnisse liegen (maximale rekursionstiefe: 6). das skript hat dann laut taskmgr ca 60mb arbeitsspeicher verbaucht, was zwar nicht unbedingt die welt ist, aber trotzdem mehr wie nichts... deswegen die nächste frage -> gehts denn noch performanter?! nur zu lernzwecken...
BlackJack

OMG, bitte sag das Du uns veralbern willst! Der Code ist so übel, den muss man mit Absicht so schrecklich schreiben. Das passiert nicht aus versehen.

Alleine die Frage nach dem Speicherverbrauch -- `dircache` "cached" alle aufgelisteten Verzeichnisse im Speicher. Darum heisst das Modul auch so. Wieso hast Du das verwendet!?

Über die Zeilen 19 und 20 sage ich mal lieber nix. Das könnte zu unfreundlich ausfallen.

Mein Tipp: Wegwerfen neu schreiben. Und zwar mit `os.walk()`, `os.path.join()`, und dem `shutil`-Modul statt `os.system()`.
Benutzeravatar
limepix
User
Beiträge: 37
Registriert: Dienstag 11. November 2008, 16:54

vielleicht weil ichs nicht besser weiss?! wie gesagt, ich benutz python wirklich nicht oft und wenn dann halten meistens die ersten 2 treffer bei google für mein problem her - "python verzeichnis auflisten" -> dircache ;-)
ausserdem sooo schlecht kanns ja wohl net sein, wenns funktioniert :oops: ...
nein spass bei seite, deswegen post ich das doch hier um anregungen / neues dazuzubekommen /-lernen ;)
ich schau mir die module mal an und kuck mal was ich draus machen kann...
Benutzeravatar
limepix
User
Beiträge: 37
Registriert: Dienstag 11. November 2008, 16:54

Code: Alles auswählen

import os
import shutil

path = 'c:\\list'

map(shutil.rmtree ,[os.path.join(path, d) for d in os.listdir(path) \
    if os.path.join(path, d) not in [os.path.join(path, d) \
        for e in os.walk(os.path.join(path, d)) if len(e[2])]])
???
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Abgesehen davon, dass die "\" am Ende überflüssig sind, solltest du vielleicht besser die LC auflösen und Schleifen benutzen. So kann das doch kein Mensch lesen. ;-) Ein "os.walk" sollte übrigens ausreichend sein.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Wow! :shock:

Eigentlich moechte man Programme moeglichst leserlich schreiben und nicht unbedingt moeglichst viel in eine Codezeile stopfen... jedenfalls sehen das Python-Programmierer so. :wink:

Vergiss am besten mal fuer einen Moment, dass es map und List-Comprehensions gibt und schreibe den Code nochmal.

Statt if len(e[2]) reicht auch if e[2], das ist aber auch das einzige, das ich auf den ersten Blick sagen kann...
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
BlackJack

Man sollte `map()` nicht als Schleife missbrauchen. Genau wie "list comprehensions" ist die Funktion dafür da eine Liste zu erstellen. Wenn man diese Liste gar nicht braucht, dann ist die Funktion nicht das Mittel der Wahl, sondern eine Schleife.
Benutzeravatar
limepix
User
Beiträge: 37
Registriert: Dienstag 11. November 2008, 16:54

@EyDu: Das mit dem "\" weiss ich schon, aber des schaut so blöd aus, wennste eine zeile hast wo man dann erst mal über drei monitore hinweg scrollen muss... wenn ich dann schon alles in "eine zeile" bratz, dann wenigstens so, dass man alles auf einen blick hat...
wie meinste das mit "ein os.walk reicht" ? is doch nur eins gewesen oder verschau ich mich grad?! :?

@EyDu, Rebecca: Das das nicht gerade leserlich ist, ist mir schon klar... ich spiel nur gerade ein wenig mit listcomprehensions, nested lc.... usw rum. mein ursprünglicher code nachdem ich das bearbeitet habe, sah ungefähr so aus:

Code: Alles auswählen

import os

path = '/home/jb/temp/'

def delete(x):
    print x

#map(delete ,[os.path.join(path, d) for d in os.listdir(path) \
#        if os.path.join(path, d) not in [os.path.join(path, d) \
 #       for e in os.walk(os.path.join(path, d)) if len(e[2])]])

for d in os.listdir(path):
    delFlag = True
    for e in os.walk(os.path.join(path, d)):
        if e[2]:
            delFlag = False
            break
    if delFlag: delete(os.path.join(path, d))
dann dachte ich mir allerdings, ob es nicht möglich wäre, die variable delFlag noch zu beseitigen, ums noch minimaler zu gestalten... (das es dann die lesbarkeit nicht sonderlich steigert ist mir schon bewusst)... und um ein bisschen mit LCs rumzuspielen...

@BlackJack: Das verstehe ich nicht ganz... :( was meinst du mit "Man sollte `map()` nicht als Schleife missbrauchen" ? ich will auf jedes element einer sequenz eine bestimmte funktion anwenden?! ist das nicht die intention der funktion "map" ?? wenn nicht, wie denn dann??

@ALL : Danke für die schnellen Antworten :) . Trotzdem noch die Frage - Wie würdet ihr das machen??

[EDIT:]
...und dann viel ihm zwei minuten später ein, wie man die variable weglassen kann...

Code: Alles auswählen

import os

path = '/home/jb/temp/'

def delete(x):
    print x

for d in os.listdir(path):
    for e in os.walk(os.path.join(path, d)):
        if e[2]: break
    else:
        delete(os.path.join(path, d))
meeensch kinners... :oops:
Zuletzt geändert von limepix am Montag 22. Juni 2009, 20:38, insgesamt 1-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

limepix hat geschrieben:@BlackJack: Das verstehe ich nicht ganz... :( was meinst du mit "Man sollte `map()` nicht als Schleife missbrauchen" ? ich will auf jedes element einer sequenz eine bestimmte funktion anwenden?! ist das nicht die intention der funktion "map" ?? wenn nicht, wie denn dann??
``map()`` ist dafür da die Rückgabewerte der Funktion die du auf jedes Element anwendest in einer Liste zu sammeln. Dabei haben die Funktionen in aller Regel keine Seiteneffekte sondern berechnen nur aus den Eingabedaten irgendeinen Ausgebewert, der dann von ``map()`` verwendet wird. Wenn du aber eine Funktion auf die Elemente einer Liste wegen den Seiteneffekten aufrufen willst, aber du dich nicht um die Rückgabewerte der Funktion kümmerst, solltest du eine Schleife verwenden.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
limepix
User
Beiträge: 37
Registriert: Dienstag 11. November 2008, 16:54

map(function, iterable, ...)
Apply function to every item of iterable and return a list of the results. ...

Beim genaueren lesen der Doku wirds dann auf einmal heller :oops: ...
dh, angenommen ich lass das in einer zeile stehen mit den LCs, wäre das dann okay:

Code: Alles auswählen

import os

path = '/home/jb/temp/'

def delete(x):
    print x

for x  in [os.path.join(path, d) for d in os.listdir(path) \
                if os.path.join(path, d) not in [os.path.join(path, d) \
                for e in os.walk(os.path.join(path, d)) if e[2]]]: 
    delete(x)

??
Zuletzt geändert von limepix am Montag 22. Juni 2009, 20:39, insgesamt 1-mal geändert.
BlackJack

Definiere mal "okay"? Als effizientes und lesbares Programm ist das ganz sicher nicht okay. Die Zeile ist zu lang, nicht wirklich so formatiert, dass man die geschachtelte LC leicht versteht, es wird zu oft `os.path.join()` mit den gleichen Argumenten aufgerufen und die innere LC wird komplett durchlaufen, auch wenn schon klar ist, dass da ein Unterordner Dateien enthält.

Also halbwegs kompakt, funktional, lazy, und trotzdem noch lesbar wäre IMHO das hier (ungetestet):

Code: Alles auswählen

from itertools import ifilter

PATH = '/tmp/'

def delete(path):
    print path

def contains_no_files(path):
    return all(not fs for _, _, fs in os.walk(path))

def main():
    paths = (os.path.join(PATH, p) for p in os.listdir(PATH))
    for path in ifilter(contains_no_files, paths):
        delete(path)
Benutzeravatar
limepix
User
Beiträge: 37
Registriert: Dienstag 11. November 2008, 16:54

@BlackJack: das "okay" hat sich nicht auf die LC bezogen ;), sondern auf das weglassen der funktion map - quasi das was du auch als erstes angekreidet hat - man soll map nich dafür verwenden eine funktion auf eine sequenz anzuwenden... is ja auch egal...
was mich jetzt aber trotzdem noch interessieren würde, wo du genau den vorteil deiner lösung gegenüber dieser hier siehst:

Code: Alles auswählen

import os

path = '/home/jb/temp/'

def delete(x):
    print x

for d in os.listdir(path):
    for e in os.walk(os.path.join(path, d)):
        if e[2]: break
    else:
        delete(os.path.join(path, d))
da hört die innere schleife doch genauso auf, sobald die erste datei gefunden wurde... da versteh ich nicht ganz (nich dass ich deinen code hier jetz anzweifeln will, sondern weil ichs wirklich nicht versteh), was genau der generator ifilter da besser macht?
BlackJack

Da gibt's keinen "echten" Vorteil von meinem Code. Meins gefällt mir besser weil's von mir ist. :-) Und weil ich gerne versuche verschiedene Aspekte zu trennen und ein wenig funktional zu programmieren.
Benutzeravatar
limepix
User
Beiträge: 37
Registriert: Dienstag 11. November 2008, 16:54

@BlackJack: dann is ja alles geklärt ;)
wobei da muss ich dir schon zustimmen, die funktion

Code: Alles auswählen

def contains_no_files(path):
    return all(not fs for _, _, fs in os.walk(path)) 
find ich schon auch schick :)
da hab ich dann wieder mal bissl was zugelernt... feinfein...
Benutzeravatar
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

Tut

Code: Alles auswählen

os.rmdir(dir_)
nur auf Unices das was der OP will?
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
Antworten