基于True / False值的Python优雅赋值

时间:2011-01-18 17:16:58

标签: python if-statement boolean

我有一个我想要设置的变量,具体取决于三个布尔值中的值。最直接的方式是if语句后跟一系列elifs:

if a and b and c:
    name = 'first'
elif a and b and not c:
    name = 'second'
elif a and not b and c:
    name = 'third'
elif a and not b and not c:
    name = 'fourth'
elif not a and b and c:
    name = 'fifth'
elif not a and b and not c:
    name = 'sixth'
elif not a and not b and c:
    name = 'seventh'
elif not a and not b and not c:
    name = 'eighth'

这有点尴尬,我想知道是否有更多的Pythonic方法来处理这个问题。想到了几个想法。

  1. 字典黑客:

    name = {a and b and c: 'first',
            a and b and not c: 'second',
            a and not b and c: 'third',
            a and not b and not c: 'fourth',
            not a and b and c: 'fifth',
            not a and b and not c: 'sixth',
            not a and not b and c: 'seventh',
            not a and not b and not c: 'eighth'}[True]
    
  2. 我把它称之为黑客,因为我并不太疯狂,其中七个键是假的并且相互压倒。

    1. 和/或魔术

      name = (a and b and c and 'first' or
              a and b and not c and 'second' or
              a and not b and c and 'third' or
              a and not b and not c and 'fourth' or
              not a and b and c and 'fifth' or
              not a and b and not c and 'sixth' or
              not a and not b and c and 'seventh' or
              not a and not b and not c and 'eighth')
      
    2. 这是有效的,因为Python ands和ors返回要评估的最后一个值,但你必须知道这是为了理解这个奇怪的代码。

      这三个选项中没有一个非常令人满意。你推荐什么?

11 个答案:

答案 0 :(得分:50)

您可以将a,b和c视为三位,当它们组合在一起形成0到7之间的数字。然后,您可以得到一个值数组['first','second',... 'eight']并使用位值作为数组的偏移量。这只是两行代码(一行用于将位组装成0-7的值,一行用于查找数组中的值)。

以下是代码:

nth = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
nth[(a and 4 or 0) | (b and 2 or 0) | (c and 1 or 0)]

答案 1 :(得分:29)

如何使用字典?

name = {(True, True, True): "first", (True, True, False): "second",
        (True, False, True): "third", (True, False, False): "fourth",
        (False, True, True): "fifth", (False, True, False): "sixth",
        (False, False, True): "seventh", (False, False, False): "eighth"}

print name[a,b,c] # prints "fifth" if a==False, b==True, c==True etc.

答案 2 :(得分:11)

也许不是更好,但是怎么样

results = ['first', 'second', 'third', 'fourth', 
           'fifth', 'sixth', 'seventh', 'eighth']
name = results[((not a) << 2) + ((not b) << 1) + (not c)]

答案 3 :(得分:5)

如果a,b,c真的是布尔值:

li = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
name = li[a*4 + b*2 + c]

如果他们不是布尔人:

li = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
a,b,c = map(bool,(a,b,c))
name = li[a*4 + b*2 + c]
来自Clint Miller的想法

答案 4 :(得分:2)

由于获得了所有组合,您可以根据以下值创建索引:

def value(a,b,c ): 
   values = ['8th','7th','6th','5th','4th','3rd','2nd','1st']
   index = ( 4 if a else 0 ) + ( 2 if b else 0 ) + ( 1 if c else 0 )
   return values[index]

if __name__ == "__main__":
   print value(True,  True,  True )
   print value(True,  True,  False )
   print value(True,  False, True )
   print value(True,  False, False )
   print value(False, True,  True )
   print value(False, True,  False)
   print value(False, False, True )
   print value(False, False, False)

输出:

1st
2nd
3rd
4th
5th
6th
7th
8th

答案 5 :(得分:1)

嵌套ifs怎么样 - 这意味着你不必多次检查所有内容并且对我来说更清楚(尽管可能不像其他一些答案那样聪明):

if a:
    if b:
        if c:
            name="first"
        else:
            name="second"
    else:
        if c:
            name="third"
        else:
            name="fourth"
else:
    if b:
        if c:
            name="fifth"
        else:
            name="sixth"
    else:
        if c:
            name="seventh"
        else:
            name="eighth"

答案 6 :(得分:1)

另一种选择是创建辅助函数:

def first_true(*args):
    true_vals = (arg for arg in args if arg[0])
    return next(true_vals)[1]

name = first_true((a and b and c, 'first'),
                  (a and b and not c, 'second'),
                  (a and not b and c, 'third'),
                  (a and not b and not c, 'fourth'),
                  (not a and b and c, 'fifth'),
                  (not a and b and not c, 'sixth'),
                  (not a and not b and c, 'seventh'),
                  (not a and not b and not c, 'eighth'))

