Подтвердить что ты не робот

Элегантное назначение Python на основе значений True/False

У меня есть переменная, которую я хочу установить в зависимости от значений в трех булевых. Самый простой способ - это утверждение 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'

Это немного неудобно, и мне интересно, есть ли более путинский способ справиться с этой проблемой. Несколько идей приходят на ум.

  • Словарь hack:

    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]
    

Я называю это взломом, потому что я не слишком одинок, когда семь из ключей являются False и переопределяют друг друга.

  • И/или магия

    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 возвращают последнее значение, которое нужно оценить, но вы должны знать, что для понимания этого другого причудливого кода.

Ни один из этих трех вариантов не очень удовлетворителен. Что вы рекомендуете?

4b9b3361

Ответ 1

Вы можете думать о a, b и c как о трех битах, которые при объединении образуют число от 0 до 7. Затем вы можете иметь массив значений ['first', 'second',... "восьмой" ] и использовать значение бита в качестве смещения в массиве. Это будет всего лишь две строки кода (один для сборки битов в значение от 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)]

Ответ 2

Как насчет использования dict?

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.

Ответ 3

Возможно, не намного лучше, но как насчет

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

Ответ 4

если 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]

идея от Клинта Миллера

Ответ 5

Поскольку вы получаете все комбинации, вы можете создать индекс, основанный на таких значениях:

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

Ответ 6

Как насчет вложенных 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"

Ответ 7

Другой вариант - создать вспомогательную функцию:

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'))

Этот метод предполагает, что один из пройденных тестов будет истинным. Его также можно было сделать более ленивым с лямбдами.

Ответ 8

Для измерения скоростей:

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

Ответ 9

Я бы выбрал решение для списка/бит @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

Ответ 10

если ваша цель - не писать много "ands" и boolean выражений, вы можете использовать простое число и только одно условие, подобное этому (пример для 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

Этот хак можно использовать для 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]

Ответ 11

Вот подход таблицы истинности:

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