Typinformation im sNamen - Pro/Contra

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.
Benutzeravatar
Michael Schneider
User
Beiträge: 566
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Typinformation im sNamen - Pro/Contra

Beitragvon Michael Schneider » Mittwoch 19. September 2007, 21:36

## Entstanden aus Thread "--->Kartenspiel programmieren<----"
## siehe http://www.python-forum.de/topic-12002.html


Hallo BlackJack!

BlackJack hat geschrieben:@Michael Schneider: Den Typ in einer dynamischen Sprache im Namen zu kodieren ist einfach irreführend und läuft dem "duck typing" zuwieder.

Das Prinzip des "duck typing" würde ich so interpretieren, dass der Typ eines Objekts egal ist, solange die benötigten Methoden und Variablen zur Verfügung stehen. Aber das betrachte ich nur an Schnittstellen als sinnvoll, wenn man Argumente empfängt. Wenn man nicht unbedingt eine Liste erwartet, kann man sich das auch offen halten, indem man das Naming für allgemeine Objekte verwendet (Cap-Case).
Trotzdem kann man den Typ eines ganz speziellen Objekts festlegen und den Namen in seinem Code entsprechend gestalten.

Und läuft ein Programm nach einem Patch nicht mehr, weil das Argument die inzwischen verwendete (Listen-)Funktion nicht unterstützt, dann war der l-Hinweis auch nicht umsonst.

BlackJack hat geschrieben:zu 2) Damit hast Du ein komplett anderes Objekt und damit auch die Bedeutung verändert. Das sollte sich natürlich auch im Namen wiederspiegeln.

Richtig. Und was meinst Du dann mit:
wenn man den Typ mal ändern sollte und nicht die Namen anpasst.

Wenn ich in meiner Funktionsdefinition durch das l-Präfix angebe, dass ich ein Objekt erwarte, welches die Listen-Schnittstelle unterstützt, dann kann das entsprechend "Duck Typing" auch jedes Objekt sein, das das gewährleistet.
Wie gesagt, wenn man sich nicht sicher ist, ob ein Wert vom Typ float oder int ist, muss man das Präfix nicht davorsetzen. Das ist für die Variablen bestimmt, die einen festgelegten Typ haben sollen.

BlackJack hat geschrieben:zu 3) Das eine nicht vorhandene Methode aufgerufen wird, kann man nur zur Laufzeit feststellen, egal wie der Name aussieht. Das findet man durch Tests, die man sowieso machen muss, weil kein Name garantiert das bei `lNamen` *wirklich* eine Liste an den Namen gebunden ist.

Das sieht man dann aber schon bei der Zuweisung, z.B.

Code: Alles auswählen

sNamen = sZeile.split()

Nochmal: es geht darum, einen neuen Code zu überfliegen und dort systematisch auf erste Fehler aufmerksam zu werden. Z.B. bei Anfragen hier im Forum, wo man nicht immer die Möglichkeit hat, gleich zu testen.

BlackJack hat geschrieben:Desweiteren will man das in der Regel auch gar nicht so festlegen. An `lKarten` kann man eine Liste binden. Wenn gar nicht die ganze `list()`-API benötigt wird, kann es aber günstiger sein ein Tupel, ein `set()` oder vielleicht ein Objekt vom Typ `Talon` oder `Hand` zu binden ohne das sich am Programm etwas ändert. Nur ist der Name `lKarten` dann "falsch", also irreführend.

Um den Umfang der genutzten Funktionalität sollte man sich vor dem Coden Gedanken machen. Denn ob eine Liste wirklich in jedem Fall später durch ein Tupel ersetzt werden kann, ist nicht immer vorauszusehen.
Auf die Gefahr hin, dass ich mich wiederhole: wenn man sich nicht festlegen will, braucht man das nicht zu tun!

BlackJack hat geschrieben:Was Du da so ein bisschen über Namenskonvention einführen willst, ist statische Typinformation.

Für mich ist das dasselbe, nur in grün. Von mir aus nennen wir es Typinformation.

BlackJack hat geschrieben:Mit den gleichen Argumenten wollen ja immer wieder Leute Python zu Java machen. "Aber dann kann man ja schon vorher Typfehler erkennen"…

Und genau so ist das auch. Warum sollte man Typfehler nur in Java vorher erkennen können? Python ist nicht dadurch Python, dass man den Typ nicht vom Namen ablesen kann, wenn man ihn schon kennt.

Für mich ist ein Code wesentlich besser lesbar, wenn ich mir nicht von diversen Namen den aktuellen Typ merken muss, sondern ihn am Namen ablesen kann.

Gruß,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

Re: Typinformation im sNamen - Pro/Contra

