Как я могу найти недостающее значение более кратко?
Следующий код проверяет, являются ли x
и y
различными значениями (переменные x
, 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'
Возможно ли это сделать более, кратким, читаемым и эффективным способом?
Ответы
Ответ 1
z = (set(("a", "b", "c")) - set((x, y))).pop()
Я предполагаю, что один из трех случаев в вашем коде. Если это так, набор set(("a", "b", "c")) - set((x, y))
будет состоять из одного элемента, который возвращается pop()
.
Изменить: Как было предложено Раймондом Хеттингером в комментариях, вы также можете использовать распаковку для распаковки одного элемента из набора:
z, = set(("a", "b", "c")) - set((x, y))
Ответ 2
Метод strip
- это еще один вариант, который быстро выполняется для меня:
z = 'abc'.strip(x+y) if x!=y else None
Ответ 3
Превосходный код Sven сделал немного слишком много работы и, возможно, использовал распаковку кортежей вместо 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}
Вот сравнительные тайминги с набором времени, чтобы показать относительную производительность:
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-инструкции запускаются различными входными значениями.
Ответ 4
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'
Ответ 5
Если три элемента, о которых идет речь, не были "a"
, "b"
и "c"
, а скорее 1
, 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'
Не ожидайте, что кто-нибудь, прочитав этот код, сразу выяснит, что это значит:)
Ответ 6
Я думаю, что решение Sven Marnach и F.J красиво, но это не ускорилось в моем маленьком тесте. Это оптимизированная версия Raymond с использованием предварительно вычисленного 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
Это оригинальное решение:
$ 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, так как все шесть сравнений должны быть опробованы. Тестирование со всеми значениями для x
и y
дает:
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
показывает ту же производительность для разных входов, но он последовательно находится между 2 и 8 раз медленнее. Причина в том, что вариант с if
имеет гораздо более простой код: тесты равенства по сравнению с хешированием.
Я думаю, что оба типа решений ценны: важно знать, что создание "сложных" структур данных, таких как наборы, требует вам чего-то в производительности, в то время как они дают вам много читаемости и скорости разработки. Сложные типы данных также намного лучше при изменении кода: легко расширить решение на основе набора до четырех, пяти,... переменных, тогда как if-утверждения быстро превращаются в кошмар обслуживания.
Ответ 7
z = 'a'*('a' not in x+y) or 'b'*('b' not in x+y) or 'c'
или менее хаки и использование условного присваивания
z = 'a' if ('a' not in x+y) else 'b' if ('b' not in x+y) else 'c'
но, вероятно, решение dict быстрее... вам придется его время.
Ответ 8
Попробуйте эту опцию, используя словари:
z = {'ab':'c', 'ba':'c', 'bc':'a', 'cb':'a', 'ac':'b', 'ca':'b'}[x+y]
Конечно, если ключ x+y
отсутствует на карте, он создаст KeyError
, который вам придется обрабатывать.
Если словарь предварительно вычисляется за один раз и сохраняется для будущего использования, доступ будет намного быстрее, так как для каждой оценки не потребуется создавать новые структуры данных, требуется только конкатенация строк и поиск по словарю:/p >
lookup_table = {'ab':'c', 'ba':'c', 'bc':'a', 'cb':'a', 'ac':'b', 'ca':'b'}
z = lookup_table[x+y]
Ответ 9
Я думаю, это должно выглядеть так:
z = (set(("a", "b", "c")) - set((x, y))).pop() if x != y else None
Ответ 10
Используя понимание списков, предполагая, что у вас есть один из трех случаев в вашем коде:
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]]
Ответ 11
Посмотрите, работает ли это
if a not in xy
z= 'a'
if b not in xy
z='b'
if c not in xy
z='c'