Unterschied zweier Befehle

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
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Die zwei Python3-Anweisungen

Code: Alles auswählen

self.tplan_ddict = defaultdict(lambda:classTplan)
und

Code: Alles auswählen

self.tplan_ddict = defaultdict(partial(classTplan))
sind trotz erstem Anschein nicht 100% gleichwertig. "classTplan" ist Name und Konstruktor einer Klasse. Was ist der Unterschied?

Wegen ganz anderen Fehlern (viel weiter vorne) erzeugt mein Code in einem und dem anderen Fall zwei ganz verschiedene Fehlermeldungen, was bei Anweisungen die genau dasselbe bewirken wohl nicht sein kann.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Wenn wir jetzt noch die Fehlermeldungen wüssten...
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Durch Ausprobieren im Interpreter sieht man leicht den Unterschied:

Code: Alles auswählen

>>> d1 = defaultdict(lambda:list)
>>> d2 = defaultdict(partial(list))
>>> d1['x']
<type 'list'>
>>> d2['x']
[]
Einmal hast du das Klassenobjekt an sich und einmal hast du eine Instanz (a.k.a. Exemplar) der Klasse.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Wärend der Aufruf der lambda-Funktion

Code: Alles auswählen

lambda:classTplan
die Klasse classTplan zurückliefert, beglückt

Code: Alles auswählen

partial(classTplan)
den Aufrufer mit einer Instanz der Klasse classTplan.

Du meinst wohl:

Code: Alles auswählen

lambda:classTplan()
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Goswin hat geschrieben:sind trotz erstem Anschein nicht 100% gleichwertig.
Das die Auffrufe unterschiedlich sind, sieht man doch leicht an der Anzahl der Argumente und an dem Ergebnis. Die erste Variante erwartet kein Attribut und das Ergebnis ist immer ``classTplan``. Die zweite Variante erwartet genau so viele Argumente wie classTplan und gibt ein classTplan-Objekt (ein Exemplar von classTplan) zurück.
Goswin hat geschrieben:"classTplan" ist Name und Konstruktor einer Klasse. Was ist der Unterschied?
Wenn du dich an PEP8 halten würdest, deine Klasse also "Plan" genannt hättest, dann hätte man es auch so gesehen ;-) Ich kenne übrigens niemanden mehr, egal welche Programmiersprache, der noch Typinformationen in Namen einbaut. Das "class" ist nicht nur überflüssig, macht zusätzliche Arbeit und sieht auch noch unleserlich aus.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

EyDu hat geschrieben:Die zweite Variante erwartet genau so viele Argumente wie classTplan und gibt ein classTplan-Objekt (ein Exemplar von classTplan) zurück.
Ich hatte offenbar "partial" nicht richtig verstanden. Ich dachte, "partial" empfängt eine Funktion/Functor als erstes Argument und gibt eine neue Funktion/Functor zurück (so steht es im Summerfield, S.387). Das ist offenbar nicht so, denn meine Klassenobjekte sind weder Funktionen noch Functoren.
Wenn du dich an PEP8 halten würdest, deine Klasse also "Plan" genannt hättest, dann hätte man es auch so gesehen ;-)
Respekt! Ich finde schon die Sprachdefinitionen schwierig genug zu verstehen, und habe mich noch nie in die PEPs hineingelesen.
Ich kenne übrigens niemanden mehr, egal welche Programmiersprache, der noch Typinformationen in Namen einbaut. Das "class" ist nicht nur überflüssig, macht zusätzliche Arbeit und sieht auch noch unleserlich aus.
Jetzt kennst du wieder jemanden :) . Bei statisch typisierten Sprachen wie Fortran und Java tue ich das auch nicht. Aber bei Python habe ich mir das angewöhnt, weil ich immer wieder vergesse, welchen Typ meine Variablen haben, haben sollen, und haben müssen, damit das Programm fehlerlos läuft. Ich baue auch oft "assert isinstance(var_typ,typ)" ein.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Der Wichtige Teil ist, dass `partial` eine Funktion zurueck gibt, die die eigentliche Funktion tatsaechlich auch aufruft, wie es im Aequivalent der Doku steht: http://docs.python.org/2/library/functo ... ls.partial