Beitragvon BlackJack » Mittwoch 19. September 2007, 22:28

Michael Schneider hat geschrieben:
BlackJack hat geschrieben:@Michael Schneider: Den Typ in einer dynamischen Sprache im Namen zu kodieren ist einfach irreführend und läuft dem "duck typing" zuwieder.

Das Prinzip des "duck typing" würde ich so interpretieren, dass der Typ eines Objekts egal ist, solange die benötigten Methoden und Variablen zur Verfügung stehen. Aber das betrachte ich nur an Schnittstellen als sinnvoll, wenn man Argumente empfängt. Wenn man nicht unbedingt eine Liste erwartet, kann man sich das auch offen halten, indem man das Naming für allgemeine Objekte verwendet (Cap-Case).


Du meinst kleinbuchstaben_mit_unterstrichen.

Trotzdem kann man den Typ eines ganz speziellen Objekts festlegen und den Namen in seinem Code entsprechend gestalten.

Und läuft ein Programm nach einem Patch nicht mehr, weil das Argument die inzwischen verwendete (Listen-)Funktion nicht unterstützt, dann war der l-Hinweis auch nicht umsonst.


Du legst damit keinen Typ fest. Man kann trotzdem weiterhin alle möglichen Typen an den Namen binden. Fehler findet man nur wenn man den Code testet.

BlackJack hat geschrieben:zu 2) Damit hast Du ein komplett anderes Objekt und damit auch die Bedeutung verändert. Das sollte sich natürlich auch im Namen wiederspiegeln.

Richtig. Und was meinst Du dann mit:
wenn man den Typ mal ändern sollte und nicht die Namen anpasst.

Wenn ich in meiner Funktionsdefinition durch das l-Präfix angebe, dass ich ein Objekt erwarte, welches die Listen-Schnittstelle unterstützt, dann kann das entsprechend "Duck Typing" auch jedes Objekt sein, das das gewährleistet.


Es kann in 99,9% der Fälle aber auch ein Objekt sein, dass das *nicht* gewährleistet. In den allermeisten Fällen reicht Indexzugriff oder auch nur "iterierbar sein" völlig aus. Mit dem 'l'-Präfix wird viel zu viel gefordert und damit läuft es dem Duck-Typing zuwieder. Wenn ich das 'l' sehe weiss ich nicht was *wirklich* erwartet wird.

Wie gesagt, wenn man sich nicht sicher ist, ob ein Wert vom Typ float oder int ist, muss man das Präfix nicht davorsetzen. Das ist für die Variablen bestimmt, die einen festgelegten Typ haben sollen.


Das sollen Variablen aber im allgemeinen nicht, sondern "nur" ein entsprechendes Verhalten.

BlackJack hat geschrieben:zu 3) Das eine nicht vorhandene Methode aufgerufen wird, kann man nur zur Laufzeit feststellen, egal wie der Name aussieht. Das findet man durch Tests, die man sowieso machen muss, weil kein Name garantiert das bei `lNamen` *wirklich* eine Liste an den Namen gebunden ist.

Das sieht man dann aber schon bei der Zuweisung, z.B.

Code: Alles auswählen

sNamen = sZeile.split()


Ich seh' das nicht. Das kann funktionierender Quelltext sein, der einfach nur die "falschen" Namen hat. Den "Fehler" kann man ja schliesslich in beide Richtungen machen. Man kann nicht nur "falsche" Objekte an "richtige" Namen binden, sondern auch den "richtigen" Objekten "falsche" Namen verpassen.

Nochmal: es geht darum, einen neuen Code zu überfliegen und dort systematisch auf erste Fehler aufmerksam zu werden. Z.B. bei Anfragen hier im Forum, wo man nicht immer die Möglichkeit hat, gleich zu testen.


Gerade bei solchem Quelltext würde ich nicht darauf vertrauen das die Namensgebung stimmt, oder vielleicht nicht noch von früheren Tests mit anderen Objekten die "falschen" Namen da stehen. Wenn man nicht testet, kann man nur raten.

BlackJack hat geschrieben:Desweiteren will man das in der Regel auch gar nicht so festlegen. An `lKarten` kann man eine Liste binden. Wenn gar nicht die ganze `list()`-API benötigt wird, kann es aber günstiger sein ein Tupel, ein `set()` oder vielleicht ein Objekt vom Typ `Talon` oder `Hand` zu binden ohne das sich am Programm etwas ändert. Nur ist der Name `lKarten` dann "falsch", also irreführend.

Um den Umfang der genutzten Funktionalität sollte man sich vor dem Coden Gedanken machen. Denn ob eine Liste wirklich in jedem Fall später durch ein Tupel ersetzt werden kann, ist nicht immer vorauszusehen.


