Instanziieren einer Klasse in einer anderen

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
Benutzeravatar
Schwarzer Wolf
User
Beiträge: 56
Registriert: Donnerstag 5. Januar 2017, 05:24

Kann man um Code zu Sparen Dinge einer Klasse in einer anderen Klasse instanziieren, oder ist das keine gute Praxis?

Hier ein Beispiel:

Code: Alles auswählen

class BAR():
    def __init__(self):
        self.foo1 = FOO()
Natürlich könnte man das über Vererbung lösen, aber dann müsste ich Mehrfachvererbung nutzen, das will ich eigentlich umgehen.

Es geht im Prinzip darum, aus mehreren Klassen Methoden bzw. statische Methoden zu nehmen und in anderen klassen zu benutzen um Code zu sparen. Beispielsweise das Lesen von Dateien, zufällige Auswahl aus listen ...
Wer in der Wildnis lebt, muss zum Wolf werden, oder als Schaf sterben.
(Syrisches Sprichwort)
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Das was Du hier tust, ist Komposition und ganz normal, denn das passiert ja auch, wenn Du einem Attribut eine neue Liste, einen Zahlenwert oder ähnliches zuweist.
Aus Deiner Beschreibung werde ich aber nicht ganz schlau, was Du mit Code-Sparen meinst. Vieles, was Du aufzählst, hört sich auf den ersten Blick nicht danach an, als ob Du dafür eine Klasse definieren müßtest. Am besten zeigst Du konkreten Code, was Du vorhast, dann kann man Dir am besten helfen.

Übrigens: Klassennamen werden CamelCase geschrieben, nicht komplett groß.
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Schwarzer Wolf: Vererbung vs. Komposition hat nichts mit Code sparen zu tun, und „statische Methoden“ klingt in diesem Zusammenhang verdächtig nach einem Entwurfsproblem, denn so etwas ist in Python eher selten, denn statische Methoden sind ja eigentlich Funktionen. Und die werden ausserhalb von Klassen definiert.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
Schwarzer Wolf
User
Beiträge: 56
Registriert: Donnerstag 5. Januar 2017, 05:24

Sirius3 hat geschrieben: Freitag 31. Mai 2019, 15:15 Das was Du hier tust, ist Komposition und ganz normal, denn das passiert ja auch, wenn Du einem Attribut eine neue Liste, einen Zahlenwert oder ähnliches zuweist.
Aus Deiner Beschreibung werde ich aber nicht ganz schlau, was Du mit Code-Sparen meinst. Vieles, was Du aufzählst, hört sich auf den ersten Blick nicht danach an, als ob Du dafür eine Klasse definieren müßtest. Am besten zeigst Du konkreten Code, was Du vorhast, dann kann man Dir am besten helfen.

Übrigens: Klassennamen werden CamelCase geschrieben, nicht komplett groß.
Das mit den Klassennamen ist mir bekannt, habe das Beispiel aus dem Internet. Du hast recht, bei den kleinen Sachen ist es nicht notwendig, aber wenn ich z. B. das in verschiedenen Klassen nutzen möchte:

Code: Alles auswählen

class Security:
    """Main class for security."""

    @staticmethod
    def get_password(count, hard=False):
        """Creates a password.

        :param count: int
        :param hard: bool -> std False
            Creates a password of the desired length. By default all
            ascii letters are used (upper and lower case) and numbers.
            If hard is on <True>, special characters are also used.
        :return: str
        """

        if hard:
            x = string.ascii_letters + string.digits + string.punctuation
        else:
            x = string.ascii_letters + string.digits

        return ''.join(secrets.choice(x) for _ in range(count))
oder als extremeres Beispiel:

Code: Alles auswählen

