返回键的dict值,如果dict不包含该键,则返回键

时间:2011-03-31 20:22:22

标签: python

背景:自从我阅读https://stackoverflow.com/questions/228181/the-zen-of-python以来已经有一段时间了,所以我去了口译员并输入import this,然后出于好奇我检查了内容新导入的this模块,看到this有一些有趣的属性,最值得注意的是:

>>> this.s
"Gur Mra bs Clguba, ol Gvz Crgref\n\nOrnhgvshy vf orggre guna htyl.\nRkcyvpvg vf orggre guna vzcyvpvg.\nFvzcyr vf orggre guna pbzcyrk.\nPbzcyrk vf orggre guna pbzcyvpngrq.\nSyng vf orggre guna arfgrq.\nFcnefr vf orggre guna qrafr.\nErnqnovyvgl pbhagf.\nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.\nNygubhtu cenpgvpnyvgl orngf chevgl.\nReebef fubhyq arire cnff fvyragyl.\nHayrff rkcyvpvgyl fvyraprq.\nVa gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.\nGurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.\nNygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.\nAbj vf orggre guna arire.\nNygubhtu arire vf bsgra orggre guna *evtug* abj.\nVs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.\nVs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.\nAnzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"
>>> this.d
{'A': 'N', 'C': 'P', 'B': 'O', 'E': 'R', 'D': 'Q', 'G': 'T', 'F': 'S', 'I': 'V', 'H': 'U', 'K': 'X', 'J': 'W', 'M': 'Z', 'L': 'Y', 'O': 'B', 'N': 'A', 'Q': 'D', 'P': 'C', 'S': 'F', 'R': 'E', 'U': 'H', 'T': 'G', 'W': 'J', 'V': 'I', 'Y': 'L', 'X': 'K', 'Z': 'M', 'a': 'n', 'c': 'p', 'b': 'o', 'e': 'r', 'd': 'q', 'g': 't', 'f': 's', 'i': 'v', 'h': 'u', 'k': 'x', 'j': 'w', 'm': 'z', 'l': 'y', 'o': 'b', 'n': 'a', 'q': 'd', 'p': 'c', 's': 'f', 'r': 'e', 'u': 'h', 't': 'g', 'w': 'j', 'v': 'i', 'y': 'l', 'x': 'k', 'z': 'm'}

我认为使用this.s中的字符映射可以从this.d获取Zen,所以我提出了以下行,实际上打印了Python的Zen:

"".join(this.d[c] if c in this.d else c for c in this.s)

然后我回到上面链接的Stack Overflow wiki,认为我会发布这些有趣的信息,但当然它已经存在,已经有两年半了。但是,该帖子使用以下方法获取Zen:

"".join(c in this.d and this.d[c] or c for c in this.s)

这可以节省一些字符,但是发生的事情稍微不那么明显(在我看来)。所以我想我会查看this.py使用哪种方法,当然这是比上述任何一种更优雅的解决方案:

"".join(this.d.get(c, c) for c in this.s)

问题:这个过程让我思考,这三种方法之间是否有任何性能差异使它们中的任何一种显然更好?

显然最后一个很好,因为它更具可读性,但我听说有一些与Python函数调用有关的开销,所以我不会惊讶地发现其他方法之一更快。

那么,如果这些方法中的任何一种方法比其他方法更有效,那么为什么呢?

3 个答案:

答案 0 :(得分:2)

唯一可以尝试的方法是尝试:)

所以这是我的设置:

>>> import timeit
>>> cmd1 = """\
... "".join(this.d[c] if c in this.d else c for c in this.s)
... """
>>> cmd2 = """\
... "".join(c in this.d and this.d[c] or c for c in this.s)
... """
>>> cmd3 = """\
... "".join(this.d.get(c, c) for c in this.s)
... """
>>> cmd4 = """\
... _get=this.d.get;"".join(_get(c, c) for c in this.s)
... """
>>> t1 = timeit.Timer(cmd1, "import this")
>>> t2 = timeit.Timer(cmd2, "import this")
>>> t3 = timeit.Timer(cmd3, "import this")
>>> t4 = timeit.Timer(cmd4, "import this")

<强>结果:

print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000)
362.67 usec/pass

>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
364.25 usec/pass

>>> print "%.2f usec/pass" % (1000000 * t3.timeit(number=100000)/100000)
391.97 usec/pass

>>> print "%.2f usec/pass" % (1000000 * t4.timeit(number=100000)/100000)
246.91 usec/pass

答案 1 :(得分:1)

正如我所料(而不是猜测),

_get=this.d.get; "".join(_get(c, c) for c in this.s)

证明是最快有关时间安排,请参阅Mike's answer


现在,在this.d.get(c, c)中,每次迭代时都必须评估get
但是如果存储get函数,可能会节省一些时间。

答案 2 :(得分:0)

感谢Mike和N 1.1的回答,我仍然对在生成器之前在本地存储this.d的相对性能感到好奇,这里是设置:

>>> t1 = timeit.Timer('"".join(this.d[c] if c in this.d else c for c in this.s)', 'import this')
>>> t2 = timeit.Timer('"".join(c in this.d and this.d[c] or c for c in this.s)', 'import this')
>>> t3 = timeit.Timer('"".join(this.d.get(c, c) for c in this.s)', 'import this')
>>> # assigning local names for this.d
>>> t4 = timeit.Timer('d = this.d;"".join(d[c] if c in d else c for c in this.s)', 'import this')
>>> t5 = timeit.Timer('d = thid.d;"".join(c in d and d[c] or c for c in this.s)', 'import this')
>>> t6 = timeit.Timer('_get = this.d.get;"".join(_get(c, c) for c in this.s)', 'import this')

结果:

>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=10000)/10000)
404.46 usec/pass

>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=10000)/10000)
410.73 usec/pass

>>> print "%.2f usec/pass" % (1000000 * t3.timeit(number=10000)/10000)
432.35 usec/pass

>>> print "%.2f usec/pass" % (1000000 * t4.timeit(number=10000)/10000)
244.88 usec/pass

>>> print "%.2f usec/pass" % (1000000 * t5.timeit(number=10000)/10000)
258.15 usec/pass

>>> print "%.2f usec/pass" % (1000000 * t6.timeit(number=10000)/10000)
245.17 usec/pass

一旦你摆脱了查询开销,看起来第一个和第三个几乎完全相同。这些测试是使用Python 2.5,不确定这是否会产生影响。