Aber das später in jedem Fall die volle `list()`-API vorhanden soll, weisst Du immer? Einfach ein 'l' davor zu pappen ist auch eine Möglichkeit sich ums Gedanken machen zu drücken.

Auf die Gefahr hin, dass ich mich wiederhole: wenn man sich nicht festlegen will, braucht man das nicht zu tun!


Wenn Deine Fehlersuchmethode auch nur halbwegs funktionieren soll, muss man das überall tun wo's nur geht.

BlackJack hat geschrieben:Was Du da so ein bisschen über Namenskonvention einführen willst, ist statische Typinformation.

Für mich ist das dasselbe, nur in grün. Von mir aus nennen wir es Typinformation.


Du willst statische Typinformation und wunderst Dich das Gegenwind von "Pythonistas" kommt!?

BlackJack hat geschrieben:Mit den gleichen Argumenten wollen ja immer wieder Leute Python zu Java machen. "Aber dann kann man ja schon vorher Typfehler erkennen"…

Und genau so ist das auch. Warum sollte man Typfehler nur in Java vorher erkennen können? Python ist nicht dadurch Python, dass man den Typ nicht vom Namen ablesen kann, wenn man ihn schon kennt.


Wenn Du so in Typen denkst, dann willst Du nicht in Python programmieren. Python ist dadurch Python dass es nicht um den Typ von Objekten, sondern um das Verhalten von Objekten geht. Das ist "duck typing" und IMHO ein wesentlicher Bestandteil von Python.
EyDu
User
Beiträge: 4866
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Re: Typinformation im sNamen - Pro/Contra

Beitragvon EyDu » Mittwoch 19. September 2007, 22:49

Michael Schneider hat geschrieben:Für mich ist ein Code wesentlich besser lesbar, wenn ich mir nicht von diversen Namen den aktuellen Typ merken muss, sondern ihn am Namen ablesen kann.

Hier versuchst du den Namen mehr Informationen mitzugeben als eigentlich gedacht ist. Ein Name sollte beschreiben welche *Aufgabe* eine Variable/Methode/... erfüllt und nicht mehr. Dadurch weiss man fast automatisch, welche Schnittstelle benötigt wird. Welcher Typ dann letztendlich verwendet wird ist vollkommen egal, so lange er funktioniert (wie ja schon erwähnt: Duck Typing).

Außerdem ist die Wartung des Codes durch solche zusätzlichen, teilweise kryptischen, Zusätze sehr aufwändig. Falls sich am Typ doch etwas ändern sollte ist man erst man am Refactoring. Mir stellt sich ebenfalls die Frage, wie du die Notation bei eigenen Namen durchführst. Führst du Variablen welche eine Instanz von "Person" sind dann mit "pers" an, oder wie markierst du diese? Letztendlich muss man sich auch hierzu die Abkürzungen merken, was viel schwiriger ist als der Typ einer Variablen, vorallem da die Präfixe ja nicht standardisiert sind.

Michael Schneider hat geschrieben:Und genau so ist das auch. Warum sollte man Typfehler nur in Java vorher erkennen können? Python ist nicht dadurch Python, dass man den Typ nicht vom Namen ablesen kann, wenn man ihn schon kennt.

Typfehler, bzw. Schnittstellenfehler, findest du in Python mit automatischen Tests. In einer vernünftigen Anwendung musst du diese so oder so entwickeln und Typtests fallen dabei einfach mit ab.
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Donnerstag 20. September 2007, 00:28

Michael, vielleicht solltest du dir PEP 3117 ansehen, dort ist eben von genau von einer Typendeklaration die Rede. Zwar nicht als Prefix sondern als Postfix, aber vielleicht gefällt dir der PEP ja dennoch.

Ich persönlich finde aber die Ungarische Notation ebenso sinnlos wie meine Vorredner, es macht aber keinen Sinn für mich deren Argumente alle noch einmal zu wiederholen ;)
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
Michael Schneider
User
Beiträge: 566
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Beitragvon Michael Schneider » Donnerstag 20. September 2007, 08:28

Hallo allerseits!

BlackJack hat geschrieben:
Michael Schneider hat geschrieben:Wenn man nicht unbedingt eine Liste erwartet, kann man sich das auch offen halten, indem man das Naming für allgemeine Objekte verwendet (Cap-Case).


Du meinst kleinbuchstaben_mit_unterstrichen.

Nein, ich meine CamelCase, Entschuldigung.

BlackJack hat geschrieben:
Trotzdem kann man den Typ eines ganz speziellen Objekts festlegen und den Namen in seinem Code entsprechend gestalten.

