我编写了一个类来稍微定制lxml.etree.ElementTree的行为,并且我相当广泛地使用它。它工作得很好,但是有一些方法我不知道我是怎么写的,还有一些其他的方法看起来非常多余。下面我将详细说明具体问题,但首先是代码。
import lxml.etree as old_etree
from xml_utilities import clean_xml
from datetime import datetime
VERSION = 'X.X.X.X'
TOOL = 'ExampleTool'
class etree(old_etree._ElementTree):
@staticmethod
def parse(path):
old_tdx = old_etree.parse(path)
new_tdx = etree(old_tdx)
new_tdx._setroot(old_tdx.getroot())
return new_tdx
@staticmethod
def fromstring(string):
old_tdx = old_etree.fromstring(string)
new_tdx = etree(old_tdx.getroottree())
new_tdx._setroot(old_tdx)
return new_tdx
@staticmethod
def getISOTime():
iso_time = datetime.now().isoformat()
format_time = iso_time.split('.')[0] + 'Z'
return format_time
@staticmethod
def SubElement(*args, **kargs):
return old_etree.SubElement(*args, **kargs)
@staticmethod
def Element(*args, **kargs):
return old_etree.Element(*args, **kargs)
def getMetaNode(self, name, default=''):
metas = self.findall('meta')
for meta in metas:
if meta.get('name') == name:
return meta
return old_etree.SubElement(self.getroot(), 'meta',
attrib={'name': name, 'value': default})
def setMetaNode(self, name, value):
node = self.getMetaNode(name)
node.set('value', value)
def write(self, path, updateTool=True):
self.setMetaNode('saved', etree.getISOTime())
if updateTool:
self.setMetaNode('version', VERSION)
self.setMetaNode('type', TOOL)
super(etree, self).write(path)
clean_xml(path)函数clean_xml是自定义编写的,并且在很大程度上不相关:它逃避了ElementTree.write默认不会遇到的麻烦字符(这是我们使用的文件规范的一个问题,而不是lxml)。关于问题:
lxml.etree继承更好的方法来扩展_ElementTree的功能?最后,我希望更改方法ElementTree.write的行为,并添加ElementTree.getMetaNode和ElementTree.setMetaNode方法。parse、fromstring、SubElement和Element方法,而不是仅仅创建引用原始方法的瘦包装?parse和fromstring在做什么吗?我知道我的方法fromstring与lxml.etree.fromstring有不同的功能,因为我希望它返回一个ElementTree而不是一个Element。这些方法主要是通过猜测和检查(oops)编写的。return old_etree.SubElement(...)行,这样它就不必占用两行了?这不是什么大事但是..。最初,我试图直接覆盖lxml.etree.ElementTree.write,但这会引发错误AttributeError: 'lxml.etree._ElementTree' object attribute 'write' is read-only。一般的批评也受到欢迎。我知道我应该在这里发表评论,但是除了我不明白的两种方法之外,一切都是非常直接的。
另外,我不确定是否有人会认为使用etree这个名称来覆盖lxml.etree是个坏主意。这样做的原因是,我只需将任何以前编写的文件顶部的import行从from lxml import etree更改为from my_lxml import etree。
发布于 2016-02-24 13:37:24
class etree(old_etree._ElementTree):
@staticmethod
def parse(path):
old_tdx = old_etree.parse(path)
new_tdx = etree(old_tdx)
new_tdx._setroot(old_tdx.getroot())
return new_tdx
@staticmethod
def fromstring(string):
old_tdx = old_etree.fromstring(string)
new_tdx = etree(old_tdx.getroottree())
new_tdx._setroot(old_tdx)
return new_tdx在那些样板函数中,我不知道"tdx“是什么意思。我想它是链接到您使用的文件规范。
这些函数是我在代码中遇到的主要问题。您将lxml.etree模块与lxml.etree.ElementTree类混合使用。这四种方法是模块的方法,而不是类!我觉得你应该:
etree模块etree类(重命名为ElementTree)放入其中这将使事情变得不那么混乱,因为它更接近于lxml.etree和标准xml.etree的工作方式。您的同事将能够更快地选择您的代码。
您将执行from lxml.etree import *,并且只重新定义要重新定义的函数。您可能不能期望获得完美的兼容性,但至少基本的API是相同的。
除了返回类型之外,对于函数的实现,我没有什么可说的:丑陋的,但我没有找到让事情变得更好的方法。是的,很容易理解它们。
@staticmethod
def SubElement(*args, **kargs):
return old_etree.SubElement(*args, **kargs)
@staticmethod
def Element(*args, **kargs):
return old_etree.Element(*args, **kargs)你不再需要用我上面的建议来处理这个问题了。
@staticmethod
def getISOTime():
iso_time = datetime.now().isoformat()
format_time = iso_time.split('.')[0] + 'Z'
return format_time添加'Z‘意味着这是一个UTC时间,但这并不是因为您使用了datetime.now()而不是datetime.utcnow()。我住在UTC+4,你要返回的format_time对我来说是4个小时的假期。
def getMetaNode(self, name, default=''):
metas = self.findall('meta')
for meta in metas:
if meta.get('name') == name:
return meta如果您想避免循环:使用lxml XPath支持,可以在这里使用meta.xpath("meta[name = $name]", name=name)。
def write(self, path, updateTool=True):
self.setMetaNode('saved', etree.getISOTime())
if updateTool:
self.setMetaNode('version', VERSION)
self.setMetaNode('type', TOOL)
super(etree, self).write(path)
clean_xml(path)吹毛求疵:在ElementTree上进行清理将防止您的文件系统看到两个不同的版本。假设在某个时候您决定使用看门狗,回调将在您有机会运行clean_xml之前启动,这可能会导致微妙的bug。
lxml.etree继承更好的方法来扩展_ElementTree的功能?最后,我希望更改方法ElementTree.write的行为,并添加ElementTree.getMetaNode和ElementTree.setMetaNode方法。因为lxml是用Cython编写的,所以我认为您不能直接将lxml设置为monkeypatch lxml,所以子类化是可行的方法。lxml可以选择使用__new__来简化事情,就像numpy为促进子类化所做的那样。
parse、fromstring、SubElement和Element方法,而不是仅仅创建引用原始方法的瘦包装?见上面的“样板”。
parse和fromstring在做什么吗?我知道我的方法fromstring与lxml.etree.fromstring有不同的功能,因为我希望它返回一个ElementTree而不是一个Element。这些方法主要是通过猜测和检查(oops)编写的。为什么要获得一个ElementTree而不是一个元素?我认为这是个坏主意,有两个原因:
return old_etree.SubElement(...)行,这样它就不必占用两行了?这不是什么大事但是..。如果有的话,我想我会用两行以上的。如果你不想再担心这样的事情,我强烈建议雅普夫。
最初,我试图直接覆盖
lxml.etree.ElementTree.write,但这会引发错误AttributeError: 'lxml.etree._ElementTree' object attribute 'write' is read-only。一般的批评也受到欢迎。我知道我应该在这里发表评论,但是除了我不明白的两种方法之外,一切都是非常直接的。
是的,你不能把它关起来(就像在回答1中说的那样),但这不是你想要做的,因为你也想保留原来的实现。一个装潢师也不会改进东西的。
另外,我不确定是否有人会认为使用
etree这个名称来覆盖lxml.etree是个坏主意。这样做的原因是,我只需将任何以前编写的文件顶部的import行从from lxml import etree更改为from my_lxml import etree。
不,我认为用etree这个名字是个好主意。在Python中提供替换是一种常见的方法,标准库xml.etree就是这样做的。然而,你需要做正确的事情。同样,请参阅上面的“样板”部分。
哦,PEP 8建议不要在模块名中使用下划线,除非它提高了可读性,但我认为mylxml足够可读性。但是,简单地在模块前面添加my并不是一个好主意,因为您只是失去了一个机会来解释什么使您的lxml与众不同。既然它似乎特定于您使用的文件规范,那么为什么不在名称中使用它呢?
https://codereview.stackexchange.com/questions/119559
复制相似问题