web-dev-qa-db-fra.com

Existe-t-il une manière plus élégante d'exprimer ((x == a et y == b) ou (x == b et y == a))?

J'essaie d'évaluer ((x == a and y == b) or (x == b and y == a)) en Python, mais cela semble un peu bavard. Existe-t-il une manière plus élégante?

108

Si les éléments sont hachables, vous pouvez utiliser des ensembles:

{a, b} == {y, x}
151
Dani Mesejo

Je pense que le mieux que vous puissiez obtenir est de les regrouper en tuples:

if (a, b) == (x, y) or (a, b) == (y, x)

Ou, peut-être envelopper cela dans une recherche d'ensemble

if (a, b) in {(x, y), (y, x)}

Tout comme cela a été mentionné par quelques commentaires, j'ai fait quelques synchronisations, et les tuples et les ensembles semblent fonctionner de manière identique ici lorsque la recherche échoue:

from timeit import timeit

x = 1
y = 2
a = 3
b = 4

>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
32.8357742

>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
31.6169182

Bien que les tuples soient en fait plus rapides lorsque la recherche réussit:

x = 1
y = 2
a = 1
b = 2

>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
35.6219458

>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
27.753138700000008

J'ai choisi d'utiliser un ensemble parce que je fais une recherche d'adhésion, et conceptuellement, un ensemble est mieux adapté à ce cas d'utilisation qu'un tuple. Si vous avez mesuré une différence significative entre les deux structures dans un cas d'utilisation particulier, optez pour la plus rapide. Je ne pense pas que la performance soit un facteur ici.

59
Carcigenicate

Les tuples le rendent légèrement plus lisible:

(x, y) == (a, b) or (x, y) == (b, a)

Cela donne un indice: nous vérifions si la séquence x, y est égal à la séquence a, b mais en ignorant la commande. C'est juste définir l'égalité!

{x, y} == {a, b}
31
Thomas

Si les articles ne sont pas lavables, mais prennent en charge les comparaisons de commande, vous pouvez essayer:

sorted((x, y)) == sorted((a, b))
27
jasonharper

La façon la plus élégante, à mon avis, serait

(x, y) in ((a, b), (b, a))

C'est un meilleur moyen que d'utiliser des ensembles, c'est-à-dire {a, b} == {y, x}, comme indiqué dans d'autres réponses car nous n'avons pas besoin de penser si les variables sont hachables.

26
Wagner Macedo

S'il s'agit de chiffres, vous pouvez utiliser (x+y)==(a+b) and (x*y)==(a*b).

S'il s'agit d'éléments comparables, vous pouvez utiliser min(x,y)==min(a,b) and max(x,y)==max(a,b).

Mais ((x == a and y == b) or (x == b and y == a)) Est clair, sûr et plus général.

25
lhf

Comme généralisation à plus de deux variables, nous pouvons utiliser itertools.permutations . C'est au lieu de

(x == a and y == b and z == c) or (x == a and y == c and z == b) or ...

nous pouvons écrire

(x, y, z) in itertools.permutations([a, b, c])

Et bien sûr la version à deux variables:

(x, y) in itertools.permutations([a, b])
22
a_guest

Vous pouvez utiliser des tuples pour représenter vos données, puis vérifier l'inclusion de l'ensemble, comme:

def test_fun(x, y):
    test_set = {(a, b), (b, a)}

    return (x, y) in test_set
15
Nils Müller

Vous avez déjà la solution la plus lisible. Il existe d'autres façons d'exprimer cela, peut-être avec moins de caractères, mais elles sont moins simples à lire.

Selon ce que les valeurs représentent réellement, votre meilleur pari est de encapsuler le chèque dans une fonction avec un nom parlant. Alternativement ou en plus, vous pouvez modéliser les objets x, y et a, b chacun dans des objets de classe supérieure dédiés que vous pouvez ensuite comparer avec la logique de la comparaison dans une méthode de contrôle d'égalité de classe ou une fonction personnalisée dédiée.

10
Frank Hopkins

Il semble que l'OP ne concernait que le cas de deux variables, mais comme StackOverflow s'adresse également à ceux qui recherchent la même question plus tard, je vais essayer d'aborder le cas générique ici en détail; Une réponse précédente contient déjà une réponse générique utilisant itertools.permutations(), mais cette méthode conduit à des comparaisons O(N*N!), car il y a N! Permutations avec N éléments chacune . (Ce fut la principale motivation de cette réponse)

Tout d'abord, résumons comment certaines des méthodes des réponses précédentes s'appliquent au cas générique, comme motivation pour la méthode présentée ici. J'utiliserai A pour faire référence à (x, y) Et B pour faire référence à (a, b), Qui peuvent être des tuples de longueur arbitraire (mais égale).

set(A) == set(B) est rapide, mais ne fonctionne que si les valeurs sont hachables et vous pouvez garantir que l'un des tuples ne contient aucune valeur en double. (Par exemple. {1, 1, 2} == {1, 2, 2}, Comme l'a souligné @ user2357112 sous la réponse de @Daniel Mesejo)

La méthode précédente peut être étendue pour fonctionner avec des valeurs en double en utilisant des dictionnaires avec des nombres, au lieu d'ensembles: (Cela a toujours la limitation que toutes les valeurs doivent être lavables, donc par exemple. Les valeurs mutables comme list ne le seront pas travail)

def counts(items):
    d = {}
    for item in items:
        d[item] = d.get(item, 0) + 1
    return d

counts(A) == counts(B)

sorted(A) == sorted(B) ne nécessite pas de valeurs lavables, mais est légèrement plus lent, et nécessite des valeurs ordonnables à la place. (Donc, par exemple. complex ne fonctionnera pas)

A in itertools.permutations(B) ne nécessite pas de valeurs lavables ou ordonnables, mais comme déjà mentionné, il a O(N*N!) complexité, donc même avec seulement 11 éléments, il peut prendre plus d'une seconde pour terminer.

Alors, existe-t-il un moyen d'être aussi général, mais le fait-il beaucoup plus rapidement? Pourquoi oui, en vérifiant "manuellement" qu'il y a la même quantité de chaque élément: (La complexité de celui-ci est O(N^2), donc ce n'est pas bon non plus pour les entrées importantes; Sur ma machine, 10 000 éléments peuvent prendre plus d'une seconde - mais avec des entrées plus petites, comme 10 articles, c'est aussi rapide que les autres)

def unordered_eq(A, B):
    for a in A:
        if A.count(a) != B.count(a):
            return False
    return True

Pour obtenir les meilleures performances, on peut vouloir essayer d'abord la méthode basée sur dict, revenir à la méthode basée sur sorted si cela échoue en raison de valeurs incontrôlables, et finalement retomber sur la méthode count basée sur la méthode si cela échoue également en raison de valeurs non ordonnables.

3
Aleksi Torhamo