MrKanister
Anmeldungsdatum: 13. Oktober 2007
Beiträge: 2105
|
Hallo zusammen, Das ganze ist mehr oder weniger angelehnt an die Threads http://forum.ubuntuusers.de/topic/121655/ http://forum.ubuntuusers.de/topic/165865/ für die ich mich schon (nicht zufrieden) an einem Shellskript versucht habe. Da seit kurzem mit Python angefangen habe und ich die Möglichkeiten, die es bietet, super finde, habe ich mir überlegt das Problem in Python anzugehen, wobei ich mich direkt darauf fixiert habe, es für Python 3000 zu schreiben. Ich habe darauf geachtet, das Skript objektorientiert zu halten, wobei man es wohl trotzdem noch eher als prozedual ansehen muss. Kommentare sind hoffentlich auch genug vorhanden... Dem Skript können eine Reihe von Optionen übergeben werden, die man sich mit "skript -h" anzeigen lassen kann. Es kann bei bestimmten Paketnamen zu Problemen kommen, da es passieren kann, das ein string mit einem Integer verglichen wird, was bei Python 3000 nicht mehr erlaubt ist. Sollte dies passieren, würde ich euch bitten, mit die Namen der Pakete mitzuteilen, bei denen der Fehler aufgetreten ist, damit ich das Skript flexibler machen kann. ▶ http://ubuntuusers.de/paste/210412/ Beim ersten Durchgang empfiehlt es sich, die Option "-d" oder "--dry-run" zu benutzen. Ich würde mich sehr über Kommentare oder Verbesserungsvorschläge freuen. ☺ Gruß Martin
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4658
Wohnort: Berlin
|
Das Programm ist nicht objektorientiert. Du hast einfach nur alle Funktionen in eine Klasse gesteckt und sie dann über Attribute kommunizieren lassen. Damit sind es nicht einmal Funktionen sondern einfach nur Namen für Quelltextabschnitte die über globale Variablen kommunizieren. Kein guter Stil. Das Paket myutils ist in Python 3.0 nicht in der Standardbibliothek. Da es im Programm auch gar nicht verwendet wird, sollte man den ``import`` vielleicht einfach rausnehmen. 😉 Man sollte mit dem Gebrauch der exit() -Funktion sparsam umgehen. Eine Funktion die unter einer bestimmten Bedingung einfach das laufende Programm komplett abbricht, ist weniger flexibel wiederverwendbar, als eine die diese Bedingungen an den Aufrufer übermittelt, der vielleicht noch etwas anderes machen kann, als einfach ab zu brechen. Ähnliches gilt für os.chdir() . Wenn man mehrere Funktionen hat, die das aktuelle Arbeitsverzeichnis wechseln und andere die darauf vertrauen, dass es sich nicht mehr ändert, ist Chaos vorprogrammiert. Der Name i in Schleifen sollte nur für ganze Zahlen verwendet werden. Das ist bei den meisten Programmieren so fest eingebrannt, dass alles andere verwirrend ist. glob.glob() wird öfter bemüht als notwendig. Statt einmal alle *.deb \s zu erfragen und dann nur den Paketnamen vor der Versionsnummer zu speichern, und dann nochmal pro Paketnamen ein glob() ab zu setzen, hätte man gleich bei der ersten Abfrage alle Informationen behalten können. Hier wäre auch ein Ansatzpunkt für OOP, nämlich das man aus so einem vollen Paket(namen) ein Objekt macht, mit den einzelnen Teilen des Namens als Attribute. Und man kann diese Objekte dann Vergleichbar machen.
In prepare_packages() geht der Index-Wahnsinn los. Bitte verwende aussagekräftige Namen statt mehrfache Indexe. Bei Zeilen wie ``self.duplicates[j][1][k] = int(self.duplicates[j][1][k])`` versteht kein Mensch mehr ohne viel im Code herum zu scrollen was für eine Datenstruktur das eigentlich ist und was für eine Bedeutung das Element an ``[j][1][k]`` hat. So gut wie immer wenn man etwas nach dem Muster ``for i in range(len(obj)):`` geschrieben hat, hat man eine unnötige Indirektion über einen Index, den man besser durch eine direkte Iteration über die Elemente mit einem aussagekräftigen Namen und ohne Indexvariable ersetzen sollte: ``for item in obj:``. Alle drei ``for``-Schleifen auf Funktionsebene gehen über die gleichen Daten, das hätte man auch alles in einem Durchgang machen können. Wenn eine Ausnahmebehandlung nichts macht, also nur ``pass`` enthält, könnte man einen Kommentar schreiben, warum man diese Ausnahme gefahrlos ignorieren kann. Ich bin mir auch nicht so sicher, dass das mit den Versionen so klappt, weil Du nur an '.' splittest, die Version aber immer noch eine Versionsnummer für das Paket enthält, die von der Versionsnummer für das Programm durch ein '-' getrennt ist. Also zum Beispiel '1.20-1'. Das teilst Du in ['1', '20-1'] auf, dass heisst der letzte Teil der Versionsnummern wird von Deinem Programm nie umgewandelt, sondern als Zeichenkette verglichen. Da kann dann aber eine falsche Reihenfolge heraus kommen: {{{In [86]: '20-10' < '20-9'
Out[86]: True}}} Das entfernen der Pakete, die man behalten möchte in einer Schleife mittels pop() ist umständlich. Das kann man mit "slicing" einfacher und schneller hinbekommen. Ausserdem glaube ich Du pop() \st am falschen Ende der Liste!?'
|
MrKanister
(Themenstarter)
Anmeldungsdatum: 13. Oktober 2007
Beiträge: 2105
|
Erstmal vielen Dank, dass du dir Mühe gemacht hast, dir meinen Code anzuschauen, der wirklich noch etwas konfus ist ☺ Marc 'BlackJack' Rintsch hat geschrieben: Das Programm ist nicht objektorientiert. Du hast einfach nur alle Funktionen in eine Klasse gesteckt und sie dann über Attribute kommunizieren lassen. Damit sind es nicht einmal Funktionen sondern einfach nur Namen für Quelltextabschnitte die über globale Variablen kommunizieren. Kein guter Stil.
Da hast du absolut Recht. Wie schon angedeutet, handelt es sich nur um prozeduralen Code, der in einer Klasse steckt...da ich bis jetzt noch nie objektorientiert programmiert habe, fällt es mir immer noch schwer Objekt-bezogen zu denken. Marc 'BlackJack' Rintsch hat geschrieben: Das Paket myutils ist in Python 3.0 nicht in der Standardbibliothek. Da es im Programm auch gar nicht verwendet wird, sollte man den ``import`` vielleicht einfach rausnehmen. 😉
"myutils" eine Ansammlung von Funktionen von mir. Die Ja/Nein-Abfrage, die ich der Übersicht halber direkt in den Code aufgenommen habe, steckte in diesem Modul, dass deshalb auch nicht mehr importiert werden muss 😀 Marc 'BlackJack' Rintsch hat geschrieben: Man sollte mit dem Gebrauch der exit() -Funktion sparsam umgehen. Eine Funktion die unter einer bestimmten Bedingung einfach das laufende Programm komplett abbricht, ist weniger flexibel wiederverwendbar, als eine die diese Bedingungen an den Aufrufer übermittelt, der vielleicht noch etwas anderes machen kann, als einfach ab zu brechen.
Ich werd mir was überlegen. Marc 'BlackJack' Rintsch hat geschrieben: Ähnliches gilt für os.chdir() . Wenn man mehrere Funktionen hat, die das aktuelle Arbeitsverzeichnis wechseln und andere die darauf vertrauen, dass es sich nicht mehr ändert, ist Chaos vorprogrammiert.
Ich hatte mir schon mal überlegt, das Verzeichnis einfach nicht zu wechseln und einfach nur, wenn es nötig ist, den Pfadanteil abzuschneiden. Scheint eine gute Idee zu sein. Marc 'BlackJack' Rintsch hat geschrieben: glob.glob() wird öfter bemüht als notwendig. Statt einmal alle *.deb \s zu erfragen und dann nur den Paketnamen vor der Versionsnummer zu speichern, und dann nochmal pro Paketnamen ein glob() ab zu setzen, hätte man gleich bei der ersten Abfrage alle Informationen behalten können. Hier wäre auch ein Ansatzpunkt für OOP, nämlich das man aus so einem vollen Paket(namen) ein Objekt macht, mit den einzelnen Teilen des Namens als Attribute. Und man kann diese Objekte dann Vergleichbar machen.
Eine Gute Idee. Marc 'BlackJack' Rintsch hat geschrieben: Der Name i in Schleifen sollte nur für ganze Zahlen verwendet werden. Das ist bei den meisten Programmieren so fest eingebrannt, dass alles andere verwirrend ist. In prepare_packages() geht der Index-Wahnsinn los. Bitte verwende aussagekräftige Namen statt mehrfache Indexe. Bei Zeilen wie ``self.duplicates[j][1][k] = int(self.duplicates[j][1][k])`` versteht kein Mensch mehr ohne viel im Code herum zu scrollen was für eine Datenstruktur das eigentlich ist und was für eine Bedeutung das Element an ``[j][1][k]`` hat. So gut wie immer wenn man etwas nach dem Muster ``for i in range(len(obj)):`` geschrieben hat, hat man eine unnötige Indirektion über einen Index, den man besser durch eine direkte Iteration über die Elemente mit einem aussagekräftigen Namen und ohne Indexvariable ersetzen sollte: ``for item in obj:``. Alle drei ``for``-Schleifen auf Funktionsebene gehen über die gleichen Daten, das hätte man auch alles in einem Durchgang machen können.[/quote] Der "Index-Wahnsinn" ist tatsächlich groß, aber wenn ich "for item in object" schreibe, dann kann ich die Elemente doch nicht so einfach verändern, weil eine Kopie des Elements übergeben wird!? (Korrigier mich, wenn ich falsch liege) Marc 'BlackJack' Rintsch hat geschrieben: Ich bin mir auch nicht so sicher, dass das mit den Versionen so klappt, weil Du nur an '.' splittest, die Version aber immer noch eine Versionsnummer für das Paket enthält, die von der Versionsnummer für das Programm durch ein '-' getrennt ist. Also zum Beispiel '1.20-1'. Das teilst Du in ['1', '20-1'] auf, dass heisst der letzte Teil der Versionsnummern wird von Deinem Programm nie umgewandelt, sondern als Zeichenkette verglichen. Da kann dann aber eine falsche Reihenfolge heraus kommen: In [86]: '20-10' < '20-9'
Out[86]: True
Danke für den Hinweis. Marc 'BlackJack' Rintsch hat geschrieben: Das entfernen der Pakete, die man behalten möchte in einer Schleife mittels pop() ist umständlich. Das kann man mit "slicing" einfacher und schneller hinbekommen.
Ich schau mir die Funktion mal an. Marc 'BlackJack' Rintsch hat geschrieben: Ausserdem glaube ich Du pop() \st am falschen Ende der Liste!?
Nene...das "pop()"t schon richtig. Die neuste Version ist nach einem "sort()" am Enden der Liste und dort wird sie auch wegge"pop()"t 😉 Danke nochmal für die Mühe. Ich werde versuchen so viele Vorschlägen umzusetzen wie möglich. ☺ Gruß martin
|
audax
Anmeldungsdatum: 15. September 2006
Beiträge: 1253
|
def confirm(question="Möchten Sie fortfahren [J/n]? ", key="N"):
return(key not in str(input(question)).upper()) Nimm raw_input. Und übrigens: Dir werden bei einem
for foo in bar:
do_stuff(foo) Referenzen übergeben, d.h. du kannst die Objekte verändern. ein
for foo in bar:
foo = 'bar' klappt natürlich aber nicht, da einfach nur der Name neu gebunden wird. Du willst in einer Schleife niemals die Liste verändern, die du bearbeitest. Das ist in den allermeißten Fällen eine doofe Idee. Wie wäre es denn stattdessen mit einem Generator, der einfach ein neues Iterable zurück gibt? Das hier wäre glaube ich was für dich: Generators
Du musst übrigens wirklich nicht krampfhaft alles in Klassen stecken. Das macht vor allem bei dem Beispiel nur wenig Sinn ☺
|
MrKanister
(Themenstarter)
Anmeldungsdatum: 13. Oktober 2007
Beiträge: 2105
|
audax hat geschrieben: def confirm(question="Möchten Sie fortfahren [J/n]? ", key="N"):
return(key not in str(input(question)).upper()) Nimm raw_input.
"raw_input()" gibts in Python 3000 nicht mehr 😉 audax hat geschrieben: Und übrigens: Dir werden bei einem
for foo in bar:
do_stuff(foo) Referenzen übergeben, d.h. du kannst die Objekte verändern. ein
for foo in bar:
foo = 'bar' klappt natürlich aber nicht, da einfach nur der Name neu gebunden wird.
Entweder ich bin blöd, oder das "in-Place" - Ersetzen klappt wirklich nicht ☹
>>> foo = ["1", "2"]
>>> for item in foo:
... int(item)
...
1
2
>>> foo
['1', '2'] Ich wäre ja jemandem für ein Beispiel, wie's geht, sehr dankbar ☺
audax hat geschrieben: Du willst in einer Schleife niemals die Liste verändern, die du bearbeitest. Das ist in den allermeißten Fällen eine doofe Idee. Wie wäre es denn stattdessen mit einem Generator, der einfach ein neues Iterable zurück gibt? Das hier wäre glaube ich was für dich: Generators
Danke. Schau ich mir mal an. audax hat geschrieben: Du musst übrigens wirklich nicht krampfhaft alles in Klassen stecken. Das macht vor allem bei dem Beispiel nur wenig Sinn ☺
Also die Idee von "Marc 'BlackJack' Rintsch", eine Klasse für ein Paket zu machen, fand ich schon ganz ansprechend. Vor allem ist der Code dadurch leichter verständlich, als bei meinem "Index-Wahnsinn" 😀 Edit: Für die, die es interessiert...im Moment sieht meine Klasse "Package" schon mal so aus: http://ubuntuusers.de/paste/211097/. Ich wollte jetzt noch eine Funktion zum löschen des Pakets einbauen und natürlich die Vergleichsfunktion.
|
audax
Anmeldungsdatum: 15. September 2006
Beiträge: 1253
|
Mr. Kanister hat geschrieben: audax hat geschrieben: def confirm(question="Möchten Sie fortfahren [J/n]? ", key="N"):
return(key not in str(input(question)).upper()) Nimm raw_input.
"raw_input()" gibts in Python 3000 nicht mehr 😉
Ach, hab ich übersehen...das ist ja Py3k :] Mr. Kanister hat geschrieben:
Entweder ich bin blöd, oder das "in-Place" - Ersetzen klappt wirklich nicht ☹
>>> foo = ["1", "2"]
>> for item in foo:
... int(item)
...
1
2
>> foo
['1', '2'] Ich wäre ja jemandem für ein Beispiel, wie's geht, sehr dankbar ☺
Sowas sollte auch nicht klappen 😉 Erstelle eben eine neue liste:
newiter = (int(item) for item in olditer) Wobei das jetzt ein iterable erstellt und keine Liste...aber nun gut. Mr. Kanister hat geschrieben:
audax hat geschrieben: Du willst in einer Schleife niemals die Liste verändern, die du bearbeitest. Das ist in den allermeißten Fällen eine doofe Idee. Wie wäre es denn stattdessen mit einem Generator, der einfach ein neues Iterable zurück gibt? Das hier wäre glaube ich was für dich: Generators
Danke. Schau ich mir mal an. audax hat geschrieben: Du musst übrigens wirklich nicht krampfhaft alles in Klassen stecken. Das macht vor allem bei dem Beispiel nur wenig Sinn ☺
Also die Idee von "Marc 'BlackJack' Rintsch", eine Klasse für ein Paket zu machen, fand ich schon ganz ansprechend. Vor allem ist der Code dadurch leichter verständlich, als bei meinem "Index-Wahnsinn" 😀
Den gäbe es bei ordentlichen Generatoren auch nicht 😉 Aber es war ja auch nur ein Hinweis, keine Anweisung 😀
|
MrKanister
(Themenstarter)
Anmeldungsdatum: 13. Oktober 2007
Beiträge: 2105
|
Ok...hier die neue Fassung: http://ubuntuusers.de/paste/211206/ Eine Sache wundert mich aber noch...ich habe der Klasse "Package" ja eine Methode zum Vergleichen hinzugefügt. Das Funktioniert mit Befehlen, wie:
cmp(Package(), Package()) auch wunderbar, aber, wenn ich eine Liste von "Package()"'s habe, dann kann ich auf die Liste nicht einfach ein "sort()" anwenden. Ich erhalte folgende Meldung:
Traceback (most recent call last):
File "./example2.py", line 62, in <module>
pakete.sort()
TypeError: unorderable types: Package() < Package() (Jetzt nicht nach Zeile 62 forschen. Den Code um den Fehler zu produzieren, habe ich gerade nur beispielhaft dort eingefügt 😉)
Gruß Martin
|
audax
Anmeldungsdatum: 15. September 2006
Beiträge: 1253
|
in py3k definiert man nur noch __key__. Da kannst du aber auch ein Tuple reinpacken aus (name, version), was dann deinem jetzigen entspricht. €dit: Ich lag falsch 😀 Man muss anscheinend __lt__ und __gt__ implementieren Oo http://paste.pocoo.org/show/46762 So quasi.
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4658
Wohnort: Berlin
|
Also ehrlich gesagt würde ich in Python 3.0 noch gar nichts grossartig schreiben. Das dauert noch eine halbe Ewigkeit, bis das im Mainstream angekommen ist. Es ist ja noch nicht einmal die "Übergangsversion" 2.6 raus. Dort wird es einige "Schalter" geben, die einzelne Eigenschaften von 3.0 "scharf" schalten und ein Skript das soviel wie möglich automatisch von 2.x nach 3.0 umwandelt. Es wäre also günstiger dann 2.6er Quelltext zu schreiben, der sich automatisch umwandeln lässt, wenn man Programme schreiben möchte, die möglichst viele Leute ausführen können. Der Backport von 3.0 nach 2.x ist nicht so schön automatisiert, da müsste man dann wahrscheinlich zwei Versionen parallel pflegen.
|
audax
Anmeldungsdatum: 15. September 2006
Beiträge: 1253
|
Und noch ist nichts von Py3000 final. Ich hoffe, dass das Sortieren noch etwas hübscher wird.
|
MrKanister
(Themenstarter)
Anmeldungsdatum: 13. Oktober 2007
Beiträge: 2105
|
audax hat geschrieben: in py3k definiert man nur noch __key__. Da kannst du aber auch ein Tuple reinpacken aus (name, version), was dann deinem jetzigen entspricht. €dit: Ich lag falsch 😀 Man muss anscheinend __lt__ und __gt__ implementieren Oo http://paste.pocoo.org/show/46762 So quasi.
hatte nach deinem Tipp mit "__key__" auch ein wenig gegoogelt: ▶ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/510403 @Marc 'BlackJack' Rintsch: Ich bin mir bewusst, dass Python 3000 erst Ende 2008 (oder vielleicht auch erst später) erscheint, und, dass mein Code so nicht auf die Menschheit losgelassen werden kann 😀
da müsste man dann wahrscheinlich zwei Versionen parallel pflegen
wahrscheinlich werde ich es auch so machen. Ich habe auch nur deshalb Python 3000 genommen, weil ich einige der Änderungen ganz nett fand 😉 An dieser Stelle nochmal Danke für die Mühe und die Anregungen...ich werde noch ein bisschen rumbasteln und euch mit neuen Versionen und Fragen auf dem Laufenden halten ☺ Gruß Martin
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4658
Wohnort: Berlin
|
@Mr. Kanister: Warum ist die Versionsinformation denn in einer Liste in einer Liste? get_pkg_informations() hätte ich mit einem führenden Unterstrich als nicht-öffentlich markiert. Ausser \__init\__() sollte das ja keiner aufrufen wollen.
Hier ist mal ein Ansatz von mir mit Objekten für die Pakete und ansonsten ziemlich funktional: http://paste.pocoo.org/show/46779/ Die Funktion paths2delete() ermittelt alle Pakete in directory die gelöscht werden sollen und keep_n gibt an, wieviele Pakete aus einer Gruppe man behalten möchte. Zu einer Gruppe gehören alle Pakete mit dem gleichen Namen und der gleichen Plattform.
|
MrKanister
(Themenstarter)
Anmeldungsdatum: 13. Oktober 2007
Beiträge: 2105
|
VERSION_RE = re.compile('\d+|[a-zA-Z]+')
...
for component in self.VERSION_RE.findall(version): Das gefällt mir 😀 Da hätte ich drauf kommen müssen.
"intertools.groupby" und "operator.attrgetter" kannte ich auch noch nicht...scheinen wie für diesen Zweck gemacht worden zu sein EDIT: Vielleicht sollte ich die "Python Library Reference" mal durcharbeiten...da schlummert bestimmt noch so einiges 😉
|
MrKanister
(Themenstarter)
Anmeldungsdatum: 13. Oktober 2007
Beiträge: 2105
|
So...ich hab mir gedacht ich melde mich nochmal... Mittlerweile bin ich von der Klasse für die Pakete abgewichen, weil sich das für ein so kleines Skript ja schon fast nicht mehr lohnt. Stattdessen hat sich das ganze, wo es möglich war, ein wenig in Richtung Generatoren bewegt (Danke audax für den wertvollen Hinweis ☺) Das Vergleichen der Paketversionen habe ich jetzt auch nicht mehr selbst implementiert, sondern wird von der Funktion "VersionCompare" aus dem modul "apt" (python-apt) übernommen, auf das ich zufällig beim lesen einiger Posts im Python-Forum aufmerksam geworden bin. ▶ http://paste.pocoo.org/show/74785/ Noch eine wichtige Frage: Ich habe bei den Blueprints für Intrepid auch den Eintrag für ein Tool gefunden, dass nach größeren Updates das System von überflüssigen Dateien befreien soll (cleanup-cruft). Jetzt würde mich interessieren, wie man sich an die Entwickler wenden kann/soll, um vielleicht zu fragen, ob sie die Idee von deb-cleaner oder vielleicht sogar Teile des Codes implementieren wollen/können. Kann man den zuständigen Personen einfach eine Email schreiben oder sollten Vorschläge dieser Art woanders gesammelt werden? Gruß Martin
|
audax
Anmeldungsdatum: 15. September 2006
Beiträge: 1253
|
if not isinstance(paths, list):
raise TypeError("list object expected, got %s." % type(paths)) Den Teil kann man sich sparen.
(os.path.abspath(path) for path in paths)
imap(os.path.abspath, paths) Welches von beiden ist vllt auch ein wenig Geschmackssache, ich persönlich mag map
Außerdem solltest du mal die Klamern um die yield, returns und prints entfernen: Noch sind wir nicht bei Py3k, und auch dann machts der Konverter 2to3.py schon selbst 😉
|