Du legst damit keinen Typ fest. Man kann trotzdem weiterhin alle möglichen Typen an den Namen binden. Fehler findet man nur wenn man den Code testet.

Du legst den Typ fest im Sinne einer Definition. Wenn Du die weitere Funktionalität nur gewährleisten willst, wenn eine Liste gegeben ist, dann kannst Du das so implizit machen oder/und es explizit im Docstring angeben.

BlackJack hat geschrieben:
BlackJack hat geschrieben:zu 2) Damit hast Du ein komplett anderes Objekt und damit auch die Bedeutung verändert. Das sollte sich natürlich auch im Namen wiederspiegeln.

Richtig. Und was meinst Du dann mit:
wenn man den Typ mal ändern sollte und nicht die Namen anpasst.

Wenn ich in meiner Funktionsdefinition durch das l-Präfix angebe, dass ich ein Objekt erwarte, welches die Listen-Schnittstelle unterstützt, dann kann das entsprechend "Duck Typing" auch jedes Objekt sein, das das gewährleistet.


Es kann in 99,9% der Fälle aber auch ein Objekt sein, dass das *nicht* gewährleistet.

99,9% finde ich etwas übertrieben. Was meinst Du mit "kann sein"? Letztlich muss jeder selber wissen, was er für ein Objekt übergibt. Ich habe definiert, dass ich eine Liste erwarte und gehe nachfolgend davon aus, dass ich die damit verbundene Funktionalität zur Verfügung habe.

BlackJack hat geschrieben:In den allermeisten Fällen reicht Indexzugriff oder auch nur "iterierbar sein" völlig aus. Mit dem 'l'-Präfix wird viel zu viel gefordert und damit läuft es dem Duck-Typing zuwieder. Wenn ich das 'l' sehe weiss ich nicht was *wirklich* erwartet wird.

Wenn ich keine Liste erwarte, sondern ein iterierbares Objekt, dann schreibe ich auch kein l davor. Denn dann ist es mir egal, solange ich über die Elemente in einer for-Schleife iterieren kann.

BlackJack hat geschrieben:
Wie gesagt, wenn man sich nicht sicher ist, ob ein Wert vom Typ float oder int ist, muss man das Präfix nicht davorsetzen. Das ist für die Variablen bestimmt, die einen festgelegten Typ haben sollen.

Das sollen Variablen aber im allgemeinen nicht, sondern "nur" ein entsprechendes Verhalten.

Es ist ein Hinweis zugunsten der besseren Lesbarkeit. Ob die gewünschte Eigenschaft eines Integer Objekts nun vom Objekt hinter Referenz "iCounter" oder "Counter" nicht erfüllt wird, ist doch nebensächlich.
Von mir aus kann hinter iCounter jedes Objekt stehen, das die angedeutete Funktionalität erfüllt.

BlackJack hat geschrieben:
BlackJack hat geschrieben:zu 3) Das eine nicht vorhandene Methode aufgerufen wird, kann man nur zur Laufzeit feststellen, egal wie der Name aussieht. Das findet man durch Tests, die man sowieso machen muss, weil kein Name garantiert das bei `lNamen` *wirklich* eine Liste an den Namen gebunden ist.

Das sieht man dann aber schon bei der Zuweisung, z.B.

Code: Alles auswählen

sNamen = sZeile.split()

Ich seh' das nicht. Das kann funktionierender Quelltext sein, der einfach nur die "falschen" Namen hat.

Und wenn man sich auf die Info im Namen verlassen will, muss man genau da stutzig werden. Natürlich ist diese Zuweisung möglich, aber sie ist formal falsch, weil die Methode Split eben definitiv eine Liste zurückgibt und durch den Namen "sNamen" eine Fehlinterpretation auftreten kann.
Wenn man sich auf die Funktionalitätsandeutung durch Präfix einlässt, dann darf man diese Liste nicht dem Namen sNamen zuweisen.
Das ist genau dasselbe, als wenn ich sie dem Namen "Zufallszahl" zuordne, der ebenfalls eine Fehlinterpretation provoziert.

BlackJack hat geschrieben:
Nochmal: es geht darum, einen neuen Code zu überfliegen und dort systematisch auf erste Fehler aufmerksam zu werden. Z.B. bei Anfragen hier im Forum, wo man nicht immer die Möglichkeit hat, gleich zu testen.

Gerade bei solchem Quelltext würde ich nicht darauf vertrauen das die Namensgebung stimmt, oder vielleicht nicht noch von früheren Tests mit anderen Objekten die "falschen" Namen da stehen. Wenn man nicht testet, kann man nur raten.

Gerade diese Präfixe helfen dabei, systematisch vorgehen und alle Zuweisungen und Erwartungen auf Richtigkeit zu prüfen, da man sie getrennt bewerten kann.

