好的,所以我坐下来算了算。请不要生我的气,我回答专门讨论 ΤZΩΤZΙΟΥ 的解决方案,但这在评论中有点难以硬塞,所以让我这样做。事实上,我也会提出一些与 OP 的问题相关的考虑。
首先,我一直在与 ΤZΩΤZΙΟΥ 讨论他的方法的优雅、正确和可行性。事实证明,它看起来像提案,虽然它确实使用(固有无序的)字典作为寄存器来存储替换对,但实际上确实始终返回正确的结果,而我曾声称它不会。这是因为在itertools.starmap()
下面的第 11 行中的调用将一个迭代器作为其第二个参数,该迭代器对看起来像[ ( 'h', 'h', ), ( 'e', 'e', ), ( 'l', 'l', ), ... ]
. 这些字符/字节对是第一个参数replacer.get
, 被重复调用的内容。没有机会遇到先'>'
被转化为'>'
,然后不经意间再次转化为的情况'>'
,因为每个字符/字节只被考虑一次用于替换。所以这部分原则上是好的并且在算法上是正确的。
下一个问题是可行性,这将包括对性能的了解。如果使用笨拙的代码在 0.01 秒内正确完成一项重要任务,但使用出色的代码在 1 秒内正确完成,那么在实践中可能会认为笨拙是可取的(但前提是 1 秒的损失实际上是无法容忍的)。这是我用于测试的代码;它包括许多不同的实现。它是用 python 3.1 编写的,因此我们可以使用 unicode 希腊字母作为标识符,这本身就很棒(zip
在 py3k 中返回与itertools.izip
在 py2 中相同):
import itertools #01
#02
_replacements = { #03
'&': '&', #04
'<': '<', #05
'>': '>', #06
'"': '"', #07
"'": ''', } #08
#09
def escape_ΤΖΩΤΖΙΟΥ( a_string ): #10
return ''.join( #11
itertools.starmap( #12
_replacements.get, #13
zip( a_string, a_string ) ) ) #14
#15
def escape_SIMPLE( text ): #16
return ''.join( _replacements.get( chr, chr ) for chr in text ) #17
#18
def escape_SIMPLE_optimized( text ): #19
get = _replacements.get #20
return ''.join( get( chr, chr ) for chr in text ) #21
#22
def escape_TRADITIONAL( text ): #23
return text.replace('&', '&').replace('<', '<').replace('>', '>')\ #24
.replace("'", ''').replace('"', '"') #25
这些是计时结果:
escaping with SIMPLE took 5.74664253sec for 100000 items
escaping with SIMPLE_optimized took 5.11457801sec for 100000 items
escaping TRADITIONAL in-situ took 0.57543013sec for 100000 items
escaping with TRADITIONAL took 0.62347413sec for 100000 items
escaping a la ΤΖΩΤΖΙΟΥ took 2.66592320sec for 100000 items
原来,原始发帖人担心“传统”方法“很快变得丑陋,并且算法性能似乎很差”,在这种情况下似乎部分没有根据。它实际上表现最好;当隐藏到函数调用中时,我们确实会看到 8% 的性能损失(“调用方法很昂贵”,但通常你仍然应该这样做)。相比之下,ΤZΩΤZΙΟΥ 的实现时间大约是传统方法的 5 倍,鉴于它具有更高的复杂性,必须与 python 久经磨练的优化字符串方法竞争也就不足为奇了。
这里还有另一种算法,简单算法。据我所知,这与 ΤZΩΤZΙΟΥ 的方法所做的完全一样:它遍历文本中的字符/字节并对每个字节执行查找,然后将所有字符/字节连接在一起并返回结果转义文本。您可以看到,其中一种方法涉及相当冗长和神秘的公式,而 SIMPLE 实现实际上一目了然。
然而,真正让我感到困惑的是,SIMPLE 方法的性能有多糟糕:它的速度大约是传统方法的 10 倍,也是 ΤZΩΤZΙΟΥ 方法的两倍。我在这里完全不知所措,也许有人能想到为什么会这样。它仅使用 python 的最基本构建块并使用两个隐式迭代,因此它避免构建一次性列表和所有内容,但它仍然很慢,我不知道为什么。
让我用 ΤZΩΤZΙΟΥ 解决方案的优点来结束这个代码审查。我已经说得很清楚了,我发现代码很难阅读,而且对于手头的任务来说太过分了。然而,比这更重要的是,我发现他对待字符的方式,并确保对于给定的小范围字符,它们会以类似字节的方式表现得有点烦人。确保它适用于手头的任务,但是一旦我迭代例如字节串'ΤZΩΤZΙΟΥ',我所做的就是迭代代表单个字符的相邻字节。在大多数情况下,这正是您应该避免的;这正是为什么在 py3k 中“字符串”现在是旧的“unicode 对象”,而旧的“字符串”变成了“字节”和“字节数组”。如果我要提名 py3k 的一个特性,它可能保证将代码从 2 系列迁移到 3 系列可能代价高昂,那将是 py3k 的这个单一属性。从那以后,我 98% 的编码问题都已经解决了,而且没有任何聪明的黑客可以让我严重怀疑我的举动。说算法不是“概念上 8 位干净和 unicode 安全”,这对我来说是一个严重的缺点,因为这是 2010 年。