Seite 1 von 1
Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 14:44
von Schwarzer Wolf
Kann man um Code zu Sparen Dinge einer Klasse in einer anderen Klasse instanziieren, oder ist das keine gute Praxis?
Hier ein Beispiel:
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 ...
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 15:15
von Sirius3
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ß.
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 15:24
von __blackjack__
@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.
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 15:29
von Schwarzer Wolf
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)
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 15:36
von Schwarzer Wolf
__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
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 15:51
von sls
@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`?
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 15:52
von Sirius3
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.
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 15:53
von __blackjack__
@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.
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 16:13
von Schwarzer Wolf
@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.
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 16:17
von __blackjack__
@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
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 16:25
von Schwarzer Wolf
__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?
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 20:09
von Atalanttore
__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?
Gruß
Atalanttore
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 20:17
von sls
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?
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.
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Freitag 31. Mai 2019, 21:50
von __blackjack__
@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.
Re: Instanziieren einer Klasse in einer anderen
Verfasst: Samstag 1. Juni 2019, 13:22
von Atalanttore
@sls & __blackjack__: Danke. Wieder was gelernt.