BlackJack hat geschrieben:Aber das später in jedem Fall die volle `list()`-API vorhanden soll, weisst Du immer? Einfach ein 'l' davor zu pappen ist auch eine Möglichkeit sich ums Gedanken machen zu drücken.

Es ist eine Definition um einen Ausgangspunkt zu schaffen.

BlackJack hat geschrieben:
Auf die Gefahr hin, dass ich mich wiederhole: wenn man sich nicht festlegen will, braucht man das nicht zu tun!

Wenn Deine Fehlersuchmethode auch nur halbwegs funktionieren soll, muss man das überall tun wo's nur geht.

Eben, wo es geht. Und das ist da, wo ich weiß, dass bei einer Zuweisung eine Liste, ein Tupel oder sonst was zurückgegeben wird!

Die Methode ist nicht vorrangig zum Suchen von Fehlern gedacht, sondern zum besseren Verständnis, wenn man sich nur einen kleineren Teil des Codes betrachtet.
Und wenn Du einen Namen "lNamen" hast und sollst das 5. Element angeben, dann ist es doch zweifellos intuitiver, als wenn Du nicht weißt, dass Du Listenfunktionen anwenden kannst.

BlackJack hat geschrieben:
BlackJack hat geschrieben:Was Du da so ein bisschen über Namenskonvention einführen willst, ist statische Typinformation.

Für mich ist das dasselbe, nur in grün. Von mir aus nennen wir es Typinformation.

Du willst statische Typinformation und wunderst Dich das Gegenwind von "Pythonistas" kommt!?

Was ich möchte, ist, den Code besser lesbar zu machen, WO ES GEHT.

BlackJack hat geschrieben:Wenn Du so in Typen denkst, dann willst Du nicht in Python programmieren. Python ist dadurch Python dass es nicht um den Typ von Objekten, sondern um das Verhalten von Objekten geht. Das ist "duck typing" und IMHO ein wesentlicher Bestandteil von Python.

Ganz meiner Meinung. Ich habe das Gefühl, dass ihr mich irgendwie falsch versteht und meine Regel typabhängiger seht, als sie ist. Was ich beabsichtige ... steht da oben. ;-)

EyDu hat geschrieben:Außerdem ist die Wartung des Codes durch solche zusätzlichen, teilweise kryptischen, Zusätze sehr aufwändig. Falls sich am Typ doch etwas ändern sollte ist man erst man am Refactoring.

Stimmt, es könnte sich ein Mehraufwand ergeben. Trotzdem zahlt sich die Typandeutung gerade beim Bugfixing und bei der Wartung aus, weil das häufig andere Programmierer sind, die vielleicht dankbar dafür sind, dass sie Dictionaries, Tupel und Listen auf den ersten Blick erkennen können. Im Prinzip könnte ich die Namen auch mit Dict..., Tuple... oder List... anfangen lassen, was aber wesentlich länger wäre.

EyDu hat geschrieben:Mir stellt sich ebenfalls die Frage, wie du die Notation bei eigenen Namen durchführst. Führst du Variablen welche eine Instanz von "Person" sind dann mit "pers" an, oder wie markierst du diese? Letztendlich muss man sich auch hierzu die Abkürzungen merken, was viel schwiriger ist als der Typ einer Variablen, vorallem da die Präfixe ja nicht standardisiert sind.

Bei mir sind sie standardisiert und ändern sich auch nicht. Mit l, d, t, i, ... sind sie sogar intuitiv. Namen für sonstige Objekte (auch eigene) sind nach PEP 8 CamelCase.

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

Beitragvon BlackJack » Donnerstag 20. September 2007, 10:04

Michael Schneider hat geschrieben:
BlackJack hat geschrieben:
Michael Schneider hat geschrieben:Wenn man nicht unbedingt eine Liste erwartet, kann man sich das auch offen halten, indem man das Naming für allgemeine Objekte verwendet (Cap-Case).


Du meinst kleinbuchstaben_mit_unterstrichen.

Nein, ich meine CamelCase, Entschuldigung.


Du schwimmst gerne gegen den Strom, gell. ;-)

BlackJack hat geschrieben:
Trotzdem kann man den Typ eines ganz speziellen Objekts festlegen und den Namen in seinem Code entsprechend gestalten.

Du legst damit keinen Typ fest. Man kann trotzdem weiterhin alle möglichen Typen an den Namen binden. Fehler findet man nur wenn man den Code testet.

Du legst den Typ fest im Sinne einer Definition. Wenn Du die weitere Funktionalität nur gewährleisten willst, wenn eine Liste gegeben ist, dann kannst Du das so implizit machen oder/und es explizit im Docstring angeben.


