以编程方式生成 if elif elif elif else

Programmatically generate if elif elif elif else

我想压缩一些湿代码,如下所示:

if slips[i] < 3000:
    ac.setBackgroundColor(wheel['slip'], 0, 0, 0)
    # setBackgroundOpacity deliberately omitted here
elif slips[i] < 3700:
    ac.setBackgroundColor(wheel['slip'], .2, .4, .2)
    ac.setBackgroundOpacity(wheel['slip'], 1)
elif slips[i] < 4100:
    ac.setBackgroundColor(wheel['slip'], 0, 1, 0)
    ac.setBackgroundOpacity(wheel['slip'], 1)
elif slips[i] < 4500:
    ac.setBackgroundColor(wheel['slip'], 0, 0, 1)
    ac.setBackgroundOpacity(wheel['slip'], 1)
else:
    ac.setBackgroundColor(wheel['slip'], 1, 0, 0)
    ac.setBackgroundOpacity(wheel['slip'], 1)

每次重复这段代码时,唯一改变的是背景 canvas(本例中为 wheel['slip'])和 if elif else 中的数字。

我第一个想把它弄干的是做一些可以像这样使用的东西:

if_replacer(wheel['slip'], slips[i], 3000, 3700, 4100, 4500)

def if_replacer(canvas, value, *args):
    # idunno

我的问题是,我将如何以编程方式生成 if elif else?我知道我可以像这样对其进行硬编码:

def if_replacer(canvas, value, c1, c2, c3, c4):
    if value < c1:
        ac.setBackgroundColor(canvas, 0, 0, 0)
        return
    elif value < c2:
        ac.setBackgroundColor(canvas, .2, .4, .2)
    elif value < c3:
        ac.setBackgroundColor(canvas, 0, 1, 0)
    elif value < c4:
        ac.setBackgroundColor(canvas, 0, 0, 1)
    else:
        ac.setBackgroundColor(canvas, 1, 0, 0)
    ac.setBackgroundOpacity(canvas, 1)

但我很感兴趣是否有一种简洁的 Pythonic 方法来实现这一点。

编辑:很多优秀的答案,但遗憾的是我只能将其中一个标记为已接受(尽管所有有效的解决方案都被投票)。我接受了我在代码中实现的答案,但对于偶然发现这个问题的任何其他人,请务必查看其他解决方案,它们都非常出色。并且,感谢所有写下答案的人。

这是一种可能的解决方案。

def least_bound_index(value, bounds):
    """return the least index such that value < bounds[i], or else len(bounds)""" 
    for i, b in enumerate(bounds):
        if value < b:
            return i
    return i+1

bounds = [3000, 3700, 4100, 4500]
bgcolors = [(0, 0, 0), (.2, .4, .2), (0, 1, 0), (0, 0, 1), (1, 0, 0)]

i = least_bound_index(slips[i], bounds)
ac.setBackgroundColor(wheel['slip'], *bgcolors[i])
if i > 0:
    ac.setBackgroundOpacity(wheel['slip'], 1)

请注意,least_bound_index 也可以称为 index_between,因为您可以想象 [a, b, c, d] 在数字轴上,它会告诉您值落在何处这些选择:

  a   b   c   d
^   ^   ^   ^   ^
0   1   2   3   4

万一 'setBackgroundOpacity' 被错误地省略了,或者它是否也包含在第一种情况中并不重要,这是您可能正在寻找的解决方案:

color_map = [ (3000, 0, 0, 0), 
              (3700, .2, .4, .2), 
              (4100, 0, 1, 0), 
              (4500, 0, 0, 1), 
              (10**10, 1, 0, 0) ]

for i, (c, r, g, b) in enumerate(color_map):
    if value < c:
        ac.setBackgroundColor(wheel['slip'], r, g, b)
        if i > 0:
            ac.setBackgroundOpacity(wheel['slip'], 1)
        break

编辑:看到关于 setBackgroundOpacity 函数的评论

Edit2:修正了拼写错误并为 10**10 添加了替代解决方案

color_map = [ (3000, 0, 0, 0), 
              (3700, .2, .4, .2), 
              (4100, 0, 1, 0), 
              (4500, 0, 0, 1), 
              (float("inf"), 1, 0, 0) ]

for i, (c, r, g, b) in enumerate(color_map):
    if value < c:
        ac.setBackgroundColor(wheel['slip'], r, g, b)
        if i > 0:
            ac.setBackgroundOpacity(wheel['slip'], 1)
        break
c = [[0,0,0],[.2,.4,.2],[0,1,0],[0,0,1]]
threshold = [3000,3700,4100,4500]
if slips[i] >= 4500:
    ac.setBackgroundColor(wheel['slip'],1,0,0)
else:
    for x in range(4):
        if slips[i] < threshold[x]:
            ac.setBackgroundColor(wheel['slip'],c[x][0],c[x][1],c[x][2])
            break
if slips[i] >= 3000:
    ac.setBackgroundOpacity(wheel['slip'], 1)

这是另一种选择,但我个人更喜欢@mpurg 的回答。

我的看法(未经实际 ac... 调用测试):

from functools import partial

mapping = [
    (3000, (0, 0, 0), None),
    (3700, (.2, .4, .2), 1),
    (4100, (0, 1, 0), 1),
    (4500, (0, 0, 1), 1),
    (float('inf'), (1, 0, 0), 1)
]

def if_replacer(canvas, value, mapping):
    set_color = partial(ac.setBackgroundColor, canvas)
    set_opacity = partial(ac.setBackgroundOpacity, canvas)
    for limit, vals, opacity in lookup:
        if value < limit:
            set_color(*vals)
            if opacity is not None:
                set_opacity(opacity)
            break

然后,如果出于某种原因您确实必须选择新的范围,那么您可以执行以下操作:

from bisect import insort_left
insort_left(mapping, (4300, (1, 1, 1), 0))

这会将 mapping 更新为:

[(3000, (0, 0, 0), None),
 (3700, (0.2, 0.4, 0.2), 1),
 (4100, (0, 1, 0), 1),
 (4300, (1, 1, 1), 0),
 (4500, (0, 0, 1), 1),
 (inf, (1, 0, 0), 1)]
def problem1_5(age):
    """Prints according to ages"""
    if age(1<7):
        print("Have a glass of milk")
    elif age(7<21):
        print("Have a cake")
    elif age>21:
        print("Have a martini")
    else:
        print(end='')