2012-03-20 15 views
5

Por ejemplo, si tengo un dict de dicts o un dict de matrices pero solo deseo una copia "profunda" a una profundidad de dos niveles, ¿hay alguna manera fácil de hacerlo?En python, ¿cómo haré una copia profunda de un dict a una profundidad particular?

Estaba mirando a su alrededor para ver si había una biblioteca que podría usar o un ejemplo pero no pude encontrar nada. Soy bastante nuevo en Python o, de lo contrario, escribiría la subrutina para hacerlo yo mismo. ¿Algunas ideas? Los fragmentos de código se apreciarán, ya que sería más rápido para mí entender que solo una explicación de cómo hacerlo.

Gracias.

INFORMACIÓN ADICIONAL:

Algunos tienen preguntar por qué me gustaría hacer esto, necesito una copia (no es un ref como voy a modificar algunos de los valores y no quiero el original modificado) de algunos de los elementos de un diccionario pero el dict es enorme (muchos dict de dicts) y por lo que no tratan de destruir mi huella de memoria

CÓDIGO dE mI eN lO qUE VA

bien, me rindo arriba. Esto fue más difícil de lo que esperaba y no tengo tiempo para resolverlo. Mi último intento con un código de depuración/prueba.

# Deep copy any iteratable item to a max depth and defaults to removing the 
# rest. If you want to keep the stuff past max depth as references to orig 
# pass the argument else_ref=1. Ex: 
# dict_copy = copy_to_depth(dict_orig, 2, else_ref=1) 
def copy_to_depth(orig, depth, **kwargs): 
    copy = type(orig)() 
    for key in orig: 
    # Cannot find a reliable and consistent way to determine if the item 
    # is iterable. 
    #print orig[key].__class__ 
    #if hasattr(orig[key], '__iter__'): 
    #if hasattr(orig[key], '__contains__'): 
    #if iterable(orig[key]): 
    #try: 
    if hasattr(orig[key], '__contains__'): 
     if depth > 0: 
     copy[key] = copy_to_depth(orig[key], depth - 1, **kwargs) 
     else: 
     if 'else_ref' in kwargs: 
      copy[key] = orig[key] 
     else: 
      copy[key] = 'PAST_MAX_DPETH_ITERABLE_REMOVED' 
    #except: 
    else: 
     copy[key] = orig[key] 
    return copy 

def iterable(a): 
    try: 
     (x for x in a) 
     return True 
    except TypeError: 
     return False 

people = {'rebecca': 34, 'dave': 'NA', 'john': 18, 'arr': [9,8,{'a':1,'b':[1,2]}], 'lvl1': 
    {'arr': [9,8,{'a':1,'b':[1,2]}], 'dave': 'NA', 'john': 18, 'rebecca': 34, 'lvl2': 
    {'arr': [9,8,{'a':1,'b':[1,2]}], 'dave': 'NA', 'john': 18, 'rebecca': 34, 'lvl3': 
     {'rebecca': 34, 'dave': 'NA', 'john': 18, 'arr': [9,8,{'a':1,'b':[1,2]}]}}}} 
print people 


ppl_cpy = copy_to_depth(people, 1) 

ppl_cpy['arr'][1] = 'nine'     # does not mod orig 
ppl_cpy['john'] = 0     # does not mod orig 
ppl_cpy['lvl1']['john'] = 1   # does not mod orig b/c copy_to_depth 
ppl_cpy['arr'][3]['a'] = 'aie'  # does not mod orig 
#ppl_cpy['lvl1']['lvl2']['john'] = 2 # Rest cause an error 
#ppl_cpy['lvl1']['lvl2']['lvl3']['john'] = 3 
print people 
print ppl_cpy 

ppl_cpy = copy_to_depth(people, 1, else_ref=1) 
ppl_cpy['john'] = 0     # does not mod orig 
ppl_cpy['lvl1']['john'] = 1   # does not mod orig b/c copy_to_depth was 1 
ppl_cpy['lvl1']['lvl2']['john'] = 2 # Rest Do not cause error but modifies orig 
ppl_cpy['lvl1']['lvl2']['lvl3']['john'] = 3 
print people 
print ppl_cpy 

No se puede encontrar una manera confiable y consistente de determinar si el elemento es iterable. He estado leyendo this post y tratando de resolverlo, pero ninguna de las soluciones parecía funcionar para mi caso de prueba.

Copiaré todo el dict y trataré de optimizar la solución más tarde (o no).

Gracias ...

+3

¿Qué problema espera resolver al hacer esto? –

+1

¿has probado el obvio 'copy.deepcopy (x)' y 'pickle'? – zenpoy

+0

@zenpoy Miré copy.epcopy (x) pero no parecía poder limitar su copia a una profundidad particular. No pensé usar Pickle y no estoy seguro de cómo funcionaría, pero tu sugerencia me hizo pensar que tal vez podrías obtener una copia para hacer la copia, ya que te permite especificar una profundidad? Sin embargo, tengo que pensar cómo funcionaría eso. – stephenmm

Respuesta

3

esto suena un poco como 'PLZ me dan teh Codz' ...

En cualquier caso, se necesita un método personalizado a menos que realmente quiere cortar el funcionalidad de iterables con subclases. Pseudocódigo:

def copy_to_depth(original, depth) 
    copy = type(original)() 
    for item in original 
     if item is iterable and depth > 0 
      copy + copy_to_depth(item, depth - 1) 
     else 
      copy + item 
    return copy 
1

En realidad, el ejemplo anterior sería simplemente copiar cualquier diccionario como es, porque si nos quedamos sin profundidad, que acaba de copiar la parte restante directamente. La versión correcta sería:

def copy_to_depth(original, depth) 
    copy = type(original)() 
    for item in original 
     if item is iterable 
      if depth > 0 
       copy + copy_to_depth(item, depth - 1) 
     else 
      copy + item 
    return copy 

Hay una diferencia sutil. (Lamentablemente, no puedo comentar la respuesta en sí)

+0

Esto omitirá agregar el elemento todos juntos si es iterable pero ha alcanzado la profundidad máxima. Copiar cualquier diccionario anidado más allá de la profundidad máxima es exactamente lo que no queremos hacer. –

+0

pero omitir el elemento si alcanzamos la profundidad máxima es lo que queremos hacer, ¿verdad? Su código es básicamente equivalente a copy = original.copy(). –

+0

No, queremos copiar por ref, esencialmente, debajo de la profundidad máxima mientras copiamos por val arriba. –

Cuestiones relacionadas