class FileBase:
    def __init__(self, filepath):
        self._filepath = filepath
        self._user_extensions = []
        self._excluded = []
        self._files = []

    def get_files_from_directory(self):
        path = os.path.join(self._filepath, '**')
        self._files.append(glob.glob(path, recursive=True))
        return self._files

    def get_specified_files_from_directory(self):
        for root, directories, files in os.walk(self._filepath):
            for directory in directories:
                dir_path = os.path.join(root, directory)
                print(dir_path)
                for file in files:
                    file_path = os.path.join(root, directory, file)
                    print(file_path)
                    for excluded in self._excluded:
                        if excluded not in file_path:
                            self._files.append(file_path)

        print(self._files)


    def set_excluded_list(self, excluded):
        """Terms or paths that are not used in the search.

        :param excluded: <list|tuple> <list|tuple>. Delivery as list.
        :return: None
        """

        if type(excluded) == list or type(excluded) == tuple:
            self._excluded.extend(excluded)

    def set_file_extensions_list(self, extensions):
        """Sets the list of file extensions.

        :param extensions: <list|tuple>. Delivery as list.
        :return: None
        """

        if type(extensions) == list or type(extensions) == tuple:
            self._user_extensions.extend(extensions)
Es wäre ja umso mehr arbeit, wenn ich das in mehreren Klassen verwende und somit doppelten Code habe.

Hier mal ein Beispiel wie ich die statische Methode einbinde:

Code: Alles auswählen

def get_root_password(self):
        pw_len = self._inputs('root password length (1 - 100): ')
        return Security.get_password(pw_len)
Wer in der Wildnis lebt, muss zum Wolf werden, oder als Schaf sterben.
(Syrisches Sprichwort)
Benutzeravatar
Schwarzer Wolf
User
Beiträge: 56
Registriert: Donnerstag 5. Januar 2017, 05:24

__blackjack__ hat geschrieben: Freitag 31. Mai 2019, 15:24 @Schwarzer Wolf: Vererbung vs. Komposition hat nichts mit Code sparen zu tun, und „statische Methoden“ klingt in diesem Zusammenhang verdächtig nach einem Entwurfsproblem, denn so etwas ist in Python eher selten, denn statische Methoden sind ja eigentlich Funktionen. Und die werden ausserhalb von Klassen definiert.
Das mit dem 'Entwurfsproblem' ist meine Intention für diese Frage.

Ich habe mir gedacht, ich mache für jeden Bereich eine eigene Klasse.

Beispielsweise:
Files, Security, Data

Dort sind dann alle wichtigen Dinge drin, die andere Klassen benutzen. Teilweise als statische Methoden, teilweise Methoden
Wer in der Wildnis lebt, muss zum Wolf werden, oder als Schaf sterben.
(Syrisches Sprichwort)
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

@Schwarzer Wolf: so wie `Security` jetzt aussieht, ist das eine einfache Funktion. Das im Anschluss in einer anderen Klasse zu verwenden, erzeugt nur Overhead.

Wo ist denn der Unterschied zwischen `Files` und `Data`?
When we say computer, we mean the electronic computer.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Du verwechselst hier Klassen mit Module. Security ist keine Klasse, die hat nichteinmal Zustand. `get_password` ist eine einfache Funktion.

Bei `FileBase` ist das schwieriger zu erkennen, weil mir der Nutzen nicht ganz klar ist.
`get_files_from_directory` ist wieder nur eine Funktion, denn den filebase kann man auch ganz einfach als Parameter übergeben.
`get_specified_files_from_directory` benutzt die excluded, aber _files macht als Attribut keinen Sinn, denn das sammelt Files aus verschiedenen Aufrufen? Das führt zu undurchsichtigem Verhalten.
Außerdem sollte eine Funktion, die get_ heißt, auch etwas zurückliefern.

`set_excluded_list` ignoriert stillschweigend einen Fehler, falls Du keine Liste oder Tupel übergibst. Das ist schon ein Fehler, der zweite Fehler ist, dass es in Python so viele Iterierbare Objekte gibt, dass es die Nutzbarkeit stark einschränkt, wenn man nur Listen oder Tupel erlaubt.

Also insgesamt solltest Du keine Klassen verwenden, Du verwendest hier ein Entwurfsmuster, das so in Python nicht üblich ist.
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Schwarzer Wolf: Das sieht mir teilweise nicht nach sinnvoller Objektorientierung aus. Die `Security`-Klasse ist beispielsweise keine Klasse sondern einfach nur eine Funktion die sinnloserweise in einer Klasse steckt und `FileBase` im Grunde so in der Form auch nicht.