`partial` gibt hier durchaus einen Funktor - im Sinne Funktionsobjekt - zurueck, akzeptiert als eingabe aber beliebige Callables und Klassen sind das auch.

Dein `lambda` gibt aber immer nur die Funktion selbst zurueck, ohne sie aufzurufen.

PEP 8 ist kein "normales" PEP sondern ein Styleguide und ich wette wuerdest du ihn befolgen, haettest du auch weniger Probleme mit den Typen ;)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Goswin hat geschrieben:
Ich kenne übrigens niemanden mehr, egal welche Programmiersprache, der noch Typinformationen in Namen einbaut. Das "class" ist nicht nur überflüssig, macht zusätzliche Arbeit und sieht auch noch unleserlich aus.
Jetzt kennst du wieder jemanden :) . Bei statisch typisierten Sprachen wie Fortran und Java tue ich das auch nicht. Aber bei Python habe ich mir das angewöhnt, weil ich immer wieder vergesse, welchen Typ meine Variablen haben, haben sollen, und haben müssen, damit das Programm fehlerlos läuft. Ich baue auch oft "assert isinstance(var_typ,typ)" ein.
Damit solltest du sofort aufhören ;-) Mit zusätzlichen Typinformationen in Namen und asserts zur Typprüfung arbeitest du gegen die Sprache und nicht mit der Sprache. Das gute an Ducktyping ist ja, dass man keinen bestimmten Typ erfüllen muss, sondern nur eine Schnittstelle. Wenn du diesen Vorteil über Bord wirfst, dann kannst stellt sich die Frage, warum du überhaupt mit einer dynamischen Sprache arbeitest.

Außerdem haben Typinformation in Namen noch den Nachteil, dass diese schwierig zu warten sind. Ein Typ änder sich durchaus mal und dann müssen alle Namen, und damit unter umständen auch die "öffentliche" Schnittstelle, angepasst werden. Das ist natürlich einiges an Aufwand, führt neue Fehler ein und bringt, im schlimmsten Fall, Inkompatibilitäten mit sich. Wenn man die Namen hingegen nicht anpasst, dann muss zwar nichts geändert werden, dafür steht der ganze Code voller falscher Informationen. Egal wie man es mit den "Zusatzinformationen" macht, man kann es nur falsch machen, wenn es nicht Teil der Syntax ist.

Prinzipiell kann man sagen: wenn du Typen in Namen codieren musst, dann ist nicht die Sprache das Problem, sondern der Programmierer. Häufig liegt es daran, dass dieser sich nicht genug Gedanken über den Namen gemacht hat. Oder dass das erzeugte Stück Code vielleicht nicht ganz so sinnvoll ist wie geglaubt.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

cofi hat geschrieben:PEP 8 ist kein "normales" PEP sondern ein Styleguide und ich wette wuerdest du ihn befolgen, haettest du auch weniger Probleme mit den Typen ;)
Stimmt, das ist das einzige PEP, dass ich irgendeinmal gelesen hatte, ich hatte mir nur die Zahl vom PEP nicht gemerkt.

Inzwischen habe ich PEP8 wieder überflogen, und siehe da: ohne dass ich mich groß darum bemüht habe, ist mein Code zu 80% PEP8-konform geraten, ich benutze auch immer die Aufrufoption "-tt". Was mir aber am meisten an PEP8 gefällt ist seine "first things first" Politik :wink: :
Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important. But most importantly: know when to be inconsistent.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Goswin hat geschrieben:
EyDu hat geschrieben:Ich kenne übrigens niemanden mehr, egal welche Programmiersprache, der noch Typinformationen in Namen einbaut. Das "class" ist nicht nur überflüssig, macht zusätzliche Arbeit und sieht auch noch unleserlich aus.
Jetzt kennst du wieder jemanden :) . Bei statisch typisierten Sprachen wie Fortran und Java tue ich das auch nicht. Aber bei Python habe ich mir das angewöhnt, weil ich immer wieder vergesse, welchen Typ meine Variablen haben, haben sollen, und haben müssen, damit das Programm fehlerlos läuft.
Sehr richtig. Hier ein paar weitere Möglichkeiten, Source Code unwartbar zu machen: How To Write Unmaintainable Code.

