Größte und neuste Datei aus Verzeichnis?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
RedSharky
User
Beiträge: 99
Registriert: Donnerstag 13. April 2006, 15:38

Hi,
ich habe mir den Kopf zermartert, stehe aber irgendwie auf dem Schlauch.

Ich möchte mir lediglich zwei Funktionen schreiben, die mir sagen, welches die größte oder welches die neuste Datei in einem Verzeichnis ist.

Das muss doch irgendwie über os gehen. Und dann irgendwie sortiert werden.
Das Problem ist, dass man zwar leicht nach z. B. der Dateigröße sortieren kann, aber dann nicht weiß, wie der Dateiname dazu ist.
Irgendwas mit Dictionary?

Habe mal etwas sinnfreien Code erzeugt:

Code: Alles auswählen

from os import *

dirpath = "C:/temp"

class CacheFile: 
    def __init__(self,n,s,t):    #constructor method
        self.name = n
        self.size = s
        self.time = t

chdir(dirpath) #changes working directory
print getcwd() #print current dir
print 

cache_files = listdir(dirpath) #list all files in current directory
print cache_files
print

cf = []
for i in xrange(len(cache_files)):
    cf.append(i)
print cf
print

#make class objects
for i in xrange(len(cf)):
    cf[i] = CacheFile(cache_files[i],stat(cache_files[i])[6],stat(cache_files[i])[8])

#output
for i in xrange(len(cf)):
    print cf[i].name, cf[i].size, cf[i].time
print

#sort a list
sizelist = []
for i in xrange(len(cf)):
     sizelist.append(cf[i].size)
print sizelist
print    

sizelist.sort()
print sizelist
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Wozu Klassen?

Code: Alles auswählen

import os, operator
files = os.listdir('.')
stats = [os.stat(f) for f in files]
ctimes = [stat.st_ctime for stat in stats]
sizes = [stat.st_size for stat in stats]
combined = zip(files, ctimes, sizes)
# newest file
print reversed(sorted(combined, key=operator.itemgetter(1))).next()
# largest file
print reversed(sorted(combined, key=operator.itemgetter(2))).next()
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Ein bisschen umständlich. Sowohl `list.sort()`, als auch `sorted()` kennen das Argument `reverse` und ab Python 2.5 (inklusive) kann man sich das sortieren sparen, weil `min()` und `max()` dann auch das `key`-Argument kennen und einfach linear das Gewünschte suchen.
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Leonidas hat geschrieben:Wozu Klassen?
Weil named tuples erst mit 2.6 kommen?
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

veers hat geschrieben:
Leonidas hat geschrieben:Wozu Klassen?
Weil named tuples erst mit 2.6 kommen?
Ich glaube, Leonidas wollte damit eher seinen alternativen Lösungsvorschlag kommentieren, der eben ohne Klassen auskommt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Vereinfacht. Tatsächlich habe ich ``min()`` und ``max()`` vergessen gehabt. Aber gut zu wissen, dass die auch ein ``key``-Argument haben.

Code: Alles auswählen

import os, operator
files = os.listdir('.')
stats = [os.stat(f) for f in files]
ctimes = [stat.st_ctime for stat in stats]
sizes = [stat.st_size for stat in stats]
combined = zip(files, ctimes, sizes)
# newest file
print max(combined, key=operator.itemgetter(1))
# largest file
print max(combined, key=operator.itemgetter(2))
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

snafu hat geschrieben:
veers hat geschrieben:
Leonidas hat geschrieben:Wozu Klassen?
Weil named tuples erst mit 2.6 kommen?
Ich glaube, Leonidas wollte damit eher seinen alternativen Lösungsvorschlag kommentieren, der eben ohne Klassen auskommt.
Ja, an Stelle derer Verwendet er Tuples und Magic Numbers:

Code: Alles auswählen

key=operator.itemgetter(1)
Leonidas, warum
stats = [os.stat(f) for f in files] und nicht stats = map(os.stat, files)?
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
BlackJack

Ich will auch mal:

Code: Alles auswählen

import os
from functools import partial
from itertools import imap

def main():
    filenames = os.listdir('.')
    tmp = zip(filenames, imap(os.stat, filenames))
    
    def key_func(attr_name, (filename, stats)):
        return getattr(stats, attr_name)
    
    for stat_attr in ('st_ctime', 'st_size'):
        print max(tmp, key=partial(key_func, stat_attr))
Und noch mal mit den `path` Modul:

Code: Alles auswählen

from path import path

def methodcaller(method_name, *args, **kwargs):
    return lambda o: getattr(o, method_name)(*args, **kwargs)

def main():
    files = path('.').listdir()
    for method_name in ('getctime', 'getsize'):
        print max(files, key=methodcaller(method_name))
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

veers hat geschrieben:Ja, an Stelle derer Verwendet er Tuples und Magic Numbers:

Code: Alles auswählen

key=operator.itemgetter(1)
So magisch finde ich es nicht, ist ja schließlich nur ein Indexzugriff. Wenn ich 1 und 2 in Konstanten auslagere würde es auch gehen aber die Lesbarkeit kaum verbessern.
veers hat geschrieben:Leonidas, warum
stats = [os.stat(f) for f in files] und nicht stats = map(os.stat, files)?
Ist als LC warscheinlich schneller.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Code: Alles auswählen

% python -mtimeit "import os; files = os.listdir('.'); [os.stat(f) for f in files]"
1000 loops, best of 3: 602 usec per loop
% python -mtimeit "import os; files = os.listdir('.'); map(os.stat, files)"        
1000 loops, best of 3: 554 usec per loop
Natürlich ist das wieder der Beste von 10 Läufen, es ist also alles im Cache.

