Найшвидший спосіб перетворити ключі та значення дикту з `unicode` в` str`?


81

Я отримую дикт від одного "шару" коду, на якому виконуються деякі обчислення / модифікації, перш ніж передати його на інший "шар". Оригінальні значення ключів і "рядкових" значень дикту є unicode, але шар, на який вони передаються, приймає лише str.

Це буде називатися часто, тому я хотів би знати, що було б найшвидшим способом конвертувати щось на зразок:

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

... до:

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

... беручи до уваги, що значення, що не є "рядковими", повинні залишатися початковим типом.

Будь-які думки?

Відповіді:


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

Припущення:

  • Ви імпортували модуль колекцій і можете використовувати абстрактні базові класи, які він надає
  • Ви раді конвертувати за допомогою кодування за замовчуванням (використовуйте, data.encode('utf-8')а не str(data)якщо вам потрібно явне кодування).

Якщо вам потрібна підтримка інших типів контейнерів, сподіваємося, очевидно, як слідувати шаблону та додавати регістри для них.


І що робити, якщо деякі значення - це списки / набори / тощо?
Філіп Б Олдхем

1
ви забули кортеж і заморожений набір, Річі
SilentGhost

3
Чому ви використовуєте type(data)(map(convert, data))замість цього map(convert, data)?
Аббасов Олександр

4
@AbbasovAlexander: Щоб ви повернули той самий тип, який ви ввели - кортеж стає кортежем, список стає списком, набір стає набором тощо.
RichieHindle

1
@Moberg: Тільки якщо ваша структура даних вкладена на багато сотень рівнів.
RichieHindle

25

Я знаю, що запізнився з цим:

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
Так, це здається правильним способом зробити це, вбудованої та інших версій насправді недостатньо для реальних сценаріїв. Шкода, що для цього немає надійного вбудованого безрекурсійного способу. Або, можливо, там засновані на конвенціях python str (...) json?
jayunit100

1
Це мій улюблений - конвертувати лише ключі, саме це я і шукав. Невелика друкарська помилка: вам потрібен додатковий () навколо аргументу dict (), який повертається.
ggll

Єдина проблема з цим рішенням полягає в тому, що ваші ключі НЕ всі рядки (тобто тип int)
MrWonderful

@MrWonderful і чому це? Я не бачу жодної проблеми у дзвінку strна інт
Германо

@Germano: Звичайно, ти можеш викликати str () на int, але ти отримуєш str .... більше не int. Тож тип ключа буде змінено з int на str, що більше, ніж зміна Unicode на str - вихідне питання.
MrWonderful

13

Якщо ви хочете зробити це вбудовано і вам не потрібен рекурсивний спуск, це може спрацювати:

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
Для 2.7 і вище це можна спростити наступним чином:{ str(key):value for key,value in data.items() }
AnjoMan

4

для вкладеного дикту (оскільки в назві не згадується цей випадок, це може бути цікаво для інших людей)

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

1
{Вул (к): вул (v) для K, V в my_dict.items ()}
yardstick17

Це допомогло перетворити мої ключі на рядки, які мені потрібно було порівняти з моїм стовпцем фрейму даних
megamind

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

передайте йому ключ і значення та додайте рекурсію до свого коду, щоб врахувати внутрішній словник.


2

Щоб зробити все вбудованим (нерекурсивним):

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

0

Просто використовуйте print(*(dict.keys()))

Знак * можна використовувати для розпакування контейнерів, наприклад списків. Для отримання додаткової інформації про * перевірте цю відповідь SO .


Хоча цей код може вирішити проблему, хороша відповідь повинна пояснити, що робить код і як він допомагає.
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'}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.