ubuntuusers.de

Bash-Skript: jeder 2. Dateipfad wird am Anfang merkwürdig abgeschnitten

Status: Gelöst | Ubuntu-Version: Ubuntu 24.04 (Noble Numbat)
Antworten |

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4784

Wohnort: Berlin

@UlfZibis: next() ist hier eher als “Operator“ zu sehen, denn das Verhalten kann man überladen. Und es ist keine Methode (mehr) weil gerade in Datenstrukturen ja gerne mal ein next-Attribut auf den nächsten Knoten in der Liste oder dem Baum verweist. Da muss man sich dann keinen anderen Namen für ausdenken.

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2149

Wohnort: Gelsenkirchen

Ich hätte das einfach mit any() auf der Rückgabe von glob() gelöst, wenn es wirklich nur darum geht, ob der Ordner mindestens eine Datei mit einer bestimmten Endung enthält. any() hört direkt auf, sobald es das erste wahre Element sieht, in diesem Fall also das erste Element des von glob() gelieferten Iterators. Mit next() finde ich es dafür deutlich umständlicher, weil mich ja das Resultat von next() nicht wirklich interessiert, sondern nur die Tatsache, dass es überhaupt eine zutreffende Datei gibt.

Und dass glob() nicht von sich aus eine Liste erzeugt, hätte man sich relativ schnell durch Ansehen des bereits erwähnten Beispiels aus der Doku erschließen können:

1
2
3
4
>>> list(p.glob('**/*.py'))
[PosixPath('test_pathlib.py'), PosixPath('setup.py'),
 PosixPath('pathlib.py'), PosixPath('docs/conf.py'),
 PosixPath('build/lib/pathlib.py')]

Da würde ja wohl kaum list() stehen, wenn glob() das schon machen würde.

Um es noch einmal ganz deutlich hinzuschreiben, das zuvor gezeigte contains() ist wie:

1
2
def contains(d, suffix):
    return any(d.glob(f"*{suffix}"))

... und somit ein kurzer Einzeiler, den man auch direkt in der LC unterbringen kann.

Auch das schreibe ich gerne noch einmal hin:

1
albums = [d for d in directory_paths if any(d.glob("*" + extension, case_sensitive=False))]

Den f-String habe ich mal lieber weggelassen, damit es für manchen Leser nicht zu anspruchsvoll wird.

Wobei man bestimmt noch ein paar Nanosekunden herauskitzeln könnte, wenn vorab das Globbing-Muster ("*" + extension) zusammensetzt. 😀

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2149

Wohnort: Gelsenkirchen

UlfZibis schrieb:

snafu1 schrieb:

Du machst es dir total umständlich und eben auch falsch. Nimm einfach glob() ...

Dass der andere doof ist, wusste ich auch schon vor Deiner Aufklärung. Wer das aus meiner Sicht dann ist, überlasse ich Deiner scharfen Logik.

Um meine vermeintliche Scharfsinnigkeit ging es mir nicht. Ich wollte dich zum selber Recherchieren und Mitdenken anregen. Dass ich mich etwas im Ton vergriffen haben könnte, tut mir leid. Ich setze manchmal vielleicht zu viel Vorwissen voraus.

UlfZibis

(Themenstarter)

Anmeldungsdatum:
13. Juli 2011

Beiträge: 3440

Wohnort: Köln

snafu1 schrieb:

Ich hätte das einfach mit any() auf der Rückgabe von glob() gelöst, wenn es wirklich nur darum geht, ..

Oh, das ist ja noch eleganter. Tolle Zusammenarbeit hier.

Und dass glob() nicht von sich aus eine Liste erzeugt, hätte man sich relativ schnell durch Ansehen des bereits erwähnten Beispiels aus der Doku erschließen können:

Ja das habe ich ja mittlerweile auch kapiert, aber danke für die detaillierten Ausführungen.

Wobei man bestimmt noch ein paar Nanosekunden herauskitzeln könnte, wenn vorab das Globbing-Muster ("*" + extension) zusammensetzt. 😀

Da das Muster hier nur einmal verwendet wird, dürfte das nix bringen 😉 Und selbst wenn doch würde ich doch mal vermuten, dass der Optimierer das merkt, und dafür das Zwischenergebnis cached.

snafu1 schrieb:

Um meine vermeintliche Scharfsinnigkeit ging es mir nicht. Ich wollte dich zum selber Recherchieren und Mitdenken anregen. Dass ich mich etwas im Ton vergriffen haben könnte, tut mir leid. Ich setze manchmal vielleicht zu viel Vorwissen voraus.

Ganz lieben Dank, das nehme ich gerne an. Ja es war tatsächlich der Ton, der mich störte, vor allem die gehäuften DU-Titulierungen und -Unterstellungen.

