我怎样才能更简洁地find缺失的值?
下面的代码检查x
和y
是否是不同的值(variablesx
, y
, z
只能有值a
, b
或c
),如果是,则将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
来检查x和y是不同的,那么它可以添加一个警卫。 以下是改进后的答案:
# 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"
,而是2
和3
,那么也可以使用二元XOR:
z = x ^ y
更一般地说,如果你想把z
设置为三个数字a
, b
和c
剩余的一个,就可以使用这个集合中的两个数字x
和y
,你可以使用
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 ,因为所有六个比较都必须尝试。 用x
和y
所有值进行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'