Unter der Rubrik Naming findet sich als Punkt 30. folgendes:
Hungarian Notation is the tactical nuclear weapon of source code obfuscation techniques; use it! Due to the sheer volume of source code contaminated by this idiom nothing can kill a maintenance engineer faster than a well planned Hungarian Notation attack.
Leider kann man bei Microsoft immer noch Charles Simonyis Artikel finden, in dem er die Ungarische Notation zum erstem Mal präsentiert hat. Was er dort schreibt, ist viel zu vernünftig und würde man Ungarische Notation so verwenden, wie dort darstellt, würde sie kaum zu Source Code Obfuscation taugen.


Ein Bekannter von mir besteht darauf, vor jeden Variablennamen ein 'o' zu setzen, weil Variablen ja auf Objekte referenzieren. Mein Argument, dass doch in Python alles ein Objekt ist, ficht ihn nicht an. Sein Gegenargument ist "Aber dann kann ich sofort sehen, was für einen Typ die Variable hat" - worauf ich entgegne "Aber damit siehst du doch nur, dass es sich um ein Objekt handelt, was ja zu erwarten ist, da in Python alles ein Objekt ist" - und er: "Aber dann kann ich sofort sehen, was für einen Typ die Variable hat", usw. etc. pp. ad. inf. Als ich diese Art der Variablen-Benennung als "oRomeo, oRomeo, oMein geliebter Romeo"-Notation verspottet habe, hat er sich zwar gemopst, aber bestand weiterhin darauf, dass das erste und wichtigste Kennzeichen vernünftigen Verhaltens (hier des Variablen-Benennungs-Verhaltens) dasjenige ist, bei dem man sich so verhält, wie man es schon immer getan hat, egal, wie gut die Gegenargumente sind. Man kann ja jedes Argument besiegen, indem man das magische Wort "trotzdem" verwendet. Man könnte diese philosophische Position als Trotzdemismus bezeichnen.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

pillmuncher hat geschrieben:Leider kann man bei Microsoft immer noch Charles Simonyis Artikel finden, in dem er die Ungarische Notation zum erstem Mal präsentiert hat. Was er dort schreibt, ist viel zu vernünftig und würde man Ungarische Notation so verwenden, wie dort darstellt, würde sie kaum zu Source Code Obfuscation taugen.
Die Ungarische Notation ist absolut pervertiert worden und war nie zur Kennzeichnung der internen Datentypen (Beispiel: lpsz = long pointer string zero-terminated) gedacht. Inzwischen hat sich aber eigentlich jeder ernst zu nehmende Programmierer davon verabschiedet.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

EyDu hat geschrieben:Das gute an Ducktyping ist ja, dass man keinen bestimmten Typ erfüllen muss, sondern nur eine Schnittstelle.
Das ist ja gerade mein Problem: Ich versuche verzweifelt, eine Schnittstelle für verschiedene Implementierungen zu programmieren :? .

In Python zeigt jeder Name auf ein Objekt, also ist es sinnlos, so etwas im Namen zu kennzeichnen. Aber nicht jedes Objekt ist eine Klasse: wie soll ich mich ohne besonderen Hinweis im Namen nach einem Monat erinnern, ob (beispielsweise) "datensatz" bereits ein leerer Datenbehälter ist oder eine Klasse, womit ich über "datensatz()" erst einen oder mehrere Datenbehälter erzeugen muss?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Das ist was ich mit meinem Hinweis auf PEP 8 meinte: An der Schreibweise. PEP8 empfiehlt fuer Klassen CamelCase.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Klassennamen werden in aller Regel groß geschrieben (bzw wie gesagt CamelCase) und Namen, die an Instanzen gebunden sind klein. Dummerweise halten sich Pythons Builtin-Klassen (`tuple`, `set`, `list`, `dict`, `int`, `float`, `str`, `bytes`) allesamt gerade nicht an diese Regel. Das heißt aber nicht, dass man sie deshalb nicht anwenden sollte.
Antworten