import innerhalb von Klassen und Klassen untereinander

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
AtomicOne
User
Beiträge: 8
Registriert: Freitag 11. September 2020, 21:31

Ich weiß, ich weiß ...

... das Thema "import" und relativer import usw. ist im Internet bereits massiv breit getreten ...
Aber auf mein Problem habe ich leider keine Lösung finden können.
Auch in der offiziellen Dokumentation habe ich keine Lösungen gefunden. Ich habe, nach meinem Verständnis alle bekannten Vorschläge ausprobiert.
Ich will aber auch nicht ausschließen dass ich mich einfach verrannt habe und mein Ansatz schlicht falsch ist.

Ich habe folgendes Szenario.

Code: Alles auswählen

|
|--connectors
|  |
|  |--dbconnector.py
|
|--entities
|  |
|  |--kunden.py
|  |--kontakte.py
|
|--main.py
In main.py importiere ich folgendermaßen

Code: Alles auswählen

from connectors.dbconnector import DbConnector
from entities.kunden import Customer
Dies funktioniert soweit.

In kunden.py will ich nun kontakte importieren (da Kunden einen oder mehrere Ansprechpartner haben können)
Dies aber gelingt mir leider in keiner Weise.

Entweder ist das Modul unbekannt oder ich bekomme den Fehler "Attepted import from beyond top-level"

Ich verwende Python 3.8.
Ich habe als keine __init__.py files.

Danke für eure Hilfe.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Erst einmal scheint es so, als ob du zu viele Dateien hast. Eine Klasse pro Datei gibt es in anderen Programmiersprachen, in Python fast man zusammengehörige Dinge auch in eine Datei. Kunden und Kontakte in einer Datei löst das Problem eleganter. Ein from . import kontakte hat nicht funktioniert?
Benutze Sql-Alchemie statt dir selbst eine Datenbankanbindung zu schreiben.
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@AtomicOne: Ergänzend: Es sollte nur ein Toplevel-Package geben und da wäre `main` dann mit drin und die Importe müssten anders aussehen.

Warum gibt es keine ``__init__.py``-Dateien? Das man die technisch nicht mehr zwingend *braucht* heisst nicht, dass es eine gute Idee ist die weg zu lassen. Und normalerweise sollten die auch Code enthalten. Hier gilt ähnliches wie Sirius3 über eine Klasse pro Modul gesagt hat: Wenn in einem Package kein Code enthalten ist, das also nicht auch als Modul verwendet wird, nutzt man sehr wahrscheinlich Packages nicht richtig. Keine `__init__.py` macht eigentlich nur bei Namespace-Packages wirklich Sinn.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
AtomicOne
User
Beiträge: 8
Registriert: Freitag 11. September 2020, 21:31

Sirius3 hat geschrieben: Samstag 12. September 2020, 09:42 Erst einmal scheint es so, als ob du zu viele Dateien hast. Eine Klasse pro Datei gibt es in anderen Programmiersprachen, in Python fast man zusammengehörige Dinge auch in eine Datei. Kunden und Kontakte in einer Datei löst das Problem eleganter. Ein from . import kontakte hat nicht funktioniert?
Benutze Sql-Alchemie statt dir selbst eine Datenbankanbindung zu schreiben.
Nein, ein

Code: Alles auswählen

from . import kontakte
Ging nicht.
Aber ... Ich habe eine allgemeine Frage dazu.
Ja, ich gebe zu, ich bin Python-Neuling, habe aber schon in anderen Sprachen programmiert.
Wo ich aber irgendwie unsicher bin ...
Angenommen ich mache den import direkt in der main, dann sollten doch alle anderen Klassen diese doch ebenfalls kennen, oder muss jede Klasse die von ihr verwendeten Sub-Klassen selbst importieren?
Dann nämlich könnte es ja passieren dass ich eine allgemeine Klasse wie z.B. "kontakte" mehrfach importieren muss.
Nämlich in der Klasse Kunden UND Lieferanten (aus genau diesem Grund wollte ich Kunden und Kontakte eben NICHT in einer Datei haben).

