我有一个XML文件,我需要从中提取ID和标题字段(在页面标记下)。这就是我正在做的,它工作正常。但是,我对elem.find('title)的三次调用感到不满意。是否有更好的方法可以通过理解避免这种情况?我知道在循环中写入将解决这个问题。
import xml.etree.ElementTree as ET
tree = ET.parse(some file)
root = tree.getroot()
id_title_list = [(elem.find('id').text, elem.find('title').text)
for elem in root.findall('page')
if elem.find('title').text.startswith('string1') or
elem.find('title').text.startswith('string2')]
答案 0 :(得分:4)
将其分解为正常循环并具有中间变量没有任何错误:
id_title_list = []
for elem in root.findall('page'):
title = elem.find('title').text
if title.startswith(('string1', 'string2')):
id_title_list.append((elem.find('id').text, title))
请注意,startswith()
支持将多个前缀作为元组传入。
另一种选择是在xpath表达式中进行startswith()
检查:
id_title_list = [(elem.find('id').text, elem.find('title').text)
for elem in root.xpath('//page[.//title[starts-with(., "string1") or starts-with(., "string2")])]']
请注意,这不适用于xml.etree.ElementTree
,因为它仅提供对xpath表达式的有限支持。 lxml
会处理此问题,只需将导入更改为:
from lxml import etree as ET
答案 1 :(得分:1)
一种方式,尊重通过理解解决这一问题的要求:
id_title_list = [
(elem.find('id').text, title)
for elem, title in
(elem, elem.find('title').text for elem in root.findall('page'))
if title.startswith(('string1', 'string2'))]
这使用内部生成器表达式来评估每个元素只有find
一次。因为它是一个懒惰的评估生成器,它应该避免中间列表的开销。它还使用startswith
的能力来获取可能的前缀元组,尽管一旦你只查找标题文本,那么简洁而不是速度。
所有这一切,我同意alexce的回答,for循环是一个更好的选择。
答案 2 :(得分:0)
使用一些高阶函数和itertools:
from operator import methodcaller
from itertools import tee, imap, izip
# Broken down into lots of small pieces; recombine as you see fit.
# Functions for calling various methods on objects
# Example: find_id(x) is the same as x.find('id')
find_id = methodcaller('find', 'id')
find_title = methodcaller('find', 'title')
is_valid = methodcaller('startswith', ('string1', 'string2'))
get_text = attrgetter('text')
found = root.findall('page') # The original results...
found_iters = tee(found, 2) # ... split into two.
# Make two iterators resulting from calling `find` on each element...
ids_iter = imap(get_text, imap(find_id, found_iters[0]))
titles_iter = imap(get_text, imap(find_title, found_iters[1]))
# And recombine them into a single iterable of tuples.
id_title_pairs = izip(ids_iter, titles_iter)
# Resulting in a nice, simple list comprehension
id_title_list = [(id, title)
for id, title in id_title_pairs if is_valid(title)]