一种有效的python值映射方法?
我有一些值需要重新映射,因为有两种方法可以指定规则。简单的一种有效的python值映射方法?,python,Python,我有一些值需要重新映射,因为有两种方法可以指定规则。简单的if/else方法似乎更有效,但我想知道是否有一种同样有效但更具python风格的方法 if mod == "I": mod = "+" elif mod == "E": mod = "-" elif mod == "D": mod = ":" elif mod == "M": mod = "." 不太有效的映射方法: mod = { "I":"+", "E":"-", "D":":", "M":"." }.get(mod,
if
/else
方法似乎更有效,但我想知道是否有一种同样有效但更具python风格的方法
if mod == "I": mod = "+"
elif mod == "E": mod = "-"
elif mod == "D": mod = ":"
elif mod == "M": mod = "."
不太有效的映射方法:
mod = { "I":"+", "E":"-", "D":":", "M":"." }.get(mod, mod)
映射将导致O(1)查找。条件句将导致O(N)。当然,如果你想对它吹毛求疵的话,在你的map实现中还需要考虑额外的函数调用 而不是理论化。结果(删除
get
调用并严格使用数组访问器,结果:
2 function calls in 0.025 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.025 0.025 0.025 0.025 {range}
4 function calls in 0.035 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
2 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.035 0.018 0.035 0.018 {range}
if_else took 12474 ms for 100000 operations
translation_table took 81650 ms for 100000 operations
dict_lookup took 66385 ms for 100000 operations
现在不可否认,这只使用给定的案例。我的假设是,如果条件的运行时间将随着案例数量的增加而线性增加,而映射查找将保持不变。如果案例数量固定且较少,那么
if else
方法是最快的(我不明白为什么它是非Pythonic的);对于足够多的情况,使用dict
查找会更好
如果案例集是动态的,那么dict
方法当然是唯一可行的方法
另外,第三种非正统的方法是使用宏元编程。这在vanilla python中是不可能的,但是有一些库允许您以(可以说)干净的方式(很可能未经python社区或Guido批准)完成这项工作
再想一想,在大多数Python宏实现中,宏方法在Python中可能不会像在Lisp中那样起作用,因为大多数Python宏实现都试图遵循普通Python的语法;即,如果无法从宏中生成elif else块,则无法在Python中生成
。字符串方法具有转换功能n可用。您必须构建256个字符的翻译表。以下是我使用的代码段:
translationTable = ' '*256
translationTable = translationTable[:68]+':'+translationTable[69:] # D to :
translationTable = translationTable[:69]+'-'+translationTable[70:] # E to -
translationTable = translationTable[:73]+'+'+translationTable[74:] # I to +
translationTable = translationTable[:77]+'.'+translationTable[78:] # M to .
print 'EIDM'.translate(translationTable)
输出:
-+:.
请记住,多级if
语句会生成大量字节码,主要在Python级别进行评估,而字典访问则发生在解释器的优化C代码中。此外,如果存在n
可能性,则if
语句需要平均进行n/2
比较:它必须按顺序检查每种可能性
这个故事的寓意是:dicts
可能已经足够快了,但当有疑问时,可以通过分析找到真正的瓶颈,而不是你怀疑的瓶颈
比较:
def f(mod):
if mod == "I": return "+"
elif mod == "E": return "-"
elif mod == "D": return ":"
elif mod == "M": return "."
dis.dis(f)
4 0 LOAD_FAST 0 (mod)
3 LOAD_CONST 1 ('I')
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_FALSE 16
12 LOAD_CONST 2 ('+')
15 RETURN_VALUE
5 >> 16 LOAD_FAST 0 (mod)
19 LOAD_CONST 3 ('E')
22 COMPARE_OP 2 (==)
25 POP_JUMP_IF_FALSE 32
28 LOAD_CONST 4 ('-')
31 RETURN_VALUE
6 >> 32 LOAD_FAST 0 (mod)
35 LOAD_CONST 5 ('D')
38 COMPARE_OP 2 (==)
41 POP_JUMP_IF_FALSE 48
44 LOAD_CONST 6 (':')
47 RETURN_VALUE
7 >> 48 LOAD_FAST 0 (mod)
51 LOAD_CONST 7 ('M')
54 COMPARE_OP 2 (==)
57 POP_JUMP_IF_FALSE 64
60 LOAD_CONST 8 ('.')
63 RETURN_VALUE
>> 64 LOAD_CONST 0 (None)
67 RETURN_VALUE
d={"I": "+", "E": "-", "D": ":", "M": "."}
def g(mod):
return d.get(mod, mod)
12 0 LOAD_GLOBAL 0 (d)
3 LOAD_ATTR 1 (get)
6 LOAD_FAST 0 (mod)
9 LOAD_FAST 0 (mod)
12 CALL_FUNCTION 2
15 RETURN_VALUE
这并不是一个真正的答案,但很多评论都集中在性能上,因为我确实问过。因此,到目前为止,我对答案进行了一些性能测试:
from datetime import datetime
from string import maketrans
tr_table = maketrans('IEDM', '+-:.')
dictionary = { "I":"+", "E":"-", "D":":", "M":"." }
if_else_val = "E"
N_OPS = 100000
now = datetime.now
def time(func):
s = now()
func()
print "%s took %d ms for %d operations" % (func.__name__, (now() - s).microseconds, N_OPS)
def translation_table():
for i in xrange(N_OPS):
"I".translate(tr_table)
"E".translate(tr_table)
"D".translate(tr_table)
"M".translate(tr_table)
def dict_lookup():
for i in xrange(N_OPS):
dictionary.get("I")
dictionary.get("E")
dictionary.get("D")
dictionary.get("M")
def if_else():
for i in xrange(N_OPS):
if if_else_val == "I": pass
elif if_else_val == "E": pass
elif if_else_val == "D": pass
elif if_else_val == "M": pass
time(if_else)
time(translation_table)
time(dict_lookup)
结果如下:
2 function calls in 0.025 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.025 0.025 0.025 0.025 {range}
4 function calls in 0.035 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
2 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.035 0.018 0.035 0.018 {range}
if_else took 12474 ms for 100000 operations
translation_table took 81650 ms for 100000 operations
dict_lookup took 66385 ms for 100000 operations
在什么意义上是有效的?你提出的方法有什么低效的地方(这是一个很好的Pythonic)?我同意Thijs的观点:如果你将dict存储为一个单独的引用,你只需要构造它一次并从此开始使用它。我想说这两种方法都是同样的pythonic。我感觉到过早的优化。Python的dict
速度太快了。如果你提到的东西真的很重要(度量!),Python可能不是这项工作的工具。这不是一种在Python上计时的好方法。看看timeit
。我发现dict
和if_else
的性能大致相同,这取决于if_else
输入的“运气”if_else
的多少。到目前为止,我能找到的最快的东西是:translate\u mod={“I:“+”,“E:“-”,“D:”,“M:”}.get
,然后可调用。它的性能始终比if_else
好15%。它更快的原因是,您的一次查找(点)更少,当然dict
只创建了一次。条件语句如何产生O(N)以外的结果“为什么你说的是最坏的情况?我知道,地图的解决方案只是一个例子,我认为这是一个更大的pythic。我实际上不是问我提供的两个解决方案中哪一个是最好的选择。”Kjjro:猜测这是一个多余的,固定的。。您尝试了第一件事,结果是正确的。@cWallenPole:不过,可以说,O表示法应该反映算法的最坏情况复杂性。因此,O(N)是条件版本的运行时间。from string import maketrans;translation_table=maketrans('IEDM','+-:。)
-我没有想到,但这确实是一个非常合理的解决方案。不过,我不确定它在引擎盖下如何工作;可能不会比一个dict
快。让我运行一些计时,我会让你知道。如果10万次操作需要12474毫秒,翻译表需要81650毫秒,10万次操作需要66385毫秒0000次操作
这不是在Python上计时的好方法。请看一下timeit
@Craig:查看IPython及其timeit
命令。