OrderedDictインスタンスを**kwargs
構文を使用する関数に渡し、順序を維持することは可能ですか?
私がしたいことは:
def I_crave_order(**kwargs):
for k, v in kwargs.items():
print k, v
example = OrderedDict([('first', 1), ('second', 2), ('third', -1)])
I_crave_order(**example)
>> first 1
>> second 2
>> third -1
ただし、実際の結果は次のとおりです。
>> second 2
>> third -1
>> first 1
すなわち、典型的なランダムな辞書の順序。
順序を明示的に設定するのが適切な他の用途があるので、通常の引数としてOrderedDictを渡すだけではなく、**kwargs
を保持したい
Python 3.6、 キーワード引数の順序は保持されます 以降。3.6より前では、OrderedDict
がdict
。
最初に理解することは、**example
に渡した値が自動的に**kwargs
の値にならないことです。 kwargs
がexample
の一部のみを持つ場合を考えます。
def f(a, **kwargs):
pass
example = {'a': 1, 'b': 2}
f(**example)
または、この例では、例よりも多くの値があります。
example = {'b': 2}
f(a=1, c=3, **example)
またはまったく重複していません:
example = {'a': 1}
f(b=2, **example)
だから、あなたが求めていることは本当に意味がありません。
それでも、キーワードがどこから来たのかに関係なく、順序付けられた**kwargs
を指定する何らかの方法があったらいいかもしれません—明示的なキーワード引数が出現順に続き、その後に**example
のすべての項目がexample
に出現する順に続きます(example
がdict
ですが、OrderedDict
の場合も意味があります。
手間のかかる詳細をすべて定義し、パフォーマンスを許容範囲に保つことは、思ったよりも難しいことがわかります。アイデアについての議論については PEP 468 とリンクされたスレッドを参照してください。今回は行き詰まっているようですが、誰かがそれを手に入れて擁護した場合(そして、C APIからアクセス可能なOrderedDict
に依存する、人々が遊ぶための参照実装を作成した場合) 3.5+に存在する)、私はそれが最終的に言語に入ると思う。
ちなみに、これが可能であれば可能であれば、ほぼ確実にOrderedDict
自体のコンストラクタで使用されます。しかし、それを試した場合、あなたがしていることは、永続的な順序として任意の順序をフリーズすることだけです。
>>> d = OrderedDict(a=1, b=2, c=3)
OrderedDict([('a', 1), ('c', 3), ('b', 2)])
一方、どのようなオプションがありますか?
まあ、明らかなオプションは、example
をアンパックする代わりに通常の引数として渡すことです:
def f(example):
pass
example = OrderedDict([('a', 1), ('b', 2)])
f(example)
または、もちろん、*args
を使用してアイテムをタプルとして渡すこともできますが、通常は醜いです。
PEPからリンクされたスレッドには他にもいくつかの回避策があるかもしれませんが、実際には、これより優れたものはありません。 (ただし、IIRCを除き、Li Haoyiは MacroPy に基づいて順序保持キーワード引数を渡すソリューションを考え出しましたが、詳細は覚えていません。MacroPyのソリューションは、 MacroPyを使用して、Pythonのように完全に読み取れないコードを書くことをいとわないが、それが常に適切であるとは限らない…)
python 3.6 のデフォルトになりました。
Python 3.6.0a4+ (default:d43f819caea7, Sep 8 2016, 13:05:34)
>>> def func(**kw): print(kw.keys())
...
>>> func(a=1, b=2, c=3, d=4, e=5)
dict_keys(['a', 'b', 'c', 'd', 'e']) # expected order
他の回答で指摘されているように、以前はそれを行うことはできません。
Python=が署名で**kwargs
構文に遭遇すると、kwargs
が「マッピング」であると想定します。これは、2つのことを意味します:(1)できることkwargs.keys()
を呼び出して、マッピングに含まれる反復可能なキーを取得します。(2)kwargs.__getitem__(key)
は、keys()
によって返される反復可能なキーごとに呼び出すことができ、結果の値は、そのキーに関連付ける必要のある値です。
内部的には、Pythonは、マッピングが辞書になっているものは何でも「変換」します。次のようになります:
**kwargs -> {key:kwargs[key] for key in kwargs.keys()}
kwargs
がすでにdict
であると考えると、これは少しばかげています。完全に等価なdict
をから構築する理由はないためです。それが渡されます。
ただし、kwargs
が必ずしもdict
でない場合は、その内容を適切なデフォルトのデータ構造にして、引数のアンパックを実行するコードが処理内容を常に認識できるようにすることが重要です。
したがって、特定のデータ型がアンパッケージされる方法をcan混乱させますが、一貫したarg-unpacking-protocolのためにdict
に変換するためです、引数のアンパックの順序に保証を付けることができない場合があります(dict
は要素が追加された順序を追跡しないため)。 Python=の言語が**kwargs
をOrderedDict
ではなくdict
に引き下げた場合(キーワード引数としてのキーの順序は次に、それらがトラバースされる順序)、OrderedDict
またはkeys()
がある種の順序を尊重するその他のデータ構造を渡すことにより、できます引数の特定の順序を期待しますdict
が標準として選択されているのは実装の癖であり、他のタイプのマッピングではありません。
「アンパック」できるクラスのばかげた例を次に示しますが、アンパックされたすべての値は常に42として扱われます(実際にはそうではありません)。
class MyOrderedDict(object):
def __init__(self, odict):
self._odict = odict
def __repr__(self):
return self._odict.__repr__()
def __getitem__(self, item):
return 42
def __setitem__(self, item, value):
self._odict[item] = value
def keys(self):
return self._odict.keys()
次に、展開されたコンテンツを出力する関数を定義します。
def foo(**kwargs):
for k, v in kwargs.iteritems():
print k, v
値を作って試してみましょう:
In [257]: import collections; od = collections.OrderedDict()
In [258]: od['a'] = 1; od['b'] = 2; od['c'] = 3;
In [259]: md = MyOrderedDict(od)
In [260]: print md
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
In [261]: md.keys()
Out[261]: ['a', 'b', 'c']
In [262]: foo(**md)
a 42
c 42
b 42
このキーと値のペアのカスタマイズされた配信(ここでは、常に42を返す)は、Pythonでの**kwargs
の動作をいじる能力の範囲です。
*args
をパッケージ化解除する方法をいじくり回すための柔軟性が少し増えました。詳細については、この質問を参照してください:< 引数の展開では反復またはアイテム取得を使用しますか? >。