Элегантное назначение Python на основе значений True / False - PullRequest
28 голосов
/ 18 января 2011

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

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

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

Это работает, потому что Python ands and orsвернуть последнее значение, которое нужно оценить, но вы должны знать, что для понимания этого странного кода в противном случае.

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

Ответы [ 11 ]

50 голосов
/ 18 января 2011

Вы можете думать о 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)]
29 голосов
/ 18 января 2011

Как насчет использования 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.
11 голосов
/ 18 января 2011

Может быть, не намного лучше, но как насчет

results = ['first', 'second', 'third', 'fourth', 
           'fifth', 'sixth', 'seventh', 'eighth']
name = results[((not a) << 2) + ((not b) << 1) + (not c)]
5 голосов
/ 18 января 2011

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

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

2 голосов
/ 18 января 2011

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

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
1 голос
/ 19 января 2011

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

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
1 голос
/ 19 января 2011

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

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

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

1 голос
/ 18 января 2011

Как насчет вложенных 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"
0 голосов
/ 12 марта 2015

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

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)]
0 голосов
/ 02 марта 2012

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