ValueError:list.remove(x):尝试使用 ElementTree 删除元素时 x 不在列表中

ValueError: list.remove(x): x not in list when trying to remove an element with ElementTree

提问人:Mike Saunders 提问时间:10/6/2023 更新时间:10/6/2023 访问量:44

问:

我有一个 marc xml 文件,一个集合中有两条记录。我想从文件中删除 955 个数据字段。

当我尝试遍历 生成的列表时,我得到一个 , 。findallValueErrorlist.remove(x): x not in list

import xml.etree.ElementTree as ET
tree = ET.parse('toggle.xml')
root = tree.getroot()

for a955 in root.findall('record/datafield[@tag="955"]'):
    root.remove(a955)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[1], line 6
      3 root = tree.getroot()
      5 for a955 in root.findall('record/datafield[@tag="955"]'):
----> 6     root.remove(a955)

ValueError: list.remove(x): x not in list

这是我尝试修改的xml(为了简洁起见,我删除了一些数据字段):

<collection>
    <record>
        <leader>00859cam a2200277Ia 4500</leader>
        <controlfield tag="005">20170510144913.0</controlfield>
        <controlfield tag="008">880930s1983    enka          00010 eng d</controlfield>
        <datafield tag="035" ind1=" " ind2=" ">
            <subfield code="a">ocm13279646 880930</subfield>
        </datafield>
        <datafield tag="035" ind1=" " ind2=" ">
            <subfield code="9">0674-46060</subfield>
        </datafield>
        <datafield tag="035" ind1=" " ind2=" ">
            <subfield code="a">(StEdNL)1580610-nlsdb-Voyager</subfield>
        </datafield>
        <datafield tag="700" ind1="1" ind2="0">
            <subfield code="a">Evans, Martin.</subfield>
        </datafield>
        <datafield tag="710" ind1="2" ind2="0">
            <subfield code="a">Health Education Council.</subfield>
            <subfield code="w">cn</subfield>
        </datafield>
        <datafield tag="710" ind1="2" ind2="0">
            <subfield code="a">Teachers' Advisory Council on Alcohol and Drug Education.</subfield>
        </datafield>
        <datafield tag="955" ind1=" " ind2=" ">
            <subfield code="a">QP4.88.1745</subfield>
            <subfield code="b">QP4DOT88DOT</subfield>
        </datafield>
        <datafield tag="956" ind1=" " ind2=" ">
            <subfield code="a">NLS</subfield>
        </datafield>
    </record>
    <record>
        <leader>01030cas a2200349 i 4500</leader>
        <controlfield tag="005">20190312175642.0</controlfield>
        <controlfield tag="008">130830c20139999stkwr ne      0   a0eng d</controlfield>
        <datafield tag="015" ind1=" " ind2=" ">
            <subfield code="a">GBB386135</subfield>
            <subfield code="2">bnb</subfield>
        </datafield>
        <datafield tag="022" ind1="1" ind2=" ">
            <subfield code="a">2053-6496</subfield>
        </datafield>
        <datafield tag="035" ind1=" " ind2=" ">
            <subfield code="a">(Uk)016484976</subfield>
        </datafield>
        <datafield tag="035" ind1=" " ind2=" ">
            <subfield code="a">2992934</subfield>
        </datafield>
        <datafield tag="035" ind1=" " ind2=" ">
            <subfield code="a">(StEdNL)5112576-nlsdb-Voyager</subfield>
        </datafield>
        <datafield tag="651" ind1=" " ind2="0">
            <subfield code="a">Troon (Scotland)</subfield>
            <subfield code="v">Newspapers.</subfield>
        </datafield>
        <datafield tag="651" ind1=" " ind2="0">
            <subfield code="a">South Ayrshire (Scotland)</subfield>
            <subfield code="v">Newspapers.</subfield>
        </datafield>
        <datafield tag="752" ind1=" " ind2=" ">
            <subfield code="a">Scotland</subfield>
            <subfield code="b">Strathclyde</subfield>
            <subfield code="d">Troon.</subfield>
            <subfield code="2">blnpn</subfield>
        </datafield>
        <datafield tag="919" ind1=" " ind2=" ">
            <subfield code="a">NBS</subfield>
        </datafield>
        <datafield tag="955" ind1=" " ind2=" ">
            <subfield code="y">2020</subfield>
            <subfield code="b">V000258858</subfield>
        </datafield>
    </record>
</collection>

我基于ElementTree文档中的示例:

for country in root.findall('country'):
    # using root.findall() to avoid removal during traversal
    rank = int(country.find('rank').text)
    if rank > 50:
        root.remove(country)

我确定我做错了一些非常基本的事情,但我就是无法弄清楚它是什么。

python xml 元素树 marc

评论


答:

1赞 Masklinn 10/6/2023 #1

根据文档:

remove(subelement)
从元素中删除子元素。

在这种情况下,子元素是直接子元素(遗憾的是,ElementTree 文档使用非常宽松的语言,因此在其他上下文中,子元素用于限定当前元素以下的任何元素)。 不会遍历整个树,寻找您要求它删除的元素。remove

您选择的元素不是 的子元素 (aka ),而是 的子元素。所以你不能通过 删除它,你需要得到一个句柄。rootcollectionrecordrootrecord

由于据我所知 ElementPath 是纯 python 的,因此您可能会从仅手动实现中受益:

for record in root.iter('record'):
    for c in reversed(record):
        if c.tag == 'datafield' and c.get('tag') == '955':
            record.remove(c)

您也可以索引并切出要删除的元素,但这似乎有点多。

评论

0赞 Mike Saunders 10/6/2023
谢谢,这有效 - 为什么这里需要 reversed()?
1赞 Masklinn 10/6/2023
我们正在删除本质上是 Python 列表的元素。Python 列表不支持在迭代过程中删除,并且在迭代下不稳定,所以假设我们正在迭代,我们在“3”处删除它,列表现在是,列表和迭代器不同步它们的信息,所以我们得到的下一项是 5 而不是 4。通过从末尾迭代,我们避免了这个问题,从末尾开始,当我们删除 3 时,无论如何下一个项目都是 2。[1, 2, 3, 4, 5][1, 2, 4, 5]
0赞 Masklinn 10/6/2023
还有其他解决方案,例如迭代副本(只要您按标识而不是索引删除),收集我们需要删除的所有元素,然后进行第二次传递以删除它们(本质上是因为它返回列表后会做什么),将我们保留的项目移动到另一个列表(然后重置),....findall
0赞 Mike Saunders 10/6/2023
太好了,现在更清楚了。我确实注意到它在没有 reversed() 的情况下工作,但我怀疑这只是因为我正在处理一个非常简单的样本并且没有重复的字段。我会把它保留成全套。
0赞 Masklinn 10/6/2023
是的,如果匹配稀疏(这里就是这种情况)并且没有连续的匹配,那么很容易错过问题。