Pass by value erzwingen?

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.
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Pass by value erzwingen?

Beitragvon Barabbas » Mittwoch 13. Mai 2009, 07:59

Hallo zusammen,

da in Python alles via Referenz auf ein Objekt übergeben wird, scheint es mir etwas problematisch von "pass-by-value" und "pass-by-reference" zu sprechen. Was ich möchte ist aber folgendes: Ich möchte in einer best. Funktion *immer* die Referenz auf eine Kopie des Ursprungsobjektes erhalten.

Momentan prüfe ich, ob ein Objekt bereits in einer Liste von Objekten ist und erstelle in diesem fall mit copy.deepcopy() eine Kopie davon. Das erscheint mir aber etwas unsauber - gibt es da kein Hausmittel?

lG

Daniel
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Beitragvon Darii » Mittwoch 13. Mai 2009, 08:36

Wenn du immer eine Kopie haben willst, warum musst du dann erst in irgendwelchen Liste suchen? Das ist etwas anders als *immer*. Am besten du postet mal einen Beispielcode oder beschreibst besser was du möchtest.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Beitragvon Dill » Mittwoch 13. Mai 2009, 08:39

der sinn eines call by value ist ja meist nicht, dass die funktion eine kopie erhält die sie behalten und damit jeglichen unsinn anstellen darf, ohne dass der ursprungswert verändert wird. es geht dabei meist um performanacevorteile. in python könnte man die eh nicht erreichen, daher macht cbv hier kein sinn.

ich tippe daher mal, dass es keine andere möglichkeit gibt als eine manuelle kopie zu erstellen.
Zuletzt geändert von Dill am Mittwoch 13. Mai 2009, 08:42, insgesamt 1-mal geändert.
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 » Mittwoch 13. Mai 2009, 08:40

Viele Leute beklagen sich, dass Python "call by value" so "schwer" macht -- und stellen dann fest, dass man diese Semantik eigentlich nie braucht.

Möglicherweise lässt sich dein Problem also auch auf eine viel einfachere Art und Weise lösen...
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Beitragvon Barabbas » Mittwoch 13. Mai 2009, 09:07

Hallo,

danke schonmal für eure Antworten. Anscheinend muss ich das wirklich mit deepcopy() machen.
Wer dennoch interessiertist, kann sich gerne die ausführliche Beschreibung antun (Achtung, ich muss da etwas auf dem Unterschied zwischen "dem gleichen" - und "dem selben" rumreiten ;).

Es sollen Lieder aus den Charts ausgelesen und zu meiner Wiedergabeliste hinzugefügt werden. In Pseudocode

Code: Alles auswählen

charts = get_charts_list()
tracks = get_track_objects_from_list(charts)

for track_id in charts:
    for track in tracks:
        if track_id == track.id:
            add_track_to_playlist(track)


Soweit so klar: Auf Grund einer Designentscheidung werden immer nur IDs von Liedern gespeichert. Die dazugehörigen Track-Objekte, die ich benötige, werden nach einem db-query erstellt. Diese sind aber erstmal nicht in der Reihenfolge der Liste (was bei Charts doof ist), also wird mit den verschränkten Schleifen nachsortiert.
Wenn nun aber in der Liste charts das gleiche Lied (also die gleiche ID) mehrfach auftaucht, wird immer die selbe Objektreferenz an die Playliste übergeben. Hier sitzt das eigentliche Problem: Dadurch, dass in den Charts das *gleiche* Lied mehrfach auftaucht, wird immer das *selbe* Objekt an die Playlist übergeben.
Hier kann ich aber nicht nachbessern, da ich diesen Vorgang soweit recht intuitiv finde und mir die Plugin-Schnittstelle nicht verhunzen will.

Da es nun durchaus passieren kann, dass ein Lied mehrfach in die Playlist gepackt wird, wird zusätzlich zur ID (die unterschiedliche Lieder voneinander unterscheidet), eine UID generiert (die auch für gleiche Lieder unterschiedlich ist) und als Attribut im Track-Objekt gespeichert.
Über diese UID erkennt mein Programm, welches Lied gerade abgespielt wird und welches z.B. das nächste ist.

Dumm natürlich, wenn zwei *gleiche* Lieder durch die *selbe* Objektreferenz repräsentiert werden: Dann haben automatisch alle gleichen Lieder die selbe UID und mein Programm kommt völlig durcheinander, wenn es darum geht, die Position des fraglichen Liedes in der Playlist zu bestimmen.

Meine Lösung also: Wenn ein Track-Objekt zur Playlist hinzugefügt wird, teste ich, ob das *selbe* Objekt bereits in der Liste ist und erstelle ggf. eine Kopie davon. Natürlich könnte ich auch einfach jedes Objekt kopieren, aber da die Kopie in der add_track_to_playlist() Funktion angefertigt wird, kann man ja auch gleich nachschauen, ob eine Kopie überhaupt nötig ist.

lG

brb
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Beitragvon mkesper » Mittwoch 13. Mai 2009, 09:35

Um in einer Liste zu erkennen, was der nächste Eintrag ist, würden mir jetzt irgendwie noch andere Wege einfallen. Ich verstehe das Konzept leider noch nicht ganz.
BlackJack

Beitragvon BlackJack » Mittwoch 13. Mai 2009, 10:26

Ich finde es auch ein wenig komisch. Ich würde eher erwarten, dass sich die Playlist die aktuelle Position merkt, dann könnte da auch 100 mal das selbe Objekt drin stehen. Die Notwendigkeit Objekte zu "klonen" ist IMHO (bei Python) ein "design smell".
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Beitragvon Barabbas » Mittwoch 13. Mai 2009, 11:24

