J'ai une méthode qui appelle 4 autres méthodes dans l'ordre pour vérifier les conditions spécifiques et renvoie immédiatement (sans vérifier les suivantes) chaque fois que l'on retourne quelque chose de vrai.
def check_all_conditions():
x = check_size()
if x:
return x
x = check_color()
if x:
return x
x = check_tone()
if x:
return x
x = check_flavor()
if x:
return x
return None
Cela ressemble à beaucoup de code de bagages. Au lieu de chaque instruction if sur 2 lignes, je préfère faire quelque chose comme:
x and return x
Mais ce n'est pas Python invalide. Me manque-t-il une solution simple et élégante ici? Par ailleurs, dans cette situation, ces quatre méthodes de vérification peuvent être coûteuses, donc je ne veux pas les appeler plusieurs fois.
python
if-statement
Bernard
la source
la source
x and return x
c'est mieux queif x: return x
? Ce dernier est beaucoup plus lisible et donc maintenable. Vous ne devriez pas trop vous soucier du nombre de caractères ou de lignes; la lisibilité compte. Ce sont exactement le même nombre de caractères non blancs de toute façon, et si vous le devez vraiment,if x: return x
cela fonctionnera bien sur une seule ligne.x
(par opposition àbool(x)
) donc tel qu'il est, je pense qu'il est sûr de supposer que les fonctions de OP peuvent retourner n'importe quoi, et il veut le premier tout ce qui est véridique.Réponses:
Vous pouvez utiliser une boucle:
Cela a l'avantage supplémentaire que vous pouvez maintenant rendre le nombre de conditions variable.
Vous pouvez utiliser
map()
+filter()
(les versions de Python 3, utilisez lesfuture_builtins
versions de Python 2) pour obtenir la première valeur correspondante:mais si c'est plus lisible, c'est discutable.
Une autre option consiste à utiliser une expression de générateur:
la source
any
au lieu de la boucle.return any(condition() for condition in conditions)
any
a presque la même implémentation à l'intérieur. Mais cela semble beaucoup mieux, veuillez le poster comme réponse)conditions
etarguments
? C'est à mon humble avis bien pire que le code d'origine, qui prend environ 10 secondes à analyser par mon analyseur de cerveau.return next((check for check in checks if check), None)
.Alternativement à la bonne réponse de Martijn, vous pouvez enchaîner
or
. Cela retournera la première valeur véridique, ouNone
s'il n'y a pas de valeur véridique:Démo:
la source
\
pour placer chaque chèque sur sa propre ligne.\
du tout étendre la ligne logique. Utilisez plutôt des parenthèses si possible; doncreturn (....)
avec des sauts de ligne insérés au besoin. Pourtant, ce sera une longue ligne logique.Ne le change pas
Il existe d'autres façons de procéder, comme le montrent les diverses autres réponses. Aucun n'est aussi clair que votre code d'origine.
la source
:
, car je considère queif x: return x
c'est assez bien, et cela rend la fonction plus compacte. Mais c'est peut-être juste moi.or
comme timgeb l'a fait est un idiome approprié et bien compris. De nombreuses langues ont cela; peut - être quand on l'appelle ,orelse
il est encore plus clair, mais même simplement vieuxor
(ou||
dans d' autres langues) est destiné à être compris comme l'alternative à essayer si le premier « ne fonctionne pas. »or
étiquettes non pythoniques ou de quelque manière que ce soit obscurcies, mais c'est une question d'opinion de toute façon - comme il se doit.En fait, la même réponse que timgeb, mais vous pouvez utiliser des parenthèses pour un formatage plus agréable:
la source
Selon la loi de Curly , vous pouvez rendre ce code plus lisible en divisant deux problèmes:
en deux fonctions:
Cela évite:
... tout en préservant un flux linéaire et facile à lire.
Vous pouvez probablement trouver des noms de fonctions encore meilleurs, selon votre situation particulière, ce qui les rend encore plus lisibles.
la source
return None
n'est pas nécessaire, car les fonctions retournentNone
par défaut. Cependant, il n'y a rien de mal à revenirNone
explicitement, et j'aime que vous ayez choisi de le faire.Ceci est une variante du premier exemple de Martijns. Il utilise également le style "collection de callables" afin de permettre les courts-circuits.
Au lieu d'une boucle, vous pouvez utiliser la fonction intégrée
any
.Notez que
any
renvoie un booléen, donc si vous avez besoin de la valeur de retour exacte du chèque, cette solution ne fonctionnera pas.any
ne distinguera pas14
,'red'
,'sharp'
,'spicy'
comme des valeurs de retour, ils seront tous retournés commeTrue
.la source
next(itertools.ifilter(None, (c() for c in conditions)))
pour obtenir la valeur réelle sans la convertir en valeur booléenne.any
- il réellement un court-circuit?x = bar(); if x: return x;
Avez-vous pensé à tout écrire
if x: return x
sur une seule ligne?Ce n'est pas moins répétitif que ce que vous aviez, mais IMNSHO, il se lit un peu plus fluide.
la source
Je suis assez surpris que personne n'ait mentionné la fonction intégrée
any
conçue à cet effet:Notez que bien que cette implémentation soit probablement la plus claire, elle évalue toutes les vérifications même si la première l'est
True
.Si vous devez vraiment vous arrêter au premier échec de vérification, envisagez d'utiliser
reduce
ce qui est fait pour convertir une liste en une valeur simple:Dans ton cas:
lambda a, f: a or f()
est la fonction qui vérifie que l'accumulateura
ou la vérification actuellef()
estTrue
. Notez que sia
estTrue
,f()
ne sera pas évalué.checks
contient des fonctions de vérification (l'f
élément du lambda)False
est la valeur initiale, sinon aucune vérification ne se produirait et le résultat serait toujoursTrue
any
etreduce
sont des outils de base pour la programmation fonctionnelle. Je vous encourage fortement à les former ainsi quemap
ce qui est génial aussi!la source
any
ne fonctionne que si les contrôles renvoient réellement une valeur booléenne, littéralementTrue
ouFalse
, mais la question ne le spécifie pas. Vous devez utiliserreduce
pour renvoyer la valeur réelle renvoyée par le chèque. De plus, il est assez facile d'éviter d'évaluer toutes les vérifications avecany
en utilisant un générateur, par exempleany(c() for c in (check_size, check_color, check_tone, check_flavor))
. Comme dans la réponse de Leonhardreduce
. Comme @DavidZ, je pense que votre solutionany
devrait utiliser un générateur et il faut souligner qu'elle se limite au retourTrue
ouFalse
.any
fonctionne réellement avec des valeurs véridiques:any([1, "abc", False]) == True
etany(["", 0]) == False
any
ne fonctionne à cette fin que si des valeurs booléennes réelles sont renvoyées par les fonctions de vérification.Si vous voulez la même structure de code, vous pouvez utiliser des instructions ternaires!
Je pense que cela semble agréable et clair si vous le regardez.
Démo:
la source
x if x else <something>
peut être réduit àx or <something>
Pour moi, la meilleure réponse est celle de @ phil-frost, suivie de @ wayne-werner.
Ce que je trouve intéressant, c'est que personne n'a dit quoi que ce soit sur le fait qu'une fonction retournera de nombreux types de données différents, ce qui rendra alors obligatoire la vérification du type de x lui-même pour effectuer d'autres travaux.
Je mélangerais donc la réponse de @ PhilFrost avec l'idée de garder un seul type:
Notez que cela
x
est passé comme argument, maisall_conditions
est également utilisé comme générateur passé de fonctions de vérification où toutes obtiennent unx
à vérifier, et retournentTrue
ouFalse
. En utilisantfunc
avecall_conditions
comme valeur par défaut, vous pouvez utiliserassessed_x(x)
ou passer un autre générateur personnalisé viafunc
.De cette façon, vous obtenez
x
dès qu'un contrôle passe, mais ce sera toujours le même type.la source
Idéalement, je réécrirais les
check_
fonctions à renvoyerTrue
ouFalse
plutôt qu'une valeur. Vos chèques deviennent alorsEn supposant que votre
x
n'est pas immuable, votre fonction peut toujours le modifier (bien qu'ils ne puissent pas le réaffecter) - mais une fonction appeléecheck
ne devrait pas vraiment le modifier de toute façon.la source
Une légère variation sur le premier exemple de Martijns ci-dessus, qui évite le si à l'intérieur de la boucle:
la source
Status or c()
sautera / court-circui évaluera les appels àc()
ifStatus
est véridique, donc le code dans cette réponse ne semble pas appeler plus de fonctions que le code de l'OP. stackoverflow.com/questions/2580136/…J'aime @ timgeb. En attendant, je voudrais ajouter que l'expression
None
dans l'return
instruction n'est pas nécessaire car la collection d'or
instructions séparées est évaluée et la première non nulle, non vide, aucune-aucune est renvoyée et s'il n'y en a pas, elleNone
est renvoyée qu'il y en aitNone
ou non!Donc, ma
check_all_conditions()
fonction ressemble à ceci:En utilisant
timeit
avec,number=10**7
j'ai regardé le temps d'exécution d'un certain nombre de suggestions. Par souci de comparaison, je viens d'utiliser larandom.random()
fonction pour renvoyer une chaîne ouNone
basée sur des nombres aléatoires. Voici tout le code:Et voici les résultats:
la source
Cette façon est un peu en dehors de la boîte, mais je pense que le résultat final est simple, lisible et joli.
L'idée de base est
raise
une exception lorsque l'une des fonctions est évaluée comme véridique et renvoie le résultat. Voici à quoi cela pourrait ressembler:Vous aurez besoin d'une
assertFalsey
fonction qui déclenche une exception lorsque l'un des arguments de la fonction appelée est évalué comme véridique:Ce qui précède pourrait être modifié afin de fournir également des arguments pour les fonctions à évaluer.
Et bien sûr, vous aurez besoin de
TruthyException
lui - même. Cette exception fournit leobject
déclencheur de l'exception:Vous pouvez transformer la fonction d'origine en quelque chose de plus général, bien sûr:
Cela peut être un peu plus lent car vous utilisez à la fois une
if
instruction et la gestion d'une exception. Cependant, l'exception n'est gérée qu'une seule fois au maximum, de sorte que l'atteinte aux performances doit être mineure, sauf si vous prévoyez d'exécuter la vérification et d'obtenir uneTrue
valeur plusieurs milliers de fois.la source
StopIteration
est un assez bon exemple: une exception est déclenchée à chaque fois que vous épuisez un itérable. Ce que vous voulez éviter, c'est de soulever successivement des exceptions, ce qui coûterait cher. Mais ce n'est pas le cas une fois.La méthode pythonique utilise soit réduire (comme quelqu'un a déjà mentionné) ou itertools (comme indiqué ci-dessous), mais il me semble que le simple fait de court-circuiter l'
or
opérateur produit un code plus clairla source
Je vais sauter ici et je n'ai jamais écrit une seule ligne de Python, mais je suppose que
if x = check_something(): return x
c'est valide?si c'est le cas:
la source
if ( x := check_size() ) :
pour le même effet.Ou utilisez
max
:la source
J'ai vu quelques implémentations intéressantes de déclarations switch / case avec dict dans le passé qui m'ont conduit à cette réponse. En utilisant l'exemple que vous avez fourni, vous obtiendrez ce qui suit. (C'est de la folie
using_complete_sentences_for_function_names
, c'est pourquoi ilcheck_all_conditions
est renomméstatus
. Voir (1))La fonction de sélection élimine le besoin d'appeler chacun
check_FUNCTION
deux fois, c'est-à-dire que vous évitezcheck_FUNCTION() if check_FUNCTION() else next
en ajoutant une autre couche de fonction. Ceci est utile pour les fonctions de longue durée. Les lambdas dans le dict retardent l'exécution de ses valeurs jusqu'à la boucle while.En bonus, vous pouvez modifier l'ordre d'exécution et même sauter certains tests en modifiant
k
ets
par exemple enk='c',s={'c':'b','b':None}
réduisant le nombre de tests et en inversant l'ordre de traitement d'origine.Les
timeit
boursiers peuvent marchander sur le coût de l'ajout d'une couche supplémentaire ou deux à la pile et le coût de la recherche de dict, mais vous semblez plus préoccupé par la beauté du code.Une implémentation plus simple peut également être la suivante:
la source
check_no/some/even/prime/every_third/fancy_conditions
mais juste cette fonction, alors pourquoi ne pas l'appelerstatus
ou si l'on insistecheck_status
. L'utilisation_all_
est superflue, il ne garantit pas l'intégrité des univers. La dénomination doit sûrement utiliser un ensemble cohérent de mots-clés en utilisant autant que possible l'espacement des noms. Les phrases longues servent mieux de docstrings. On a rarement besoin de plus de 8 à 10 caractères pour décrire quelque chose succinctement.check_all_conditions
c'est un mauvais nom, car il ne vérifie pas toutes les conditions si l'une est vraie. J'utiliserais quelque chose commematches_any_condition
.