Переопределите нотацию {...}, поэтому я получаю 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"]