Учитывая диктат d
, теперь, когда мы знаем, начиная с Python 3.7, что порядок (вставки) сохраняется, существует ли встроенный способ запросить тот же диктовку с теми же ключами, за исключением того, что некоторые ключи k1, k2,... должны приходи первым?
Пример:
a
(если есть) должен быть первым,first
(если есть) должен присутствовать рядомЯ придумал это:
def reorder_dict(d, first_keys):
new_keys = [k for k in first_keys if k in d.keys()] + [k for k in d.keys() if k not in first_keys]
new_d = {k: d[k] for k in new_keys}
return new_d
d1 = {"c": 3, "b": 2, "a": 1}
reorder_dict(d1, ["a", "first"]) # {'a': 1, 'c': 3, 'b': 2} as expected ; NB: "first" is not present
Есть ли встроенный способ сделать это более напрямую?
Я думаю, что ваш первоначальный подход хорош, но его можно улучшить с помощью небольшой корректировки. Часть if k not in first_keys
делает ваш код квадратичным.
Этой неэффективности можно избежать:
def reorder_dict(d, first_keys):
result = {k: d[k] for k in first_keys if k in d}
result.update(d)
return result
В Python 3.9+ однострочный вариант возможен с использованием оператора объединения dict
>>> d = {"c": 3, "b": 2, "a": 1}
>>> first_keys = ["a", "first"]
>>> {k: d[k] for k in first_keys if k in d} | d
{'a': 1, 'c': 3, 'b': 2}
first
не является ключом в исходном словаре и не должен быть в результате. Это в примере ОП, чтобы показать, что решение не должно добавлять новые ключи.
Ключи, перечисленные в параметре упорядочения, должны располагаться в том же порядке, что и этот параметр. Ваш код этого не гарантирует. Он помещает их все вперед, но в том порядке, в котором они были в исходном словаре, а не в порядке параметра.
@Бармар, я не понимаю, что ты пытаешься сказать. Я перечитал вопрос, и этот результат кажется правильным, он не добавляет никаких новых ключей в словарь.
Я думал, что dict.fromkeys(["first", "b", "a"])
— это часть решения, а не создание входных данных.
Ваша функция по-прежнему не гарантирует, что ключи в результате будут в том же порядке, что и first_keys
.
Он разбивает словарь на разделы, но не упорядочивает его.
@Бармар, я все еще не понимаю, о чем ты говоришь. Можете ли вы привести контрпример?
reorder_dict({'a': 1, 'c': 3, 'b': 2}, ['c', 'a'])
должен вернуться {'c': 3, 'a': 1, 'b': 2}
@Barmar И это так. Покажите мне контрпример, для которого этот код не работает.
@Basj first не запрограммирован жестко в функции. О чем ты говоришь?
Вы правы, это работает. Я не до конца понимал, что tuple(v != x for v in first_keys)
делает. Объяснение алгоритма имело бы большое значение — если такой эксперт, как я, этого не видит, какая надежда есть у новичков?
@Barmar Хорошо, я полностью удалил пример с ключом кортежа.
@wim Я говорил о более раннем редактировании, когда "first"
был в состоянии, мой комментарий больше не полезен, я удалил его! PS: не могли бы вы повторно включить версию tuple(...)
? Мне всегда нравится узнавать новые способы сделать это :) Спасибо за ваш ответ!
PS: зачем нужен lookup = dict.fromkeys(first_keys)
, если бы lookup = first_keys
не сработало бы?
@Basj Это потому, что поиск в dict — это O (1), а поиск в списке — O (n). Я не буду повторно включать эту технику с кортежным ключом, потому что она уступает и сбивает с толку даже самопровозглашенных «экспертов». Но вы всегда можете просмотреть его в истории изменений: stackoverflow.com/revisions/78342346/3
@wim Да, насчет поиска O(1), но при выполнении dict.fromkeys(first_keys)
создание словаря из исходного списка в любом случае требует O(n). О, понятно, ты делаешь это, чтобы ускорить очередь result.update({... if k not in lookup})
?
@Basj Да, это была идея, но теперь, когда вы об этом упомянули, это вообще-то не требуется. Отредактировал свой ответ.
Да, вы можете добиться этого, используя класс OrderedDict из модуля коллекций. Вы можете создать новый OrderedDict, в котором явно укажете порядок ключей, гарантируя, что определенные ключи будут первыми. Вот пример:
from collections import OrderedDict
# Your original dictionary
d = {'b': 2, 'c': 3, 'a': 1, 'first': 0}
# Define the order of keys
desired_order = ['a', 'first']
# Create a new OrderedDict with the desired order
ordered_d = OrderedDict((key, d[key]) for key in desired_order if key in d)
# Add remaining keys in their original order
for key in d:
if key not in ordered_d:
ordered_d[key] = d[key]
print(ordered_d)
Для этого вам не нужен OrderedDict
. Начиная с версии 3.8 словари Python должны сохранять порядок вставки.
Чем это существенно отличается от того, что написал ОП?
Этот @Barmar ставит под сомнение следующий (не по теме) вопрос: начиная с Python 3.8, существует ли еще (много) вариантов использования OrderedDict
?
В вашем примере, @HammerChi, вам следует удалить ключ first
из d
: это было сделано специально: функция не должна вызывать ошибку, если first
находится в desired_order
, но не в d
(такой случай может случиться).
@Basj Я не говорил, что он ни для чего не нужен, просто для этого не нужен.
Да, конечно @Barmar, я это понял. Но это заставило меня задуматься над таким оффтопом: полезен ли OrderedDict начиная с 3.8, и если да, то зачем?
См. stackoverflow.com/questions/50872498/…
Как уже упоминал Бармар, я не уверен, как это вообще улучшает существующий код OP или какой смысл использовать OrderedDict
, похоже, он не использует ни одну из функций OrderedDict
.
Вы хотите изменить существующий словарь или создать новый? Насколько велик
len(first_keys)
по сравнению сlen(d)
? Какой процентfirst_keys
ожидается отсутствующим вd
? (Я не отрицающий голос)