Und was spricht dagegen es nur im Docstring zu machen? Wenn Du eine Funktion hast, bei der aus Doku und der Verwendung der Objekte nicht mehr ersichtlich ist, was da passiert, ist die Funktion zu komplex.

BlackJack hat geschrieben:Wenn ich in meiner Funktionsdefinition durch das l-Präfix angebe, dass ich ein Objekt erwarte, welches die Listen-Schnittstelle unterstützt, dann kann das entsprechend "Duck Typing" auch jedes Objekt sein, das das gewährleistet.


Es kann in 99,9% der Fälle aber auch ein Objekt sein, dass das *nicht* gewährleistet.

99,9% finde ich etwas übertrieben. Was meinst Du mit "kann sein"? Letztlich muss jeder selber wissen, was er für ein Objekt übergibt. Ich habe definiert, dass ich eine Liste erwarte und gehe nachfolgend davon aus, dass ich die damit verbundene Funktionalität zur Verfügung habe.


Eben, also kann ich eigentlich nur eine Liste übergeben weil Du ja nicht weiter dokumentieren wolltest, was von Listen verwendet wird und was nicht. Hättest Du das getan, bräuchte man das 'l' nicht und könnte sich sicherer sein das ein "iterable", eine Sequenz oder eine Abbildung auch möglich ist. Und dass das in Zukunft auch noch geht.

BlackJack hat geschrieben:In den allermeisten Fällen reicht Indexzugriff oder auch nur "iterierbar sein" völlig aus. Mit dem 'l'-Präfix wird viel zu viel gefordert und damit läuft es dem Duck-Typing zuwieder. Wenn ich das 'l' sehe weiss ich nicht was *wirklich* erwartet wird.

Wenn ich keine Liste erwarte, sondern ein iterierbares Objekt, dann schreibe ich auch kein l davor. Denn dann ist es mir egal, solange ich über die Elemente in einer for-Schleife iterieren kann.


Dein Quelltext sieht aber eher danach aus als wenn Du "type inference" per Hand betreibst und als Typen in den Namen kodierst, was immer sich in dem konkreten Fall aus den konkret verwendeten Typen ergibt. Ob deren API nun komplett verwendet wird oder nicht. Bei `create_order_list()` wird von `tAusgespielt` nur das "sequenze unpacking" verwendet, das muss also kein Tupel sein, sondern es geht jedes "iterable" der "Länge" zwei. Das kann das Ergebnis einer Generator-Funktion sein, eine Liste, sogar ein Dictionary mit zwei entsprechenden Schlüsseln. Oder eine komplett eigene Klasse.

BlackJack hat geschrieben:
Nochmal: es geht darum, einen neuen Code zu überfliegen und dort systematisch auf erste Fehler aufmerksam zu werden. Z.B. bei Anfragen hier im Forum, wo man nicht immer die Möglichkeit hat, gleich zu testen.

Gerade bei solchem Quelltext würde ich nicht darauf vertrauen das die Namensgebung stimmt, oder vielleicht nicht noch von früheren Tests mit anderen Objekten die "falschen" Namen da stehen. Wenn man nicht testet, kann man nur raten.

Gerade diese Präfixe helfen dabei, systematisch vorgehen und alle Zuweisungen und Erwartungen auf Richtigkeit zu prüfen, da man sie getrennt bewerten kann.


Dabei helfen die Präfixe überhaupt nicht. Nur wenn man wirklich statisch typt und *genau* die geforderten Objekte verwendet. Ansonsten muss man sich immer die Operationen im Detail anschauen. Oder eben einfach *testen*. Deine Argumente sind die gleichen die dauernd von Leuten kommen, die behaupten man würde mit statischer Typisierung weniger Fehler machen. Statische Typisierung ist mir schon zu umständlich und einschränkend, bei Deiner Namenskonvention muss man ja *noch* mehr selber machen weil hier die "Überprüfung" per Hand vorgenommen werden muss, was bei Java wenigstens noch der Compiler übernimmt.

Das Verhältnis von Aufwand und Nutzen stimmt aber überhaupt nicht. Ich bin von Java und Co "weg" weil mich das da stört und nun willst Du das noch umständlicher als Namenskonvention wieder einführen. Und das sieht, zumindest in der Python-Newsgroup die überwiegende Mehrzahl der Programmierer so.

Falls Du mit dieser Methode wirklich eine signifikante Anzahl von Fehlern findest oder vermeidest, dann ist Python nicht die geeignete Sprache für Dich.

BlackJack hat geschrieben:Aber das später in jedem Fall die volle `list()`-API vorhanden soll, weisst Du immer? Einfach ein 'l' davor zu pappen ist auch eine Möglichkeit sich ums Gedanken machen zu drücken.