Meine Datenbank-Klasse tut nicht viel außer mir meine konkreten Anwendungsfälle in eigene Methoden zu kapseln.
Direkt dahinter ist dann Firebird (fdb).

SQL Alchemy ... Hmmm ... ich glaube das ist mir zu viel Aufwand (Einarbeitung und Lernkurve) dafür dass ich im Grunde nur eine Hand voll Queries umsetzen muss.
AtomicOne
User
Beiträge: 8
Registriert: Freitag 11. September 2020, 21:31

__blackjack__ hat geschrieben: Samstag 12. September 2020, 10:21 @AtomicOne: Ergänzend: Es sollte nur ein Toplevel-Package geben und da wäre `main` dann mit drin und die Importe müssten anders aussehen.

Warum gibt es keine ``__init__.py``-Dateien? Das man die technisch nicht mehr zwingend *braucht* heisst nicht, dass es eine gute Idee ist die weg zu lassen. Und normalerweise sollten die auch Code enthalten. Hier gilt ähnliches wie Sirius3 über eine Klasse pro Modul gesagt hat: Wenn in einem Package kein Code enthalten ist, das also nicht auch als Modul verwendet wird, nutzt man sehr wahrscheinlich Packages nicht richtig. Keine `__init__.py` macht eigentlich nur bei Namespace-Packages wirklich Sinn.
Okay ... vielleicht habe ich auch noch nicht komplett verstanden wie in Python ein "import" überhaupt funktioniert.

Also meine kunden.py und kontakte.py enthalten nichts weiter als

Code: Alles auswählen

class Kunde:
bzw. eben Kontakte mit den entsprechenden @property und @<eigenschaft>.setter und die jeweiligen Methoden.

Ich könnte rein theoretisch auch alles in eine Datei schreiben und hätte vermutlich gar kein Problem.
Aber dann wird mir die Datei einfach zu unübersichtlich.
Aus anderen Sprachen kenne ich es so, dass im Grunde durch einen Präprozessor nichts anderes geschieht als die importierte Datei direkt an der entsprechenden Stelle einzufügen vor der Ausführung.
Da muss ich mich hier wohl irgendwie umgewöhnen.
AtomicOne
User
Beiträge: 8
Registriert: Freitag 11. September 2020, 21:31

Und macht man den import in der Datei VOR der jeweiligen Klasse oder sogar direkt in der Klasse selbst ?

Code: Alles auswählen

import json
import time, datetime

class Customer:
	pass
oder

Code: Alles auswählen

class Customer:
	import json
	import time, datetime
Diese Frage konnte ich leider auch in keinem einzigen Tutorial, Forum usw. beantworten.
Vermutlich gibt es diese Antwort gar nicht, weil ich einfach einen Knoten im Kopf habe und das Schema insgesamt nicht verstanden habe.
AtomicOne
User
Beiträge: 8
Registriert: Freitag 11. September 2020, 21:31

Okay, also mit einer __init__.py kann ich nun mit

Code: Alles auswählen

from .customer import Customer
from .customers_contacts import CustomersContact
from .interestedsource import InterestedSource
from .notes import Notes
from .relations import Relations
from .user import User
mehrere Klassen auf einmal importieren.

Mache ich aber in der main nun

Code: Alles auswählen

import entities
sind die ganzen Klassen trotzdem alle unbekannt.


EDIT: Okay, da war ich zu schnell.
Ich habe erkannt dass ich dann in der main anders importieren muss mittels

Code: Alles auswählen

from entities import *
Zuletzt geändert von AtomicOne am Samstag 12. September 2020, 14:41, insgesamt 1-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@AtomicOne: ``import`` hat nichts mit Präprozessor zu tun, so etwas gibt es in Python nicht, und ich bin auch etwas verwirrt durch die Aussage das Klassen in Klassen etc. importiert werden. Importanweisungen selbst stehen in Modulen und zwar am Anfang vom Modul. Davor kommt eventuell noch ein Docstring und/oder eine She-Bang-Zeile wenn das Modul ein Programm ist.

Pro Modul-Import eine Zeile. Also nicht ``import time, datetime`` sondern ``import datetime`` und ``import time``. Normalerweise irgendwie sinnvoll gruppiert/sortiert. Üblich ist Gruppierung nach Standardbibliothek, Drittanbieter, eigene lokale Module. Und die Gruppen dann beispielsweise alphabetisch sortiert.