Auf jeden Fall läuft in der `FileBase`-Klasse einiges schief, denn die `get_*()`-Methoden haben gar keinen Rückgabewert sondern erweitern ein internes `_files`-Attribut auf das man von aussen gar nicht heran kommt – sofern man sich nicht über die Konventionen hinweg setzt. Ich sehe auch nicht ob das wirklich sinnvoll ist diese Liste in mehreren Aufrufen zu erweitern. Das sieht mir nach zwei Funktionen aus die umständlich in eine Klasse gesteckt wurden.

Die `set_*()`-Methoden setzen nichts sondern *erweitern* etwas, das ist auch wieder total unerwartet. Zudem würde man erwarten, das man die Werte auch schon beim Erstellen eines `FileBase`-Objekts angeben kann.

Die Typtests an sich sind schon bekloppt, die mit `type()` und ``==`` zu machen dann technisch auch noch daneben. Der Code selbst stellt als einzige Anforderung an die Argumente von den `set_*()`-Methoden, dass die iterierbar sind. Warum schränkst Du das auf `list` und `tuple` ein? Was wenn man ein `set` hat? Oder einen Generatorausdruck?

Und wenn man keine Liste oder kein Tupel übergibt, passiert einfach *gar nichts*‽

Einen Rückgabewert dokumentiert man nur wenn eine Funktion/Methode auch tatsächlich einen Rückgabewert hat. Bei solchen die keinen expliziten Rückgabewert haben überall `None` zu dokumentieren macht nur Arbeit und bringt dem Leser nix.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
Schwarzer Wolf
User
Beiträge: 56
Registriert: Donnerstag 5. Januar 2017, 05:24

@Sirius3
Ist mir gerade auf bewusst geworden beim Nachlesen von Modulen im Buch. Also ich packe alles in Module oder Pakete. Und Klassen dann nur, wenn ich etwas brauche, was einen Zustand hat? Beispielsweise ein Sensor bei einem Robot?

@sls
Files: Dateien
Data: Datenumwandlung

@__blackjack__
Ich werde versuchen, deine Ratschläge zu beherzigen und mich in den genannten Dingen mehr einzulesen. Die FileBase ist schon was älter und ich dachte mir schon, dass da einige Bugs drin sind.
Wer in der Wildnis lebt, muss zum Wolf werden, oder als Schaf sterben.
(Syrisches Sprichwort)
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Schwarzer Wolf: Was ist eigentlich `secrets.choice()`? Hast Du etwa das `random`-Modul in `secrets` umbenannt?

Was auch etwas schräg ist `get_files_from_directory()` erweitert die `self._files`-Liste gar nicht um die Dateinamen, sondern hängt da eine *Liste* mit Dateinamen an, während `get_specified_files_from_directory()` die einzelnen Dateinamen an diese Liste anhängt‽

Die beiden Methoden unterscheiden sich zudem noch darin ob sie nur Dateinamen oder auch Ordnernamen im Ergebnis haben. `self._user_extensions` wird nirgends verwendet. Die Klasse ist echt ziemlich schräg.

`dir_path` wird in `get_specified_files_from_directory()` gar nicht verwendet.

Ausserdem wird pro Unterverzeichnis auf der aktuellen Ebene von `os.walk()` jede Datei verarbeitet, dass heisst wenn es kein Unterverzeichnis gibt aber Dateien dann wird keine davon verarbeitet, wenn es drei Unterverzeichnisse gibt, dann wird jede Datei dreimal verarbeitet und so weiter. Dass das `directory` auch noch an `file_path` gejoined wird ist falsch. Das ist ziemlich kaputt. Hast Du diese Klasse überhaupt schon mal irgendwo benutzt? Unittests sind ein tolle Sache. :-)

Was hier letztlich übrig bleibt sind folgende drei Funktionen (ungetestet):

Code: Alles auswählen

def create_password(length, hard=False):
    """Create a password.

    :param length: int
    :param hard: bool -> std False
        Creates a password of the desired length. By default all
        ascii letters are used (upper and lower case) and numbers.
        If hard is on <True>, special characters are also used.
    :return: str
    """
    characters = string.ascii_letters + string.digits
    if hard:
        characters += string.punctuation
    return ''.join(random.choice(characters) for _ in range(length))


