关于if语句:基于True / False值的Python优雅赋值

Python elegant assignment based on True/False values

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
    2
    3
    4
    5
    6
    7
    8
    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]
  • 我把它称之为黑客,因为我并不太疯狂,因为我认为其中七个键是假的并且相互重叠。

  • 和/或魔术

    1
    2
    3
    4
    5
    6
    7
    8
    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')
  • 这是有效的,因为Python ands和ors返回要评估的最后一个值,但你必须知道这是为了理解这个奇怪的代码。

    这三个选项都不令人满意。 您有什么推荐的吗?


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

    这是代码:

    1
    2
    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)]


    用dict怎么样?

    1
    2
    3
    4
    5
    6
    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.


    也许不是更好,但怎么样

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


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

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

    如果他们不是布尔人:

    1
    2
    3
    li = ['eighth', 'seventh', 'sixth', 'fifth', 'fourth', 'third', 'second', 'first']
    a,b,c = map(bool,(a,b,c))
    name = li[a*4 + b*2 + c]

    克林特米勒的想法


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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)

    输出:

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

    要测量速度:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    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),'
    '
    , min(B),'

    '
    , min(C),'

    '
    , min(D),'
    '
    ,min(E),'

    '
    ,min(F),'
    '
    , min(G),'

    '
    , min(H)

    结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    0.0480533140385
    0.0450973517584

    0.0309056039245

    0.0295291720037
    0.0286550385594

    0.0280122194301
    0.0266760160858

    0.0249769174574


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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使它更加懒散。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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"


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

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

    所以

    1
    2
    3
    4
    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个素数等等

    像这样...

    1
    2
    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]

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    S1 = frozenset(['first', 'second', 'third', 'fourth'])
    S2 = frozenset(['first', 'second', 'fifth', 'sixth'])
    S3 = frozenset(['first', 'third', 'fifth', 'seventh'])
    last = 'eighth'
    empty = frozenset([])
    </p>

    <p>
    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
    </p>

    <p>


    这是一个真值表方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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)]