【问题标题】:Python dictionary instead of switch/casePython 字典而不是 switch/case
【发布时间】:2010-02-08 16:00:31
【问题描述】:

我最近了解到 python 没有 switch/case 语句。我一直在阅读有关使用字典的信息,例如:

values = { 
     value1: do_some_stuff1, 
     value2: do_some_stuff2, 
     valueN: do_some_stuffN,
}
values.get(var, do_default_stuff)()

我不知道如何应用它来进行范围测试。因此,如果value1=4 说,而不是做一些事情,如果value1<4. 做一些事情所以这样的事情(我知道这是行不通的):

values = { 
     if value1 <val: do_some_stuff1, 
     if value2 >val: do_some_stuff2, 
}
values.get(var, do_default_stuff)()

我试过用 if/elif/else 语句来做这件事。它工作正常,但与我根本不需要 if 语句的情况相比,它似乎要慢得多(这可能是显而易见的不可避免的事情)。所以这是我的带有 if/elif/else 语句的代码:

if sep_ang(val1,val2,X,Y)>=ROI :
    main.removeChild(source)
elif sep_ang(val1,val2,X,Y)<=5.0:
    integral=float(spectrum[0].getElementsByTagName("parameter")[0].getAttribute("free"))
    index=float(spectrum[0].getElementsByTagName("parameter")[0].getAttribute("free"))              
    print name,val1,val2,sep_ang(val1,val2,X,Y),integral,index
    print >> reg,'fk5;point(',val1,val2,')# point=cross text={',name,'}'
else:
    spectrum[0].getElementsByTagName("parameter")[0].setAttribute("free","0") #Integral
    spectrum[0].getElementsByTagName("parameter")[1].setAttribute("free","0") #Index
    integral=float(spectrum[0].getElementsByTagName("parameter")[0].getAttribute("free"))
    index=float(spectrum[0].getElementsByTagName("parameter")[0].getAttribute("free"))
    print name,val1,val2,sep_ang(val1,val2,X,Y),integral,index
    print >> reg,'fk5;point(',val1,val2,')# point=cross text={',name,'}'

检查 var sep_ang 的大约 1500 个值大约需要 5 分钟。好像我不想使用 setAttribute() 根据 sep_ang 的值更改我的 xml 文件中的值,我使用这个简单的 if else:

if sep_ang(val1,val2,X,Y)>=ROI :
    main.removeChild(source)
else:
    print name,val1,val2,ang_sep(val1,val2,X,Y);print >> reg,'fk5;point(',val1,val2,')# point

这只需要大约 30 秒。我再次知道添加 elif 语句和更改该属性的值可能会不可避免地大大增加我的代码的执行时间,我只是好奇是否有解决方法。

编辑: 在我的情况下,使用 bisect 而不是 if/elif 语句的好处是它可以比使用一堆 elif 语句更快地检查某个范围内的值吗?

看来我仍然需要使用 elif 语句。比如这样:

range=[10,100]
options='abc' 
def func(val)
     return options[bisect(range, val)]
if func(val)=a:
     do stuff
elif func(val)=b:
     do other stuff
else:
     do other other stuff

那么我的 elif 语句只检查一个值。

非常感谢您的帮助,非常感谢。

【问题讨论】:

  • 您是在比较 14 行代码和 4 行代码吗?你是在问为什么 14 行代码可能比 4 行代码慢?
  • 当然我知道运行 14 行代码比运行 4 行代码需要更长的时间(这就是为什么我说它运行得更慢可能是显而易见且不可避免的)。我不认为仅仅因为某些代码比另一个代码多 10 行,它就应该慢 10 倍。我认为我的代码大大减慢了它的速度是每个 elif 语句都必须根据我的 ang_sep 变量的 1500 个左右的值进行检查。我知道我的代码使用 elifs 会变慢,我只是好奇我做事是否效率低下,是否有更好的方法来做到这一点。

标签: python dictionary switch-statement


【解决方案1】:

字典是错误的结构。 bisect examples 展示了这种范围测试的示例。

【讨论】:

    【解决方案2】:

    虽然字典方法适用于单个值,但如果您想要范围,if ... else if ... else if 可能是最简单的方法。

    如果您正在寻找单个值,这与字典非常匹配 - 因为这是字典的用途 - 但如果您正在寻找一个范围,它就不起作用了。你可以用dict 来做到这一点:

    values = {
        lambda x: x < 4: foo,
        lambda x: x > 4: bar
    }
    

    然后循环遍历字典中的所有键值对,传递你的值键,如果键函数返回 true,则将值作为函数运行。

    但是,与许多 if 语句相比,这不会给您带来任何好处,并且更难维护和调试。所以不要这样做,而是使用if

    【讨论】:

    • 我同意,虽然这是可能的,但它不是正确的解决方案。只需使用一系列 if/else 语句。代码将更容易理解和维护。
    【解决方案3】:

    在这种情况下,您将使用 if/then/else。你也不能用开关来做到这一点。

    switch 语句的想法是你有一个值 V,你可以根据 N 个可能的结果来测试它的身份。您可以使用 if-construct 执行此操作 - 但是平均需要 O(N) 运行时间。开关每次都会给你恒定的 O(1)。

    这对于范围显然是不可能的(因为它们不容易散列),因此您可以在这些情况下使用 if-constructs。

    例子

    if value1 <val: do_some_stuff1()
    elif value2 >val: do_some_stuff2()
    

    请注意,这实际上比尝试使用字典要小。

    【讨论】:

    • 问题不在于范围难以散列,而在于当您的字典中只有一个值时不知道要在哪个范围中查找。
    • 您可能想要查找位置敏感散列。这就是为什么我写“困难”而不是“不可能”。
    • 所以你是说没有办法将同一范围内的值散列到同一散列键?
    【解决方案4】:

    dict 不适合这样做(开关也不适合!)。

    一些张贴者建议使用包含功能的 dict,但这根本不是您想要的解决方案。它是 O(n)(就像一个 if 语句),它并没有真正起作用(因为你可能有重叠的条件),是不可预测的(因为你不知道你将执行循环的顺序),并且不太清楚比等效的 if 语句。如果您有一个简短的、静态长度的条件列表来应用,那么 if 语句可能是您想要的方式。

    如果您有大量条件,或者它们可能会因您的程序而改变,您需要不同的数据结构。您可以实现二叉树或保持已排序的list 并使用bisect 模块查找与给定范围关联的值。

    【讨论】:

      【解决方案5】:

      我不知道任何可行的解决方案。如果您想使用 guess what it does 方法,尽管您可以执行以下操作:

      obsure_switch = {
           lambda x: 1<x<6 : some_function,
           ...
      }
      
      [action() for condition,action in obscure_switch.iteritems() if condition(var)]
      

      【讨论】:

      • 这完全违背了使用字典获得 O(1) 的想法。
      【解决方案6】:

      终于想通了!

      所以我没有使用一堆 elif 语句,而是这样做了:

      range=[10,100]
      options='abc' 
      def func(val)
           choose=str(options[bisect(range,val)])
           exec choose+"()"
      def a():
            do_stuff
      def b():
            do_other_stuff
      def c():
            do_other_other stuff
      

      它不仅有效,而且运行速度几乎与我没有更改任何值的原始 4 行代码一样快!

      【讨论】:

      • 使用函数列表,而不是exec
      猜你喜欢
      • 2021-08-05
      • 1970-01-01
      • 2020-10-14
      • 1970-01-01
      • 2010-10-10
      • 2011-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多