我怎样才能更简洁地find缺失的值?

下面的代码检查xy是否是不同的值(variablesxyz只能有值abc ),如果是,则将z设置为第三个字符:

 if x == 'a' and y == 'b' or x == 'b' and y == 'a': z = 'c' elif x == 'b' and y == 'c' or x == 'c' and y == 'b': z = 'a' elif x == 'a' and y == 'c' or x == 'c' and y == 'a': z = 'b' 

是否有可能以更简洁,可读和高效的方式来做到这一点?

 z = (set(("a", "b", "c")) - set((x, y))).pop() 

我假设您的代码中的三个案件之一成立。 如果是这样的话, set(("a", "b", "c")) - set((x, y))将由pop()返回的单个元素组成。

编辑:正如Raymond Hettinger在评论中所build议的那样,你也可以使用tuple unpacking从集合中提取单个元素:

 z, = set(("a", "b", "c")) - set((x, y)) 

strip方法是另一种选项,可以为我快速运行:

 z = 'abc'.strip(x+y) if x!=y else None 

Sven的优秀的代码只是做了太多的工作,并且使用tuple解包而不是pop() 。 另外, if x != y来检查xy是不同的,那么它可以添加一个警卫。 以下是改进后的答案:

 # create the set just once choices = {'a', 'b', 'c'} x = 'a' y = 'b' # the main code can be used in a loop if x != y: z, = choices - {x, y} 

下面是与时间套件比较的时间表示相对performance:

 import timeit, itertools setup_template = ''' x = %r y = %r choices = {'a', 'b', 'c'} ''' new_version = ''' if x != y: z, = choices - {x, y} ''' original_version = ''' if x == 'a' and y == 'b' or x == 'b' and y == 'a': z = 'c' elif x == 'b' and y == 'c' or x == 'c' and y == 'b': z = 'a' elif x == 'a' and y == 'c' or x == 'c' and y == 'a': z = 'b' ''' for x, y in itertools.product('abc', repeat=2): print '\nTesting with x=%r and y=%r' % (x, y) setup = setup_template % (x, y) for stmt, name in zip([original_version, new_version], ['if', 'set']): print min(timeit.Timer(stmt, setup).repeat(7, 100000)), print '\t%s_version' % name 

以下是时间的结果:

 Testing with x='a' and y='a' 0.0410830974579 original_version 0.00535297393799 new_version Testing with x='a' and y='b' 0.0112571716309 original_version 0.0524711608887 new_version Testing with x='a' and y='c' 0.0383319854736 original_version 0.048309803009 new_version Testing with x='b' and y='a' 0.0175108909607 original_version 0.0508949756622 new_version Testing with x='b' and y='b' 0.0386209487915 original_version 0.00529098510742 new_version Testing with x='b' and y='c' 0.0259420871735 original_version 0.0472128391266 new_version Testing with x='c' and y='a' 0.0423510074615 original_version 0.0481910705566 new_version Testing with x='c' and y='b' 0.0295209884644 original_version 0.0478219985962 new_version Testing with x='c' and y='c' 0.0383579730988 original_version 0.00530385971069 new_version 

这些时间表明, 原始版本的性能取决于哪些if语句由各种input值触发而变化很大。

 z = (set('abc') - set(x + y)).pop() 

这里有所有的情况来表明它的工作原理:

 >>> (set('abc') - set('ab')).pop() # x is a/b and y is b/a 'c' >>> (set('abc') - set('bc')).pop() # x is b/c and y is c/b 'a' >>> (set('abc') - set('ac')).pop() # x is a/c and y is c/a 'b' 

如果三个问题不是"a""b""c" ,而是23 ,那么也可以使用二元XOR:

 z = x ^ y 

更一般地说,如果你想把z设置为三个数字abc剩余的一个,就可以使用这个集合中的两个数字xy ,你可以使用

 z = x ^ y ^ a ^ b ^ c 

当然,如果数字是固定的,你可以预先计算a ^ b ^ c

这种方法也可以与原始字母一起使用:

 z = chr(ord(x) ^ ord(y) ^ 96) 

例:

 >>> chr(ord("a") ^ ord("c") ^ 96) 'b' 

不要期望任何人读这个代码,立即弄清楚它是什么意思:)

我认为Sven Marnach和FJ的解决scheme很漂亮,但在我的小testing中速度并不快。 这是使用预先计算的set雷蒙德优化版本:

 $ python -m timeit -s "choices = set('abc')" \ -s "x = 'c'" \ -s "y = 'a'" \ "z, = choices - set(x + y)" 1000000 loops, best of 3: 0.689 usec per loop 

这是原来的解决scheme:

 $ python -m timeit -s "x = 'c'" \ -s "y = 'a'" \ "if x == 'a' and y == 'b' or x == 'b' and y == 'a':" \ " z = 'c'" \ "elif x == 'b' and y == 'c' or x == 'c' and y == 'b':" \ " z = 'a'" \ "elif x == 'a' and y == 'c' or x == 'c' and y == 'a':" \ " z = 'b'" 10000000 loops, best of 3: 0.310 usec per loop 

请注意,这是if -statements 可能最糟糕的input ,因为所有六个比较都必须尝试。 用xy所有值进行testing得出:

 x = 'a', y = 'b': 0.084 usec per loop x = 'a', y = 'c': 0.254 usec per loop x = 'b', y = 'a': 0.133 usec per loop x = 'b', y = 'c': 0.186 usec per loop x = 'c', y = 'a': 0.310 usec per loop x = 'c', y = 'b': 0.204 usec per loop 

基于set的变体显示不同的input相同的性能,但它一直慢2到8倍 。 原因在于基于if的变体运行更简单的代码:与哈希相比,相等性testing。

我认为这两种解决scheme都是有价值的:重要的是要知道创build“复杂”的数据结构像集合会降低性能,同时还会带来很多可读性和开发速度 。 当代码改变时,复杂的数据types也会好得多:将基于集合的解决scheme扩展到四个,五个…variables是很容易的,而if语句很快就会变成维护的噩梦。

试试这个选项,使用字典:

 z = {'ab':'c', 'ba':'c', 'bc':'a', 'cb':'a', 'ac':'b', 'ca':'b'}[x+y] 

当然,如果map中不存在x+y键,它将产生一个KeyError ,你必须处理。

如果字典是预先计算一次并存储以供将来使用,访问将快得多,因为不必为每个评估创build新的数据结构,只需要string连接和字典查找:

 lookup_table = {'ab':'c', 'ba':'c', 'bc':'a', 'cb':'a', 'ac':'b', 'ca':'b'} z = lookup_table[x+y] 
 z = 'a'*('a' not in x+y) or 'b'*('b' not in x+y) or 'c' 

或更less的骇人听闻,并使用条件分配

 z = 'a' if ('a' not in x+y) else 'b' if ('b' not in x+y) else 'c' 

但可能字典解决scheme更快…你不得不这样做。

我认为它应该是这样的:

 z = (set(("a", "b", "c")) - set((x, y))).pop() if x != y else None 

使用列表理解,假设你的代码中的三种情况之一就像其他人一样:

 l = ['a', 'b', 'c'] z = [n for n in l if n not in [x,y]].pop() 

或者,就像在接受的答案中那样,利用元组来解开它,

 z, = [n for n in l if n not in [x,y]] 

看看这是否有效

 if a not in xy z= 'a' if b not in xy z='b' if c not in xy z='c'