``import ...``/``from ... import ...`` sind ausführbare Anweisungen die zur Laufzeit an der Stelle wo sie stehen ausgeführt werden. Ganz normal der Reihe nach. Falls das betroffene Modul das erste mal importiert wird, wird der Code auf Modulebene ausgeführt. Wenn es nicht das erste mal war, dann wird das bereits vorhandene Modulobjekt verwendet. Was ``import`` auf keinen Fall ist, ist irgendwelches Quelltext kopieren/einfügen.

``import entities`` macht genau *einen* Namen bekannt: `entities`. Falls die `__init__.py` in einem `entities`-Verzeichnis liegt, dann hast Du dort die Namen in das `entities`-Modul importiert. Da sind sie dann. Man kann dann ``import entities`` machen und mit `entities.Customer` darauf zugreifen, oder man kann ``from entities import Customer`` schreiben und hat dann den Namen `Customer` im importierenden Modul.

Eingerückt wird mit vier Leerzeichen pro Ebene. Nicht mehr, nicht weniger, und schon gar keine Tab-Zeichen.

Schau mal in den Style Guide for Python Code.

Ich hoffe mal die @property und @<eigenschaft>.setter machen auch irgendwas sinnvolles und sind nicht nur triviale getter/setter. Das macht man in Python nicht. Und ich hoffe auch das Du keines der verkackten Tutorials gefunden hast die sagen es gäbe so etwas wie ``private`` aus anderen Programmiersprachen und das wären Attribute mit zwei führende Unterstrichen. Gleich vergessen den Quatsch. Implementierungsdetails werden mit *einem* führenden Unterstrich gekennzeichnet, und `property()` verwendet man nur wenn da auch tatsächlich irgendwas über ein simples abfragen und setzen hinaus gemacht wird.

Schreib das erst mal alles in eine Datei. Aufteilen kann man das dann später immer noch. Falls es unübersichtlich ist, könnte es auch daran liegen, dass Du kein Python schreibst, sondern welche Sprache mit Präprozessor Du da im Kopf hast in Python-Syntax zwingst.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
AtomicOne
User
Beiträge: 8
Registriert: Freitag 11. September 2020, 21:31

Wie du auf die Leerzeichen und Tabs kommst weiß ich nicht.
Falls hier in dem Thread im "code" Block ein Tab ist, dann weil ich das dort so eingegeben habe. Aber ich schreibe in VS Code. Der macht bei einem Tab automatisch die vier Leerzeichen.
Dessen bin ich mir schon bewusst.

Das mit dem import habe ich ja nun schon verstanden.
in den __init__.py importiere ich ja nun alles aus dem Unterverzeichnis und in der main dann mit from entities import * sind dann alle entsprechenden Namen auch in der Main bekannt.

Das mit mehreren imports in einer Zeile habe ich tatsächlich von wo anders kopiert.
Aber es funktioniert. Also warum nicht?

Durch den minimal geänderten import Aufbau und mittels __init__.py klappt nun auch ein

Code: Alles auswählen

from .customers_contacts import CustomersContact
innerhalb der Klasse Customer
Das hat also schon mal geholfen.

Ja, die Getter und Setter sind tatsächlich stumpfe Getter und Setter ohne weiteren Code.
Wenn es kein private gibt, wie kapselt man denn sonst Attribute einer Klasse / Objekt?

Das Python keinen Präprozessor hat ist mir klar. Ich meinte das bloß als Analogie.

Nein, bisher habe ich auch kein import innerhalb einer Klasse versucht. Das fühlt sich auch nicht richtig an.
Ich war mir lediglich unsicher.
Also immer davor. Check.

Aber ob und wie man seine import Anweisungen sortiert ist doch nun wirklich Geschmacksache. Am Ergebnis sollte das doch hoffentlich nichts ändern.

Du fragst übrigens nach property().
Ich verwende allerdings das @property.
So wie ich es verstanden habe ist das noch ein Unterschied.
Das tut zumindest genau das was ich erwarte.
Es macht mir eine Eigenschaft "public". Während die mit __ private bleiben.
Was daran falsch ist verstehe ich noch nicht ganz.

