Parametertyp explizit übegeben

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
Arthur Dent
User
Beiträge: 23
Registriert: Montag 12. September 2011, 09:51

Hallo

Ich probiere erst erst seit ein par Tagen mit Python umher, bin aber schon ziemlich beeindruckt, vorallem was das Eleganz und Schlankheitvon Pythoncode betrifft. Dementsprechend bin ich auch noch nicht so richtig mit den Stilkonventionen für Pythoncode vertraut und tippe eher einfach so drauf los. Für Anmerkungen und Korrekturen dazu bin ich jederzeit Dankbar.

Leider führt die dynamische Typisierung ab und an zu gewisser Verwirrung meinerseits, was zu meinem aktuellem Problem führt.

Ich möchte mir ein objekt Anlegen welches im Konstruktor und auch in anderen Methoden Parameter übergibt, welche explizit von einem bestimmten Typ sind ( z.B. Listen oder irgendwelche anderen Objekttypen), weiß aber nicht wie man das mit Pythen macht.
In C# beispielsweise stellte sich mir so eine Frage nie, da man immer die Typen der übergebenen Parameter explizit festlegen muß.

Code: Alles auswählen


# beam element class
class beam:
    """
    description:
    represents an 2D beam element
    in the sense of the matrix-stiffness-method
    """

    # Constructor with delivery of needed parameters
    def __init__(self, Startnode, Endnode, Section, Material):

        # members
        # parameters passed by constructor
        self.__start = Startnode # Hier soll jetzt ein festgelegter Typ rein
        self.__end = Endnode
        self.__section = Section
        self.__material = Material

Das gleiche Beicpiel mit C# :

