我有一些值要重新映射,因为有两种方法可以指定规则。简单的if
/ else
方法似乎是更有效的变体,但我想知道是否存在同样有效但更加pythonic的方法?
if mod == "I": mod = "+"
elif mod == "E": mod = "-"
elif mod == "D": mod = ":"
elif mod == "M": mod = "."
不那么有效的映射方法:
mod = { "I":"+", "E":"-", "D":":", "M":"." }.get(mod, mod)
答案 0 :(得分:2)
映射将导致O(1)查找。条件将导致O(N)。当然,如果你想得到所有关于它的挑剔,你的地图实现中还有额外的函数调用。
而不是理论化,I ran a quick benchmark。结果(删除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}
现在可以肯定的是,这只使用了给定的案例。我的假设是,如果条件的运行时间将随着案例的数量线性增加,而地图查找将保持不变。
答案 1 :(得分:1)
如果你有固定的少量案例,if-elif-else
方法是最快的(我不明白为什么它会是非Pythonic);对于足够多的案例,dict
查找会更好。
如果案例集是动态的,dict
方法当然是唯一可行的方法。
另外,第三种非正统的方法是使用宏观元编程。这对于vanilla python是不可能的,但是有一些库,例如https://github.com/lihaoyi/macropy允许您以(可以说是)干净的方式执行此操作(很可能未经Python社区或Guido批准)。
P.S。第二个想法,宏观方法可能不适用于Python,就像在Python的大多数宏实现的情况下一样,它试图坚持使用vanilla Python的语法;即,从宏内生成if-elif-else
块是不可能的。
答案 2 :(得分:1)
请记住,多级if
语句会生成大量字节代码,主要在Python级别进行评估,而字典访问则在解释器的优化C代码中进行。此外,if
语句需要平均n/2
次比较,如果有n
种可能性:它必须按顺序检查每种可能性。
故事的寓意: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
答案 3 :(得分:0)
字符串方法具有可用的转换功能。您必须构建一个长度为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)
输出:
-+:.
答案 4 :(得分:0)
这不是一个真正的答案,但很多评论集中在表现上,因为我问过。所以我到目前为止对答案进行了一些性能测试:
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)
结果如下:
if_else took 12474 ms for 100000 operations
translation_table took 81650 ms for 100000 operations
dict_lookup took 66385 ms for 100000 operations