Je veux écrire une fonction en Python qui retourne différentes valeurs fixes basées sur la valeur d'un index d'entrée.
Dans d'autres langues, j'utiliserais une instruction switch
or case
, mais Python ne semble pas avoir de switch
déclaration. Quelles sont les solutions Python recommandées dans ce scénario?
python
switch-statement
Michael Schneider
la source
la source
switch
est en fait plus "polyvalent" que quelque chose renvoyant des valeurs fixes différentes en fonction de la valeur d'un index d'entrée. Il permet d'exécuter différents morceaux de code. Il n'a même pas besoin de renvoyer de valeur. Je me demande si certaines des réponses ici sont de bons remplacements pour uneswitch
déclaration générale , ou seulement pour le cas de renvoyer des valeurs sans possibilité d'exécuter des morceaux de code généraux.Réponses:
Vous pouvez utiliser un dictionnaire:
la source
get
méthode serait probablement plus normal que d'utiliser uncollections.defaultdict
dans ce cas.}.get(x, default)
place s'il doit y avoir une valeur par défaut. (Remarque: c'est beaucoup plus agréable que ce qui se passe si vous laissez la valeur par défaut sur une instruction switch!)Si vous souhaitez des valeurs par défaut, vous pouvez utiliser la
get(key[, default])
méthode du dictionnaire :la source
J'ai toujours aimé le faire de cette façon
D'ici
la source
.get()
(comme les réponses les plus élevées actuelles) devront évaluer avec impatience toutes les possibilités avant la répartition, et donc non seulement sont (pas seulement très mais) extrêmement inefficaces et ne peuvent pas non plus avoir d'effets secondaires; cette réponse contourne ce problème, mais est plus détaillée. J'utiliserais simplement if / elif / else, et même ceux-ci prennent autant de temps à écrire que 'case'.[value]
, qui ne renverra qu'une seule des 3 fonctions (en supposant qu'ilvalue
s'agit de l'une des 3 clés). La fonction n'a pas encore été appelée à ce stade. Appelle ensuite(x)
la fonction qui vient d'être renvoyée avecx
comme argument (et le résultat passe àresult
). Les 2 autres fonctions ne seront pas appelées.En plus des méthodes de dictionnaire (que j'aime beaucoup, BTW), vous pouvez également utiliser
if
-elif
-else
pour obtenir la fonctionnalitéswitch
/case
/default
:Bien sûr, ce n'est pas identique à switch / case - vous ne pouvez pas avoir une transition aussi facilement que de laisser la
break
déclaration, mais vous pouvez avoir un test plus compliqué. Son formatage est plus agréable qu'une série deif
s imbriqués , même si fonctionnellement c'est ce dont il est le plus proche.la source
get
méthode, mais la méthode standard est tout simplement plus lisible.x = the.other.thing
avant. En règle générale, vous auriez un seul if, plusieurs elif et un seul autre, car c'est plus facile à comprendre.if/elif/else
?x in 'bc'
, gardez à l'esprit que"" in "bc"
c'estTrue
.Ma recette Python préférée pour commutateur / boîtier est:
Court et simple pour des scénarios simples.
Comparez à plus de 11 lignes de code C:
Vous pouvez même affecter plusieurs variables en utilisant des tuples:
la source
default = -1; result = choices.get(key, default)
.result=key=='a'?1:key==b?2:-1
result = 1 if key == 'a' else (2 if key == 'b' else 'default')
. mais le liner est-il lisible?Usage:
Tests:
la source
if
instruction standard ?case(2)
bloc a appelé une autre fonction qui utilise switch (), lorscase(2, 3, 5, 7)
de l'exécution de etc pour rechercher le prochain cas à exécuter, il utilisera la valeur de commutateur définie par l'autre fonction et non celle définie par l'instruction switch actuelle .Ma recette préférée est une très bonne recette . Vous l'aimerez vraiment. C'est le plus proche que j'ai vu des déclarations de cas de commutation réelles, en particulier dans les fonctionnalités.
Voici un exemple:
la source
for case in switch()
parwith switch() as case
, a plus de sens, car il ne doit s'exécuter qu'une seule fois.with
cela ne le permet pasbreak
, donc l'option de remplacement est supprimée.if c in set(range(0,9)): print "digit" elif c in set(map(chr, range(ord('a'), ord('z')))): print "lowercase"
?la source
value
propriété (publique) à la classe Switch afin que vous puissiez référencer lecase.value
dans l'instruction.Il y a un modèle que j'ai appris du code Twisted Python.
Vous pouvez l'utiliser à tout moment pour envoyer un jeton et exécuter un morceau de code étendu. Dans une machine d'état, vous auriez des
state_
méthodes et vous enverriezself.state
. Ce commutateur peut être proprement étendu en héritant de la classe de base et en définissant vos propresdo_
méthodes. Souvent, vous n'aurez même pasdo_
méthodes dans la classe de base.Edit: comment est-ce exactement utilisé
En cas de SMTP, vous recevrez
HELO
du fil. Le code pertinent (detwisted/mail/smtp.py
, modifié pour notre cas) ressemble à ceciVous recevrez
' HELO foo.bar.com '
(ou vous pourriez obtenir'QUIT'
ou'RCPT TO: foo'
). Ceci est symbolisé enparts
as['HELO', 'foo.bar.com']
. Le nom de recherche de méthode réel est tiré departs[0]
.(La méthode d'origine est également appelée
state_COMMAND
, car elle utilise le même modèle pour implémenter une machine à états, c.-à-d.getattr(self, 'state_' + self.mode)
)la source
Disons que vous ne voulez pas simplement renvoyer une valeur, mais que vous souhaitez utiliser des méthodes qui changent quelque chose sur un objet. Utiliser l'approche indiquée ici serait:
Ce qui se passe ici, c'est que python évalue toutes les méthodes du dictionnaire. Donc, même si votre valeur est «a», l'objet sera incrémenté et décrémenté de x.
Solution:
Vous obtenez donc une liste contenant une fonction et ses arguments. De cette façon, seuls le pointeur de fonction et la liste d'arguments sont retournés, non évalués. 'result' évalue ensuite l'appel de fonction retourné.
la source
Je vais juste déposer mes deux cents ici. La raison pour laquelle il n'y a pas d'instruction case / switch en Python est parce que Python suit le principe de «Il n'y a qu'une seule bonne façon de faire quelque chose». Donc, évidemment, vous pouvez trouver différentes façons de recréer la fonctionnalité commutateur / boîtier, mais la façon Pythonique d'accomplir cela est la construction if / elif. c'est à dire
Je pensais juste que PEP 8 méritait un signe de tête ici. L'une des belles choses à propos de Python est sa simplicité et son élégance. Cela découle en grande partie des principes énoncés dans le PEP 8, notamment: «Il n'y a qu'une seule bonne façon de faire quelque chose»
la source
élargir l'idée «dict as switch». si vous souhaitez utiliser une valeur par défaut pour votre commutateur:
la source
'default'
) sur la règle (obtenir quelque chose de ce dict). De par leur conception, les programmes Python utilisent des exceptions à la baisse d'un chapeau. Cela étant dit, l'utilisationget
pourrait potentiellement rendre le code un peu plus agréable.Si vous avez un bloc de cas compliqué, vous pouvez envisager d'utiliser une table de recherche de dictionnaire de fonctions ...
Si vous ne l'avez pas fait auparavant, c'est une bonne idée d'entrer dans votre débogueur et de voir exactement comment le dictionnaire recherche chaque fonction.
REMARQUE: n'utilisez pas "()" dans la recherche de cas / dictionnaire ou cela appellera chacune de vos fonctions lors de la création du bloc dictionnaire / cas. N'oubliez pas ceci car vous ne souhaitez appeler chaque fonction qu'une seule fois à l'aide d'une recherche de style de hachage.
la source
Si vous recherchez une extra-instruction, comme "switch", j'ai construit un module python qui étend Python. Ça s'appelle ESPY tant que «Structure améliorée pour Python» et il est disponible pour Python 2.x et Python 3.x.
Par exemple, dans ce cas, une instruction switch peut être exécutée par le code suivant:
qui peut être utilisé comme ceci:
alors espy le traduit en Python comme:
la source
while True:
la partie supérieure du code Python généré? Cela frappera inévitablementbreak
le bas du code Python généré, il me semble donc que lewhile True:
etbreak
pourraient être supprimés. De plus, ESPY est-il assez intelligent pour changer le nom decont
si l'utilisateur utilise ce même nom dans son propre code? Dans tous les cas, je veux utiliser Python vanille donc je ne vais pas l'utiliser, mais c'est quand même cool. +1 pour la fraîcheur pure.while True:
etbreak
s est d'autoriser, mais pas d'exiger, une interruption .J'ai trouvé qu'une structure de commutateur commune:
peut être exprimé en Python comme suit:
ou formaté de manière plus claire:
Au lieu d'être une instruction, la version python est une expression qui évalue une valeur.
la source
parameter
etp1==parameter
f = lambda x: 'a' if x==0 else 'b' if x==1 else 'c'
. Quand j'ai appelé plus tardf(2)
, j'ai reçu'c'
;f(1)
,'b'
; etf(0)
,'a'
. Quant à p1 (x), il désigne un prédicat; tant qu'il revientTrue
ouFalse
, peu importe qu'il s'agisse d'un appel de fonction ou d'une expression, ça va.La plupart des réponses ici sont assez anciennes, et en particulier les réponses acceptées, il semble donc utile de les mettre à jour.
Tout d'abord, la FAQ Python officielle couvre cela et recommande la
elif
chaîne pour les cas simples et la chaîne pour les casdict
plus grands ou plus complexes. Il suggère également un ensemble devisit_
méthodes (un style utilisé par de nombreux frameworks de serveurs) pour certains cas:La FAQ mentionne également PEP 275 , qui a été écrit pour obtenir une décision officielle une fois pour toutes sur l'ajout d'instructions de commutateur de style C. Mais ce PEP a en fait été reporté à Python 3, et il n'a été officiellement rejeté qu'en tant que proposition distincte, PEP 3103 . La réponse était, bien sûr, non, mais les deux PPE ont des liens vers des informations supplémentaires si vous êtes intéressé par les raisons ou l'historique.
Une chose qui est revenue plusieurs fois (et peut être vue dans PEP 275, même si elle a été découpée comme une recommandation réelle) est que si vous êtes vraiment gêné d'avoir 8 lignes de code pour gérer 4 cas, contre les 6 lignes que vous auriez en C ou Bash, vous pouvez toujours écrire ceci:
Ce n'est pas exactement encouragé par PEP 8, mais c'est lisible et pas trop unidiomatique.
Plus d'une décennie après le rejet du PEP 3103, la question des déclarations de cas de style C, ou même la version légèrement plus puissante de Go, a été considérée comme morte; chaque fois que quelqu'un en parle sur python-ideas ou -dev, ils sont renvoyés à l'ancienne décision.
Cependant, l'idée d'une correspondance complète des modèles de style ML surgit toutes les quelques années, d'autant plus que des langues comme Swift et Rust l'ont adoptée. Le problème est qu'il est difficile d'utiliser beaucoup la correspondance de modèles sans types de données algébriques. Bien que Guido ait sympathisé avec l'idée, personne n'a proposé une proposition qui s'intègre très bien dans Python. (Vous pouvez lire mon modèle de 2014 pour un exemple.) Cela pourrait changer avec
dataclass
en 3.7 et certaines propositions sporadiques pour un plus puissantenum
pour gérer les types de somme, ou avec diverses propositions pour différents types de liaisons instruction-locales (comme PEP 3150 , ou le ensemble de propositions en cours de discussion sur les idées). Mais jusqu'à présent, ce n'est pas le cas.Il y a aussi parfois des propositions pour la correspondance de style Perl 6, qui est fondamentalement un méli-mélo de tout, de l'
elif
expression régulière à la commutation de type à répartition unique.la source
Solution pour exécuter des fonctions:
où foo1 (), foo2 (), foo3 () et default () sont des fonctions
la source
foo2()
, lesfoo1()
,foo3()
et lesdefault()
fonctions sont tout aussi vont courir, ce qui signifie des choses pourrait prendre beaucoup de tempsget(option)()
. problème résolu.Je n'ai trouvé la réponse simple que je cherchais nulle part dans la recherche Google. Mais je l'ai quand même compris. C'est vraiment assez simple. Décidé de le poster, et peut-être d'éviter quelques rayures de moins sur la tête de quelqu'un d'autre. La clé est simplement "in" et tuples. Voici le comportement de l'instruction switch avec fall-through, y compris RANDOM fall-through.
Fournit:
la source
break
à la fin du code pour acase
. Mais je pense que nous n'avons pas besoin d'une telle "solution" :)Les solutions que j'utilise:
Une combinaison de 2 des solutions publiées ici, qui est relativement facile à lire et prend en charge les valeurs par défaut.
où
regarde
"lambda x: x - 2"
dans le dict et l'utilise avecx=23
ne le trouve pas dans le dict et utilise la valeur
"lambda x: x - 22"
par défaut avecx=44
.la source
la source
la source
J'ai aimé la réponse de Mark Bies
Puisque la
x
variable doit être utilisée deux fois, j'ai modifié les fonctions lambda en sans paramètre.Je dois courir avec
results[value](value)
Edit: j'ai remarqué que je peux utiliser
None
type avec des dictionnaires. Donc, cela imiteraitswitch ; case else
la source
result[None]()
?result = {'a': 100, None:5000}; result[None]
None:
se comporte comme çadefault:
.Court et facile à lire, a une valeur par défaut et prend en charge les expressions dans les conditions et les valeurs de retour.
Cependant, il est moins efficace que la solution avec un dictionnaire. Par exemple, Python doit parcourir toutes les conditions avant de renvoyer la valeur par défaut.
la source
vous pouvez utiliser un dict expédié:
Production:
la source
Simple, non testé; chaque condition est évaluée indépendamment: il n'y a pas de transition, mais tous les cas sont évalués (bien que l'expression à activer n'est évaluée qu'une seule fois), sauf s'il existe une instruction break. Par exemple,
imprime
Was 1. Was 1 or 2. Was something.
(Bon sang! Pourquoi ne puis-je pas avoir d'espaces de fin dans les blocs de code en ligne?) siexpression
évalue à1
,Was 2.
siexpression
évalue à2
, ouWas something.
siexpression
évalue à autre chose.la source
Définition:
vous permet d'utiliser une syntaxe assez simple, avec les cas regroupés dans une carte:
J'ai continué à essayer de redéfinir le commutateur d'une manière qui me permettrait de me débarrasser du "lambda:", mais j'ai abandonné. Ajuster la définition:
Me permet de mapper plusieurs cas au même code et de fournir une option par défaut:
Chaque cas répliqué doit être dans son propre dictionnaire; switch () consolide les dictionnaires avant de rechercher la valeur. C'est encore plus laid que je ne le souhaiterais, mais il a l'efficacité de base d'utiliser une recherche hachée sur l'expression, plutôt qu'une boucle à travers toutes les clés.
la source
Je pense que la meilleure façon est d' utiliser les idiomes du langage python pour garder votre code testable . Comme indiqué dans les réponses précédentes, j'utilise des dictionnaires pour tirer parti des structures et du langage python et garder le code "case" isolé dans différentes méthodes. Ci-dessous, il y a une classe, mais vous pouvez utiliser directement un module, des globaux et des fonctions. La classe a des méthodes qui peuvent être testées avec isolation . Selon vos besoins, vous pouvez également jouer avec des méthodes et des attributs statiques.
Il est possible de profiter de cette méthode en utilisant également des classes comme clés de "__choice_table". De cette façon, vous pouvez éviter la toxicomanie et garder tout propre et testable.
Supposons que vous deviez traiter un grand nombre de messages ou de paquets à partir du net ou de votre MQ. Chaque paquet a sa propre structure et son code de gestion (de manière générique). Avec le code ci-dessus, il est possible de faire quelque chose comme ceci:
La complexité n'est donc pas répartie dans le flux de code, mais elle est rendue dans la structure de code .
la source
Développant la réponse de Greg Hewgill - Nous pouvons encapsuler la solution de dictionnaire en utilisant un décorateur:
Cela peut ensuite être utilisé avec le
@case
-décorateurLa bonne nouvelle est que cela a déjà été fait dans le module NeoPySwitch . Installez simplement en utilisant pip:
la source
Une solution que j'ai tendance à utiliser et qui utilise également des dictionnaires est:
Cela a l'avantage de ne pas essayer d'évaluer les fonctions à chaque fois, et il suffit de s'assurer que la fonction externe obtient toutes les informations dont les fonctions internes ont besoin.
la source
Jusqu'à présent, il y a eu beaucoup de réponses qui ont dit: "nous n'avons pas de commutateur en Python, faites-le de cette façon". Cependant, je voudrais souligner que l'instruction switch elle-même est une construction facilement exploitable qui peut et doit être évitée dans la plupart des cas car elle favorise la programmation paresseuse. Exemple concret:
Maintenant, vous pouvez le faire avec une instruction switch (si Python en proposait une) mais vous perdriez votre temps car il existe des méthodes qui le font très bien. Ou peut-être avez-vous quelque chose de moins évident:
Cependant, ce type d'opération peut et doit être géré avec un dictionnaire car il sera plus rapide, moins complexe, moins sujet aux erreurs et plus compact.
Et la grande majorité des «cas d'utilisation» pour les instructions switch tomberont dans l'un de ces deux cas; il n'y a que très peu de raisons d'en utiliser un si vous avez bien réfléchi à votre problème.
Ainsi, plutôt que de demander "comment puis-je basculer en Python?", Nous devrions peut-être demander, "pourquoi est-ce que je veux passer en Python?" car c'est souvent la question la plus intéressante et elle expose souvent des défauts dans la conception de tout ce que vous construisez.
Maintenant, cela ne veut pas dire que les commutateurs ne devraient jamais être utilisés non plus. Les machines d'état, les lexers, les analyseurs et les automates les utilisent tous dans une certaine mesure et, en général, lorsque vous démarrez à partir d'une entrée symétrique et passez à une sortie asymétrique, ils peuvent être utiles; vous devez juste vous assurer que vous n'utilisez pas le commutateur comme un marteau car vous voyez un tas de clous dans votre code.
la source