1

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 = 9050received = 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。しかし、なぜそれが現れたのか知りたいです!

4

2 に答える 2

2

@Chad Miller@Jochen Ritzel の両方が、私が生成したルート要素を数えていないことを指摘しました。これは意図的なものでした。私が考えたのは、ジェネレーター オブジェクトがリセットしないのと同じように、ジェネレーター関数がリセットされないということです。そのため、 でループを開始したときfor elem in get_entries(dictionary)、ルート要素は既に消費されていると考えました。

ただし、ルート要素を生成する前に print ステートメントを追加すると、2 回出力されます。私が見たデータの重複は、ツリーの最初の要素を使用して(したがってその子を検索して) 取得elem.insert(2, segmented_form)するルートで呼び出されたことが原因でした。segmented_formelem.find

つまり、重複が見られた理由は、ジェネレーター関数がジェネレーター オブジェクトと同じように動作しないためです。学んだ教訓!

于 2013-08-17T07:19:50.667 に答える