Passwortgenerator

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Malta
User
Beiträge: 83
Registriert: Samstag 8. Januar 2011, 23:51

Hallo,

hab auf meiner Homepage paar Python Quelltexte veröffentlicht:
Passwortgenerator:
http://markush.cwsurf.de/joomla_17/inde ... tgenerator
Überarbeitet am 12.1.2011, jetzt mit chinesischen Schriftzeichen ;)

Kleiner Webserver:
http://markush.cwsurf.de/joomla_17/inde ... mit-python

Wörterzählen:
http://markush.cwsurf.de/joomla_17/inde ... mit-python

Hab diese Programm aus Spass programmiert und auf meine Seite gestellt um meinen Besuchern zu zeigen wie einfach Python ist.

Aber vielleicht hab ihr noch die ein oder andere Verbesserung.
Zuletzt geändert von Malta am Sonntag 15. Januar 2012, 11:37, insgesamt 3-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hui... das sieht teilweise leider sehr gruselig aus!

Trennung von Logik und GUI hast Du vermutlich noch nicht gehört ;-)

Beim Wörterzählen solltest Du ggf. mal über ein kontinuierliches Zählen mittels Dicts / Listen nachdenken.

Es ist nichts kommentiert - das wirkt sicherlich ziemlich abschreckend auf dritte und wird Dir auch auf Dauer nicht weiterhelfen, solltest Du so ein Script noch mal anfassen wollen.

Du beachtest den Styleguide PEP8 nicht.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Malta
User
Beiträge: 83
Registriert: Samstag 8. Januar 2011, 23:51

Ja mit den Kommentaren bin schön sparsam umgegangen aber kann ich ändern.

Was ist ein Styleguide PEP8, hab das noch nie gehört.
Ok, hab was gefunden: http://wiki.python-forum.de/PEP%208%20% ... setzung%29
Zuletzt geändert von Malta am Dienstag 11. Januar 2011, 23:11, insgesamt 1-mal geändert.
BlackJack

@Malta: Zum Passwortgenerator:

Sternchenimporte sollte man nicht machen -- damit importiert man alle Namen aus einem Modul. Wenn man das überall machen würde, dann hätte das Modul ja auch schon alle Namen aus anderen Modulen importiert, und damit geht der Sinn ein Programm auf Module aufzuteilen irgend wie verloren.

`print_value()` ist ein ziemlich generischer Name. `generate_password()` wäre vielleicht besser.

Wenn man Namen durchnummeriert, ist das oft ein Zeichen, dass man eigentlich eine Datenstruktur verwenden möchte. Hier zum Beispiel Listen. Dann kann man einiges von dem Code auch mit Schleifen kürzer schreiben.

`range()` erzeugt eine Liste mit Zahlen -- wenn man nur eine bestimmte Anzahl von Schleifendurchläufen benötigt, oder allgemein eben nicht wirklich eine Liste mit Zahlen, dann ist `xrange()` geeigneter.

Zeichenketten sollte man nicht durch wiederholtes "aufaddieren" erzeugen. Die Datenmenge ist hier zwar noch sehr gering, aber das kann ein sehr schlechtes Laufzeitverhalten haben, weil jedes mal eine neue Zeichenkette aus den beiden Operanden erzeugt wird. Idiomatischer ist das erstellen einer Liste, die dann mit `str.join()` zusammengefügt wird.

Du bist sehr inkonsistent bei der Namensvergabe was die Schreibweise angeht. Mal `kleinbuchstaben_mit_unterstrichen`, mal `mixedCase` und sogar GROSSBUCHSTABEN für Attribute die eigentlich nicht als Konstanten durchgehen. Da könntest Du Dich mal an den Style Guide (PEP8) halten.

Doppelte führende Unterstriche sind dazu gedacht Namenskollisionen bei der Vererbung zu vermeiden. Die Gefahr dürfte bei `__scrollHandler()` nicht wirklich bestehen. Das ``*L``-Argument sieht mir auch ein wenig so aus als wenn Du das mal ausprobieren wolltest, und nicht, dass diese "Magie" da tatsächlich benötigt wird. Das macht das Programm einfach nur schwerer lesbar.

Dictionaries bei `Tkinter`-Methoden zu übergeben ist mehr als veraltet. Ich habe so etwas noch nie in Programmen gesehen, nur ab und zu bei Anfängern die Code-Beispiele aus den Anfangszeiten von Python im Netz gefunden haben. Ausserdem mischst Du munter Zeichenkettenliterale mit Konstanten aus `Tkinter` -- teilweise für die selben Werte.