Danke für den Style Guide.
Werde ich mir mal ansehen.
Wobei mir das oft tatsächlich ein wenig zu Religiös ist. Ob man in Java nun ...

Code: Alles auswählen

function add(a, b) {
	return a+b
}
macht oder

Code: Alles auswählen

function add(a, b)
{
	return a+b
}
Ich finde z.B. die zweite Variante lesbarer.
Diesen Streit wird man niemals beenden können.
Und das wird es in Python genauso geben.
Oder ein

Code: Alles auswählen

if not booleanVariable:

# gegenüber

if booleanVariable != True:
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@AtomicOne: Sternchen-Importe sind Böse™. Nicht machen! Da holt man sich alle Namen aus einem Modul in den Namensraum des importierenden Moduls. Nicht nur die Namen die dort definiert wurden sondern auch die Namen die das Modul selbst wieder von irgendwo anders importiert hat. Dann kann man sich Namensräume auch gleich sparen. Man sollte an den Importen sehen können was woher kommt.

Es funktioniert alles Mögliche. Python ist da ja sowieso recht flexibel und verbietet kaum etwas. Um so mehr wird aber auf Konventionen Wert gelegt. Um sauberen, leicht verständlichen Code zu schreiben. Und deswegen die Ordnung bei Importen und nur ein Modul pro Import-Anweisung. Damit man den Überblick nicht verliert, alles ordentlich sortiert ist, man nichts mehrfach importiert, und auch nicht verwendete Importe leichter erkennen und beseitigen kann.

Wenn man einen trivialen Getter und Setter für ein Attribut hat dann ist das doch überhaupt gar nicht gekapselt, denn es macht Null unterschied ob das über triviale Getter/Setter oder einfach als Attribut verfügbar ist. Ansonsten kapselt man in dem man halt nicht auf Sachen zugreift auf die nicht zugegriffen werden sollte. Wie gesagt ist ein führender Unterstrich die Konvention um nicht-öffentliche Attribute zu kennzeichnen. Kapselung ist nicht gleichbedeutend mit Zugriffsschutz. Der ist (in anderen Programmiersprachen) nur dazu da um die Kapselung gegen Leute durchzusetzen die sie durchbrechen wollen. In Python geht man vom mündigen Programmierer aus, der das nicht macht weil er weiss, dass das keine gute Idee ist, nicht weil die Sprache ihm auf die Finger haut wenn er es macht.

Ganz wichtig: Vergiss ``private`` und doppelte führende Unterstriche. Wenn Du das willst, dann willst Du nicht wirklich in Python programmieren und schreibst am Ende furchtbar viel unsinnigen Boilerplate-Code.

Bezüglich Geschmackssache: Es gibt in Python einige Konventionen die wirklich ernst genommen werden. Zum Beispiel das mit dem Tab: Nette Erklärung, dass das in Deinem Editor sonst nicht passiert — aber wenn das hier im Forum im gezeigten Code keine vier Leerzeichen pro Ebene sind, dann wird Dir das von so ziemlich jedem Regular gesagt. Und das auch jedes mal. Namenschreibweisen und das auf Modulebene nur Code gehört der Konstanten, Funktionen, und Klassen definiert ist auch in Stein gemeisselt.

Wie man Importe sortiert ist auch nicht wirklich Geschmackssache wenn das a) übersichtlich sein soll, b) importierte Namen schnell und leicht zu finden sein sollen, c) diffs bei Änderungen möglichst klein sein und möglichst nur die tatsächliche Änderung enthalten sollen, d) bei statischer Analyse die Zeile mit der Markierung möglichst wenige Fehler/Warnungen enthalten soll. Und da hat sich halt die beschriebene Gruppierung/Sortierung durchgesetzt.

Ich schrob `property()` weil es eine Funktion ist und ich die immer mit Klammern versehe, um das deutlich zu machen. Das was Du bei ``@property`` verwendest ist ja auch genau die Funktion, da aber dann mit der Dekorator-Syntax verwendet. Das ist letzlich ”nur” syntaktischer Zucker für folgendes:

