Wie geht ihr mit Datentypen und Parametern in Python um?

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
Solix0x
User
Beiträge: 1
Registriert: Montag 25. März 2019, 08:00

Hallo :)

Ich bin neu bei Python, habe mich ca. 2 Wochen mit Python beschäftigt und habe bisher ca. 2 Jahre sonst noch mit Java, C# und C++ zu tun gehabt. Bei Python ist mir aufgefallen, dass Visual Studio Code als auch in PyCharm nicht viele Information einem gibt, wenn man zum Beispiel eine Funktion oder einen Konstruktor mit Parametern aufruft.

Hier ein Beispiel:

Bild

Habe leider noch nicht herausgefunden, wie ich hier eigene Bilder bzw. Screenshots verwenden kann, aber es sollte trotzdem verständlich sein, was ich meine. Bei den anderen Sprachen wird mir angezeigt, dass ich dort ein int, dann ein string usw. hineingeben muss. Nun kann man bei Funktionen den Datentypen von den Parametern auch optional angeben. In Visual Studio Code gibt es auch sehr oft eine Beschreibung von den Parametern, jedoch wäre es besseres Design, wenn man sowas hat wie:

Code: Alles auswählen

class Person:
	
	def __init__(self, name: str, age: int):
		self.name = name
		self.age = age
Da weiß man direkt was die Parameter bedeuten und auch, was man reingibt.

Aber bei libraries wie numpy ist das eher nicht gegeben (mit den Datentypen) und daher frage ich mich:

Merkt ihr euch die Parameter? Weiß man das einfach mit der Zeit? Oder schaut ihr in der Dokumentation nach?
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Du musst dich von dem lösen, was du von bisherigen Sprachen kennst. Wenn du weiterhin wissen willst, von welchem Typ Parameter sein müssen, dann musst du bei Sprachen bleiben, die das erzwingen. In Python ist das schlicht nicht üblich. Im Gegenteil: Es läuft dem zuwider, was Python ausmacht.

Hast du einmal von Duck-Typing gehört?

Oh und dein Beispiel oben auf dem Screenshot ist super. Da steht ja in dem beschreibenden Text durchaus, welche Parameter übergeben werden müssen. Und da wird dann auch schnell offensichtlich, dass dort verschiedene Typen passen.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, ich schaue in der Dokumentation, Interaktiv: help(xxx), nach und mit der Zeit weiß man das dann auch einfach.
Du hast eine falsche Vorstellung vom Programmieren, wenn Du denkst, dass es einfach reicht, den richtigen Typ einer Funktion zu übergeben. Was hilft Dir, dass `age` eine Zahl braucht, wenn Du nicht weißt, was `age` bedeutet? Umgekehr mußt Du gar nicht wissen, dass das Programm ein `int` erwartet, wenn Du weißt, dass `age` das Alter der Person bedeutet und damit ganz natürlicherweise nur eine Zahl sinnvoll ist.
Typannotationen sind nur unsinniger Ballast, und sind meist falsch, und fast immer zu einschränkend. Eine gute Dokumentation ist viel besser.

Übrigens: eingerückt wird nicht mit 4 Leerzeichen pro Ebene, nicht mit Tabs.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Sirius3 hat geschrieben: Montag 25. März 2019, 08:58 Übrigens: eingerückt wird nicht mit 4 Leerzeichen pro Ebene, nicht mit Tabs.
Das erste "nicht" ist zu viel.
Benutzeravatar
noisefloor
User
Beiträge: 3853
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ich verstehe die Frage offen gesagt nicht.... In der grauen Box, die VS einblendet steht doch alles drin, was du wissen musst / willst?

Und auch das Beispiel mit der Klassendefinition verstehe ich nicht... Erwartest du, dass die VS oder PyCharm immer die __init__ Methode jeder Klasse anzeigt?
Type Annotations sind ja "erst" mit Python 3.5 eingeführt worden und sind immer optional. Der Python Interpreter macht damit auch nichts, darauf greifen z.B. externen Type Checker zu. Im deinem Beispiel würde die die Python Interpreter nicht davon abhalten age="vierzig" zu übergeben. Den Fehler bekommst du erst später, wenn du versuchen würdest, in einer Methode mit `self.age` zu rechnen, also zu Laufzeit des Programms.

Im Gegensatz zu Java, C/C++ ist Python halt dynamisch typisiert und das Programm wird erst zu Laufzeit in Bytecode übersetzt, den der Interprter dann abarbeitet.
Oder schaut ihr in der Dokumentation nach?
So mache ich das.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Solix0x: Ich schaue in die Dokumentation und nutze `help()` (``?`` in IPython). In manchen Fällen ist auch ein Blick in den Quelltext hilfreich (``??`` in IPython).

Für eigene Programme ist in Python Namensgebung vielleicht wichtiger als bei Programmiersprachen mit „manifest typing“ wo man überall die Typen noch mal hinschreiben muss und deswegen auch oft mit an sich schlechten Namen arbeiten kann.

Anmerkung zum Bildschirmfoto: `numpy` mit `t` abzukürzen ist ungewöhnlich. Üblich ist `np`, und so eine Abkürzung beim importieren ist auch nur bei bestimmten Modulen üblich, das sollte man nicht generell machen.