Code: Alles auswählen

	public class Beam
	{
		// Felder
		private Node start;	  // Startknoten
		private Node end;		  // Endknoten
		private Material M;        // Materialeigenschaften
		private Section Sec;      // Querschnittswerte
		
		// Konstruktor
		//****************************************************************
		public Beam( node Startnode, node Endnode, Material Mat,Section Crosssection)
		{
			start = Startnode;
			end   = Endnode;
			M     = Mat;
			Sec   = Crosssection;
Optimismus ist, bei Gewitter auf dem höchsten Berg in einer Kupferrüstung zu stehen und "scheiß Götter" zu rufen

Terry Pratchett
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Knappe Antwort: Das geht in Python nicht, da es eben dynamisch typisiert ist!

Du kannst natürlich so etwas machen:

Code: Alles auswählen

def foo(param):
    if not isinstance(param, list):
        raise TypeError
    # weiter im Code
Aber das ist idR unnötig und hässlich und unpythonisch.

Solche Typüberprüfungen macht man ab und an, wenn man explizit mehrere grundverschiedene Arten von Typen unterstützen will. Als Beispiel fallen mir da parse-Methoden aus div. xml-Modulen ein. Dort kann man oftmals einen Dateinamen (String) angeben oder bereits ein File-Objekt. Bei ersterem muss der Parser selber erst die Datei öffnen und somit das File-Objekt erzeugen.

Generell gilt in Python eben DuckTyping. Wieso auf einen Typen festlegen, wenn es doch auf das richtige Protokoll ankommt?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Arthur Dent
User
Beiträge: 23
Registriert: Montag 12. September 2011, 09:51

Nun gut

Diese Antwort erleichtert mich insofern, dass ich mich nicht mehr wundern muss warum ich dazu keine Beispiele gefunden habe. :)

Was heißt das aber nun für mein weiteres vorgehen? Ich möchte ja erreichen, dass in

Code: Alles auswählen

self.__start = Startnode
ein List-Objekt steckt, mit dem dann innerhalb des beam-objektes irgendetwas gemacht wird.

Ist es nun so dass ich bei der weiteren Programmierung erstmal nur davon außgehe, dass das benötigte Objekt schon in self.start steckt obwohl das eigentlich noch nicht tut. Wobei ich dann beim Anlegen oder Ändern von Instanzen des beam-objektes nur noch aufpassen muß, dass die richtigen Parametertypen übergeben werden.

Wenn ja dann erscheint mir das als ne ziemlich fiese Fehlerquelle.
Optimismus ist, bei Gewitter auf dem höchsten Berg in einer Kupferrüstung zu stehen und "scheiß Götter" zu rufen

Terry Pratchett
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Arthur Dent hat geschrieben:Ist es nun so dass ich bei der weiteren Programmierung erstmal nur davon außgehe, dass das benötigte Objekt schon in self.start steckt obwohl das eigentlich noch nicht tut.
Irgendein Objekt wird ja auf jeden Fall in `self.start` stecken. Ob es vom richtigen (Duck-)Typ ist, wirst du im weiteren Verlauf der Behandlung dieses Objektes herausfinden. Wenn eine bestimmte Methode nicht vorhanden ist, wird automatisch ein `AttributeError` geworfen. Dokumentiere einfach, welche Objektart du erwartest und dann liegt alles weitere beim Anwender. Ob ihm dann der besagte `AttributeError` oder ein `TypeError` um die Ohren fliegt, dürfte keine so große Rolle spielen, da man sich als Python-Programmierer nicht auf sowas verlassen sollte. Eine Ausnahme beim `AttributeError` wäre höchstens, wenn man explizit herausfinden möchte, ob das Objekt ein bestimmtes Attribut besitzt. Das kann man aber meistens eleganter mittels des Builtins `hasattr()` machen.

Du solltest dir auf jeden Fall bewusst machen, dass du eine Ducktype-basierte Sprache benutzt, weshalb du bereit sein solltest, das entsprechende Konzept anzunehmen. Alles andere führt zu sog. "unpythonischem" Code, also einem Programmieren *gegen* die Konzepte der Sprache.
Zuletzt geändert von snafu am Mittwoch 14. September 2011, 11:34, insgesamt 1-mal geändert.
BlackJack

@Arthur Dent: Das erscheint vielen als fiese Fehlerquelle die von statisch typisierten Sprachen kommen. In der Praxis ist das dann doch kein so grosses Problem. Wenn Du mit dem Objekt versuchst etwas zu machen was es nicht unterstützt, dann wird es schon irgend eine Ausnahme geben.

Und man testet Code ja. Da fallen auch solche Typfehler schnell auf.

Zu den Konventionen: ``private`` ist nur ein Unterstrich. Zwei Unterstriche verändern den Namen des Attributs hinter Deinem Rücken und das ist dafür gedacht um Namenskollisionen zu vermeiden. Zum Beispiel bei Mehrfachvererbung. *Die* wird aber obwohl sie möglich ist, recht selten überhaupt praktiziert.

Zu snafu's Bemerkung zum Dokumentieren vielleicht noch als Ergänzung, dass man nicht immer den konkreten Typ dokumentieren muss, sondern wirklich nur das was man als Verhalten erwartet. Zum Beispiel dass das Objekt eine Sequenz, eine Abbildung, oder etwas Iterierbares ist.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Arthur Dent hat geschrieben:Was heißt das aber nun für mein weiteres vorgehen? Ich möchte ja erreichen, dass in

Code: Alles auswählen

self.__start = Startnode
ein List-Objekt steckt, mit dem dann innerhalb des beam-objektes irgendetwas gemacht wird.
Jetzt kommt der Trick an dem ganzen: Du möchtest das gar nicht wirklich, du weißt das nur noch nicht. ;-)

Du möchtest dort etwas haben, was sich wie eine Liste verhält oder auch nur teilweise wie eine Liste verhält, je nachdem, welche Operationen du damit durchführst. Du hast dann die volle Flexibilität ob du nun wirklich ein Objekt vom Typ list oder irgendetwas anderes Passendes als Parameter übergibst. Python ist nicht Java und wenn man anfängt die Restriktionen von Java nachzubauen, dann kann man auch gleich in Java programmieren. Wenn du wirklich einmal glaubst zum Überprüfen eines Typs gezwungen zu sein, dann kannst du isinstance oder besser issubclass verwenden, aber ich rate gerade in der Lernphase dringend davon ab. Entdecke die Möglichkeiten von Python!

Hier noch etwas zum Thema Konventionen:PEP 8 -- Style Guide for Python Code. Die doppelten Unterstriche vor Bezeichnernamen solltest du dir übrigens gar nicht erst angewöhnen. Ein einfacher Unterstrich ist normalerweise absolut ausreichend.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