Code: Alles auswählen

 % python -mtimeit "map(int, range(10))"                                    
100000 loops, best of 3: 3.79 usec per loop
% python -mtimeit "[int(n) for n in range(10)]" 
100000 loops, best of 3: 5.11 usec per loop
Und hier nochmal, mit möglichst wenig overhead.

map > LC ;)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

audax hat geschrieben:map > LC ;)
Und jetzt die Erklärung?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
lunar

os.listdir liefert relative Pfade, man muss also os.path.join einschalten:

Code: Alles auswählen

def largest_file(directory):
    files = (os.path.join(directory, f) for f in os.listdir(directory))
    return max(((f, os.path.getsize(f)) for f in files if os.path.isfile(f)),
               key=operator.itemgetter(1))
Und für die neueste Datei:

Code: Alles auswählen

def newest_file(directory):
    files = (os.path.join(directory, f) for f in os.listdir(directory))
    return max(((f, os.path.getmtime(f)) for f in files if os.path.isfile(f)),
               key=operator.itemgetter(1))
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Leonidas hat geschrieben:
audax hat geschrieben:map > LC ;)
Und jetzt die Erklärung?
hmm?
Man sollte map() nicht krampfhaft durch LC ersetzen, vor allem nicht aus performance-Gründen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

audax hat geschrieben:
Leonidas hat geschrieben:
audax hat geschrieben:map > LC ;)
Und jetzt die Erklärung?
hmm?
Naja, schließlich haben wir bei ``map()`` genauso viele Aufrufe der inneren Funktion (``os.stat()``) und noch dazu den ``map()`` Aufruf. Der ist auch nicht so billig, da man die Funktion erst im Namensraum finden muss und dann ausführen muss. Intern muss genauso wie bei einer LC über die Werte iteriert werden von daher würde ich gerne wissen, warum ``map()`` dennoch schneller ist.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Code: Alles auswählen

  4           0 BUILD_LIST               0
              3 DUP_TOP             
              4 STORE_FAST               0 (_[1])
              7 LOAD_GLOBAL              0 (range)
             10 LOAD_CONST               1 (10)
             13 CALL_FUNCTION            1
             16 GET_ITER            
        >>   17 FOR_ITER                19 (to 39)
             20 STORE_FAST               1 (n)
             23 LOAD_FAST                0 (_[1])
             26 LOAD_GLOBAL              1 (int)
             29 LOAD_FAST                1 (n)
             32 CALL_FUNCTION            1
             35 LIST_APPEND         
             36 JUMP_ABSOLUTE           17
        >>   39 DELETE_FAST              0 (_[1])
             42 POP_TOP             
             43 LOAD_CONST               0 (None)
             46 RETURN_VALUE        
Das ist die LC.

Code: Alles auswählen

  7           0 LOAD_GLOBAL              0 (map)
              3 LOAD_GLOBAL              1 (int)
              6 LOAD_GLOBAL              2 (range)
              9 LOAD_CONST               1 (10)
             12 CALL_FUNCTION            1
             15 CALL_FUNCTION            2
             18 POP_TOP             
             19 LOAD_CONST               0 (None)
             22 RETURN_VALUE  
Und das mit map.

Bei einer LC findet wohl das Bilden der Liste komplett in Python statt, bei map() in C, das macht wohl den kleinen unterschied aus.

btw, der Code:

Code: Alles auswählen

import dis 

def f():
    [int(n) for n in range(10)]

def g():
    map(int, range(10))

dis.dis(f)

dis.dis(g)
Und auch bei einer LC muss die Liste nachgeschlagne werden:

Code: Alles auswählen

             23 LOAD_FAST                0 (_[1])
BlackJack

Der Unterschied ist das Nachschlagen der Funktion; bei `map()` wird `os.stat` bzw. `int` nur *einmal* nachgeschlagen, bei der LC *für jedes Element*.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Noch ein unerwarteter Effekt:
Ich hatte den Code versucht auszuführen, mich aber gewundert, daß er scheinbar loopt und bin erst nach nach mehrmaligem Ausprobieren darauf gekommen, daß die erste Zeile

Code: Alles auswählen

#!/bin/bash
statt

Code: Alles auswählen

#!/usr/bin/python
lautete. D'oh!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:Der Unterschied ist das Nachschlagen der Funktion; bei `map()` wird `os.stat` bzw. `int` nur *einmal* nachgeschlagen, bei der LC *für jedes Element*.
Ja, diese Vermutung hatte ich ab irgendeinem Zeitpunkt auch. Das finde ich dennoch recht erstaunlich, dass die LC die Referenzen auf die Funktionen nicht irgendwo für schnellere Verwendung zwischenspeichert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Leonidas hat geschrieben:Das finde ich dennoch recht erstaunlich, dass die LC die Referenzen auf die Funktionen nicht irgendwo für schnellere Verwendung zwischenspeichert.
Mich wunderts auch das es in Summe so lange dauert den Namen immer wieder neu zuzuordnen.
Aber bei einer LC ist dies nötig da man ja auch den Funktionsnamen wärend der Bearbeitung neu binden kann.
Z.B für so nen Blödsinn ;)

Code: Alles auswählen

In [27]: [f(i) for i in range(10) for f in [[str, int][i % 2]] ]
Out[27]: ['0', 1, '2', 3, '4', 5, '6', 7, '8', 9]
RedSharky
User
Beiträge: 99
Registriert: Donnerstag 13. April 2006, 15:38

Danke für die große Hilfsbereitschaft.
Kaum ist man mal einen Tag nicht da, geht's zu wie im Taubenschlag. :)
Da sind wirklich ein paar interessante Lösungen dabei, die mir gänzlich unbekannt sind. Danke!
Antworten