Seite 1 von 1
Sollte object nach inplace Änderungen zurückgegeben werden?
Verfasst: Sonntag 8. Dezember 2013, 11:09
von mutetella
Hallo,
wenn ich ein dictionary innerhalb einer Funktion via `setdefault` verändere, welcher Weg ist "sauberer":
Code: Alles auswählen
def setdefaults(dict_, defaults):
for key, value in defaults.iteritems():
dict_.setdefault(key, value)
return dict_
>>> d = setdefaults(d, other_d)
oder
Code: Alles auswählen
def setdefaults(dict_, defaults):
for key, value in defaults.iteritems():
dict_.setdefault(key, value)
>>> setdefaults(d, other_d)
Ich bin mir bei solchen Sachen immer ein wenig unsicher, da es eigentlich überhaupt keine Rolle spielt, andererseits: 'Explicit is better than implicit.' Allerdings ist die explizite erste Variante doch irgendwie "doppelt gemobbelt", oder?
mutetella
Re: Sollte object nach inplace Änderungen zurückgegeben werd
Verfasst: Sonntag 8. Dezember 2013, 12:13
von BlackJack
@mutetella: Ich glaube ich würde als erstes mal nicht `setdefault()` verwenden, weil Du den Rückgabwert überhaupt nicht verwendest. Das wäre ein mir ein explizites ``if key not in dict_: dict_[key] = value`` irgendwie lieber. Ist wahrscheinlich Geschmackssache.
Ich würde es wahrscheinlich sowieso anders herum angehen: Ein Wörterbuch mit Default-Werten erstellen und dann das mit `update()` durch die Werte in `d` aktualisieren.
Zur eigentlichen Frage: Wenn eine Funktion vom Wesen her das ist was Pascal-Programmierer eine Prozedur nennen, dann würde ich nichts zurückgeben. Der Aufrufer *hat* das Objekt ja schon, warum sollte er es noch einmal zurückgegeben bekommen? Das macht nur Sinn wenn man APIs erstellen will in denen man den „train wreck style” verwenden kann, also auf Teufel komm raus elend lange Verkettungen von Funktions und Methodenaufrufen. Das dieser Stil im Python-Umfeld öfter mal so bezeichnet wird, deutet darauf hin das er nicht so beliebt ist. Also bei Python. In einigen anderen Sprachen ist so etwas durchaus gerne gesehen.
Re: Sollte object nach inplace Änderungen zurückgegeben werd
Verfasst: Sonntag 8. Dezember 2013, 16:02
von mutetella
BlackJack hat geschrieben:Ich würde es wahrscheinlich sowieso anders herum angehen: Ein Wörterbuch mit Default-Werten erstellen und dann das mit `update()` durch die Werte in `d` aktualisieren.
Ich habe ein dictionary mit defaults, die der Nutzer setzen kann aber nicht muss. Nicht gesetzte Werte werden durch hart kodierte ergänzt. Der komplette Weg sieht daher so aus:
Code: Alles auswählen
defaults = dict(stylesheet.get('defaults', {}))
defaults.setdefault('alignment', '<')
defaults.setdefault('ellipsis', '…')
defaults.setdefault(...)
...
element = dict(stylesheet['element'])
setdefaults(element, defaults)
`update` ergänzt nicht nur fehlende Werte, sondern überschreibt auch vorhandene. Das würde bedeuten, dass auch die Werte, die vom Nutzer gesetzt wurden, überschrieben werden.
BlackJack hat geschrieben:Der Aufrufer *hat* das Objekt ja schon, warum sollte er es noch einmal zurückgegeben bekommen?
Natürlich nicht empfehlenswert, aber letztlich müssen veränderliche Objekte nie zurückgegeben werden, da alle Namen, die darauf zeigen, immer auch auf jede Änderung zeigen. Mit anderen Worten sind veränderliche Objekte immer auch global vorhanden. Das hat mich schon nach meinen ersten Gehversuchen mit Python verwirrt. Inzwischen weiß ich damit umzugehen, aber "sauber" finde ich es nicht...
mutetella
Re: Sollte object nach inplace Änderungen zurückgegeben werd
Verfasst: Sonntag 8. Dezember 2013, 16:57
von BlackJack
@mutetella: Ich weiss wie `update()` arbeitet, darum hätte ich ja auch die Reihenfolge umgedreht, damit das gleiche Ergebnis heraus kommt.
Code: Alles auswählen
defaults = {'alignment': '<', 'ellipsis': '…'}
defaults.update(stylesheet.get('defaults', {}))
element = dict(defaults)
element.update(stylesheet['element'])
Mit der Aussage das veränderliche Objekte immer auch „global” vorhanden sind, hast Du *mich* jetzt verwirrt. Das sind sie natürlich nicht, sondern immer nur dort wo man sie auch hin übergeben hat. Und man sollte darauf vertrauen das sie nicht verändert werden solange das nicht klar aus dem Funktions-/Methodennamen oder der Dokumentation hervorgeht wo man das Objekt übergibt.
Veränderliche Objekte müssen natürlich auch zurückgegeben werden. Wenn man das *nie* machen würde, käme man nicht weit, denn solche Objekte würden ja auch lokal in Funktionen neu erzeugt werden können — und dann möchte man sie manchmal auch dem Aufrufer geben. Und man möchte sie sicher auch gerne mal in Datenstrukturen stecken — und da auch wieder heraus holen!
Wenn Du das generell unsauber findest, dann ist Python nicht die richtige Sprache für Dich weil Objekte mit veränderbarem Zustand in der Objektorientierung ziemlich zentral sind, und weil Python so gar nicht dafür bekannt ist dem Programmierer da irgendwelche Hürden in den Weg zu legen. Da wäre eine funktionale Programmiersprache die deutlich mehr Fokus auf unveränderliche Werttypen legt, vielleicht eher Dein Geschmack. Clojure zum Beispiel. Oder eventuell auch Rust.
In Python selbst kannst Du natürlich auch versuchen funktionaler zu Programmieren. Dann würde Deine `setdefault()`-Funktion zum Beispiel ein *neues* Wörterbuch zurück geben und die beiden Argumente unverändert lassen. Das habe ich letztlich ohne Funktion mit meinem `update()`-Beispiel weiter oben gemacht. Das kann man auch in eine (echte) Funktion verpacken:
Code: Alles auswählen
def updated(mapping_a, mapping_b):
result = dict(mapping_a)
result.update(mapping_b)
return result
Und der Beispielcode:
Code: Alles auswählen
defaults = updated(
{'alignment': '<', 'ellipsis': '…'}, stylesheet.get('defaults', {})
)
element = updated(defaults, stylesheet['element'])
Re: Sollte object nach inplace Änderungen zurückgegeben werd
Verfasst: Sonntag 8. Dezember 2013, 17:46
von mutetella
BlackJack hat geschrieben:Ich weiss wie `update()` arbeitet, ...
Ok, wenn ich eines auf gar keinen Fall wollte, dann Dich zu belehren! Wie denn auch?
BlackJack hat geschrieben:... darum hätte ich ja auch die Reihenfolge umgedreht, damit das gleiche Ergebnis heraus kommt.
Code: Alles auswählen
defaults = {'alignment': '<', 'ellipsis': '…'}
defaults.update(stylesheet.get('defaults', {}))
element = dict(defaults)
element.update(stylesheet['element'])
Oh Mann...

