Jak mogę skopiować ciąg znaków Pythona?

92

Robię to:

a = 'hello'

A teraz chcę mieć niezależną kopię a:

import copy

b = str(a)
c = a[:]
d = a + ''
e = copy.copy(a)

map( id, [ a,b,c,d,e ] )

Na zewnątrz [3]:

[4365576160, 4365576160, 4365576160, 4365576160, 4365576160]

Dlaczego wszystkie mają ten sam adres w pamięci i jak mogę uzyskać kopię a?

zwykły ja
źródło
3
Aby uzyskać odpowiedź inną niż Martijin (która jest całkowicie poprawna, chociaż niekoniecznie odpowiada na pytanie, jak podano), możesz podać więcej szczegółów / przypadku użycia, aby pokazać, dlaczego chcesz je skopiować.
elmo
4
Jak sugeruje @elemo, może to być problem XY .
martineau
2
Interesowało mnie oszacowanie wykorzystania pamięci zagnieżdżonego słownika w postaci d[ 'hello' ] = e, gdzie e[ 'hi' ] = 'again'. Aby wygenerować taki zagnieżdżony słownik, wygenerowałem pojedynczy esłownik i skopiowałem go wiele razy. Zauważyłem, że zużycie pamięci było bardzo niskie, co doprowadziło do mojego pytania. Teraz rozumiem, że nie utworzono żadnych kopii ciągów, stąd niskie zużycie pamięci.
zwykle ja
1
Jeśli chcesz bbyć zmodyfikowaną wersją abez modyfikowania a, po prostu pozwól bbyć wynikiem dowolnej operacji. np. b = a[2:-1]ustawia bsię 'll'i apozostaje ” hello'.
OJFord,
Ollie ma rację. Dzieje się tak, ponieważ str jest typem niezmiennym. Z powodu używania przez Pythona singletonów (i prawdopodobnie innych wewnętrznych optymalizacji), podczas kopiowania słownika e nie zobaczysz, że pamięć rozszerzy się tak, jak się spodziewasz.
FizxMike

Odpowiedzi:

137

Nie musisz kopiować ciągu znaków Pythona. Są niezmienne, a copymoduł zawsze zwraca oryginał w takich przypadkach, tak jak str()cały kawałek ciągu i łącząc go z pustym ciągiem.

Co więcej, twój 'hello'ciąg jest internowany ( niektóre ciągi są ). Python celowo stara się zachować tylko jedną kopię, ponieważ to przyspiesza przeszukiwanie słownika.

Jednym ze sposobów obejścia tego problemu jest utworzenie nowego ciągu, a następnie wycięcie go z powrotem do oryginalnej zawartości:

>>> a = 'hello'
>>> b = (a + '.')[:-1]
>>> id(a), id(b)
(4435312528, 4435312432)

Ale wszystko, co teraz robisz, to marnowanie pamięci. W końcu nie jest tak, że można w jakikolwiek sposób mutować te obiekty typu string.

Jeśli wszystko, co chciałeś wiedzieć, to ile pamięci wymaga obiekt Pythona, użyj sys.getsizeof(); daje ślad pamięci dowolnego obiektu Pythona.

W przypadku pojemników nie obejmuje to zawartości; musiałbyś powtórzyć w każdym kontenerze, aby obliczyć całkowity rozmiar pamięci:

>>> import sys
>>> a = 'hello'
>>> sys.getsizeof(a)
42
>>> b = {'foo': 'bar'}
>>> sys.getsizeof(b)
280
>>> sys.getsizeof(b) + sum(sys.getsizeof(k) + sys.getsizeof(v) for k, v in b.items())
360

Następnie możesz użyć id()śledzenia, aby pobrać rzeczywisty ślad pamięci lub oszacować maksymalny ślad, gdyby obiekty nie były buforowane i ponownie używane.

Martijn Pieters
źródło
4
Istnieje więcej niż tylko jeden sposób tworzenia nowego obiektu ciągu, takiego jak b = ''.join(a).
martineau
@martineau: jasne, naprawdę chciałem powiedzieć „w jedną stronę”.
Martijn Pieters
10
Nacisk na „Nie musisz kopiować ciągu znaków Pythona”. Istnieje powód, dla którego te operacje po prostu zwracają ten sam ciąg.
tcooc
1
W tym przypadku jednak OP próbuje marnować pamięć. Ponieważ chce wiedzieć, ile pamięci zostanie zużyte przez określoną liczbę strun, taki jest rzeczywisty cel. Oczywiście mógł wygenerować unikalne ciągi, ale to tylko niepotrzebna praca jako obejście.
Gabe,
8
+1 dla „od niechcenia” na przykładzie, który dałby wynik 42 .
Bakuriu,
11