Code: Alles auswählen

@decorator
def function(...):
    ...

# <=>

def function(...):
    ...

function = decorator(function)
Für ``decorator`` kann da ein beliebiger Ausdruck stehen der zu etwas aufrufbaren ausgewertet wird das *ein* Argument erwartet. Und anstelle von ``def function(...):`` kann da auch ``class Class(...):`` stehen. `property()` gab es schon länger und die Dekorator-Syntax wurde eingeführt, damit man das gleich am Anfang der Funktion/Methode/Klasse sehen kann was da passiert, und nicht erst nach dem Funktion-/Methoden-/Klassenkörper. Der kann ja recht lang sein, und da ist das eventuell etwas überraschend, dass danach noch mal etwas mit dem Objekt gemacht wird und es ersetzt wird durch das Ergebnis des Aufrufs vom Ergebnis von ``decorator``.

Ja, es ist religiös. Die Klammerfrage gibt es in Python nicht. Da gibt es eigentlich nur das Blöcke die nur aus einer Zeile bestehen trotzdem eingerückt auf eine eigene Zeile kommen. Aber mit literalen `True`/`False`-Werten vergleicht man nicht. Auch das wird Dir jedes mal gesagt werden.

Und das `booleanVariable` in Python `boolean_variable` geschrieben wird.

Das Schlüsselwort ``function`` gibt's in Java nicht und Typen sind da im Beispiel auch nicht deklariert. Oder habe ich da etwas verpasst. Zudem funktioniert ``import`` in Java auch deutlich ähnlicher zu dem in Python im Vergleich zu Sprachen wo das eher ein kopieren von Quelltext oder Code an die importierende Stelle ist.

Das `add()` schreibt man in Python übrigens so: ``from operator import add``. Die ganzen Operatoren gibt es als Funktionen in der Standardbibliothek.

Um noch mal auf eine Klasse pro Datei zu sprechen zu kommen: Eine Datei ist in Python ein Modul. Und ein Modul ist ein Namensraum. Namensräume sind dazu da um Namen für zusammengehörende Dinge/Werte/Objekte zusammen zu fassen. Wenn man also in der Regel jeden Namensraum dazu verwendet um genau einen Namen zu definieren, dann ist das objektiv *falsch*. Das heisst nicht, dass man nicht auch mal bei einer sinnvollen, logischen Aufteilung nur eine Klasse in einem Modul haben kann, aber das ist halt nicht die Regel. In Java wäre das beispielsweise so als würde man in jedem Package genau eine Klasse ablegen, also immer nur eine Klasse pro Verzeichnis. Da wird auch jeder Java-Programmierer sagen das ist falsch, auch wenn man das natürlich machen kann und es funktioniert.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
AtomicOne
User
Beiträge: 8
Registriert: Freitag 11. September 2020, 21:31

Tut mir Leid, aber deine Antworten klingen leider wie so oft in solchen Foren nach "ich bin der Schlaueste" und nicht nach netter Hilfe.
Du korrigierst mich an Stellen die komplett unwichtig sind und von mir lediglich Beispiele in Form von Pseudo-Code darstellen sollten.

Ob function ein Schlüsselwort in Java ist ist doch mal komplett Banane hier ... Dann eben in JavaScript. Sch**ß der Hund drauf.

Und ich bleibe dabei es ist "convenience" wie man importe sortiert. Deine Argumente mögen richtig sein. Fallen aber eben genau in den Bereich der "Religion" von der ich sprach.

Das mit dem * import verstehe ich und erscheint richtig was du sagst. Ist in diesem Fall aber okay für mich, da ich dort ausschließlich Module importiere die ich selbst geschrieben habe.

Und das add() war auch nur ein Beispiel. Ich hatte überhaupt nicht vor darauf inhaltlich einzugehen. Ebenso wenig auf die Klammern und Einrückungen und was du sonst noch so geschrieben hast.
Das hat alles nichts mehr mit meiner Frage zu tun.