def get_paths(path):
    return glob.glob(os.path.join(path, '**'), recursive=True)


def get_filenames(path, excluded=()):
    result = list()
    for root, _, filenames in os.walk(path):
        for filename in filenames:
            full_path = os.path.join(root, filename)
            if not any(part in full_path for part in excluded):
                result.append(full_path)
    return result
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
Schwarzer Wolf
User
Beiträge: 56
Registriert: Donnerstag 5. Januar 2017, 05:24

__blackjack__ hat geschrieben: Freitag 31. Mai 2019, 16:17 @Schwarzer Wolf: Was ist eigentlich `secrets.choice()`? Hast Du etwa das `random`-Modul in `secrets` umbenannt?

https://docs.python.org/3/library/secrets.html


Zum Rest kann ich dir nicht viel sagen. Ich habe den Code vor Monaten geschrieben und mich dann weitgehend mit Django befasst. Dass er aber nicht fertig war, ist mir bewusst. Deshalb bin ich momentan dabei alles umzuschreiben und zu testen.

Also dann wie ich gefragt habe:
Also ich packe alles in Module oder Pakete. Und Klassen dann nur, wenn ich etwas brauche, was einen Zustand hat? Beispielsweise ein Sensor bei einem Robot?
Wer in der Wildnis lebt, muss zum Wolf werden, oder als Schaf sterben.
(Syrisches Sprichwort)
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

__blackjack__ hat geschrieben: Freitag 31. Mai 2019, 15:24 @Schwarzer Wolf: Vererbung vs. Komposition hat nichts mit Code sparen zu tun, und „statische Methoden“ klingt in diesem Zusammenhang verdächtig nach einem Entwurfsproblem, denn so etwas ist in Python eher selten, denn statische Methoden sind ja eigentlich Funktionen. Und die werden ausserhalb von Klassen definiert.
Ich habe bisher schon mehrere statische Methoden innerhalb von Klassen definiert, wenn man deren Funktionalität außerhalb der Klasse nicht gebrauchen kann. Sind das eher schlechte Entscheidungen gewesen? :roll:

Gruß
Atalanttore
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Atalanttore hat geschrieben: Freitag 31. Mai 2019, 20:09 Ich habe bisher schon mehrere statische Methoden innerhalb von Klassen definiert, wenn man deren Funktionalität außerhalb der Klasse nicht gebrauchen kann. Sind das eher schlechte Entscheidungen gewesen? :roll:
Atalanttore
Ich denke nicht dass das immer ein Anzeichen für ein schlechtes Design ist. Eine statische Methode hast du ja dann, wenn diese nicht zwangsweise auf Attribute der Klasse zugreifen muss. Manchmal möchte man diese Methode aber nicht als Funktion auf Modulebene auslagern, sondern lieber im Namensraum der Klasse definieren, wodurch die Absicht der Methode etwas verdeutlicht wird.
When we say computer, we mean the electronic computer.
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Statische Methoden *ausserhalb* von Klassen definieren geht ja auch nicht, denn dann sind es keine Methoden mehr, also auch technisch nicht. 😉

Ich schrieb extra „eher selten“ und nicht „nie“. Es kommt schon vor, dass man eine Funktion hat, die so sehr mit der Klasse ”verbunden” ist, dass es keinen Sinn macht die ausserhalb zu definieren. Das kommt halt nicht *so* oft vor, und vor allem kommt das nicht so oft vor wie in Programmiersprachen die einen dazu zwingen, weil sie gar keine Funktionen kennen. Bei Java zum Beispiel ist es üblich Klassen nur mit statischen Methoden zu haben, eben so in die Richtung wie Schwarzer Wolf das beschrieben hat, die Klasse als Sammelbecken für thematisch zusammengehörige „statische Methoden“.

Eine andere Klasse von „statischen Methoden“ in Java (und ähnlichen Sprachen) sind ”alternative Konstruktoren”, manchmal auch „Fabrikmethoden“ genannt. Die bildet man in Python allerdings als Klassenmethoden mit `classmethod()` als Dekorator ab. Etwas was Java nicht kennt.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@sls & __blackjack__: Danke. Wieder was gelernt. :)
Antworten