【问题标题】:Iterative find/replace from a list of tuples in Python从 Python 中的元组列表中迭代查找/替换
【发布时间】:2010-11-13 14:47:42
【问题描述】:

我有一个元组列表,每个元组都包含我想应用于字符串的查找/替换值。这样做最有效的方法是什么?我将反复应用它,所以性能是我最关心的问题。

更具体地说,processThis() 的内部结构是什么样的?

x = 'find1, find2, find3'
y = [('find1', 'replace1'), ('find2', 'replace2'), ('find3', 'replace3')]

def processThis(str,lst):
     # Do something here
     return something

>>> processThis(x,y)
'replace1, replace2, replace3'

谢谢大家!

【问题讨论】:

    标签: python django list iteration tuples


    【解决方案1】:

    与 mhawke 相同的答案,包含在 str_replace 方法中

    def str_replace(data, search_n_replace_dict):
        import re
        REPLACEMENTS = search_n_replace_dict
    
        def replacer(m):
            return REPLACEMENTS[m.group(0)]
    
        r = re.compile('|'.join(REPLACEMENTS.keys()))
        return r.sub(replacer, data)
    

    那么我们就可以通过下面的例子调用这个方法

    s = "abcd abcd efgh efgh;;;;;; lkmnkd kkkkk"
    d = dict({ 'abcd' : 'aaaa', 'efgh' : 'eeee', 'mnkd' : 'mmmm' })
    
    
    print (s)
    print ("\n")
    print(str_replace(s, d))
    

    输出:

    abcd abcd efgh efgh;;;;;; lkmnkd kkkkk
    
    
    aaaa aaaa eeee eeee;;;;;; lkmmmm kkkkk
    

    【讨论】:

      【解决方案2】:

      几个注意事项:

      1. 关于过早优化、基准测试、瓶颈、100 小等的样板参数。
      2. 在某些情况下,不同的解决方案会返回不同的结果。如果y = [('one', 'two'), ('two', 'three')] 和x = 'one' 然后mhawke 的解决方案给你'two' 和未知的给'three'。
      3. 在一个愚蠢的设计示例中对此进行测试,mhawke 的解决方案比 小 快了一点。不过,用您的数据进行尝试应该很容易。

      【讨论】:

      • 如果你愿意接受我,我很想听听样板的论点。我仍然是个菜鸟:)
      • +1 指出结果不同。这很重要!
      • @cpharmston:每当出现这样的问题时,在 StackOverflow 或其他地方,一定有人会抱怨性能不是最重要的考虑因素,除非这个功能实际上是你的瓶颈应用。他们会建议最好担心边缘情况的正确性、可读性/可维护性甚至编程时间。我讨厌成为那个人(下班),因为这个讨论很有趣,但是由于这个讨论是渐进式的,并且存在正确性差异,我想我至少会暗示这个论点。
      • 在 StackOverflow 上搜索“过早优化”,以查看有关这些问题的大量讨论;甚至包括一个关于如何回应抱怨过早优化的人的元问题 :-) stackoverflow.com/questions/438158/…
      • 啊,谢谢 jtb。反对担心性能的论点是有道理的,但我有两个相反的观点:(1) 这最终将编写一个可重用的 Django 应用程序,因此在这种情况下,为最坏的情况做计划可能是谨慎的。 (2) 我把它作为学术练习的部分原因是为了帮助我学习 Python。即使语用差异可以忽略不计,通常也有正确的做事方式。关于正确方式的讨论(或分歧)通常会导致启发性的对话,就像这样。谢谢大家!
      【解决方案3】:

      你可以考虑使用re.sub:

      import re
      REPLACEMENTS = dict([('find1', 'replace1'),
                           ('find2', 'replace2'),
                           ('find3', 'replace3')])
      
      def replacer(m):
          return REPLACEMENTS[m.group(0)]
      
      x = 'find1, find2, find3'
      r = re.compile('|'.join(REPLACEMENTS.keys()))
      print r.sub(replacer, x)
      

      【讨论】:

      • 谢谢 mhawke!这在性能方面与 Unknown 的解决方案相比如何?
      • @cpharmston:使用 replace() 要求每次替换调用一次。如果有很多替换和/或工作字符串很长,这将是低效的。 re.sub() 应该处理一次工作字符串,但有一些设置开销。
      • Python 已经维护了一个已编译正则表达式的缓存。 fgrep cache your_python_directory/Lib/re.py
      • @mhawke: re.sub 遍历文本中的每个位置并测试“查找”是否在该位置匹配——没有自动机。时间是 O((文本大小)*(“发现”的数量)*(“发现”的平均大小))。 str.replace() 的多次使用:相同。但是: str.replace 使用 Boyer-Moore 变体快速跳过文本,但多次遍历文本,可能会破坏内存缓存,并且会切碎内存,因为每次“查找”必须被替换时它都会创建一个新的替换字符串。 re.sub 遍历文本一次,不跳过,只创建一次 repl 字符串。 re.sub 概率获胜者;做基准测试。
      • @cpharmston:当然。这是我的圆周率公式。有什么相关性?
      【解决方案4】:
      s = reduce(lambda x, repl: str.replace(x, *repl), lst, s)
      

      【讨论】:

        【解决方案5】:
        x = 'find1, find2, find3'
        y = [('find1', 'replace1'), ('find2', 'replace2'), ('find3', 'replace3')]
        
        def processThis(str,lst):
            for find, replace in lst:
                str = str.replace(find, replace)
        
            return str
        
        >>> processThis(x,y)
        'replace1, replace2, replace3'
        

        【讨论】:

        • 你比我快 5 秒 :)
        • 谢谢,未知!这是显而易见的解决方案,效果很好,但我担心性能。如果 len(y) 为 100,并且我在一页上应用 10 次,我们在一页中执行 1000 次离散的 str.replace() 调用。这对我来说似乎不是最理想的;正则表达式不是更适合这个吗?
        • 不,您仍然会使用正则表达式执行相同的 re.sub。如果您想要绝对最快的速度,您可能需要降级到 C 级别来实现您自己的基于流的 Regex,它基于开关表进行替换。
        • “未知”是错误的。使用包含所有要匹配的源字符串的正则表达式执行此操作--“find1|find2|find3”--不会重复扫描字符串。请参阅 mhawke 的回答。
        • 快速测试: - 在 len(y) = 3 的非常小的测试中,在 30 个字符的字符串上运行 3 次,进行 2 次替换,str.replace() 稍微快一些 (~8%) -在 len(y) = 20 的中型测试中,在 200 个字符的字符串上运行 20 次,进行 10 次替换,re.sub() 的速度大大提高 (~22%) - 在 len(y) = 500 的大型测试中,在 5000 个字符的字符串上运行 500 次,进行 25 次替换,re.sub 快得多(~60%) 感谢大家的精彩讨论!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-02-20
        • 2011-02-04
        • 2018-08-18
        • 1970-01-01
        • 2022-01-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多