Und meine Erklärung mit MEINEM Editor finde ich absolut legitim.
Warum soll mich das irgendwie beschäftigen wenn es Editoren gibt die einem diese Last abnehmen?
Und Linter die zusätzlich prüfen ob alles halbwegs richtig geschrieben ist und ggf. grobe Formatierungsfehler sogar korrigieren.
Du befasst dich mit Dingen die für mich im Alltag total unwichtig sind.

Das ist so wie, "nutzt mein Linux systemd, SysVInit oder upstart?". Das ist sowas von langweilig und nicht Zielführend so lange mein System läuft.
Dem Anwender der auf dem Desktop bunte Icons anklickt dürfte das komplett egal sein.

Das mit den Gettern und Settern verstehe ich.
Von mir aus ohne, dann kann ich mir den ganzen Quark sparen. Finde ich gut. Danke dafür.
Da werde ich noch mal genauer nachlesen wie da die Python-Philosophie ist.

Also nicht dass du mich falsch verstehst.
Du lieferst durchaus nette und hilfreiche Informationen für mich (und andere).
Aber es gibt halt ein paar Dinge die mich bei Programmierern jedes mal nerven ... und das ist dieses ... ich nenne es mal "Klugscheißen" und der Bereich der Esoterik.
Und, "man tut dies nicht und man macht das nicht" (obwohl es gar nicht gefragt war).
Ich habe bisher in C# Erfahrungen und in der Shell-Programmierung (habe aber beides nie wirklich gelernt und ich programmiere gerade mal seit 4 Jahren aktiv).
Ich wollte nicht den Planeten hacken und ich habe auch nicht den Anspruch 100% clean code zu programmieren der wirklich allen Regeln und Konventionen gerecht wird (ordentlich und gut strukturiert hingegen sehr gerne).
Ich bin zufrieden wenn mein kleines Programm hinterher funktioniert und JSON-Daten aus einer EMail (Web-Formular aus WordPress) in eine Datenbank importiert (unter Anwendung von einer Hand voll Regeln).
Mehr nicht.

Und da dies in Python mein erstes Projekt ist und durch welches ich überhaupt erst Python lerne, bin ich schon recht zufrieden.
Zumindest tut es nach 3 Tagen Arbeit bereits genau das was es soll (ein paar Ausnahme-Behandlungen falls etwas schief geht müssen noch her).
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Hier schreiben Leute, die haben Erfahrung. Und weil du selbst in vielen Punkten zustimmst, es haben alle Konventionen gute Gründe.
AtomicOne
User
Beiträge: 8
Registriert: Freitag 11. September 2020, 21:31

Sirius3 hat geschrieben: Sonntag 13. September 2020, 21:35 Hier schreiben Leute, die haben Erfahrung. Und weil du selbst in vielen Punkten zustimmst, es haben alle Konventionen gute Gründe.
Das habe ich auch nicht bezweifelt.
Nur ist es sehr frustrierend wenn einem zum einen gleich erzählt wird dass man praktisch alles falsch macht (klar, es ist nur gut gemeint um späteren Frust zu vermeiden).
Und zum anderen wenn Beispiele die wirklich nur Beispiele waren und absolut nichts weiter als pseudo-code, bis ins Kleinste zerlegt werden.
Das ist dann eben weder Zielführend noch auf die Frage bezogen.

Ich habe übrigens auch alle Hinweise direkt in meinem Code erfolgreich umgesetzt (sogar das Sortieren der imports).
Benutzeravatar
snafu
User
Beiträge: 6867
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Brauchst du ein Taschentuch? Ich hätte günstige von Netto... :)
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, Programmieren kann frustrierend sein. Vor allem, wenn man für ein Problem keine schöne Lösung findet. Natürlich kennt man als Anfänger noch nicht alle Konventionen und Muster. Fehler machen gehört dazu. Frustrierend wird es erst, wenn niemand einen auf die Fehler aufmerksam macht. Dagegen ist es ein Erfolgserlebnis, wenn man mit den neuen Kenntnissen nur noch ein Viertel der Zeilen Code braucht.

Zu jedem Handwerk gehört es dazu, dass man sein Werk auf den Müll schmeißt, weil es nicht gut genug geworden ist und man so lange bearbeitet bis es den eigenen Ansprüchen genügt.
@snafu: Nachtreten ist unfair.
Antworten