Der schnellste Weg, um die Schlüssel und Werte eines Diktats von "Unicode" in "Str" umzuwandeln?


81

Ich erhalte ein Diktat von einer "Codeebene", für das einige Berechnungen / Änderungen durchgeführt werden, bevor es an eine andere "Ebene" übergeben wird. Die Schlüssel- und Zeichenfolgenwerte des ursprünglichen Diktats sind unicode, aber die Ebene, auf die sie übergeben werden, akzeptiert nur str.

Dies wird häufig aufgerufen, daher möchte ich wissen, wie Sie am schnellsten Folgendes konvertieren können:

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

...zu:

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

... unter Berücksichtigung der Nicht-String-Werte muss der ursprüngliche Typ beibehalten werden.

Irgendwelche Gedanken?

Antworten:


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

Annahmen:

  • Sie haben das Sammlungsmodul importiert und können die darin enthaltenen abstrakten Basisklassen verwenden
  • Sie konvertieren gerne mit der Standardcodierung (verwenden Sie diese, data.encode('utf-8')anstatt str(data)eine explizite Codierung zu benötigen).

Wenn Sie andere Containertypen unterstützen müssen, ist es hoffentlich offensichtlich, wie Sie dem Muster folgen und Fälle für sie hinzufügen.


Und was würde man tun, wenn einige Werte Listen / Mengen / etc sind?
Phillip B Oldham

@Philip: Fügen Sie Fälle für sie hinzu. Antwort aktualisiert und dann erneut aktualisiert, um Container in Containern zu verschachteln.
RichieHindle

1
Du hast Tupel und Frozenset vergessen, Richi
SilentGhost

3
Warum benutzt du type(data)(map(convert, data))stattdessen map(convert, data)?
Abbasov Alexander

4
@AbbasovAlexander: Damit Sie den gleichen Typ zurückerhalten, den Sie eingegeben haben - ein Tupel wird zu einem Tupel, eine Liste wird zu einer Liste, eine Menge wird zu einer Menge und so weiter.
RichieHindle

23

Ich weiß, dass ich zu spät komme:

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
Ja, dies scheint die richtige Vorgehensweise zu sein. Inline- und andere Versionen reichen für reale Szenarien nicht aus. Schade, dass es keinen zuverlässigen rekursionsfreien Inline-Weg gibt, um dies zu erreichen. Oder vielleicht gibt es basierend auf Python Str (...) Json Konventionen?
Jayunit100

1
Dies ist mein Favorit, nur die Schlüssel zu konvertieren, was ich gesucht habe. Kleiner Tippfehler: Sie benötigen ein zusätzliches () um das zurückgegebene dict () - Argument.
ggll

Das einzige Problem mit dieser Lösung ist, wenn Ihre Schlüssel NICHT alle Zeichenfolgen sind (dh int-Typ)
MrWonderful

@ MrWonderful und warum ist das so? Ich kann kein Problem darin sehen, streinen int
Germano

@Germano: Natürlich kannst du str () auf einem int aufrufen, aber du bekommst ein str .... kein int mehr. Der Typ des Schlüssels würde also von int in str geändert, was mehr ist als das Ändern von Unicode in str - die ursprüngliche Frage.
MrWonderful

13

Wenn Sie dies inline tun wollten und keinen rekursiven Abstieg benötigten, könnte dies funktionieren:

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
{ str(key):value for key,value in data.items() }
Ab

4

für ein nicht verschachteltes Diktat (da der Titel diesen Fall nicht erwähnt, könnte es für andere Personen interessant sein)

{str(k): str(v) for k, v in my_dict.items()}

1
{str (k): str (v) für k, v in my_dict.items ()}
Maßstab

Dies half mir, meine Schlüssel in Strings umzuwandeln, die ich mit meiner Dataframe-Spalte vergleichen musste
Megamind

3
def to_str(key, value):
    if isinstance(key, unicode):
        key = str(key)
    if isinstance(value, unicode):
        value = str(value)
    return key, value

Übergeben Sie den Schlüssel und den Wert und fügen Sie Ihrem Code eine Rekursion hinzu, um das innere Wörterbuch zu berücksichtigen.


2

Um alles inline zu machen (nicht rekursiv):

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

0

Benutz einfach print(*(dict.keys()))

Das * kann zum Auspacken von Containern verwendet werden, z. B. Listen. Weitere Informationen zu * finden Sie in dieser SO-Antwort .


Obwohl dieser Code das Problem lösen könnte, sollte eine gute Antwort erklären, was der Code tut und wie er hilft.
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'}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.