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?

Phillip B. Oldham
quelle

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.

RichieHindle
quelle
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())
Germano
quelle
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 } }"
Samuel Clay
quelle
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()}
maxbellec
quelle
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.

SilentGhost
quelle
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()}
Ben
quelle
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 .

Coddy
quelle
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'}
lyu.l
quelle