ElementTree の iterparse 関数を使用して、XML ファイルで表された言語辞書を解析しています。ジェネレータ関数でフィルタリングしていますが、奇妙な実行順序の誤解により、エントリが重複しています。セットアップ コードを次に示します (これは実際には関数内で行われていますが、その他の詳細は重要ではありません)。
import xml.etree.cElementTree as ET
dictionary = iter(ET.iterparse("../dictionaries/language_name.xml",
events=("start", "end")))
#We can discard the original iterable, I think
フィルタリング
次に、イテレータを受け取り、それをフィルタリングする関数があります (グローバル変数は無視してください。問題をデバッグするためだけです)。
def get_entries(iterparsed):
global yielded
root = next(iterparsed)[1] #iterpase gives (event, element)
yield root
for event, elem in iterparsed:
if event == "end" and elem.tag == "entry":
yielded += 1
print("Num yielded:", yielded)
print("Yielding", ET.tostring(elem, encoding="utf-8"))
yield elem
処理
次に、次のように使用します (ここでも、デバッグ用の一時的なグローバル)。
root = next(get_entries(dictionary))
for elem in get_entries(dictionary):
global received
received += 1
print("Num received:", received)
print("I got", ET.tostring(elem, encoding="utf-8"))
raw_input("Continue? ")
#I only yield the first item once, but receive it twice? :(
process_entry(elem) #Defined elsewhere, adds a <sgmtd> node to each entry
root.clear() #Clears the processed children of root node
出力
すべてを実行するとyielded = 9050
、received = 9051
. そして問題のある出力:
Num received: 1
I got <entry><form>aː</form><ortho>a:</ortho><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
Continue?
Num yielded: 1
Yielding <entry><form>aː</form><ortho>a:</ortho><sgmtd /><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
Num received: 2
I got <entry><form>aː</form><ortho>a:</ortho><sgmtd /><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
Continue?
Num yielded: 2
Yielding <entry><form>aːčáx</form><ortho>a:cháj</ortho><pos>n</pos><sense><def><en>axe</en><es>hacha</es></def></sense></entry>
Num received: 3
I got <entry><form>aːčáx</form><ortho>a:cháj</ortho><pos>n</pos><sense><def><en>axe</en><es>hacha</es></def></sense></entry>
Continue?
質問
今、確認しましelem
たが、ループの開始前に定義されていません。いいえ、ファイルの先頭に 2 つの同一の要素はありません。最初の "I received" ビットの後、すべてが期待どおりに機能しているように見えます - 物事は譲られてから受け取られます (たとえば、a:cháj axが最初に譲られ、次に受け取られます)。
さらに奇妙なことに、その最初の要素は、for ループの最後でクリアされることなく、生成される前に処理されます。初めて「受信」したときは、<sgmtd> ノードがありません。初めて「生成」されたとき、すでに <sgmtd> ノードがあり、処理済みであることを示しています。次に、それが再び受信され、(という行があるにもかかわらずif not elem.find("sgmtd"): elem.insert(2, segmented_form)
) 2 番目の <sgmtd> ノードが追加され、ファイルに書き出されます。したがって、出力ファイルは次のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<lexicon>
<entry><form>aː</form><ortho>a:</ortho><sgmtd /><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
<entry><form>aː</form><ortho>a:</ortho><sgmtd /><sgmtd /><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
ここで私は何を誤解していますか?yield
ステートメントが実行される前のコードなしで、アイテムがジェネレーター関数から「受信」されるのはどうしてですか?
if not elem.find("sgmtd")
行を に変更if elem.find("sgmtd") is None
すると、重複アイテムの処理が停止することがわかりました。Element
オブジェクトは暗黙のうちに期待どおりに変換されないと思いますTrue
。しかし、なぜそれが現れたのか知りたいです!