Kann man
BESCHEUERT eigentlich messen und wenn ja, wie
BESCHEUERT kann ein einzelner Mensch sein? Wenn mir das alles hier nicht solchen Spaß machen würde, ich sollte es besser sein lassen...
BlackJack hat geschrieben:Veränderliche Objekte müssen natürlich auch zurückgegeben werden. Wenn man das *nie* machen würde, käme man nicht weit, denn solche Objekte würden ja auch lokal in Funktionen neu erzeugt werden können — und dann möchte man sie manchmal auch dem Aufrufer geben. Und man möchte sie sicher auch gerne mal in Datenstrukturen stecken — und da auch wieder heraus holen!
Wenn ein solches Objekt innerhalb eines anderen Namensraumes erzeugt wird, muss man natürlich einen Namen zurückgeben, klar.
Nur das Objekt selbst ist ja nicht innerhalb oder außerhalb irgendeines Namensraumes vorhanden, sondern..., keine Ahnung wo. Ich übergebe und bekomme doch nur einen Namen (Zeiger finde ich fast noch treffender), oder? Und diesen Namen muss ich natürlich an eine Funktion übergeben, andernfalls hätte ich dort ja keinen Zugriff auf das Objekt. Und eben das verstehe ich offensichtlich nicht richtig, denn weshalb sollte man denn diesen Namen (der ja nur auf das veränderliche Objekt zeigt) zurückgeben, wenn doch bereits im aufrufenden Namensraum ein auf dasselbe Objekt zeigender Name vorhanden ist? Wenn man jetzt mal davon absieht, dass andernfalls Code nur sauschwer nachvollziehbar wäre, könnte man doch auf die Rückgabe von Namen, die auf veränderliche Objekte verweisen, verzichten, oder?
mutetella
Re: Sollte object nach inplace Änderungen zurückgegeben werd
Verfasst: Sonntag 8. Dezember 2013, 18:01
von snafu
@mutella: Man gibt keine Namen zurück, sondern Referenzen auf Objekte. Diese Referenzen werden bei Bedarf an Namen gebunden.
Re: Sollte object nach inplace Änderungen zurückgegeben werd
Verfasst: Montag 9. Dezember 2013, 09:22
von mutetella
@snafo: Gut, dann liegt zwischen Namen und Objekt noch eine weitere Schicht. Das ändert aber nichts an meiner Vorstellung, dass sich das Objekt über allen Namensräumen befindet und deshalb an einen Namensraum, innerhalb dessen ein Name auf dieses Objekt zeigt, nicht zurückgegeben werden muss bzw. nicht einmal zurückgegeben werden kann, weil es sich weder im aufrufenden noch im aufgerufenen Namensraum befindet.
Ich will ja nicht darauf herumreiten! Sollte meine Vorstellung zutreffen ist mir natürlich klar, dass man das aus gutem Grund so nicht macht...
mutetella
Re: Sollte object nach inplace Änderungen zurückgegeben werd
Verfasst: Montag 9. Dezember 2013, 09:35
von mutetella
Die
Python Doku schreibt:
"A scope defines the visibility of a name within a block."
So stelle ich mir das eben vor. Innerhalb eines Namensraumes befinden sich lediglich Referenzen auf Objekte. Diese befinden sich aber nie innerhalb dieses Raumes. Von daher ist eine Rückgabe, abgesehen von allen vernünftigen Gründen, es trotzdem zu tun, nicht nötig.
mutetella
Re: Sollte object nach inplace Änderungen zurückgegeben werd
Verfasst: Montag 9. Dezember 2013, 09:57
von BlackJack
@mutetella: Ich weiss nicht ob wir hier aneinander vorbeireden aber es *gibt keine* vernünftigen Gründe ein Objekt zurückzugeben welches man als Argument übergeben bekommen hat und in der Funktion oder Methode nur verändert. Eben weil das keinen Sinn macht. Deswegen gibt `list.sort()` zum Beispiel die Liste selbst nicht zurück und es gibt die `sorted()`-Funktion die eine neue sortierte Liste erstell und zurück gibt. Analog dazu habe ich ja die `updated()`-Funktion geschrieben. `sorted()` würde fast genau so aussehen, nur halt mit `list()` und `sort()` anstelle von `dict()` und `update()`.