Najszybszy sposób na przekonwertowanie kluczy i wartości dyktu z „unicode” na „str”?

81

Otrzymuję dyktando z jednej „warstwy” kodu, na którym są wykonywane pewne obliczenia / modyfikacje przed przekazaniem go do innej „warstwy”. Oryginalne klucze i wartości „string” są takie unicode, ale warstwa, do której są przekazywane, akceptuje tylko str.

Będzie to często nazywane, więc chciałbym wiedzieć, jaki byłby najszybszy sposób na konwersję czegoś takiego:

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

...do:

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

... pamiętając, że wartości niebędące „ciągami znaków” muszą pozostać w swoim oryginalnym typie.

jakieś pomysły?

Phillip B Oldham
źródło

Odpowiedzi:

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

Założenia:

  • Zaimportowałeś moduł collections i możesz użyć abstrakcyjnych klas bazowych, które zapewnia
  • Z przyjemnością konwertujesz przy użyciu domyślnego kodowania (użyj data.encode('utf-8')raczej niż str(data)jeśli potrzebujesz jawnego kodowania).

Jeśli potrzebujesz obsługiwać inne typy kontenerów, miejmy nadzieję, że jest oczywiste, jak postępować zgodnie ze wzorcem i dodawać do nich przypadki.

RichieHindle
źródło
A co by się stało, gdyby niektóre wartości to listy / zestawy / itp.?
Phillip B Oldham
1
zapomniałeś krotki i zamrożenia, Richi
SilentGhost
3
Dlaczego type(data)(map(convert, data))zamiast tego używasz map(convert, data)?
Abbasov Alexander,
4
@AbbasovAlexander: Abyś wrócił do tego samego typu, który wstawiłeś - krotka staje się krotką, lista staje się listą, zbiór staje się zestawem i tak dalej.
RichieHindle,
1
@Moberg: Tylko jeśli struktura danych jest zagnieżdżona na wiele setek poziomów.
RichieHindle
25

Wiem, że się spóźniłem:

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
źródło
1
Tak, wydaje się, że to właściwy sposób, aby to zrobić, wersje wbudowane i inne naprawdę nie są wystarczające dla rzeczywistych scenariuszy. Szkoda, że ​​nie ma niezawodnego, bezrekurencyjnego sposobu na osiągnięcie tego celu. A może jest oparty o konwencje pythona str (...) json?
jayunit100
1
To mój ulubiony, aby konwertować tylko klucze, czego szukałem. Mała literówka: potrzebujesz dodatkowego () wokół zwracanego argumentu dict ().
ggll
Jedynym problemem z tym rozwiązaniem jest to, że twoje klucze NIE są wszystkimi ciągami (tj. Typu int)
MrWonderful
@MrWonderful i dlaczego tak jest? Nie widzę problemu w dzwonieniu strna int
Germano
@Germano: Oczywiście możesz wywołać str () na int, ale otrzymasz str .... już nie int. Więc typ klucza zostałby zmieniony z int na str, co jest czymś więcej niż zamianą unicode na str - pierwotne pytanie.
MrWonderful
13

Jeśli chcesz to zrobić inline i nie potrzebujesz rekursywnego zejścia, może to zadziałać:

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
źródło
4
W wersji 2.7 i { str(key):value for key,value in data.items() }
nowszych
4

dla niezagnieżdżonego dyktu (ponieważ tytuł nie wspomina o tym przypadku, może być interesujący dla innych osób)

{str(k): str(v) for k, v in my_dict.items()}
maxbellec
źródło
1
{str (k): str (v) dla k, v w my_dict.items ()}
yardstick17
Pomogło to przekonwertować moje klucze na ciągi, które musiałem porównać z moją kolumną
Dataframe
3
def to_str(key, value):
    if isinstance(key, unicode):
        key = str(key)
    if isinstance(value, unicode):
        value = str(value)
    return key, value

Przekaż klucz i wartość do niego, a następnie dodaj rekursję do kodu, aby uwzględnić słownik wewnętrzny.

SilentGhost
źródło
2

Aby to wszystko było wbudowane (nierekurencyjne):

{str(k):(str(v) if isinstance(v, unicode) else v) for k,v in my_dict.items()}
Ben
źródło
0

Po prostu użyj print(*(dict.keys()))

Znak * może służyć do rozpakowywania kontenerów np. List. Aby uzyskać więcej informacji na temat *, sprawdź tę odpowiedź SO .

Coddy
źródło
Chociaż ten kod może rozwiązać problem, dobra odpowiedź powinna wyjaśniać, co robi kod i jak pomaga.
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
źródło