Możesz skopiować ciąg w pythonie poprzez formatowanie ciągu:

>>> a = 'foo'  
>>> b = '%s' % a  
>>> id(a), id(b)  
(140595444686784, 140595444726400)  
Richard Urban
źródło
4
Nieprawda w Pythonie 3.6.5. id (a) i id (b) są identyczne. Wyniki nie różnią się od siebie, nawet gdy użyłem nowoczesnej wersji formatu, a mianowicieb = '{:s}'.format(a)
Seshadri R
7

Właśnie zaczynam manipulować strunami i znalazłem to pytanie. Prawdopodobnie próbowałem zrobić coś takiego jak OP, „zwykły ja”. Poprzednie odpowiedzi nie wyjaśniły mojego zmieszania, ale po zastanowieniu się nad tym w końcu „zrozumiałem”.

Dopóki a, b, c, d, i emają taką samą wartość, referencję one do tego samego miejsca. Pamięć została zapisana. Gdy tylko zmienna zacznie mieć różne wartości, zaczną mieć różne odniesienia. Moje doświadczenie w nauce pochodzi z tego kodu:

import copy
a = 'hello'
b = str(a)
c = a[:]
d = a + ''
e = copy.copy(a)

print map( id, [ a,b,c,d,e ] )

print a, b, c, d, e

e = a + 'something'
a = 'goodbye'
print map( id, [ a,b,c,d,e ] )
print a, b, c, d, e

Wydruk to:

[4538504992, 4538504992, 4538504992, 4538504992, 4538504992]

hello hello hello hello hello

[6113502048, 4538504992, 4538504992, 4538504992, 5570935808]

goodbye hello hello hello hello something
karl s
źródło
Więcej szczegółów dotyczących zachowania opisano w tym poście stackoverflow.com/questions/2123925/ ...
dlasalle
3

Kopiowanie łańcucha można wykonać na dwa sposoby: skopiuj lokalizację a = "a" b = a lub możesz sklonować, co oznacza, że ​​b nie zostanie zmieniony, gdy a zostanie zmienione, co jest wykonywane przez a = 'a' b = a [:]

Thomas Youngson
źródło
2

Inaczej mówiąc, "id ()" nie jest tym, na czym ci zależy. Chcesz wiedzieć, czy nazwę zmiennej można zmodyfikować bez szkody dla nazwy zmiennej źródłowej.

>>> a = 'hello'                                                                                                                                                                                                                                                                                        
>>> b = a[:]                                                                                                                                                                                                                                                                                           
>>> c = a                                                                                                                                                                                                                                                                                              
>>> b += ' world'                                                                                                                                                                                                                                                                                      
>>> c += ', bye'                                                                                                                                                                                                                                                                                       
>>> a                                                                                                                                                                                                                                                                                                  
'hello'                                                                                                                                                                                                                                                                                                
>>> b                                                                                                                                                                                                                                                                                                  
'hello world'                                                                                                                                                                                                                                                                                          
>>> c                                                                                                                                                                                                                                                                                                  
'hello, bye'                                                                                                                                                                                                                                                                                           

Jeśli jesteś przyzwyczajony do C, to są one jak zmienne wskaźnikowe, z wyjątkiem tego, że nie możesz ich usunąć, aby zmodyfikować to, na co wskazują, ale id () powie ci, gdzie aktualnie wskazują.

Problem dla programistów Pythona pojawia się, gdy weźmiesz pod uwagę głębsze struktury, takie jak listy lub dykty:

>>> o={'a': 10}                                                                                                                                                                                                                                                                                        
>>> x=o                                                                                                                                                                                                                                                                                                
>>> y=o.copy()                                                                                                                                                                                                                                                                                         
>>> x['a'] = 20                                                                                                                                                                                                                                                                                        
>>> y['a'] = 30                                                                                                                                                                                                                                                                                        
>>> o                                                                                                                                                                                                                                                                                                  
{'a': 20}                                                                                                                                                                                                                                                                                              
>>> x                                                                                                                                                                                                                                                                                                  
{'a': 20}                                                                                                                                                                                                                                                                                              
>>> y                                                                                                                                                                                                                                                                                                  
{'a': 30}                                                                                                                                                                                                                                                                                              

Tutaj o i x odnoszą się do tego samego dyktu o ['a'] i x ['a'], a ten dykt jest „zmienny” w tym sensie, że można zmienić wartość klucza „a”. Dlatego „y” musi być kopią, a y [„a”] może odnosić się do czegoś innego.

Charles Thayer
źródło