Python lxml对象标识符似乎可以在对象处于活动状态时重用

Python lxml对象标识符似乎可以在对象处于活动状态时重用,python,lxml,Python,Lxml,我正在Ubuntu上使用Python 3.6.8和lxml-4.3.4 我所追求的是将大型XML内容分解为片段文件,使其更易于工作,并保留解析元素的源文件名和行号,以便形成有用的解析时错误消息。当XML格式良好时,我将提出的错误特定于我的应用程序 以下是一些示例XML片段文件: one.xml: <?xml version="1.0" encoding="UTF-8" ?> <data> <one>1</one> <one>11

我正在Ubuntu上使用Python 3.6.8和lxml-4.3.4

我所追求的是将大型XML内容分解为片段文件,使其更易于工作,并保留解析元素的源文件名和行号,以便形成有用的解析时错误消息。当XML格式良好时,我将提出的错误特定于我的应用程序

以下是一些示例XML片段文件:

one.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<data>
  <one>1</one>
  <one>11</one>
  <one>111</one>
  <one>1111</one>
</data>

1.
11
111
1111
two.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<data>
  <two>2</two>
  <two>22</two>
  <two>222</two>
  <two>2222</two>
  <two>22222</two>
  <two>222222</two>
</data>

2.
22
222
2222
22222
222222
我的计划是使用lxml解析每个文件,并简单地将元素树缝合在一起,形成一个根。然后我的程序的其余部分可以使用完整的树

如果某个元素的内容对我的应用程序无效,我想给出片段文件及其来源的行号。lxml已经有行号,但没有源文件。所以我想追踪一下。注意,我决定不尝试扩展lxml的类,而是使用元素对象标识符到片段文件的映射,我希望即使lxml重构其源代码,该映射也是持久的

from lxml import etree

# Too much data for one source file, so let's define
# fragment files, each of which looks like a stand
# alone XML file w/ header and root <data>...</data>
# to make syntax highlighters happy.
xmlFragmentFiles = ['one.xml', 'two.xml']

# lxml tracks line number for parsed elements, but not
# source filename. Rather than try to extend the deep
# inner classes of the module, let's try keeping a map
# from parsed elements to fragment file they just came
# from.
element2fragment = {}
def AddFragmentFileToETree(element, fragmentFile):
  # The entry we're just about to add.
  print('%s:%s' % (id(element), fragmentFile))
  element2fragment[id(element)] = fragmentFile
  for child in element:
    AddFragmentFileToETree(child, fragmentFile)

# Fabricate a root that we'll stitch each fragment's
# children onto as we parse them.
root = etree.fromstring('<data></data>')
AddFragmentFileToETree(root, 'Programmatic Root')

for filename in xmlFragmentFiles:
  # It doesn't seem to matter whether we create a new
  # parser per fragment, or reuse a single parser.
  parser = etree.XMLParser(remove_comments=True)
  subroot = etree.parse(filename, parser).getroot()  
  for child in subroot:
    root.append(child)
    AddFragmentFileToETree(child, filename)

# Clearly the final desired tree is here, and presumably
# all the subelements we care about are reachable from
# the programmatic root meaning the objects are still
# live, so why did any object identifier get reused?
print(etree.tostring(
  root, encoding=str, pretty_print=True))
从lxml导入etree
#一个源文件的数据太多,所以让我们定义
#片段文件,每个文件看起来都像一个支架
#单独的XML文件w/头和根。。。
#让语法高明者高兴。
xmlFragmentFiles=['one.xml','two.xml']
#lxml跟踪已解析元素的行号,但不跟踪
#源文件名。而不是试图延伸到深处
#在模块的内部类中,让我们尝试保留一个映射
#从解析的元素到片段文件,它们刚刚出现
#从。
element2fragment={}
def AddFragmentFileToETree(元素,碎片文件):
#我们将要添加的条目。
打印(“%s:%s%”(id(元素),碎片文件))
element2fragment[id(element)]=碎片文件
对于元素中的子元素:
AddFragmentFileToETree(子级,fragmentFile)
#制作一个根,我们将缝合每个碎片的
#当我们分析孩子时,他们会被带到我们的电脑上。
root=etree.fromstring(“”)
AddFragmentFileToETree(根,“编程根”)
对于xmlFragmentFiles中的文件名:
#我们是否创造一个新的世界似乎并不重要
#每个片段的语法分析器,或者重用单个语法分析器。
parser=etree.XMLParser(remove_comments=True)
subroot=etree.parse(文件名,解析器).getroot()
对于子轨迹中的子轨迹:
root.append(子级)
AddFragmentFileToETree(子级,文件名)
#很明显,最后一棵理想的树就在这里,而且大概是这样
#我们关心的所有子元素都可以从
#编程根表示对象仍然是
#live,那么为什么要重用任何对象标识符呢?
打印(etree.tostring)(
root,encoding=str,pretty_print=True)
当我运行这个程序时,我可以看到包含片段文件中每个不同元素的整个所需树都有一个漂亮的打印。但是,查看我们插入的地图条目,我们可以清楚地看到对象正在被重用

140611035114248:Programmatic Root
140611035114056:one.xml <-- see here
140611035114376:one.xml
140611035114440:one.xml
140611035114056:one.xml <-- and here
140611035114312:two.xml
140611035114120:two.xml
140611035114056:two.xml <-- and here
140611035114312:two.xml
140611035114120:two.xml
140611035114056:two.xml <-- and again
<data><one>1</one>
  <one>11</one>
  <one>111</one>
  <one>1111</one>
<two>2</two>
  <two>22</two>   <-- yet all distinct elements still exist
  <two>222</two>
  <two>2222</two>
  <two>22222</two>
  <two>222222</two>
</data>
140611035114248:编程根目录

140611035114056:one.xml我决定继续扩展/定制解析器。。。找到了这个原始问题的答案

它们警告python元素代理是无状态的

元素实例是根据需要创建和垃圾收集的,因此 通常无法预测何时以及多久创建一次代理 为了他们

他们接着说,如果你真的需要它们来承载状态,你必须保留对每一个的实时引用:

这对我很管用。我假设当元素具有对子元素的实时引用时,根就足够了,但是代理显然是根据C中维护的真实树的需要出现的

proxy_cache = list(root.iter())