Es ist eine Definition um einen Ausgangspunkt zu schaffen.


Aber den falschen, weil viel zu restriktiv.

BlackJack hat geschrieben:
Auf die Gefahr hin, dass ich mich wiederhole: wenn man sich nicht festlegen will, braucht man das nicht zu tun!

Wenn Deine Fehlersuchmethode auch nur halbwegs funktionieren soll, muss man das überall tun wo's nur geht.

Eben, wo es geht. Und das ist da, wo ich weiß, dass bei einer Zuweisung eine Liste, ein Tupel oder sonst was zurückgegeben wird!


Und dann ziehst Du diese Informationen weiter Funktionsargumente. Wenn sich jetzt eine der Zuweisungen ändert, müssen die betroffenen Funktionssignaturen und damit alle Namen in den Funktionen angepasst werden. Was eine Fehlerquelle darstellt. Juhuu.

Die Methode ist nicht vorrangig zum Suchen von Fehlern gedacht, sondern zum besseren Verständnis, wenn man sich nur einen kleineren Teil des Codes betrachtet.
Und wenn Du einen Namen "lNamen" hast und sollst das 5. Element angeben, dann ist es doch zweifellos intuitiver, als wenn Du nicht weißt, dass Du Listenfunktionen anwenden kannst.


Diese Aufgabenstellung ist mir bisher noch nicht untergekommen. Einen Namen komplett ohne Kontext hatte ich bisher noch nicht. Es gab immer eine Zuweisung und ich konnte nachsehen woher das Objekt kommt, oder eine Funktionssignatur, dann weiss ich aber selber was ich da erwarte.

BlackJack hat geschrieben:Du willst statische Typinformation und wunderst Dich das Gegenwind von "Pythonistas" kommt!?

Was ich möchte, ist, den Code besser lesbar zu machen, WO ES GEHT.


Und das machst Du IMHO mit den Typinformationen nicht. Beispiel:

Code: Alles auswählen

def einlesen(zeilen):
    result = list()
    for zeile in zeilen:
        namen = zeile.split()
        result.append(namen)
    return result

def einlesen(zeilen):
    l_result = list()
    for s_zeile in zeilen:
        l_namen = s_zeile.split()
        l_result.append(l_namen)
    return l_result


Die "Typinformationen" in der zweiten Variante bringen ausser mehr Tipparbeit nur Verwirrung. Und ob `s_zeile` stimmt ist auch fraglich weil `zeilen` ja auch ein "iterable" über Unicode-Objekte sein könnte. Oder ganz was anderes was eine entsprechende `split()`-Methode bietet! Duck typing eben.

EyDu hat geschrieben:Außerdem ist die Wartung des Codes durch solche zusätzlichen, teilweise kryptischen, Zusätze sehr aufwändig. Falls sich am Typ doch etwas ändern sollte ist man erst man am Refactoring.

Stimmt, es könnte sich ein Mehraufwand ergeben. Trotzdem zahlt sich die Typandeutung gerade beim Bugfixing und bei der Wartung aus, weil das häufig andere Programmierer sind, die vielleicht dankbar dafür sind, dass sie Dictionaries, Tupel und Listen auf den ersten Blick erkennen können.


Das können sie nicht. Wenn etwas nicht funktioniert, dann stimmen diese Typangaben nicht zwingend. Wenn man bei der Fehlersuche an irgendeiner Stelle wissen will was für ein Typ an einen Namen gebunden ist, kann man sich nicht blind auf Präfixe verlassen sondern muss ``print type(obj)`` bemühen. Das, plus Tracebacks, ist wesentlich einfacher als per Hand den Datenfluss anhand von vielleicht falschen Typpräfixen als "Trockenübung" nachzuvollziehen.

Bei mir sind sie standardisiert und ändern sich auch nicht. Mit l, d, t, i, ... sind sie sogar intuitiv. Namen für sonstige Objekte (auch eigene) sind nach PEP 8 CamelCase.


Du solltest PEP 8 nochmal lesen. `camelCase` wird da überhaupt nicht empfohlen und `CapCase` nur für Klassen.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Beitragvon mkesper » Donnerstag 20. September 2007, 15:31

BlackJack hat geschrieben:Beispiel:

Code: Alles auswählen

def einlesen(zeilen):
    result = list()
    for zeile in zeilen:
        namen = zeile.split()
        result.append(namen)
    return result

def einlesen(zeilen):
    l_result = list()
    for s_zeile in zeilen:
        l_namen = s_zeile.split()
        l_result.append(l_namen)
    return l_result


Die "Typinformationen" in der zweiten Variante bringen ausser mehr Tipparbeit nur Verwirrung.