/me hat geschrieben:Python ist nicht Java und wenn man anfängt die Restriktionen von Java nachzubauen, dann kann man auch gleich in Java programmieren.
Wobei er aber in C# programmiert. Ändert aber grundsätzlich nichts an deiner Aussage. ;)
Benutzeravatar
Arthur Dent
User
Beiträge: 23
Registriert: Montag 12. September 2011, 09:51

Danke für die Antworten und für den Styleguide.
Wie bereits erwähnt bin ich statisch typisierte Sprachen gewöhnt und diese Art der dynamischen typisierung ist für mich etwas exotisch und auf dehn ersten blick ziemlich radikal, weil ich mir angewöhnt habe jeden Furz auch ganz streng Furz zu nennen.
Jedoch trauere ich den restriktionen von C# oder Java kein bisschen nach, "exotisch" kann auch aufregend bedeuten.
Ich bin mehr und mehr von Python fasziniert, nichts desto Trotz bedeutet es für mich eine ziemliche Umgewöhnung. Dinge die in anderen Sprachen irgendwie immer ähnlich funktionieren sind in Python irgendwie immer ein bisschen anders und ich muß mich da erstmal reindenken
Optimismus ist, bei Gewitter auf dem höchsten Berg in einer Kupferrüstung zu stehen und "scheiß Götter" zu rufen

Terry Pratchett
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Arthur Dent hat geschrieben:...weil ich mir angewöhnt habe jeden Furz auch ganz streng Furz zu nennen.
Nichtmal `TrockenerFurz`? SCNR :twisted:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

cofi hat geschrieben:
Arthur Dent hat geschrieben:...weil ich mir angewöhnt habe jeden Furz auch ganz streng Furz zu nennen.
Nichtmal `TrockenerFurz`? SCNR :twisted:
Bau doch mal eine Klassenhirarchie auf! :-D
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Arthur Dent: Gemäß Ducktyping würde man jetzt sagen: "Wenn es sich wie ein Furz verhält, dann ist es ein Furz". Von daher spielt das für die Benennung eigentlich keine so große Rolle. Du hast ja bisher sicherlich auch spezialisierte Klassen entsprechend abgewandelt benannt, was aber für die behandelnde Klasse keine Rolle gespielt hat, da es ihr nur um den Grundtypen ging.

Blödes Beispiel: Du hast eine `SortedList`, welche vom Builtin `list` geerbt hat. Deine Klasse, die irgendeine Art von Liste erwartet, hat in der Signatur ihrer `__init__()`-Methode das Argument `lst` (zur Vermeidung von Namenskollisionen) und bindet dies als ein entsprechend benanntes Attribut. Ob die Liste jetzt vorsortiert ist oder meinetwegen jede Änderung automatisch in ein Logfile schreibt, kann dieser Klasse ja ziemlich egal sein. Es will halt nur irgendwelche Aktionen auf dieser Liste durchführen (z.B. über die Elemente der Liste iterieren).

In der Praxis würde man wohl nicht einmal `lst` schreiben, sondern noch viel generischer etwa `names` verwenden, wenn es eben eine Liste mit irgendwelchen Namen sein soll (je nach Kontext des Programms). Diese Bennenung hätte den Vorteil, dass deine `names` genau so gut ein Tupel oder ein ähnlicher Container-Typ sein könnten. Das ist eben die Sache, die man Java oder C# erstmal "erklären" müsste (z.B. durch einen gemeinsamen Grundtypen) - bei Python geht es so gesehen rein um die Intention der späteren Verwendung und der Typ ist auf dieser Ebene völlig egal.
Benutzeravatar
Arthur Dent
User
Beiträge: 23
Registriert: Montag 12. September 2011, 09:51

Schön das meine Furz-Analogie hier für Belustigung sorgt.

Ich denke ich hab die intention des Ducktypings verstanden. Bis ich das richtig verinnerlicht habe vergehen sicher noch n par Tage, aber ich finds schon ziemlich cool.
Optimismus ist, bei Gewitter auf dem höchsten Berg in einer Kupferrüstung zu stehen und "scheiß Götter" zu rufen

Terry Pratchett
Antworten