Was man nicht in anderen Methoden benötigt, sollte man auch nicht an das Objekt binden.

Eine Widget-Klasse sollte sich nicht selbst in einem Container "layouten". Das macht keine der bereits vorhandenen Klassen. Man nimmt sich so die Möglichkeit das Widget flexibel wieder zu verwenden und in eine grössere GUI einzubauen.

Dass man den Passwortgenerator mit ``python Wörterzählen.pyw`` startet, glaube ich nicht. ;-)
BlackJack

@Malta: Zum Wörter zählen:

Hyperion hat es ja schon gesagt: Programmlogik und GUI sind zu eng verbunden.

Die ganzen `replace()`-Aufrufe könnte man in einer Schleife zusammenfassen oder mit der `translate()`-Methode auf Zeichenketten erledigen. Und da gibt es sicher auch noch weitere Zeichen die nicht zu Wörtern gehören, wie '#' oder '='.

Eingerückt wird per Konvention mit *vier* Leerzeichen pro Ebene und nicht mit zwei.

Das Zählen sieht komisch und umständlich aus. ``for i in range(len(obj))`` ist ein Anti-Pattern wenn `obj` iterierbar ist und `i` zum Indexzugriff benutzt wird. Man kann in Python *direkt* über die Elemente von solchen Objekten iterieren. Wenn man *zusätzlich* den Index haben möchte, gibt es die `enumerate()`-Funktion. Zum Zählen wäre ein Dictionary, das Wort auf Anzahl abbildet, einfacher. Noch einfacher geht es mit einem `collections.defaultdict`, weil man dann den ersten Eintrag nicht mehr besonders behandeln muss.

Das Label mit den Beschriftungen für die Texteingabe und die beiden Listen funktioniert nur bei Dir oder Leuten, die die gleichen Schriftgrössen und die gleiche Monitorauflösung verwenden. Selbst dann vielleicht auch nur auf dem gleichen Betriebssystem/Fenstersystem. Wenn Du das zuverlässig positionieren willst, musst Du die Labels und Widgets jeweils in Frames zusammen fassen, oder einen Frame mit einem `grid()`-Layout verwenden.
BlackJack

@Malta: Und der Webserver:

Das `httpd` in `st_server()` (wofür steht eigentlich das `st_`?) kommt aus dem "nichts". So etwas sollte nicht auf Modulebene existieren, weil es das Verständniss des Datenflusses in einem Programm erschwert. Werte sollten Funktionen als Argumente betreten und als Rückgabewerte verlassen. Ausnahme sind Konstanten.

Namen sollte man nicht unnötig abkürzen. `verz` ist zum Beispiel ein schlechter Name für eine Methode. Idealerweise sollte ein Name die Bedeutung eines Objektes klar machen und nicht Fragezeichen beim Leser hinterlassen.

Der zu bevorzugende "Ungleich"-Operator ist ``!=`` und nicht ``<>``, was es in Python 3.x auch nicht mehr gibt.

Das `thread`-Modul sollte nicht mehr verwendet werden. Die Dokumentation weist ja auch auf das `threading`-Modul hin.

Man kann anscheinend mit dem Programm mehrere Threads starten, die alle versuchen Anfragen über den selben Port zu beantworten. Das kann nicht gut gehen.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ein Passwortgenerator habe ich auch noch, allerding ohne GUI.
Vieleicht hat ja jemand Lust mal drüber zusehen, würde mich zumindest freuen :D .

Code: Alles auswählen

#!/usr/bin/env python
import random
import string


class PasswordGenerator(object):

    def __init__(self):
        self.options = {"uppercases"   : [True, string.ascii_uppercase],
                        "lowercases"   : [True, string.ascii_lowercase],
                        "digits"       : [True, string.digits],
                        "punctuations" : [True, string.punctuation],
                        "..." : [False, ""]
                        }

    def __str__(self):
        status = lambda able: "enable" if able else "disable"
        options = ["{0} : {1}".format(option, status(values[0]))
                   for option, values in self.options.iteritems()]
        return "Passwort Generator hat die Optionen:\n\t- {0}\n" \
               "".format("\n\t- ".join(options))

    def _set(self, option, value=None):        
        if option in self.options:
            if value is None:
                value = not self.options[option][0]
            self.options[option][0] = value
        else:
            print(self)        
                        
    def enable(self, option):
        self._set(option, True)

    def disable(self, option):
        self._set(option, False)

    def switch(self, option):
        self._set(option)        

    def generate(self, length, prefix=""):
        pool = []
        for for_pool, letters in self.options.values():
            if for_pool:
                pool.append(letters)
                
        pool = "".join(pool)
        password = [prefix, ]
        for _ in xrange(length - len(prefix)):
            password.append(random.choice(pool))
        return "".join(password)

