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