此方法假定传入的其中一个测试将为true。也可以用lambdas使它变得更加懒散。

答案 7 :(得分:1)

测量速度:

from time import clock
a,b,c = True,False,False

A,B,C,D,E,F,G,H = [],[],[],[],[],[],[],[]


for j in xrange(30):


    te = clock()
    for i in xrange(10000):
        name = (a and b and c and 'first' or
                a and b and not c and 'second' or
                a and not b and c and 'third' or
                a and not b and not c and 'fourth' or
                not a and b and c and 'fifth' or
                not a and b and not c and 'sixth' or
                not a and not b and c and 'seventh' or
                not a and not b and not c and 'eighth')
    A.append(clock()-te)



    te = clock()
    for i in xrange(10000):
        if a and b and c:
            name = 'first'
        elif a and b and not c:
            name = 'second'
        elif a and not b and c:
            name = 'third'
        elif a and not b and not c:
            name = 'fourth'
        elif not a and b and c:
            name = 'fifth'
        elif not a and b and not c:
            name = 'sixth'
        elif not a and not b and c:
            name = 'seventh'
        elif not a and not b and not c:
            name = 'eighth'
    B.append(clock()-te)

    #=====================================================================================

    li = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
    te = clock()
    for i in xrange(10000):
        name = li[a*4 + b*2 + c]
    C.append(clock()-te)

    #=====================================================================================

    nth = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
    te = clock()
    for i in xrange(10000):
        name = nth[(a and 4 or 0) | (b and 2 or 0) | (c and 1 or 0)]
    D.append(clock()-te)


    nth = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
    te = clock()
    for i in xrange(10000):
        name = nth[(a and 4 or 0) + (b and 2 or 0) + (c and 1 or 0)]
    E.append(clock()-te)

    #=====================================================================================

    values = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
    te = clock()
    for i in xrange(10000):
        name = values[( 4 if a else 0 )| ( 2 if b else 0 ) | ( 1 if c else 0 )]
    F.append(clock()-te)


    values = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
    te = clock()
    for i in xrange(10000):
        name = values[( 4 if a else 0 ) + ( 2 if b else 0 ) + ( 1 if c else 0 )]
    G.append(clock()-te)

    #=====================================================================================

    dic = {(True, True, True): "first",
           (True, True, False): "second",
           (True, False, True): "third",
           (True, False, False): "fourth",
           (False, True, True): "fifth",
           (False, True, False): "sixth",
           (False, False, True): "seventh",
           (False, False, False): "eighth"}
    te = clock()
    for i in xrange(10000):
        name = dic[a,b,c]
    H.append(clock()-te)




print min(A),'\n', min(B),'\n\n', min(C),'\n\n', min(D),'\n',min(E),'\n\n',min(F),'\n', min(G),'\n\n', min(H)

结果

0.0480533140385 
0.0450973517584 

0.0309056039245 

0.0295291720037 
0.0286550385594 

0.0280122194301 
0.0266760160858 

0.0249769174574

答案 8 :(得分:0)

我会选择@OscarRyz,@ Clint和@Sven的列表/位解决方案,但这是另一个:


S1 = frozenset(['first', 'second', 'third', 'fourth'])
S2 = frozenset(['first', 'second', 'fifth', 'sixth'])
S3 = frozenset(['first', 'third', 'fifth', 'seventh'])
last = 'eighth'
empty = frozenset([])

def value(a, b, c): for r in (a and S1 or empty) & (b and S2 or empty) & (c and S3 or empty): return r return last

答案 9 :(得分:0)

如果你的目标是避免编写大量的“ands”和布尔表达式,你可以使用素数而只有一个这样的条件(2个条件的例子)

 cond = (2**cond_1)*(3**cond_2)

所以

cond == 1 #means cond_1 and cond_2 are False
cond == 2 #means cond_1 is True and con_2 is False
cond == 3 #means cond_1 is False and con_2 is True
cond == 6 #means con_1 and Con_2 are True

这个hack可以用于3个条件,使用3个素数等等

喜欢这个......

cond = (2**a)*(3**b)*(5**c)
name = {30:'first', 6: 'second', 10:'third', 2:'fourth',
        15:'fifth', 3:'sixth', 5:'seventh', 1:'eighth'}[cond]

答案 10 :(得分:-1)

这是一个真值表方法:

lookup = {'000': 'eighth',
          '001': 'seventh',
          '010': 'sixth',
          '011': 'fifth',
          '100': 'fourth',
          '101': 'third',
          '110': 'second',
          '111': 'first'}

def key(a, b, c):
    return ''.join([str(a),str(b),str(c)])

name = lookup[key(0,1,1)]