【问题标题】:Python: Better solution to repeat function invocation in comprehensionPython:在理解中重复函数调用的更好解决方案
【发布时间】:2014-11-04 11:59:07
【问题描述】:

我有一个 XML 文件,我需要从中提取 ID 和 Title 字段(在页面标签下)。这就是我正在做的,它工作正常。但是,我对 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')]

【问题讨论】:

  • 这三个电话是一个问题,还是premature optimization(万恶之源)的情况?
  • 不是两次调用startswith,而是使用元组('string1', 'string2')作为参数进行一次调用。

标签: python xml list list-comprehension elementtree


【解决方案1】:

将其分解为正常循环并具有中间变量并没有错:

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

【讨论】:

  • 请。不要在不必要的时候滥用列表推导。没有人喜欢简单的 for 循环了。
【解决方案2】:

一种方式,尊重通过理解解决此问题的请求:

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 循环在这里是更好的选择。

【讨论】:

    【解决方案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)]
    

    【讨论】:

      猜你喜欢
      • 2022-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多