Dem kann ich nur 100% zustimmen.
Und für Quellcode gilt: er wird öfter gelesen als geschrieben, muß also sehr leicht lesbar sein.
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Beitragvon poker » Freitag 21. September 2007, 00:07

@Michael Schneider
http://de.wikipedia.org/wiki/Ungarische_Notation#Kritik
Kritiker der ungarischen Notation führen an, die Präfixe seien lediglich eine Art Kommentar. Erfahrungsgemäß veralten Kommentare aber und werden nicht so gepflegt wie die tatsächliche Programmlogik, so daß Notation und tatsächliche Verwendung irgendwann nicht mehr zusammenpassen.

Weiterhin erschwere die Ansammlung von kryptischen Abkürzungen die Lesbarkeit des Codes. Insbesondere in objektorientiertem Code bringen die Präfixe keinen Informationsgewinn.

Die Ungarische Notation sei besonders vorteilhaft für nicht streng-typisierte Sprachen wie C, für die sie ursprünglich entwickelt wurde. Für moderne, objektorientierte Hochsprachen wie Java oder C# ist sie nicht mehr von so großer Bedeutung. Microsoft empfiehlt für das neue .NET-Framework, die ungarische Notation nicht mehr einzusetzen.


Dazu ist noch anzumerken, das Microsoft das Konzept der Ungarischen Notation damals fehlinterpretiert hat. Sie benutzten das damals um den Datentype mit im Namen zu codieren. Angedacht - vom Erfinder - war es um die Funktionalität mit im Namen zu codieren -- Also ob z.B. eine Laufvariable vorliegt. -- Beide Konzepte sind total sinnlos und führen nicht dazu, dass der Quelltext leserlicher wird.

Mein Fazit:
Es sieht beschissen aus, erhöht den Wartungsaufwand (UN => tatsächliche Einsatzweck) und führt das Konzept des ducktypings ad absurdum.

Und das Argument dass das hilfreich ist, um durch einen Quelltext durchzusteigen ist total :roll:... IMO sollte der jennige der am fremden Code mitarbeiten will gefälligst die Doku und Referenzen lesen, und mit den Maintainern korrespondieren oder einfach die Finger davon lassen!

mfg
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Beitragvon birkenfeld » Freitag 21. September 2007, 08:15

poker hat geschrieben:Dazu ist noch anzumerken, das Microsoft das Konzept der Ungarischen Notation damals fehlinterpretiert hat. Sie benutzten das damals um den Datentype mit im Namen zu codieren. Angedacht - vom Erfinder - war es um die Funktionalität mit im Namen zu codieren -- Also ob z.B. eine Laufvariable vorliegt. -- Beide Konzepte sind total sinnlos und führen nicht dazu, dass der Quelltext leserlicher wird.


Joel Spolsky ist anderer Meinung: http://www.joelonsoftware.com/articles/Wrong.html
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Freitag 21. September 2007, 11:52

birkenfeld hat geschrieben:Joel Spolsky ist anderer Meinung

Joel? Ist das nicht einer von Fefes Lieblingsfreunden?

Ich finde seine Lösung trotzdem ziemlich bescheuert, die Unsafe-String Sache schützt einen nicht vor Fehlern, da ist es besser eine Unsafe-String-Klasse die von Unicode erbt zu verwenden, die automatisch escaped, wenn man sie wie einen Unicode-String verwendet und mit über ``name.unsafe_content`` auf den unsicheren Inhalt zugreifen lässt. Das funktioniert dann *automatisch*, dazu braucht man dann niemanden der nach irgendwelchen ``us``-Prefixes sucht. ``us``-Prefixe muss man dann auch noch irgendwo Dokumentieren, denn jemand der es nicht weiß der muss sowieso nachschlagen. Oder aber er ignoriert sie weil es ja ganz normale Strings ist und baut hübsche Sicherheitslücken.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Beitragvon birkenfeld » Freitag 21. September 2007, 12:09

Ich mag ihn auch nicht besonders. Aber es ist immer interessant zu lesen, was Profis zu solchen Themen sagen.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Beitragvon poker » Freitag 21. September 2007, 18:42

birkenfeld hat geschrieben:Joel Spolsky ist anderer Meinung: http://www.joelonsoftware.com/articles/Wrong.html
Das darf er auch gerne sein. -- BTW: Ich könnte ja nun auch noch ein par andere Links dagegen halten, aber das wäre dann reichlich albern, oder(?)
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Beitragvon birkenfeld » Samstag 22. September 2007, 09:03

Kannst du natürlich gern. Ich dachte nur, nachdem er schon lange bei Microsoft gearbeitet hat, ist seine Erklärung vielleicht interessant.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder