Erste OOP Gehversuche

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
desmater
User
Beiträge: 32
Registriert: Donnerstag 18. April 2013, 20:53

Heute hab ich mich dann mal an die objektorientierte Programmierung gewagt.

Als erste kleine eigene Übung habe ich dann den folgenden Code produziert. Funktioniert auch soweit wie ich es mir vorgestellt habe, allerdings gehe ich davon aus, das noch viele stilistische Fehler enthalten sind, oder eben andere Fehler.

Ich denke ich brauche nicht erklären was das Script macht :P

Code: Alles auswählen

import random

class GeneratePassword(object):
    """
    Generates a random password.

    Attribute : with_special_character, password_lenth
    """
    
    def __init__(self, with_special_character = False, password_lenth = 8):
        self.password_lenth = password_lenth
        self.with_special_character = with_special_character
        self.password = ""

    characters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
                  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    
    special_characters = ['!', '@', '#', '$', '%', '^', '&', '*', '?']
    
    def make_password(self):
        """
        create the random password
        """
        random_range = 3
        if self.with_special_character:
            random_range = 4
        for i in range(self.password_lenth):
            character_kind = random.randrange(0, random_range)
            if character_kind == 0:
                self.password += self.characters[random.randrange(0, 24)].lower()
            elif character_kind == 1:
                self.password += self.characters[random.randrange(0, 24)]
            elif character_kind == 2:
                self.password += str(random.randrange(0,10))
            elif character_kind == 3:
                self.password += self.special_characters[random.randrange(0, 9)]


def main():        
    for i in range(10):
        new_password = GeneratePassword()
        new_password.make_password()
        print new_password.password


if __name__ == "__main__":
    main()

Bin mir auch bewusst, das es nicht nötig war dafür eine Klasse zu erstellen ^^.
Aber irgendwie muss man das ja üben :D

Freue mich über jede konstruktive Kritik und alles was mir hilft irgendwann einmal ein guter Programmierer zu sein :mrgreen:

Mit freundlichem Gruß
:)
BlackJack

@desmater: Das grösste Problem in bezug auf OOP hast Du ja schon selber erkannt: Die Klasse ist hier sinnlos. Man kann OOP nicht an solchen sinnfreien Klassen lernen, denn ein wichtiger Aspekt ist ja gerade das man es nicht um seiner selbst willen einsetzt, sondern da wo es Sinn macht.

Ansonsten könntest Du Dir mal `random.choice()` anschauen und Sequenzen für alle Typen erstellen. Dann lässt sich die Indirektion über die Zahlen loswerden.
Benutzeravatar
diesch
User
Beiträge: 80
Registriert: Dienstag 14. April 2009, 13:36
Wohnort: Brandenburg a.d. Havel
Kontaktdaten:

Es ist üblich, Klassen mit Substantiven zu benennen, und ihnen auch eine dazu passende Funktion zu geben. Hier wäre z.B. PasswordGenerator naheliegend.

Es scheint mir wenig sinnvoll, das zuletzt erzeugte Passwort in einer Instanzvariablen zu speichern, da das keine wirklich interessante Eigenschaft einer Instanz ist. Statt dessen würde ich aus make_password eine Methode machen, die das Passwort zurückgibt.

In main() würde ich dann einmal eine PasswordGenerator-Instanz erzeugen, und die dann in der Schleife mehrere Passwörter erzeugen lassen:

Code: Alles auswählen

def main():        
    pwgen = PasswordGenerator()
    for i in range(10):
        print pwgen.make_password()
@BlackJack: Ich halte die Klasse für sinnvoll. Es wären z.B. auch Unterklassen denkbar, die aussprechbare Passwörter erzeugen, oder Passwörter, die keine leicht verwechselbaren Zeichen enthalten, o.ä.
http://www.florian-diesch.de
BlackJack

@diesch: Genau so gut könnte man auch eine Funktion schreiben, welche die Zeichenklassen als Argument bekommt und dann entweder verschiedene Funktionen, welche diese Funktion mit verschiedenen Argumenten aufruft, oder man erzeugt die mit `functools.partial()`. Die Klasse hat ja keinen veränderbaren Zustand und für eine `__init__()` und nur *eine* weitere Methode finde ich das ziemlich dünn. Das ist partielle Funktionsanwendung umständlich als Klasse verpackt. Das sieht für mich eher nach Java aus. :-)
desmater
User
Beiträge: 32
Registriert: Donnerstag 18. April 2013, 20:53

Ich bedanke mich erst einmal für das Feedback :)

Also das es für eine Klasse eigentlich zu wenig ist, war mir bewusst, es ging mir in erster Linie darum zu schauen, ob ich die Syntax und ähnliches zumindest halbwegs verstanden habe.

Eure Verbesserungsvorschläge werde ich natürlich versuchen umzusetzen.

Gibt es denn so typische mini Programme, mit denen man ein wenig die OOP üben kann?
Was ich schon oft gelesen hab wäre z.B. ein Adressbuch. Aber so spontan würde mir jetzt nichts kleines ( unter 500 Zeilen Code ) einfallen, womit ich das sinnvoll üben könnte.
Vielleicht fällt mir ja morgen etwas ein :)

Freundlichen Gruß

desmater :)


EDIT:

So ich habe einmal meinen (java :P) Code verbessert :
Die angesprochene Sequenz in Verbindung mit random.choice
Den Namen der Klasse entsprechend geändert und die main() überarbeitet

Code: Alles auswählen

import random

class PasswordGenrator(object):
    """
    Generates a random password.

    Attribute : with_special_character, password_lenth
    """
    
    def __init__(self, with_special_character = False, password_length = 8):
        self.password_length = password_length
        self.with_special_character = with_special_character

    characters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
                  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    
    special_characters = ['!', '@', '#', '$', '%', '^', '&', '*', '?']
    
    def make_password(self):
        """
        create the random password
        """
        password = ""
        choices = ["lower", "upper", "number"]
        if self.with_special_character:
            choices.append("special")
        for i in range(self.password_length):
            character_kind = random.choice(choices)
            if character_kind == "lower":
                password += self.characters[random.randrange(0, 24)].lower()
            elif character_kind == "upper":
                password += self.characters[random.randrange(0, 24)]
            elif character_kind == "number":
                password += str(random.randrange(0,10))
            elif character_kind == "special":
                password += self.special_characters[random.randrange(0, 9)]
        return password


def main():
    pwgen = PasswordGenrator(True)
    for i in range(10):
        print pwgen.make_password()


if __name__ == "__main__":
    main()
Zuletzt geändert von desmater am Dienstag 21. Mai 2013, 23:42, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo desmater,

ich will Dir ja nicht den Spaß verderben, aber für einen Einzeiler braucht man wirklich keine Klasse:

Code: Alles auswählen

import random

CHAR_SETS = [
    'abcdefghijklmnopqrstuvwxyz',
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
    '0123456789',
    '!@#$%^&*?'
]
    
def make_password(password_length=4, with_special_character=False):
    """
    create the random password
    """
    cset = CHAR_SETS[:None if with_special_character else -1]
    return ''.join(random.choice(random.choice(cset))
                   for _ in range(password_length))
Benutzeravatar
diesch
User
Beiträge: 80
Registriert: Dienstag 14. April 2009, 13:36
Wohnort: Brandenburg a.d. Havel
Kontaktdaten:

BlackJack hat geschrieben: Die Klasse hat ja keinen veränderbaren Zustand und für eine `__init__()` :-)
password_lenth (da fehlt BTW ein "g") und with_special_character sind veränderbare Zustände. Die Klasse definiert eine einfache Schnittstelle für einen Passwort-Generator, die von Unterklassen erweitert werden kann.

Das ist sicher nicht dass ultimative Lehrbuch-Beispiel, aber auch nicht völlig sinnlos und praxisfern.

In Java würde man mindestens die Interfaces Passwordable und GenerablePasswordable und über XML konfigurierbare Klassen PasswordFactory und PasswordGeneratorFactory definierten. ;-)
http://www.florian-diesch.de
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

@Sirius3: Deine Lösung ist aber ganz offensichtlich nicht identisch. Du verwendest das ganze Alphabet und nicht nur die ersten 24 Buchstaben :mrgreen:

Ansonsten gibt es natürlich auch noch das schöne string-Modul:

Code: Alles auswählen