if __name__ == "__main__":
    pwgen = PasswordGenerator()
    print(pwgen.generate(25, "Hello"))
    print(pwgen)
    pwgen.disable("lowercases")
    print(pwgen)
    print(pwgen.generate(25))
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
lunar

@Xynon1: Wie bist Du nur auf die Idee gekommen, das mit dieser Klasse zu implementieren?! Eine Funktion mit einem zusätzlichen Argument für die zu verwendenden Zeichenmengen wäre doch vollkommen ausreichend gewesen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hui... Threadhijacking ;-)

Also ich finde die Klasse irgend wie zu kompliziert und aufgesetzt! Wozu braucht man denn Optionen, die man aktivieren oder deaktivieren kann? So etwas macht doch erst dann Sinn, wenn man das Objekt länger im Speicher vorhalten will, da die Erstellung teuer ist oder multiple Instanzen zu viel Speicher bräuchten. In der Praxis wird man einfach ein neues Objekt erstellen. Wo es dann wiederum wenig Sinn macht, eine Klasse zu nutzen. Letztlich rufst Du ja nur die generate()-Methode auf, die wiederum im Gegensatz zum Konstruktor Parameter kennt.

Da imho das PW-Erstellen ein eher einzigartiger Prozess ist, würde ich auf eine Funktion setzen und dieser eben entsprechende Elemente als Zeichenpool übergeben.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@lunar und Hyperion
Ja, es ist zu kompliziert aufgesetzt, der Script ist auch schon etwas älter.
Ja man hätte es auch mit einer Funktion machen können, im Prinzip reicht ja die hälfte der "generate"-Methode um Passwörter zuerzeugen.
Lohnt sich halt nur, wenn man eine ganze Menge pws erzeugen muss :P
Hui... Threadhijacking ;-)
Sorry :oops:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Xynon1 hat geschrieben: Lohnt sich halt nur, wenn man eine ganze Menge pws erzeugen muss :P
Da Du den "Pool" immer erst in der generate-Methode zusammensetzt, lohnt nicht mal das wirklich. Denn dann wäre es wohl effizienter, den Pool extern zu generieren und immer wieder auf den selben zuzugreifen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Außer wenn sich die Optionen andauernd ändern :)
Im Prinzip, ist die Hauptaufgabe der Klasse die Verwaltung der Optionen. (und ich habe ja schon eingesehen, das man das auch einfacher machen kann)

Edit: Mir fällt gerade ein das man doch über GUI dafür nachdenken könnte, denn die Klasse regelt wie oben erwähnt ja eigentlich nicht die Generierung des Passwortes, sondern die Verwaltung der Optionen.
Oder ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Malta
User
Beiträge: 83
Registriert: Samstag 8. Januar 2011, 23:51

Xynon1 hat geschrieben: Edit: Mir fällt gerade ein das man doch über GUI dafür nachdenken könnte, denn die Klasse regelt wie oben erwähnt ja eigentlich nicht die Generierung des Passwortes, sondern die Verwaltung der Optionen.
Oder ?
Du kannst auch das Layout von mein Passwortprogramm verwenden, hab es gerade überarbeitet. Link siehe oben ;)
Zuletzt geändert von Malta am Mittwoch 12. Januar 2011, 22:58, insgesamt 1-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hoffentlich nicht, denn dann sieht es furchtbar aus. Nichts persönliches aber von diesem "Layout" streiken ja die Augen. Zudem sind die Widgets ausgesucht hässlich.

Ich persönlich nutze zum Generieren von Passwörtern KeePassX. Das hat auch eine GUI, die erträglich ist.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Malta
User
Beiträge: 83
Registriert: Samstag 8. Januar 2011, 23:51

Also ich finde es gut, und das mit den Prefix ist ein gute Idee. Vielleicht baue ich die Funktion auch mal bei mir ein!
Edit: Noch mehr Python gibt es auf http://markush.cwsurf.de/joomla_17/index.php/python.
Antworten