66

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

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

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

では、可能であればどのようにすればよいのでしょうか。CPythonだけで動くならOKです。構文についてO{k: v}は、r'various' u'string' b'objects'.

注: もちろん、代わりにジェネレータ式を使用することもできますが、文法の観点から、python がどれほどハッキング可能かを確認することにもっと興味があります。

4

3 に答える 3

95

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

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

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

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

于 2014-01-14T00:03:03.373 に答える
32

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

間接的な方法もありますが、気に入らないでしょう。import systemのドキュメントを読むと、キャッシュされたコンパイル済みコードを検索したり、コンパイラを呼び出したりするのはインポーターであり、パーサーを呼び出すのはコンパイラなどであることがわかります。Python 3.3+ では、このチェーンのほとんどすべてが純粋な Python で記述されているか、代替の純粋な Python 実装を備えているため、コードをフォークして独自のことを行うことができます。これには、AST を構築する独自の PyParsing コードを使用してソースを解析する、辞書内包表記 AST ノードをデフォルトではなく独自のカスタム バイトコードにコンパイルする、バイトコードを後処理する、または…</p>

多くの場合、インポート フックで十分です。そうでない場合は、いつでもカスタム ファインダーとローダーを作成できます。

まだ Python 3.3 以降を使用していない場合は、これを試す前に移行することを強くお勧めします。古いバージョンでは、それはより難しく、十分に文書化されておらず、移行するたびに時代遅れになるものを学ぶために、最終的には 10 倍の労力を費やすことになります。

とにかく、このアプローチが興味深いと思われる場合は、MacroPyを見てください。そこからいくつかのコードを借りることができます。そして、おそらくもっと重要なこととして、これらの機能 (ドキュメントには良い例がありません) のいくつかがどのように使用されているかを学ぶことができます。

または、あまりクールではないもので解決したい場合は、 を使用MacroPyして「odict 内包マクロ」を作成し、それを使用することができます。(現在、MacroPy は Python 2.7 でのみ動作し、3.x では動作しないことに注意してください。) 完全に を取得することはできませんo{…}が、たとえば を取得することはできますod[{…}]。これはそれほど悪くはありません。od.pyrealmain.py、および をダウンロードmain.pyして実行python main.pyし、動作することを確認します。キーはこのコードで、AST を受け取り、それをkey-value s でDictionaryComp同等のものに変換し、 toでラップします。GeneratorExprTupleCallcollections.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 にコンパイルすることをお勧めします。幸いなことに、文法を変更する必要は実際にはありません (毛むくじゃらではありません…)。

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

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

CPython のソースをハックしたい場合、必要なコードはPython/compile.cPython/ceval.c、およびObjects/dictobject.cにあります。開発ガイドには、必要なものすべてを見つける方法が記載されています。しかし、 PyPy ソースは C ではなく Python (のサブセット) でほとんど書かれているため、代わりにPyPy ソースのハッキングを検討することをお勧めします。


補足として、すべてが Python 言語レベルで行われたとしても、あなたの試みはうまくいきませんでした。モジュールのグローバルで名前がolddict, dict = dict, OrderedDict付けられたバインディングを作成します。これは組み込みの名前を隠しますが、置き換えません。ビルトイン内のものを置き換えることができます(まあ、Pythonはこれを保証しませんが、私が試したすべての実装/バージョンで機能する実装/バージョン固有のものがあります…)が、あなたがしたことはそれを行う方法ではありません。dict

于 2014-01-14T00:09:24.483 に答える
16

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

>>> OrderedDict([(i, i * i) for i in range(5)])
OrderedDict([(0, 0), 
             (1, 1), 
             (2, 4), 
             (3, 9), 
             (4, 16)])
于 2015-04-28T21:16:14.557 に答える