TL;DR: 解決策は、「LOOK HERE!」とコメントされた 2 行にあります。出力がリストのリストになることを受け入れれば、YAML をプログラム内の辞書として扱い、保存されたファイル/テキストの順序で扱うことができます。
!!python/ordered_dict や !!omap のような恐ろしく醜い明示的な型を気にしない場合は、ファイルをポイ捨てすることもできます。私の投票は!!omapに行きますが、それをサポートするツール/ライブラリの数はわかりません(!!python/ordered_dictをサポートするツールは少ないと確信しています)。最終的に、dict 自体と、キーの順序を定義するメタデータの 2 つの独立したデータ セットを処理します。
(どこでも !!python/ordered_dict または !!omap 混乱なしで YAML で順序付けられた辞書を強制する半魔法の方法がありますが、それらは壊れやすく、辞書の定義そのものと矛盾し、基礎となる YAML ライブラリが進化するにつれて壊れる可能性がありますちなみに、この状況は JSON の場合と同じです。YAML は JSON のスーパーセットであり、キーの順序を保証しないためです。つまり、標準準拠のツール/ユーザーが初めてファイルをいじると、回避策が機能しなくなります。)
この投稿の残りの部分は、例/検証コードと、なぜこのようになっているのかの説明です。
from __future__ import print_function
import yaml
# Setting up some example data
d = {'name': 'A Project',
'version': {'major': 1, 'minor': 4, 'patch': 2},
'add-ons': ['foo', 'bar', 'baz']}
# LOOK HERE!
ordering = ['name', 'version', 'add-ons', 'papayas']
ordered_set = [[x, d[x]] for x in ordering if x in d.keys()]
# In the event you only care about a few keys,
# you can tack the unspecified ones onto the end
# Note that 'papayas' isn't a key. You can establish an ordering that
# includes optional keys by using 'if' as a guard in the list comprehension.
# Demonstration
things = {'unordered.yaml': d, 'ordered.yaml': ordered_set}
for k in things:
f = open(k, 'w')
f.write(yaml.dump(things[k], default_flow_style=False, allow_unicode=True))
f.close()
# Let's check the result
output = []
for k in things:
f = open(k, 'r')
output.append(dict(yaml.load(f.read())))
f.close()
# Should print 'OK'
if output[0] == output[1]:
print('OK')
else:
print('Something is wrong')
作成されたファイルは次のようになります。
注文した.yaml:
- - name
- A Project
- - version
- major: 1
minor: 4
patch: 2
- - add-ons
- - foo
- bar
- baz
unordered.yaml:
add-ons:
- foo
- bar
- baz
name: A Project
version:
major: 1
minor: 4
patch: 2
これは、期待するほどきれいな YAML ドキュメントを生成しません。そうは言っても、最初の入力としてかなりの YAML を使用できます (やった!)。きれいではない順序付けられた YAML から、きれいで順序付けられたままの dict スタイルの YAML への変換のスクリプトを作成するのは簡単です (これは演習として残しておきます)。 .
保持したいキーの順序がある場合は、それを順序付きリスト/タプルに書き込みます。そのリストを使用して、リストの順序付きリストを生成します (ただし、YAML で !!python/tuple タイプを取得するため、タプルのリストではありません)。それを YAML にダンプします。それを通常どおりに読み戻すには、その構造体を dict() に渡すと、最初に使用した元の辞書に戻ります。順序を保持する必要があるネストされた構造がある場合は、構造を再帰的に下降する必要がある場合があります (これは、散文で説明するよりもコードで行う方が簡単です。これはおそらく既に知っていることです)。
この例では、プロジェクトの「名前」をファイルの最初に配置し、次に「バージョン」番号の要素、次に「アドオン」を配置します。通常、PyYAML は dump() を呼び出すときに辞書キーを英数字順に並べますが、これは将来変更される可能性があり、YAML 標準にはこれを必要とするものがないため、これは信頼できません。このように物事を行います。「アドオン」が「名前」の前に来るので、順序付けに問題があります。だから私は自分の順序を定義し、リストの順序付きリストをパックし、それをダンプします。
本質的に順序付けられていないものから順序付けを求めています。ディクショナリは、検索速度のために内部的に順序付けられたハッシュ テーブルです。明日、辞書を実装するより高速な方法が発見された場合、ランタイムは、辞書がハッシュテーブルの有用な抽象化であることに依存するすべてのコードを壊すことなく、それを実装する必要があるため、その順序を台無しにすることはできません。
同様に、YAML はマークアップ言語ではなく (元は「Yaml Ain't a Markup Language」の略でした)、データ形式です。違いは重要です。タプルやリストなど、一部のデータは順序付けされています。キーと値のペアのバッグのように、そうでないものもあります (ハッシュ テーブルとは少し異なりますが、概念的には似ています)。
この種のソリューションの再帰バージョンを使用して、人間が読みやすくするためではなく、さまざまな YAML 実装で YAML 出力を保証しますが、YAML で多くのデータを渡す必要があり、各レコードはキーで署名する必要があり、無期限の順序で防ぐことができます。辞書/ハッシュが使用されているときはいつでも統一された署名。