>>> import string
>>> string.ascii_lowercase
'abcdefghijklmnopqrstuvwxyz'
>>> string.ascii_uppercase
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> string.digits
'0123456789'
Das Leben ist wie ein Tennisball.
desmater
User
Beiträge: 32
Registriert: Donnerstag 18. April 2013, 20:53

Ich weiß ja das sich das ganze auch ohne Klasse leicht lösen lässt, wie gesagt, diente dies nur für als erster Versuch ob ich den die Syntax und Aufbau einer Klasse verstanden hatte/habe. Zu diesem Zweck wollte ich aber nichts schreiben was an sich schon komplex ist, um es mir nicht noch schwerer zu machen :K
Aber für meine nächsten Übungsprogramme versuche ich bessere bzw praxisnähere Sachen zu finden :mrgreen:

Und stimmt, da fehlt ja nen g :roll: :mrgreen:
Benutzeravatar
diesch
User
Beiträge: 80
Registriert: Dienstag 14. April 2009, 13:36
Wohnort: Brandenburg a.d. Havel
Kontaktdaten:

Sirius3 hat geschrieben:Hallo desmater,
ich will Dir ja nicht den Spaß verderben, aber für einen Einzeiler braucht man wirklich keine Klasse:
Das ist nicht wirklich ein Einzeiler, und man muss die Paramater für jeden Aufruf neu angeben.

Man könnte z.B. einen Closure verwenden:
def get_make_password_func(password_length=4, with_special_character=False):
"""
create the random password
"""
cset = CHAR_SETS[:None if with_special_character else -1]
return lambda:''.join(random.choice(random.choice(cset))
for _ in range(password_length))

mkpass = get_make_password_func()
for i in range(10):
print mkpass()
Mit ``functools.partial()`` könnte man das, wie BlackJack vorgeschlagen hat, noch etwas eleganter lösen. Im Unterschied zu einer Klasse gibrt es da aber keine (einfache) Möglichkeit, die Parameter nachträglich zu ändern.
http://www.florian-diesch.de
BlackJack

@desmater: Für ein sinnvolles OOP-Beispiel braucht man Daten und mehrere Funktionen die auf diesen Daten operieren, damit man das zu einer Klasse zusammenfassen kann. Oft hat man zu einer Klasse auch noch eine Container-Klasse, welche die Exemplare der ersten Klasse verwaltet, also auch wieder mehrere Funktionen die auf einer Sammlung von Werten operieren. Für das Beispiel einer Adressverwaltung wäre das zum einen ein Datentyp, der eine einzelne Adresse modelliert, und dann einer, der viele davon zusammenfasst und Operationen zum hinzufügen, löschen, laden, speichern, und so weiter, zur Verfügung stellt. Dann hat man oft noch Funktionen, welche die Klassen verwenden. Zum Beispiel ein kleines Textmenü was eine Benutzerschnittstelle für die Adressverwaltung bereit stellt. Damit sind wirklich kleine Programme schon mal vom Tisch. OOP spielt seine Stärken ja auch gerade dort aus: grössere Programme übersichtlich und wartbar halten.

@diesch: Das sind technisch veränderbare Attribute, weil man das in Python halt tun kann, aber ich würde bei der API ein neues Exemplar erstellen wenn ich andere Werte haben möchte und nicht das vorhandene verändern.

Mit Unterklassen erweitern sehe ich auch nicht wirklich. Das wird dann ja *noch* unnötiger komplexer wenn man davon ableitet, nur um die Klassenattribute anders zu belegen. Das würde mir auch irgendwie, ich weiss nicht wie ich es ausdrücken soll, *unsymmetrisch* (!?) vorkommen, wenn man das bei dieser Klasse machen würde. Dafür würde man eher eine abstrakte Basisklasse schreiben und davon dann auch *diese* Klasse ableiten. Dann sieht es aber noch mehr nach Java aus. Die andere Möglichkeit wäre die einzige Methode zu überschreiben, aber da greift dann wieder das Argument, dass man auch einfach eine Funktion statt der Klasse schreiben kann und dann eben eine weitere Funktion schreibt, statt die neue Funktion unnötig in eine abgeleitete Klasse zu stecken.

@EyDu: Ergänzend noch: `string.punctuation`.
Antworten