¿La forma más rápida de convertir las claves y valores de un dictado de `unicode` a` str`?

81

Recibo un dictado de una "capa" de código sobre el cual se realizan algunos cálculos / modificaciones antes de pasarlo a otra "capa". Las claves y los valores de "cadena" del unicodedictado original lo son , pero la capa a la que se pasan solo acepta str.

Esto se llamará a menudo, así que me gustaría saber cuál sería la forma más rápida de convertir algo como:

{ u'spam': u'eggs', u'foo': True, u'bar': { u'baz': 97 } }

...a:

{ 'spam': 'eggs', 'foo': True, 'bar': { 'baz': 97 } }

... teniendo en cuenta que los valores que no son de "cadena" deben permanecer como su tipo original.

¿Alguna idea?

Phillip B. Oldham
fuente

Respuestas:

150
DATA = { u'spam': u'eggs', u'foo': frozenset([u'Gah!']), u'bar': { u'baz': 97 },
         u'list': [u'list', (True, u'Maybe'), set([u'and', u'a', u'set', 1])]}

def convert(data):
    if isinstance(data, basestring):
        return str(data)
    elif isinstance(data, collections.Mapping):
        return dict(map(convert, data.iteritems()))
    elif isinstance(data, collections.Iterable):
        return type(data)(map(convert, data))
    else:
        return data

print DATA
print convert(DATA)
# Prints:
# {u'list': [u'list', (True, u'Maybe'), set([u'and', u'a', u'set', 1])], u'foo': frozenset([u'Gah!']), u'bar': {u'baz': 97}, u'spam': u'eggs'}
# {'bar': {'baz': 97}, 'foo': frozenset(['Gah!']), 'list': ['list', (True, 'Maybe'), set(['and', 'a', 'set', 1])], 'spam': 'eggs'}

Supuestos:

  • Ha importado el módulo de colecciones y puede hacer uso de las clases base abstractas que proporciona
  • Está feliz de convertir usando la codificación predeterminada (use en data.encode('utf-8')lugar de str(data)si necesita una codificación explícita).

Si necesita admitir otros tipos de contenedores, es de esperar que sea obvio cómo seguir el patrón y agregar casos para ellos.

RichieHindle
fuente
¿Y qué haría uno si algunos valores son listas / conjuntos / etc.?
Phillip B Oldham
@Philip: agregue casos para ellos. Respuesta actualizada y luego actualizada nuevamente para anidar contenedores dentro de contenedores.
RichieHindle
1
te olvidaste de tuple y frozenset, Richi
SilentGhost
3
¿Por qué usas en su type(data)(map(convert, data))lugar map(convert, data)?
Abbasov Alexander
4
@AbbasovAlexander: Para que recupere el mismo tipo que ingresó: una tupla se convierte en una tupla, una lista se convierte en una lista, un conjunto se convierte en un conjunto, y así sucesivamente.
RichieHindle
23

Sé que llego tarde en este:

def convert_keys_to_string(dictionary):
    """Recursively converts dictionary keys to strings."""
    if not isinstance(dictionary, dict):
        return dictionary
    return dict((str(k), convert_keys_to_string(v)) 
        for k, v in dictionary.items())
Germano
fuente
1
Sí, esta parece la forma correcta de hacerlo, las versiones en línea y otras realmente no son suficientes para escenarios del mundo real. Lástima que no haya una forma confiable sin recursividad en línea para lograr esto. ¿O tal vez hay convenciones basadas en python str (...) json?
jayunit100
1
Este es mi favorito, convertir solo las claves, que es lo que estaba buscando. Pequeño error tipográfico: necesita un () adicional alrededor del argumento dict () que se devuelve.
ggll
El único problema con esta solución es si sus claves NO son todas cadenas (es decir, tipo int)
MrWonderful
@MrWonderful y ¿por qué? No veo ningún problema en llamar stra un int
Germano
@Germano: Por supuesto que puedes llamar a str () en un int, pero obtienes un str .... ya no un int. Entonces, el tipo de clave se cambiaría de un int a un str, que es más que cambiar unicode a str, la pregunta original.
MrWonderful
13

Si desea hacer esto en línea y no necesita un descenso recursivo, esto podría funcionar:

DATA = { u'spam': u'eggs', u'foo': True, u'bar': { u'baz': 97 } }
print DATA
# "{ u'spam': u'eggs', u'foo': True, u'bar': { u'baz': 97 } }"

STRING_DATA = dict([(str(k), v) for k, v in data.items()])
print STRING_DATA
# "{ 'spam': 'eggs', 'foo': True, 'bar': { u'baz': 97 } }"
Samuel Clay
fuente
4
Para 2.7 y en adelante, esto se puede simplificar de la siguiente manera:{ str(key):value for key,value in data.items() }
AnjoMan
4

para un dict no anidado (dado que el título no menciona ese caso, podría ser interesante para otras personas)

{str(k): str(v) for k, v in my_dict.items()}
maxbellec
fuente
1
{str (k): str (v) para k, v en my_dict.items ()}
criterio
Esto ayudó a convertir mis claves en cadenas que necesitaba comparar con mi columna de
marco de datos
3
def to_str(key, value):
    if isinstance(key, unicode):
        key = str(key)
    if isinstance(value, unicode):
        value = str(value)
    return key, value

pasarle la clave y el valor, y agregar recursividad a su código para dar cuenta del diccionario interno.

SilentGhost
fuente
2

Para hacerlo todo en línea (no recursivo):

{str(k):(str(v) if isinstance(v, unicode) else v) for k,v in my_dict.items()}
Ben
fuente
0

Solo usa print(*(dict.keys()))

El * se puede utilizar para desempacar contenedores, por ejemplo, listas. Para obtener más información sobre *, consulte esta respuesta SO .

Coddy
fuente
Aunque este código podría resolver el problema, una buena respuesta debería explicar qué hace el código y cómo ayuda.
BDL
0
>>> d = {u"a": u"b", u"c": u"d"}
>>> d
{u'a': u'b', u'c': u'd'}
>>> import json
>>> import yaml
>>> d = {u"a": u"b", u"c": u"d"}
>>> yaml.safe_load(json.dumps(d))
{'a': 'b', 'c': 'd'}
lyu.l
fuente