Und anders herum gesehen bin wohl auch ich von zu viel "Vorwissen" ausgegangen, nämlich warum ich das "an anderer Stelle" schon benutzte Konstrukt gerade nicht benutzen wollte, denn das baute mit sorted() ja unnötigerweise erst mal die ganze Liste auf.

snafu1 schrieb:

Wie man glob() benutzt, steht in der Dokumentation (https://docs.python.org/3/library/pathlib.html) und zwar ziemlich weit oben, bereits im zweiten Beispiel. Da fragt man sich wirklich, wie du recherchierst (falls du das überhaupt tust), ...

Genau da steht eben weder, welchen Objekt-Typ glob() zurückgibt, dass es ein Iterator ist, noch ein Verweis darauf, noch ein Beispiel für die isolierte Nutzung ohne list(), ja noch nicht mal weiter unten in der separaten Detail-Beschreibung. Dass glob() vermutlich sowas wie einen Iterator oder vielleicht auch Generator liefert, war mir auch "irgendwie" klar, nur wie man damit eben allgemein und isoliert umgeht eben noch nicht. Grob hatte ich schon mal nach den Stichwörtern "Iterator" und "Generator" gesucht, und was da stand war mir zugegebenermaßen zu kompliziert, sodass ich dann dachte, vielleicht gibt's ja einen ganz einfachen Syntax-Trick, wie ich in dem LC-Ausdruck eine for-Schleife unterbringe, die beim ersten Fund abbricht. Offensichtlich ist darin nur eine quasi statische / lineare Berechnung möglich.

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2149

Wohnort: Gelsenkirchen

UlfZibis schrieb:

Grob hatte ich schon mal nach den Stichwörtern "Iterator" und "Generator" gesucht, und was da stand war mir zugegebenermaßen zu kompliziert, sodass ich dann dachte, vielleicht gibt's ja einen ganz einfachen Syntax-Trick, wie ich in dem LC-Ausdruck eine for-Schleife unterbringe, die beim ersten Fund abbricht. Offensichtlich ist darin nur eine quasi statische / lineare Berechnung möglich.

Das Vorgehen ist jetzt aber hoffentlich klar geworden. Die Schleife zur Bearbeitung des Iterators wird innerhalb der LC untergebracht, indem man eine Funktion dafür benutzt. Entweder eine selbstgeschriebene so wie du es gemacht hast oder eine bereits in Python eingebaute Funktion wie any() oder all(). Diese beiden arbeiten mit der sogenannten Kurzschlussauswertung und steigen unmittelbar aus, sobald das Ergebnis für den Gesamtausdruck feststeht.

So steigt any() wie gesagt beim ersten wahren Element aus, weil dies bereits den Sinn der Logik, die hinter any() steckt, erfüllt. Nur wenn erst das letzte Element wahr ist und alle vorherigen falsch sind, dann muss any() die komplette Schleife durchlaufen. Es muss sich eben so lange Elemente "angucken" bis eins davon wahr ist. Sollte selbst das letzte Element falsch sein, dann liefert es als Gesamtausdruck "False". Umgekehrt ist es bei all(): Hier will ich als Aufrufer der Funktion wissen, ob alle Elemente wahr sind. Somit kann diese Funktion im Umkehrschluss früh aussteigen, sobald sie auf ein falsches Element trifft, weil der Gesamtausdruck bereits an dieser Stelle schon "False" sein muss.

In der LC arbeitet man somit einerseits mit der ausgeschriebenen for-Schleife und verwendet zusätzlich im if-Teil zum Überprüfen der gewünschten Bedingung eine "versteckte" Schleife. Denn was any() und all() intern machen, ist auch wieder eine Schleife, nur dass du diese Schleife nicht unmittelbar im Code erkennen kannst. Genau auf diesem Wege kommst du dann zum potenziellen Performance-Vorteil für die Laufzeit deines Programms, je nachdem wie geeignet die Elemente sind, die dein Programm verarbeitet.

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4784

Wohnort: Berlin

@UlfZibis: Von dem ”Optimierer” solltest Du nichts erwarten. Im Grunde gibt es da nichts was über einfaches „constant folding“ hinaus geht. Und selbst das wird noch nicht besonders lange gemacht.

Der Rückgabetyp ist aber schon aus der Dokumentation von glob() erkennbar. Da steht „…, yielding all matching files …“, womit Generatorfunktion/-methode angenommen werden muss. Sonst würde ich ein Ticket wegen sehr irreführender Formulierung aufmachen. Die Methode muss dann ja nicht zwingend das Schlüsselwort yield verwenden, aber doch zumindest einen Iterator/Generator liefern.

Antworten |