Naja, da mögt ihr recht haben, aber das Programm ist ja nicht - wie hier beschrieben - 10 Zeilen lang sondern 15.000.
Im konkreten Fall fand ich es sinnvoll, dass jedes Lied in der Playlist auch durch ein eigenes Lied-Objekt repräsentiert wird - und nicht durch zwei Referenzen auf das selbe Objekt. Egal, ich diskutiere hier gerade am PC ganze Zeit mit mir selbst die Vor- und Nachteile meines Aufbaus, bin mir der Probleme bewusst und glaube doch, dass das Design doch nicht völlig verhunzt ist.

Eigentlich hat sich die Frage auch erübrigt: Ich kann mir nicht vorstellen, dass es eine Möglichkeit gibt, im Kopf einer Methode festzulegen, dass ein bestimmter Parameter bitte die Referenz auf eine Kopie des via Referenz übergebenen Objektes sein solle - das wäre doch etwas weit her geholt.

Danke für eure Mühe, insbesondere für die Kritik ;).

Daniel
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Mittwoch 13. Mai 2009, 12:12

Dill hat geschrieben:der sinn eines call by value ist ja meist nicht, dass die funktion eine kopie erhält die sie behalten und damit jeglichen unsinn anstellen darf, ohne dass der ursprungswert verändert wird. es geht dabei meist um performanacevorteile.

Was sollte denn Call-by-value für Performancevorteile haben? Letztendlich muss dazu bei jedem Aufruf der Funktion der Parameter kopiert werden, bei Call-by-reference muss man nur eine sowieso schon existierende Referenz an die Funktion weitergeben.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Beitragvon Dill » Mittwoch 13. Mai 2009, 12:33

der vorteil ist, dass bei cbv der funktion die variable auf den stack gelegt wird.
BlackJack

Beitragvon BlackJack » Mittwoch 13. Mai 2009, 12:55

In wie weit ist das jetzt ein Vorteil?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Mittwoch 13. Mai 2009, 19:25

Hier muss ein Missverständnis vorliegen. Python benutzt ausschließlich "call-by-value". Die Werte sind allerdings Referenzen (aka Zeiger) auf Objekte. Oder anders ausgedrückt, die Objektdaten selbst werden nicht dupliziert für einen Funktionsaufruf. Dies ist das normale Verhalten. C und C++ verhalten sich hier ungewöhnlich, indem sie implizit Strukturen kopieren.

"call-by-reference" ist AFAIK so definiert:

Code: Alles auswählen

def a(x): x = 1
y = 0
a(y)
assert y == 1

Dies gilt NICHT in Python. Als dritten im Bunde gibt es übrigens noch "call-by-name", den kann Python auch nicht. Ruby oder Smalltalk haben auch nur CbV. Eine Sprache, die alles kann wäre z.B. Algol-68. Pascal und C# fallen mir für CbR und CbV ein. Scala hat CbV und CbN.

Stefan
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

Beitragvon BlackVivi » Mittwoch 13. Mai 2009, 19:31

sma hat geschrieben:Hier muss ein Missverständnis vorliegen. Python benutzt ausschließlich "call-by-value". Die Werte sind allerdings Referenzen (aka Zeiger) auf Objekte. Oder anders ausgedrückt, die Objektdaten selbst werden nicht dupliziert für einen Funktionsaufruf. Dies ist das normale Verhalten. C und C++ verhalten sich hier ungewöhnlich, indem sie implizit Strukturen kopieren.

"call-by-reference" ist AFAIK so definiert:

Code: Alles auswählen

def a(x): x = 1
y = 0
a(y)
assert y == 1

Dies gilt NICHT in Python. Als dritten im Bunde gibt es übrigens noch "call-by-name", den kann Python auch nicht. Ruby oder Smalltalk haben auch nur CbV. Eine Sprache, die alles kann wäre z.B. Algol-68. Pascal und C# fallen mir für CbR und CbV ein. Scala hat CbV und CbN.

Stefan


Code: Alles auswählen

>>> def f(x): x.append("foo")
...
>>> y = []
>>> f(y)
>>> f(y)
>>> y
['foo', 'foo']
>>> f(y)
>>> f(y)
>>> y
['foo', 'foo', 'foo', 'foo']


=DD

...Nun denn, unrecht hat Stefan trotzdem nicht. Finde eh, dass man eher nebeneffektsfreie Funktionen schreiben sollte, wenn es geht.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Mittwoch 13. Mai 2009, 19:35

BlackVivi, dein Beispiel zeigt etwas GANZ ANDERES. Die entscheidende Zeile für CvR ist ``y = 0``. Du weist in deinem Beispiel `y` in der Funktion niemals etwas anderes zu. `y` hält vor und nach dem Funktionsauf das SELBE Objekt. Die Variable hat sich NICHT verändert, nur das Objekt, aber das ist EGAL. Das R von CvR bezieht sich darauf, dass eine REFERENZ auf die Variable übergeben wird.

Stefan
BlackJack

Beitragvon BlackJack » Mittwoch 13. Mai 2009, 20:00

Au ja, schon wieder *die* Diskussion. Call-by-value ist es in Python nicht, weil die Referenzen keine Werte sind. Es gibt nicht den Datentyp "Referenz" in Python. Die Bezeichnung bei Python ist call-by-sharing oder call-by-object.

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder