2015-11-16 1 views
0

J'ai une méthode qui retourne un tuple à trois éléments de valeurs booléennes et je l'appelle sur une boucle. Je veux enfin obtenir un tuple à trois éléments contenant le résultat or des tuples individuels. Si la méthode ne retourne un booléen il serait juste:La façon la plus pythonique de 'ou' tuples?

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

Puis-je généraliser ce d'une manière élégante ou les tuples qui method() retourne maintenant? Bien sûr, je pourrais faire:

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 

mais semble un peu inélégant.

EDIT: Je veux clarifier retourner le résultat dans les deux cas - d'abord un booléen puis un tuple de booléens

+2

Avez-vous une raison particulière d'être en utilisant l'opérateur OR sur 'bool's? –

+0

@Josh: C'est ce qui était utilisé dans le code - devrais-je le changer ou? –

+2

Je le pense. Voir [Opérateurs booléens Python vs opérateurs bitwise] (http://stackoverflow.com/q/3845018) si vous souhaitez plus d'informations. –

Répondre

2

Déballez-le un peu.

Il n'y a pas de manière intégrée de faire or par élément (logique ou bit à bit) sur deux tuples. La réponse de Morgan Thrapp montre un bon moyen d'écrire le vôtre, donc si vous voulez maintenir un état de fonctionnement dans votre boucle for, c'est comme ça que j'irais. L'expression du générateur va être facilement comprise par les personnes familières avec Python - bien que j'utilise tuple(a or b for a, b in zip(result, new_result)) plutôt que a | b si je ne veux pas réellement la version bitwise.

Les tableaux numériques ont une fonction logical_or qui fonctionne par élément, mais ce serait trop grave si vous n'aviez qu'un nombre modéré de tuples de booléens.

Une alternative à la conservation d'un état en cours consiste à collecter tous les tuples de résultat et à calculer le tuple final de la valeur booléenne à partir de la liste des tuples de résultat. Cela ne serait pas approprié si vous voulez terminer la boucle tôt (par exemple, si tous les éléments de votre tuple de résultat cumulé sont True) ou si votre boucle a suffisamment d'itérations que l'utilisation de la mémoire a de l'importance. Selon vos goûts, cependant, il pourrait être plus clair. C'est essentiellement la même décision que de conserver un total cumulé de valeurs numériques plutôt que de simplement collecter les valeurs lorsque la boucle s'exécute et que vous additionnez, et ce serait mon approche préférée si vous retravailliez votre boucle for comme itérateur de tuples de résultat.

qui ressemblerait à quelque chose comme:

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

zip sur la liste des résultats d'étoile élargie regroupera tous les première/deuxième/troisième valeurs de votre liste de tuples comme tuples de longueur n où n est le nombre des résultats, et any effectivement or s ensemble.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) 

Depuis or plus booléens est équivalent à l'addition sur le nombre et any équivaut à sum, vous pouvez envisager des modèles similaires avec ints. La boucle for/multiple version attribution:

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

vs la version somme courante expression du générateur:

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

vs accumuler et additionner la liste des résultats:

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

Cette version est particulièrement bien si votre boucle for n'a pas d'autre corps, puisque vous pouvez réduire le tout dans n'importe quel niveau d'expressions de générateur/liste de compréhension que vous aimez:

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

ou:

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

Hmm - réfléchira un peu :) Encore comment cela s'adapterait-il à la boucle? Je veux dire, ça n'aurait pas besoin de plus de lignes? –

+0

Cette version n'a pas besoin d'une boucle for explicite - l'expression du générateur ('for grouped in zip (liste_liste)') prend en charge l'itération. –

+0

Voir la boucle dans ma question - c'est une grande boucle –

6

Vous pouvez le faire dans une compréhension de liste avec zip.

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

Ou dans le cas de votre exemple:

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

Si elle doit être un tuple, vous pouvez modifier la liste maquette à un générateur et l'envelopper dans tuple().

Vous pouvez également déplacer l'appel vers method(j) dans l'appel de zip au lieu de l'affecter à une variable intermédiaire. Je pense que c'est un peu moins lisible, mais c'est une question de préférence personnelle.

+0

Je trouve toujours ça un peu exagéré - c'est juste trois valeurs ... –

+0

@Mr_and_Mrs_D C'est beaucoup plus efficace que de le faire manuellement, et cela fonctionnera, peu importe le nombre de valeurs que vous avez. Je ne comprends pas quelle solution vous pensez être plus "élégante". –

+1

Vous voulez dire que 'result = res1 | résultat [0], res2 | résultat [1], res3 | le résultat [2] 'est moins efficace que' result = [a | b pour a, b dans zip (résultat, xor)] '? Pouvez-vous le chronométrer? –