Slicing & Copy

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.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Hallo zusammen,

ich habe mal eine Verständnisfrage zum Kopieren von Strings.

Wenn ich einen String kopiere möchte, so dachte ich, dass ich dieses per Slicing erledigen kann:
>>> a = "Text"
>>> b = a[:]
>>> a
'Text'
>>> b
'Text'
>>> id(a)
319552
>>> id(b)
319552
Nun erhalte ich aber die gleiche ID. Ist das nun eine Kopie oder nicht? Es scheint, als ob es wohl nur eine Quasi-Kopie mit einer Referenz ist.

Wenn ich das per Zuweisung mache, kommt das gleiche raus:
>>> a = "Text"
>>> b = a
>>> a
'Text'
>>> b
'Text'
>>> id(a)
319552
>>> id(b)
319552
Frage: Wie lege ich denn nun eine wirkliche Kopie an? Also, mit einer neuen ID?

CU,
API
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Da Slicing flache Kopien erstellt, suchst du wohl nach tiefen.

Dennoch würde ich nicht übermäßigen Gebrauch davon machen und mich lieber mit den Eigenheiten der Programmiersprache anfreunden.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Das soll jetzt was heissen?

Ist es eine Eigenart von Python keine Kopien (tief) von Strings anzulegen? Oder was meinst du damit?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Strings sind in Python *immutuable*; d.h. Du kannst sie nicht ändern. Jede String-Methode erzeugt also einen neuen String im Speicher. Nun überlege mal, wofür man also eine 1:1 Kopie eines Strings gebrauchen könnte?

@webspider: `copy` nützt Dir hier auch nichts:

Code: Alles auswählen

In [55]: import copy

In [56]: s = "Hallo Welt"

In [57]: s_copy = copy.copy(s)

In [58]: s is s_copy
Out[58]: True

In [59]: s_copy = copy.deepcopy(s)

In [60]: s is s_copy
Out[60]: True
Ist doch eigentlich nett, dass Python hier "optimiert" ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich frage mal anders: Wieso willst du von einem String eine Kopie anlegen? Zeichenketten sind in Python unveränderlich. Für welchen Einsatzzweck soll das also gut sein?

Die Frage nach der selben ID kann ich nicht beantworten, gehe aber stark davon aus, dass sie etwas mit der besagten Unveränderlichkeit zu tun hat.

Hier gibt's aber sicher jemanden, der das weiß. Einfach mal abwarten... ;)
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Ja danke schonmal - das war sehr hilfreich.

Also, liegt das wohl daran, dass Strings "immutable" sind...
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Hyperion hat geschrieben:Ist doch eigentlich nett, dass Python hier "optimiert" ;-)
Ja und wie :mrgreen:

Wie gesagt, mir hat diese Eigenart nur anfangs Probleme gemacht, erst mit Strings ("Es wäre doch besser wenn es nur einen Buchstaben verändert statt gleich einen neuen String zu erstellen!"), dann mit Listen. Sobald man lernt "pythonischer" zu denken, die alten Gewohnheiten hinter sich lässt und versteht wie intern optimiert wird, lernt man IMO Python erst richtig zu schätzen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Numbers sind übrigens auch immutable - einen Integerwert kann man auch nicht "kopieren" ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Nun ja, ich habe ja gar nicht gesagt, dass ich eine neue ID will - ich wollte es nur verstehen, warum Python sich so verhält :?:

Da Python hier wohl eigenständig Optimierung betreibt ist schon gut - man muss es halt nur wissen... :D
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

api hat geschrieben: Da Python hier wohl eigenständig Optimierung betreibt ist schon gut - man muss es halt nur wissen... :D
Hm... wo trifft Dich das denn in der Praxis? Die Anfängerfragen in diesem Forum mal als repräsentative Basis genommen, taucht diese Frage sehr selten auf - der andere Fall, nämlich das Kopieren von veränderbaren Objekten macht da viel mehr Probleme... und da Anfänger das alles meist eh nicht wissen, scheint das (Nicht-)Wissen über diesen Fall wohl keine große Relevanz zu haben ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

CPython gibt im [:] Falle einen Pointer auf den zu slicenden String zurück:

Code: Alles auswählen

if (i == 0 && j == Py_SIZE(a) && PyString_CheckExact(a)) {
  /* It's the same as a */
  Py_INCREF(a);
  return (PyObject *)a;
}
Daher die Objektidentität.

Urgh, die '&'s oben sind eigentlich '&', kA wie man das dem Codehighlighter beibringt.
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

In dem Zusammenhang übrigens auch ganz "interessant":

Code: Alles auswählen

>>> 'foo' is 'foo'
True
>>> 'foo' is ''.join('foo')
False
Wobei natürlich:

Code: Alles auswählen

>>> ''.join('foo')
'foo'
Auch ein Folgeaufruf von `id(''.join('foo'))` führt zu jeweils unterschiedlichen Ergebnissen, während ein simples `id('foo')` immer dieselbe ID zurückgibt. :o

Das ist zwar alles sehr strange und bestimmt auch kein garantiertes Verhalten, aber zumindest hätte man somit offenbar eine "echte" Kopie des Strings. Wie gesagt: Von der Verwendung in diesem Zusammenhang würde ich jedoch lieber abraten.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Sicherlich ist das mit der eigenen ID - bei einer Kopie - nichts, was man im täglichen Leben benötigt. Aber ich habe das mit der ID nunmal ausprobiert und mich anschließend gefragt, warum da immer die gleiche ID rauskommt.
Ich war eigentlich davon ausgegangen, dass es dann zwei unterschiedliche IDs gibt.
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hui, man kann das Verhalten von `str.join()` nichtmal so begründen, dass zusammengesetzte Strings etwas anderes sind als "normale" Strings, denn:

Code: Alles auswählen

>>> "foo" is "f" + "o" + "o"
True
BlackJack

Noch etwas zum Nachdenken über IDs:

Code: Alles auswählen

In [173]: id([1, 2]) == id([4, 5, 6])
Out[173]: True
Vielleicht ist es sofort klar was hier passiert, vielleicht muss der ein oder andere ein wenig darüber nachdenken. :-)
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hab hier noch sowas:

Code: Alles auswählen

>>> result = ""
>>> for char in "foo":
...     result += char
... 
>>> result is "foo"
False
>>> result
'foo'
>>> result = "foo" # Neuzuweisung
>>> result is "foo"
True
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Und weiter gehts:

Code: Alles auswählen

>>> a='abc'
>>> b='abc'
>>> a is b
True
>>> a='a bc'
>>> b='a bc'
>>> a is b
False
>>> a='abc'
>>> b='abc'
>>> a is b
True
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Gibt es da ein bestimmtes System hinter oder sollte man mit dem Vergleich auf Objektidentität wirklich sehr sehr vorsichtig umgehen? Es ist ja ohnehin klar, dass Vergleiche vornehmlich über den Wert gemacht werden sollten und nicht über die Identität, weil's halt etwas ganz anderes ist, was manchmal nur zufällig übereinstimmt.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

@snafu: Also bei ergibt das folgende Konstrukt aber ein "False"...
>>> "foo" is "f" + "o" + "o"
False
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

@snafu: Ich muss mich korrigieren. Es gibt Unterschiede zwischen Python 2.x und 3.x (ok, natürlich, aber ich meine diesen speziellen Fall jetzt...)

Python 2:
>>> "foo" is "f" + "o" + "o"
False
Python 3:
>>> "foo" is "f" + "o" + "o"
True
Es sieht so aus, als ob Python3 noch etwas mehr optimiert... :)
Antworten