-1

根据有两种指定规则的方法,我有一些值要重新映射。简单的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)
4

5 回答 5

2

映射将导致 O(1) 查找。条件将导致 O(N)。当然,如果你想对它进行所有的挑剔,你的地图实现中还有一个额外的函数调用需要考虑。

我没有进行理论化,而是运行了一个快速基准测试。结果(删除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}

现在诚然,这仅使用给定的情况。我的假设是,如果条件的运行时间会随着案例的数量线性增加,而地图查找将保持不变。

于 2013-10-18T20:09:00.607 回答
1

如果您有固定且少量的案例,则该if-elif-else方法是最快的(我不明白为什么它会不符合 Pythonic);对于足够多的情况,dict查找会更好。

如果这组案例是动态的,那么该dict方法当然是唯一可行的方法。

此外,第三种非正统方法是使用宏元编程。这对于 vanilla python 是不可能的,但是有一些库,例如https://github.com/lihaoyi/macropy允许您以(可以说)干净的方式执行此操作(很可能没有得到 Python 社区或 Guido 的批准)。

PS 再三考虑,宏方法在 Python 中可能不会像在 Lisp 中那样在大多数 Python 宏实现的情况下工作,它试图坚持原生 Python 的语法;即if-elif-else不可能从宏中生成块。

于 2013-10-18T20:12:35.837 回答
1

请记住,多级if语句会生成大量字节码,并且主要在 Python 级别进行评估,而字典访问发生在解释器的优化 C 代码中。此外,如果存在可能性,该if语句需要平均比较:它必须按顺序检查每种可能性。n/2n

故事的寓意: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
于 2013-10-18T20:41:25.167 回答
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
于 2013-10-18T21:03:19.107 回答
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)

输出:

-+:.
于 2013-10-18T20:18:43.327 回答