Переопределить нотацию {...}, чтобы я получил OrderedDict () вместо dict ()? - PullRequest
40 голосов
/ 24 октября 2011

Я хочу использовать файл .py, такой как файл конфигурации.Таким образом, используя нотацию {...}, я могу создать словарь, используя строки в качестве ключей, но порядок определения теряется в стандартном словаре Python.

Мой вопрос: можно ли переопределить нотацию {...}, чтобы яполучить OrderedDict() вместо dict()?

Я надеялся, что простой переопределенный конструктор dict с OrderedDict (dict = OrderedDict) сработает, но это не так.

Например:

dict = OrderedDict
dictname = {
   'B key': 'value1',
   'A key': 'value2',
   'C key': 'value3'
   }

print dictname.items()

Вывод:

[('B key', 'value1'), ('A key', 'value2'), ('C key', 'value3')]

Ответы [ 7 ]

76 голосов
/ 16 мая 2016

Вот хак, который почти дает вам нужный синтаксис:

class _OrderedDictMaker(object):
    def __getitem__(self, keys):
        if not isinstance(keys, tuple):
            keys = (keys,)
        assert all(isinstance(key, slice) for key in keys)

        return OrderedDict([(k.start, k.stop) for k in keys])

ordereddict = _OrderedDictMaker()
from nastyhacks import ordereddict

menu = ordereddict[
   "about" : "about",
   "login" : "login",
   'signup': "signup"
]

Редактировать: Кто-то другой обнаружил это независимо и опубликовал пакет odictliteral для PyPI, который обеспечивает чуть более тщательную реализацию - используйте его вместо

39 голосов
/ 24 октября 2011

Чтобы буквально получить то, что вы просите, вы должны поиграть с синтаксическим деревом вашего файла.Я не думаю, что это целесообразно, но я не мог удержаться от соблазна попробовать.Итак, начнем.

Сначала мы создадим модуль с функцией my_execfile(), которая работает как встроенная execfile(), за исключением того, что все вхождения отображаемых словаря, например, {3: 4, "a": 2}, заменяются явнымивызывает конструктор dict(), например dict([(3, 4), ('a', 2)]).(Конечно, мы могли бы напрямую заменить их вызовами collections.OrderedDict(), но мы не хотим быть слишком навязчивыми.) Вот код:

import ast

class DictDisplayTransformer(ast.NodeTransformer):
    def visit_Dict(self, node):
        self.generic_visit(node)
        list_node = ast.List(
            [ast.copy_location(ast.Tuple(list(x), ast.Load()), x[0])
             for x in zip(node.keys, node.values)],
            ast.Load())
        name_node = ast.Name("dict", ast.Load())
        new_node = ast.Call(ast.copy_location(name_node, node),
                            [ast.copy_location(list_node, node)],
                            [], None, None)
        return ast.copy_location(new_node, node)

def my_execfile(filename, globals=None, locals=None):
    if globals is None:
        globals = {}
    if locals is None:
        locals = globals
    node = ast.parse(open(filename).read())
    transformed = DictDisplayTransformer().visit(node)
    exec compile(transformed, filename, "exec") in globals, locals

С этой модификацией мы может изменить поведение отображения словаря, переписав dict.Вот пример:

# test.py
from collections import OrderedDict
print {3: 4, "a": 2}
dict = OrderedDict
print {3: 4, "a": 2}

Теперь мы можем запустить этот файл, используя my_execfile("test.py"), получив вывод

{'a': 2, 3: 4}
OrderedDict([(3, 4), ('a', 2)])

Обратите внимание, что для простоты приведенный выше код не касается словаряпонимания, которые должны быть преобразованы в выражения генератора, передаваемые в конструктор dict().Вам нужно добавить метод visit_DictComp() в класс DictDisplayTransformer.Учитывая приведенный выше пример кода, это должно быть просто.

Опять же, я не рекомендую этот вид возиться с семантикой языка.Вы смотрели в модуль ConfigParser?

13 голосов
/ 24 октября 2011

OrderedDict не является "стандартным синтаксисом Python", однако упорядоченный набор пар ключ-значение (в стандартном синтаксисе Python) просто:

[('key1 name', 'value1'), ('key2 name', 'value2'), ('key3 name', 'value3')]

Чтобы явно получить OrderedDict:

OrderedDict([('key1 name', 'value1'), ('key2 name', 'value2'), ('key3 name', 'value3')])

Другая альтернатива - сортировка dictname.items(), если это все, что вам нужно:

sorted(dictname.items())
5 голосов
/ 24 октября 2011

То, что вы просите, невозможно, но если файл конфигурации в синтаксисе JSON достаточен, вы можете сделать что-то подобное с json модулем :

>>> import json, collections
>>> d = json.JSONDecoder(object_pairs_hook = collections.OrderedDict)
>>> d.decode('{"a":5,"b":6}')
OrderedDict([(u'a', 5), (u'b', 6)])
4 голосов
/ 03 декабря 2014

Единственное решение, которое я нашел, - это исправить сам Python, заставив объект dict запомнить порядок вставки.

Это тогда работает для всех видов синтаксисов:

x = {'a': 1, 'b':2, 'c':3 }
y = dict(a=1, b=2, c=3)

и т.д.

Я взял реализацию ordereddict C из https://pypi.python.org/pypi/ruamel.ordereddict/ и слился с основным кодом Python.

Если вы не против перестроить интерпретатор python, вот патч для Python 2.7.8: https://github.com/fwyzard/cpython/compare/2.7.8...ordereddict-2.7.8.diff .A

3 голосов
/ 17 декабря 2016

Начиная с python 3.6, все словари будут упорядочены по умолчанию .На данный момент это деталь реализации dict, на которую нельзя полагаться, но она, вероятно, станет стандартной после v3.6.

Порядок вставки всегда сохраняется в новой реализации dict:

>>>x = {'a': 1, 'b':2, 'c':3 }
>>>list(x.keys())
['a', 'b', 'c']

Начиная с python 3.6 **kwargs order [PEP468] и порядок атрибутов класса [ PEP520 ] сохраняются.Новая компактная, упорядоченная реализация словаря используется для реализации упорядочения обоих.

0 голосов
/ 17 сентября 2014

Если то, что вы ищете, это способ получить простой в использовании синтаксис инициализации - рассмотрите возможность создания подкласса OrderedDict и добавления к нему операторов, которые обновляют dict, например:

from collections import OrderedDict

class OrderedMap(OrderedDict):
    def __add__(self,other):
        self.update(other)
        return self

d = OrderedMap()+{1:2}+{4:3}+{"key":"value"}

d будет OrderedMap ([(1, 2), (4, 3), ('key', 'value')])


Другой возможный пример синтаксического сахара с использованием синтаксиса среза:

class OrderedMap(OrderedDict):
    def __getitem__(self, index):
        if isinstance(index, slice):
            self[index.start] = index.stop 
            return self
        else:
            return OrderedDict.__getitem__(self, index)

d = OrderedMap()[1:2][6:4][4:7]["a":"H"]
...