Das `Person`-Beispiel verstehe ich nicht. Also ich verstehe nicht wie man nicht wissen kann was `name` und `age` bedeuten und welche (Duck-)Typen man da übergeben kann/soll. Dazu braucht man doch keine Typannotation. Umgekehrt gibt es Beispiele bei denen das nicht so offensichtlich ist, und wo Typen auch nur bedingt helfen. Da braucht man dann Dokumentation welche die Bedeutung beschreibt. Und bei den Typen hat man dann auch das Problem das viele Funktionen/Methoden keinen Typen, sondern nur ein bestimmtes Verhalten erwarten. Zum Beispiel Funktionen zum laden von irgendwelchen Daten(formaten) die ein Objekt haben wollen, das eine `read()`-Methode hat, die Bytes liefert. Welchen Typ schreibst Du dafür als Annotation hin? Davon gibt es alleine in der Standardbibliothek mehrere Typen, die alle keine gemeinsame Basisklasse haben (von `object` mal abgesehen) und Interfaces gibt es formal auch nicht.

Wenn man `age` beim `Person`-Beispiel als `int` annotiert, sind viele Kinder unglücklich die darauf bestehen nicht 7 sondern schon 7½ Jahre alt zu sein. Also warum ohne weitere Gründe zu haben `float`, `decimal.Decimal`, `fractions.Fraction` ausschliessen. Oder die ganzen verschiedenen Numpy-Datentypen für ganze oder gebrochene Zahlen. Oder Werte die neben der Zahl auch die Einheit enthalten, sich beim Rechnen aber ansonsten wie Zahlen verhalten.

Manchmal kann es auch Sinn machen an etwas das eigentlich konkrete Zahlen erwartet etwas symbolisches aus SymPy zu übergeben. Oder statt skalare Werte, Numpy-Arrays.

Davon abgesehen kannst Du ja wenn es Dir Spass macht, mit Typannotationen arbeiten. Seit Python 3.5 geht das ja.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6736
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

noisefloor hat geschrieben: Montag 25. März 2019, 09:03 (...) das Programm wird erst zu Laufzeit in Bytecode übersetzt, den der Interprter dann abarbeitet.
Wenn man ganz genau sein möchte: Python kompiliert den Quelltext ebenso vor der eigentlichen Ausführung des Programms wie bei anderen Sprachen. Der Unterschied zu C, C++, Java usw ist, dass der Programmierer den Kompiliervorgang nicht explizit anstoßen muss, um ein ausführbares Programm zu erhalten. Das Kompilieren findet auch nur statt, wenn sich der Quelltext geändert hat oder wenn noch kein passendes Kompilat (*.pyc-Datei) vorliegt. Ansonsten wird der bereits kompilierte Bytecode einfach wiederverwendet.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Wenn man noch genauer sein möchte, dann kann compile- und run-time nicht streng getrennt werden: Wenn ein Modul importiert wird, wird dieses kompiliert und anschließend ausgeführt, bevor die Kompilation des importierenden Moduls fortgeführt wird. Ähnliches gilt auch für Code auf Klassenebene.

@Solix0x: wie würdest Du die Methode append auf Listen annotieren wollen? Hier ergibt nur object Sinn, dann kann man es aber auch gleich bleiben lassen. Eine gute Dokumentation ist im Zweifelsfall zielführender. Python ist eben dynamisch, und das finde ich sehr vorteilhaft. Wenn das nicht gewünscht wird, so gibt es andere Sprachen, bei denen das anders ist.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@kbr: Die Kompilierung findet schon immer komplett am Stück statt, sonst könnte man ja keine Syntaxfehler sofort bekommen. Ausgeführt wird es erst anschließend.
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Die Beschreibung von kbr stimmt schon: es wird nicht das gesamte Programm, also alle Module, erst übersetzt und dann ausgeführt, so das man Übersetzungszeit und Laufzeit trennen kann. *Ein* Modul wird immer komplett am Stück kompiliert, aber eben erst zur Laufzeit wenn ein ``import`` ausgeführt wird.

@kbr: Man kann `list.append()` problemlos so annotieren wie Du das beschrieben hast: ``def append(self, item: object) -> None:``. Und Einschränken kann man das dann wenn man Namen an die solche Listen dann bindet, etwas genauer annotiert. Also zum Beispiel ``numbers: List[int] = [1, 2, 3]``.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6736
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Also zumindest findet ab dem zweiten Modul aufgrund von "nachgelagerten" Importen die Kompilierung zur Laufzeit statt, sofern denn der aktuelle Quelltext des jeweiligen Moduls bisher noch nicht kompiliert wurde.

Trotzdem hat man den Fall in aller Regel nur dann, wenn man die Module selbst entwickelt, d.h. wenn Änderungen am Code vorgenommen wurden. Im "Alltagsgebrauch" von Modulen anderer Entwickler werden dennoch die kompilierten Dateien verwendet.

So pauschal kann man daher nicht sagen, dass Python-Quelltext erst zur Laufzeit übersetzt wird, da vorhandener Bytecode wie gesagt in den meisten Fällen wiederverwendet wird.

EDIT: Hat sich da mit Python 3 was geändert? Wenn man am Beispiel des subprocess-Moduls nachschaut, wird einem in Python 2.7 die subprocess.pyc ausgegeben, während unter Python 3.7 bloß subprocess.py angegeben wird...?
Antworten