Переопределите нотацию {...}, поэтому я получаю OrderedDict() вместо dict()?

Я хочу использовать файл .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')]

Ответ 1

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

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, который обеспечивает чуть более тщательную реализацию - используйте вместо этого пакет

Ответ 2

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

Сначала мы создаем модуль с функцией 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?

Ответ 3

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())

Ответ 4

То, что вы просите, невозможно, но если файл конфигурации с синтаксисом 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)])

Ответ 5

Единственное решение, которое я нашел, - это исправить сам 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

Ответ 6

Начиная с 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] сохраняются. Новая реализация компактная, упорядоченная реализация словаря используется для реализации заказа для обоих из них.

Ответ 7

Если то, что вы ищете, это способ получить простой в использовании синтаксис инициализации - подумайте о создании подкласса 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), ( "ключ", "значение" )])


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

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"]