¿Cómo puedo copiar una cadena de Python?

92

Hago esto:

a = 'hello'

Y ahora solo quiero una copia independiente de a :

import copy

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

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

Fuera [3]:

[4365576160, 4365576160, 4365576160, 4365576160, 4365576160]

¿Por qué todos tienen la misma dirección de memoria y cómo puedo obtener una copia a?

yo habitual
fuente
3
Para obtener una respuesta diferente a la de Martijin (que es completamente correcta, aunque no necesariamente responde a la pregunta como se indica), es posible que desee proporcionar más detalles / casos de uso para mostrar por qué desea que se copie.
elmo
4
Como indica @elemo, esto podría ser un problema XY .
martineau
2
Estaba interesado en estimar el uso de memoria de un diccionario anidado de la forma d[ 'hello' ] = e, donde e[ 'hi' ] = 'again'. Para generar un diccionario anidado, generé un solo ediccionario y lo copié varias veces. Noté que el consumo de memoria era muy bajo, lo que llevó a mi pregunta aquí. Ahora entiendo que no se crearon copias de cadena, de ahí el bajo consumo de memoria.
yo habitual
1
Si quieres bser una versión modificada de asin modificar a, deja que bsea ​​el resultado de cualquier operación. por ejemplo, se b = a[2:-1]establece ben 'll'y apermanece ' hello'.
OJFord
Ollie tiene razón. Esto se debe a que str es un tipo inmutable. Debido al uso de python de singletons (y probablemente otras optimizaciones internas), no verá la memoria expandirse como espera al copiar el diccionario e.
FizxMike

Respuestas:

137

No necesitas copiar una cadena de Python. Son inmutables, y el copymódulo siempre devuelve el original en tales casos, al igual que str()el segmento de cadena completo y se concatena con una cadena vacía.

Además, tu 'hello'cuerda está internada ( ciertas cuerdas están ). Python intenta deliberadamente mantener una sola copia, ya que eso hace que las búsquedas en el diccionario sean más rápidas.

Una forma de solucionar esto es crear una nueva cadena y luego volver a cortar esa cadena al contenido original:

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

Pero todo lo que estás haciendo ahora es desperdiciar memoria. Después de todo, no es como si pudiera mutar estos objetos de cadena de alguna manera.

Si todo lo que quería saber es cuánta memoria requiere un objeto Python, use sys.getsizeof() ; le da la huella de memoria de cualquier objeto de Python.

Para contenedores, esto no incluye el contenido; tendría que recurrir a cada contenedor para calcular un tamaño total de memoria:

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

Luego, puede optar por utilizar el id()seguimiento para tomar una huella de memoria real o para estimar una huella máxima si los objetos no se almacenaron en caché ni se reutilizaron.

Martijn Pieters
fuente
4
Hay más de una forma de crear un nuevo objeto de cadena, como b = ''.join(a).
Martineau
@martineau: seguro, realmente quería decir 'unidireccional'.
Martijn Pieters
10
Énfasis en "No es necesario copiar una cadena de Python". Hay una razón por la que esas operaciones simplemente devuelven la misma cadena.
tcooc
1
En este caso, sin embargo, el OP está intentando desperdiciar memoria. Dado que quiere saber cuánta memoria utilizará una cierta cantidad de cadenas, ese es el objetivo real. Obviamente, podría generar cadenas únicas, pero eso es un trabajo innecesario como solución.
Gabe
8
+1 para "casualmente" usando un ejemplo que daría como resultado 42 .
Bakuriu
11

Puede copiar una cadena en Python a través del formato de cadena:

>>> a = 'foo'  
>>> b = '%s' % a  
>>> id(a), id(b)  
(140595444686784, 140595444726400)  
Richard Urban
fuente
4
No es cierto en Python 3.6.5. id (a) e id (b) son idénticos. Los resultados no son diferentes incluso cuando utilicé la versión moderna del formato, a saber,b = '{:s}'.format(a)
Seshadri R
7

Acabo de comenzar algunas manipulaciones de cadenas y encontré esta pregunta. Probablemente estaba tratando de hacer algo como el OP, "yo habitual". Las respuestas anteriores no aclararon mi confusión, pero después de pensar un poco al respecto finalmente "lo entendí".

Mientras a, b, c, d, y etienen el mismo valor, los que hacen referencia al mismo lugar. La memoria está guardada. Tan pronto como la variable comienza a tener diferentes valores, comienzan a tener diferentes referencias. Mi experiencia de aprendizaje provino de este código:

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

La salida impresa es:

[4538504992, 4538504992, 4538504992, 4538504992, 4538504992]

hello hello hello hello hello

[6113502048, 4538504992, 4538504992, 4538504992, 5570935808]

goodbye hello hello hello hello something
karl s
fuente
Más detalles del comportamiento se describen en esta publicación stackoverflow.com/questions/2123925/…
dlasalle
3

La copia de una cadena se puede hacer de dos maneras, ya sea copiando la ubicación a = "a" b = a o puede clonar, lo que significa que b no se verá afectado cuando se cambie a, lo cual se hace mediante a = 'a' b = a [:]

Thomas Youngson
fuente
2

Para decirlo de otra manera, "id ()" no es lo que te importa. Desea saber si el nombre de la variable se puede modificar sin dañar el nombre de la variable de origen.

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

Si está acostumbrado a C, entonces estas son como variables de puntero, excepto que no puede des-referenciarlas para modificar a qué apuntan, pero id () le dirá dónde apuntan actualmente.

El problema para los programadores de Python surge cuando considera estructuras más profundas como listas o dictados:

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

Aquí oyx se refieren al mismo dict o ['a'] yx ['a'], y ese dict es "mutable" en el sentido de que puede cambiar el valor de la clave 'a'. Es por eso que "y" debe ser una copia y y ['a'] puede referirse a otra cosa.

Charles Thayer
fuente