web-dev-qa-db-ja.com

OrderedDict内包表記

collectionsモジュールのOrderedDictやdictを継承する独自のタイプなど、他の辞書の辞書内包表記のpythonで構文を拡張できますか?

dictの名前を再バインドするだけでは明らかに動作しません。{key: value}内包構文は、内包表記とリテラルの単純で古い辞書を提供します。

>>> 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'プレフィックスを付けて試してみます。

注:もちろん、代わりにジェネレーター式を使用できますが、文法の観点からハック可能python.

58
wim

言語内からPythonの構文を直接変更する方法はありません。辞書内包表記(または単純な表示)は常にdictを作成しますが、それについてできることは何もありません。 CPythonを使用している場合は、dictを直接生成する特別なバイトコードを使用しており、最終的に PyDict API関数および/またはそのAPIで使用される同じ基礎関数を呼び出します。 PyPyを使用している場合、これらのバイトコードは、代わりにRPython dictオブジェクトの上に実装され、コンパイルされ最適化されたPython dict。 等々。

indirect方法がありますが、あなたはそれを好きではありません。 インポートシステム のドキュメントを読むと、キャッシュされたコンパイル済みコードを検索したり、コンパイラーを呼び出したり、パーサーを呼び出すコンパイラーなどがインポーターであることがわかります。 Python 3.3+では、このチェーンのほとんどすべてが純粋なPythonで書かれているか、代替の純粋なPython実装、つまり、コードをフォークし、 ASTを構築する独自のPyParsingコードでソースを解析するか、dict Comprehension ASTノードをデフォルトの代わりに独自のカスタムバイトコードにコンパイルするか、バイトコードを後処理します、または…

多くの場合、 import hook で十分です。そうでない場合は、いつでもカスタムFinderおよびローダーを作成できます。

まだ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を取得し、キー値GeneratorExprsで同等のTupleに変換し、ラップします。 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{…}_構文のアイデアを捨て、通常のdict内包表記をodictにコンパイルすることをお勧めします。幸いなことに、文法を変更する必要はありません(毛深いことを超えています...)、次のいずれか1つだけです。

  • dictcompsがコンパイルするバイトコード、
  • インタープリターがこれらのバイトコードを実行する方法、または
  • PyDictタイプの実装

悪いニュースは、これらはすべて文法を変更するよりもはるかに簡単ですが、拡張モジュールからは実行できません。 (まあ、あなたは基本的に純粋なPythonからやることと同じことをすることで最初のものを行うことができます...そしてあなたはあなたの関数にパッチを当てるために.so/.dll/.dylibをフックすることでそれらのいずれかを行うことができますが、それはPythonに加えて、実行時にフックする追加の作業)とまったく同じ作業。

CPythonソース をハッキングする場合、必要なコードは_Python/compile.c_、_Python/ceval.c_、および_Objects/dictobject.c_にあり、 devガイド 必要なものをすべて見つける方法を示します。ただし、 PyPyソース のハッキングを検討することをお勧めします。これは、Cではなく(=のサブセット)Python.


サイドノートとして、すべてがPython言語レベルで行われたとしても、あなたの試みは機能しませんでした。_olddict, dict = dict, OrderedDict_は、モジュールのグローバルでdictという名前のバインディングを作成します、組み込みshadowsの名前ですが、置き換えられません。あなたcan組み込みのものを置き換えます(まあ、Pythonはこれを保証しませんが、実装/バージョンごとに動作する可能性がある実装/バージョン固有のものがあります。試しました…)、しかしあなたがしたことはそれをする方法ではありません。

29
abarnert

申し訳ありませんが、できません。 Dictリテラルとdict内包表記は、Cレベルでハードコーディングされた方法で、組み込みdictタイプにマップされます。それをオーバーライドすることはできません。

ただし、これを代替手段として使用できます。

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

補遺:Python 3.6、すべてのPython辞書は順序付けられています。 .7以降 、それは言語仕様の一部です。これらのバージョンのPythonを使用している場合、OrderedDictは必要ありません。dict内包表記はJust Work(TM)になります。

85
Max Noel

@Max Noelの応答を少し変更すると、ジェネレーターの代わりにリスト内包表記を使用して、順序付けられた方法でOrderedDictを作成できます(もちろんdict内包表記では不可能です)。

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