Упорядоченные слова - программирование
Подтвердить что ты не робот

Упорядоченные слова

Могу ли я расширить синтаксис в python для понимания dict для других dicts, таких как OrderedDict в модуле collections или моих собственных типов, которые наследуют от dict?

Просто переупорядочение имени dict явно не работает, синтаксис {key: value} по-прежнему дает вам простой старый dict для понимания и литералов.

>>> from collections import OrderedDict
>>> olddict, dict = dict, OrderedDict
>>> {i: i*i for i in range(3)}.__class__
<type 'dict'>

Итак, если возможно, как бы я это сделал? Это нормально, если он работает только в CPython. Для синтаксиса я предполагаю, что попробую его с префиксом O{k: v}, как у нас на r'various' u'string' b'objects'.

note: Конечно, мы можем использовать выражение генератора вместо этого, но мне больше интересно узнать, как хакерский питон в терминах грамматики.

4b9b3361

Ответ 1

Нет прямого способа изменить синтаксис Python из языка. Понимание словаря (или простого отображения) всегда будет создавать dict, и вы ничего не можете с этим поделать. Если вы используете CPython, он использует специальные байт-коды, которые напрямую генерируют dict, которые в конечном итоге вызывают функции API PyDict и/или одинаковые базовые функции, используемые этим API. Если вы используете PyPy, эти байт-коды вместо этого реализуются поверх объекта RPython dict, который, в свою очередь, реализован поверх скомпилированного и оптимизированного Python dict. И так далее.

Существует косвенный способ сделать это, но вам это не понравится. Если вы прочитаете документы в системе импорта, вы увидите, что импортер, который ищет кешированный скомпилированный код или вызывает компилятор, и компилятор, который вызывает парсер, и так далее. В Python 3.3+ почти все в этой цепочке либо написано на чистом Python, либо имеет альтернативную чистую реализацию Python, то есть вы можете развить код и сделать свое дело. Который включает в себя синтаксический анализ исходного кода с вашим собственным кодом PyParsing, который строит AST или компилирует понимание AD ASTM node в свой собственный байт-код вместо стандартного или пост-обработки байтового кода или...

Во многих случаях достаточно import hook; если нет, вы всегда можете написать пользовательский искатель и загрузчик.

Если вы еще не используете Python 3.3 или более позднюю версию, я настоятельно рекомендую выполнить миграцию, прежде чем играть с этим материалом. В более ранних версиях это сложнее и менее хорошо документировано, и в конечном итоге вы будете в 10 раз пытаться узнать что-то, что будет устаревшим, когда вы будете мигрировать.

В любом случае, если этот подход кажется вам интересным, вы можете взглянуть на MacroPy. Вы можете одолжить у него некоторый код - и, что еще важнее, узнать, как используются некоторые из этих функций (которые не имеют хороших примеров в документах).

Или, если вы готовы согласиться на что-то менее крутое, вы можете просто использовать MacroPy для создания "макроса понимания выражения" и использовать его. (Обратите внимание, что MacroPy в настоящее время работает только в Python 2.7, а не 3.x.) Вы не можете получить o{…}, но вы можете получить, скажем, od[{…}], что не так уж плохо. Загрузите od.py, realmain.py и main.py и запустите python main.py, чтобы увидеть, как он работает. Ключ этот код, который принимает DictionaryComp AST, преобразует его в эквивалентный GeneratorExpr на значение ключа Tuple s и переносит его в Call в collections.OrderedDict:

def od(tree, **kw):
    pair = ast.Tuple(elts=[tree.key, tree.value])
    gx = ast.GeneratorExp(elt=pair, generators=tree.generators)
    odict = ast.Attribute(value=ast.Name(id='collections'), 
                          attr='OrderedDict')
    call = ast.Call(func=odict, args=[gx], keywords=[])
    return call

Другой альтернативой является, конечно, изменение интерпретатора Python.

Я бы посоветовал отказаться от идеи синтаксиса o{…} для вашего первого выхода, и просто сделать компиляцию нормальных диктонов компиляцией в odicts. Хорошая новость заключается в том, что вам действительно не нужно менять грамматику (которая выше волосатой...), просто любая из:

  • байт-коды, которые скомпилируют dictcomps,
  • способ, которым интерпретатор запускает эти байт-коды, или
  • реализация типа PyDict

Плохая новость, в то время как все это намного проще, чем изменение грамматики, ни один из них не может быть выполнен из модуля расширения. (Ну, вы можете сделать первый, выполнив в основном то же самое, что и с чистого Python... и вы можете сделать любой из них, подключив .so/.dll/.dylib, чтобы исправлять ваши собственные функции, но это точно такая же работа, как взлом на Python плюс дополнительная работа по подключению во время выполнения.)

Если вы хотите взломать источник CPython, нужный код находится в Python/compile.c, Python/ceval.c и Objects/dictobject.c, и dev guide рассказывает вам, как найти все, что вам нужно. Но вместо этого вы можете рассмотреть возможность взлома источника PyPy, поскольку он в основном написан на (подмножестве) Python, а не на C.


В качестве побочного примечания ваша попытка не сработала бы, даже если бы все было выполнено на уровне языка Python. olddict, dict = dict, OrderedDict создает привязку с именем dict в вашем глобальном глобальном модуле, что затеняет имя в встроенных функциях, но не заменяет его. Вы можете заменить вещи во встроенных (ну, Python не гарантирует этого, но есть вещи, связанные с реализацией/версией, что-бы-случиться для каждой реализации/версии, которую я пробовал...), но вы сделали это Это способ сделать это.

Ответ 2

Извините, не возможно. Литералы Дикта и понимание диктофона соответствуют встроенному типу dict, таким образом, что он жестко закодирован на уровне C. Это нельзя переопределить.

Вы можете использовать это как альтернативу, хотя:

OrderedDict((i, i * i) for i in range(3))

Ответ 3

Немного изменяя ответ @Max Noel, вы можете использовать представление списка вместо генератора для создания OrderedDict упорядоченным способом (что, конечно, невозможно с использованием понимания dict).

>>> OrderedDict([(i, i * i) for i in range(5)])
OrderedDict([(0, 0), 
             (1, 1), 
             (2, 4), 
             (3, 9), 
             (4, 16)])