2015-11-16 4 views
0

У меня есть метод, который возвращает трехэлементный кортеж логических значений, и я вызываю его в цикле. Я хочу, наконец, получить трехэлементный кортеж, содержащий результат or отдельных кортежей. Если метод только возвращается логическое значение было бы просто:Самый пифонический путь к 'или' кортежам?

result = False 
for j in some_list: # there is more processing in the loop, omitted 
    result |= method(j) 
return result 

Могу ли я обобщить это в какой-то элегантный способ или кортежей, которые method() теперь возвращается? Конечно, я мог бы сделать:

result = False, False, False 
for j in some_list: 
    res1, res2, res3 = method(j) 
    result = res1 | result[0], res2 | result[1], res3 | result[2] 
return result 

но кажется немного неэлегантным.

EDIT: Я хочу уточнить, чтобы вернуть результат в обоих случаях - первое логическое значение, то кортеж булевы

+2

У вас есть особая причина, чтобы быть с помощью побитового оператора на 'bool's? –

+0

@Josh: Это то, что использовалось в коде - нужно ли его изменить или? –

+2

Я бы так подумал. См. [Логические операторы Python vs побитовые операторы] (http://stackoverflow.com/q/3845018), если вам нужна дополнительная информация. –

ответ

2

Давайте распакуем это немного.

Нет встроенного способа сделать элемент-мудрый or (логический или побитовый) на двух кортежах. Ответ Моргана Трэппа показывает хороший способ написать свой собственный, поэтому, если вы хотите поддерживать текущее состояние в цикле for, это так, как я бы пошел. Выражение генератора будет легко понято людьми, знакомыми с Python - хотя я бы использовал tuple(a or b for a, b in zip(result, new_result)), а не a | b, если я действительно не хочу побитовой версии.

В массивах Numpy есть функция logical_or, которая работает по-элементной, но это было бы серьезным излишеством, если бы у вас было только небольшое количество кортежей с булевыми элементами.

Альтернативой сохранению запущенного состояния является сбор всех исходных кортежей и вычисление окончательного булева кортежа значений из списка кортежей результатов. Это было бы неприемлемо, если вы собираетесь закончить цикл раньше (скажем, если все элементы вашего накопленного кортежа результата равны True), или если ваш цикл имеет достаточно итераций, которые важны для использования памяти. Однако, в зависимости от ваших вкусов, это может быть яснее. Это, по сути, то же самое решение, что и выполнение общего числа числовых значений по сравнению с просто собиранием значений по мере того, как цикл запускается и суммируется впоследствии, и будет моим предпочтительным подходом, если для экземпляров вы переработаете цикл for как итератор кортежей результатов.

Это будет выглядеть примерно так:

result_list = [] 
for j in some_list: 
    result_list.append(method(j)) 
result = tuple(any(grouped) for grouped in zip(*result_list)) 

zip на звезды вспененного результата списка будет группы всего первого/второго/третье значения в списке кортежей как п длиной кортежи, где n означает число от результатов, и any эффективно or s их вместе.EG:

>>> result_list = [(False, True, False), (True, False, False), (False, False, False), (True, True, False)] 
>>> zip(*result_list) 
[(False, True, False, True), (True, False, False, True), (False, False, False, False)] 
>>> tuple(any(grouped) for grouped in zip(*result_list)) 
(True, True, False) 

С or над булевыми эквивалентно добавлением над числами, и any эквивалентно sum, вы можете рассмотреть аналогичные модели с Интс. цикл/версия множественного присваивания:

sums = (0, 0, 0) 
for j in some_list: 
    result = method(j) 
    sums[0] += result[0] 
    sums[1] += result[1] 
    sums[2] += result[2] 

против экспрессии генератора работающей версии Сумма:

sums = (0, 0, 0) 
for j in some_list: 
    result = method(j) 
    sums = (a + b for a, b in zip(sums, result)) 

против накопления и суммирования списка результатов:

result_list = [] 
for j in some_list: 
    result_list.append(method(j)) 
sums = tuple(sum(grouped) for grouped in zip(*result_list)) 

Этот вариант является особенно хорошо, если ваш цикл for не имеет никакого другого тела, так как вы можете свернуть все это на любой уровень выражений генератора/списка, который вам нравится:

result_list = [method(j) for j in some_list] 
sums = tuple(sum(grouped) for grouped in zip(*result_list)) 

или:

sums = tuple(sum(grouped) for grouped in zip(*(method(j) for j in some_list))) 
+0

Хм - поразмыслите над этим немного :) Еще бы как это адаптироваться к петле? Я имею в виду, не нужно ли еще больше строк? –

+0

Эта версия не нуждается в явном для цикла - выражение генератора ('для группировки в zip (some_list)') выполняет итерацию. –

+0

См. Цикл в моем вопросе - это большой цикл. –

6

Вы можете сделать это в списке понимание с застежкой-молнией.

result = False, True, False 
xor = True, True, False 
result = [a|b for a,b in zip(result,xor)] 
print(result) 

Или в случае вашего примера:

result = False, False, False 
for j in some_list: 
    xor = method(j) 
    result = [a|b for a,b in zip(result,xor)] 

Если он должен быть кортежем, вы можете изменить список Комп к генератору и завернуть его в tuple().

Вы также можете перевести вызов на method(j) внутри вызова в zip вместо того, чтобы назначать его промежуточной переменной. Я думаю, что это делает его немного менее читаемым, но это вопрос личных предпочтений.

+0

Я все еще нахожу его немного излишним - это всего лишь три значения ... –

+0

@Mr_and_Mrs_D Это намного эффективнее, чем делать это вручную, плюс это будет работать независимо от того, сколько у вас значений. Я не понимаю, какое решение вы считаете более «элегантным». –

+1

Вы имеете в виду, что 'result = res1 | result [0], res2 | результат [1], res3 | результат [2] 'менее эффективен, чем' result = [a | b для a, b в zip (result, xor)] '? Можете ли вы время? –

 Смежные вопросы

  • Нет связанных вопросов^_^