Как я могу скопировать строку Python?

92

Я делаю это:

a = 'hello'

А теперь мне просто нужна независимая копия a:

import copy

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

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

Из [3]:

[4365576160, 4365576160, 4365576160, 4365576160, 4365576160]

Почему все они имеют одинаковый адрес памяти и как получить копию a?

обычно я
источник
3
Чтобы получить ответ, отличный от ответа Мартиджина (который является полностью правильным, хотя и не обязательно отвечает на поставленный вопрос), вы можете предоставить более подробную информацию / вариант использования, чтобы показать, почему вы хотите его скопировать.
elmo
4
Как предполагает @elemo, это может быть проблема XY .
Мартино
2
Мне было интересно оценить использование памяти вложенным словарем формы d[ 'hello' ] = e, где e[ 'hi' ] = 'again'. Чтобы создать такой вложенный словарь, я создал один eсловарь и скопировал его несколько раз. Я заметил, что потребление памяти было очень низким, что привело к моему вопросу здесь. Теперь я понимаю, что копий строк не создавалось, отсюда и низкое потребление памяти.
обычно я
1
Если вы хотите bбыть модифицированной версией aбез изменений a, просто позвольте bбыть результатом любой операции. например , b = a[2:-1]наборы bдля 'll'и aостается " hello'.
OJFord
Олли прав. Это потому, что str - неизменяемый тип. Из-за того, что Python использует синглтоны (и, возможно, другие внутренние оптимизации), вы не увидите расширения памяти, как вы ожидаете, при копировании электронного словаря.
FizxMike

Ответы:

137

Вам не нужно копировать строку Python. Они неизменяемы, и copyв таких случаях модуль всегда возвращает оригинал, как и str()весь фрагмент строки и конкатенацию с пустой строкой.

Более того, ваша 'hello'строка интернирована ( некоторые строки есть ). Python намеренно пытается сохранить только одну копию, так как это ускоряет поиск по словарю.

Один из способов обойти это - создать новую строку, а затем нарезать ее обратно до исходного содержимого:

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

Но все, что вы делаете сейчас, это тратите память. В конце концов, вы не можете каким-либо образом изменить эти строковые объекты.

Если все, что вам нужно знать, это сколько памяти требуется объекту Python, используйте sys.getsizeof(); он дает вам объем памяти любого объекта Python.

Для контейнеров это не включает содержимое; вам нужно будет пройти рекурсию в каждый контейнер, чтобы вычислить общий размер памяти:

>>> 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

Затем вы можете выбрать использование id()отслеживания для получения фактического объема памяти или для оценки максимального объема памяти, если объекты не были кэшированы и повторно использованы.

Мартейн Питерс
источник
4
Есть несколько способов создать новый строковый объект, например b = ''.join(a).
Мартино
@martineau: конечно, я действительно хотел сказать «в одну сторону».
Martijn Pieters
10
Акцент на «Вам не нужно копировать строку Python». Есть причина, по которой эти операции просто возвращают одну и ту же строку.
tcooc
1
Однако в этом случае OP пытается тратить впустую память. Так как он хочет знать, сколько памяти будет использовано определенным количеством строк, это и есть настоящая цель. Очевидно, он мог генерировать уникальные строки, но это просто ненужная работа в качестве обходного пути.
Гейб
8
+1 для «случайно», используя пример, который выведет 42 .
Bakuriu
11

Вы можете скопировать строку в Python с помощью форматирования строки:

>>> a = 'foo'  
>>> b = '%s' % a  
>>> id(a), id(b)  
(140595444686784, 140595444726400)  
Ричард Урбан
источник
4
Неправда в Python 3.6.5. id (a) и id (b) идентичны. Результаты ничем не отличаются, даже когда я использовал современную версию формата, а именно,b = '{:s}'.format(a)
Сешадри Р.
7

Я только начинаю манипуляции со строками и нашел этот вопрос. Я, наверное, пытался сделать что-то вроде OP, «обычный я». Предыдущие ответы не прояснили мое замешательство, но, немного подумав, я, наконец, "понял".

До тех пор , как a, b, c, dи eимеют такое же значение, они ссылаются на то же место. Память сохранена. Как только переменные начинают иметь разные значения, они начинают иметь разные ссылки. Мой опыт обучения исходил из этого кода:

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

Распечатанный результат:

[4538504992, 4538504992, 4538504992, 4538504992, 4538504992]

hello hello hello hello hello

[6113502048, 4538504992, 4538504992, 4538504992, 5570935808]

goodbye hello hello hello hello something
Карл с
источник
Более подробная информация о поведении описана в этой публикации stackoverflow.com/questions/2123925/…
dlasalle 01
3

Копирование строки может быть выполнено двумя способами: либо скопировать местоположение a = "a" b = a, либо вы можете клонировать, что означает, что b не пострадает при изменении a, что выполняется a = 'a' b = a [:]

Томас Янгсон
источник
2

Другими словами, id () - это не то, что вас волнует. Вы хотите знать, можно ли изменить имя переменной без ущерба для имени исходной переменной.

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

Если вы привыкли к C, то они похожи на переменные-указатели, за исключением того, что вы не можете отменить ссылку на них, чтобы изменить то, на что они указывают, но id () сообщит вам, где они в настоящее время указывают.

Проблема для программистов на Python возникает, когда вы рассматриваете более глубокие структуры, такие как списки или dicts:

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

Здесь o и x относятся к одному и тому же dict o ['a'] и x ['a'], и этот dict является "изменяемым" в том смысле, что вы можете изменить значение для ключа 'a'. Вот почему «y» должно быть копией, а y ['a'] может относиться к чему-то еще.

Чарльз Тайер
источник