Seite 1 von 1

Elemente aus Liste löschen

Verfasst: Donnerstag 17. August 2006, 15:39
von Sanji
Hallo,

ich komme ursprünglich aus der PHP-welt und will mich nach 4 Jahren mal ein wenig in anderen Sprachen austoben. Dazu habe ich in Python mal ein kleines Backup-Script geschrieben. Da habe ich eine kleine Verständnisfrage:

Ich habe, um aus der Liste der Dateien/Verzeichnisse alle Dateien zu löschen, folgenden Code erstellt (und schäme mich richtig dafür):

Code: Alles auswählen

import dircache
import operator
import os
import time

backupTarget = "backup"               # das Zielverzeichnis für die Backups
directories  = dircache.listdir(".")  # das zu durchsuchende Verzeichnis (. = Verzeichnis, in dem die backup.py liegt)
winrar       = "D:\\WinRAR\\rar"      # Pfad zur Kommandozeilen-Version von WinRAR
options      = "a -r -m5 -inul"       # Optionen für WinRAR (a = hinzufügen, r = rekusriv, m5 = höchste Kompression, inul = keine Ausgabe)

# Verzeichnisse kennzeichnen
dircache.annotate('.',directories)

# Unnoetiges Verzeichnis entfernen
directories.remove(backupTarget+"/");

#
# Alle anderen Dateien entfernen
# (Sicher schrecklich gelöst, aber ich bin Phyton-Anfänger...)
#

deleted = 1

while deleted:
	for i in range(len(directories)):
		string = directories[i]
		if string[-1] != "/":
			del directories[i]
			deleted = 1
			break
	else:
		deleted = 0
Wie gesagt, ich bin von allen anderen Sprachen dieser Welt die "andere" FOR-Schleife gewöhnt und habe keinen einfacheren Weg gefunden, aus der Liste die Dateien zu entfernen. Ich habe beim Durchiterieren immer einen "Index out of bounds" (sinngemäß) erhalten. Das ist ja auch verständlich, wenn ich die Liste (oder war's ein Tupel?) beim Durchlaufen verkürze.
Aber das muss doch auch wesentlich eleganter gehen mit dem Löschen von Elementen. (Die Funktion isdir() habe ich erst nach der Variante mit dem / am Ende entdeckt (ich hab (PHP) nach is_dir() gesucht ^^).

Helft mir mal auf die Sprünge, so will ich den Code nicht lassen, das ist ja beschämend.

Sanji

Verfasst: Donnerstag 17. August 2006, 15:51
von birkenfeld
Einfach:

Code: Alles auswählen

directories = [dir for dir in os.listdir(".") if os.path.isdir(dir)]
Wenn man mit Verzeichnishierarchien arbeitet, lohnt es sich auch die Funktion os.walk anzuschauen.

Verfasst: Donnerstag 17. August 2006, 18:23
von Sanji
Ausgezeichnet ... diese "List Comprehensions" scheinen ja wirklich extrem nützlich zu sein ...

Verfasst: Donnerstag 17. August 2006, 20:54
von Michael Schneider
Hallo,

das könnte man auch direkt über einen Systemaufruf lösen. Ich weiß nicht, ob das ok ist, wenn man von einem popen-Objekt direkt liest, aber man kann das ja noch strecken. Unter Windows würde das so aussehen:

Code: Alles auswählen

from os import popen
l = popen("dir /a:d /b").read().strip().split("\n")
Die Zeile führt einen Systembefehl (Suche nach Verzeichnissen, kurze Ausgabe) aus, liest das Ergebnis aus der Pipe, entfernt führende und nachstehende Whitespaces und trennt die Zeilen nach Linefeed - in der Reihenfolge. :-)

Grüße,
der Michel

Verfasst: Donnerstag 17. August 2006, 21:06
von Sanji
Ich hab jetzt auch nochmal richtig nachgelesen: Im Tutorial steht ja eindeutig: Wenn man eine Liste in einer FOR-Schleife veändern will, soll man über eine Kopie der Liste iterieren.

Verfasst: Donnerstag 17. August 2006, 21:27
von Michael Schneider
Hi Sanji,

ich bin mir jetzt nicht ganz sicher aber ich denke es ist auch möglich, wenn Du von hinten her über die Liste iterierst:

Code: Alles auswählen

for i in range(len(directories), 0, -1):
    i -= 1      # notwendig weil Index bei null beginnt
    if os.path.isdir(directories[i]) : del directories[i]
Oder besser noch Du zählst vorwärts und indizierst nur vom Ende (mit negativen Indizes). Das sieht eleganter aus. :-)

Code: Alles auswählen

iLen = len(directories)
for i in range(iLen):
    if directories[i-iLen] > 4: del directories[i-iLen]
Grüße,
der Michel

Verfasst: Freitag 18. August 2006, 13:02
von birkenfeld
Oder man nimmt einfach eine andere Schleife:

Code: Alles auswählen

i = 0
while i < len(directories):
    if os.path.isdir(directories[i]):
        del directories[i]
    else:
        i += 1

Verfasst: Freitag 18. August 2006, 22:47
von jAN
ich hab nen Einzeiler:

Code: Alles auswählen

[directories.remove(directories[i]) for i in range(len(directories)+1) if directories[i]>4]

Verfasst: Freitag 18. August 2006, 23:54
von Joghurt
Ah! Meine Augen! @jAN: Nur weil es geht, muss man es noch lange nicht machen ;)

Code: Alles auswählen

[sys.stdout.write("Das ist böse!\n") for x in xrange(1000)]
Außerdem wird dann unnötigerweise eine Liste angelegt und gleich wieder gelöscht.

Verfasst: Samstag 19. August 2006, 06:27
von birkenfeld
Es ist auch falsch:

Code: Alles auswählen

>>> directories= [1,2,3,4,5,6]
>>> [directories.remove(directories[i]) for i in range(len(directories)+1) if directories[i]>4]
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
IndexError: list index out of range

Verfasst: Samstag 19. August 2006, 12:08
von Python 47
Es muss so sein:

Code: Alles auswählen

[directories.remove(directories[i]) for i in range(len(directories)-1) if directories[i]>4]

Verfasst: Samstag 19. August 2006, 12:19
von Joghurt
Sag mir bitte, das das ein Scherz ist! :shock:
Das ist noch fälscher als das Ursprüngliche!
1. range(x) liefert immer nur bis x-1
2. Wenn directories=[6,5,4,3,2,1] ist, geht es auch nicht
Das liegt einfach daran, dass du über eine kopie der Liste iterieren musst.
Und das das ganze Konstrukt sowieso schlecht, unnötig kompliziert und gefährlich ist, sagte ich ja schon.

Verfasst: Samstag 19. August 2006, 15:04
von jAN
sry es war spät und ich hab kein python zur hand... ;)

Verfasst: Montag 21. August 2006, 18:36
von Michael Schneider
Hi!
Joghurt hat geschrieben: Das liegt einfach daran, dass du über eine kopie der Liste iterieren musst.
... oder rückwärts oder von hinten indizieren, siehe oben. ;-)

Grüße,
Michael