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

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.


¿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())

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

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()}

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.


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()}

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 .


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'}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.