Python:理解中重复函数调用的更好解决方案

2024-04-18 02:39:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个XML文件,需要从中提取ID和Title字段(在page标签下)。这就是我正在做的,而且效果很好。但是,我不高兴有三个电话给你元素查找(“标题”)。有没有更好的方法来避免这种理解?我知道在循环中写作可以解决这个问题。你知道吗

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')]

Tags: 文件textidtreetitlepageroot标签
3条回答

使用一些高阶函数和工具:

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)]

一种方法是,尊重以理解的方式解决这一问题的要求:

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循环是一个更好的选择。你知道吗

把它分解成一个正常的循环并使用中间变量没有什么错:

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))

注意,^{}支持作为元组传入的多个前缀。你知道吗


另一个选项是在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表达式提供有限的支持。^{}将处理此问题,只需将导入更改为:

from lxml import etree as ET

相关问题 更多 >