Existe-t-il un meilleur moyen d'écrire des instructions imbriquées if en python? [fermé]

34

Existe-t-il un moyen plus pythonique de faire des instructions imbriquées if else que celle-ci:

def convert_what(numeral_sys_1, numeral_sys_2):

    if numeral_sys_1 == numeral_sys_2:      
        return 0
    elif numeral_sys_1 == "Hexadecimal":
        if numeral_sys_2 == "Decimal":
            return 1
        elif numeral_sys_2 == "Binary":
            return 2
    elif numeral_sys_1 == "Decimal":
        if numeral_sys_2 == "Hexadecimal":
            return 4
        elif numeral_sys_2 == "Binary":
            return 6
    elif numeral_sys_1 == "Binary":
        if numeral_sys_2 == "Hexadecimal":
            return 5
        elif numeral_sys_2 == "Decimal":
            return 3
    else:
        return 0

Ce script fait partie d'un simple convertisseur.

Module_art
la source
Sans utiliser une autre structure de données, vous pouvez déplacer les instructions if-else imbriquées dans des andconditions pour les instructions if-else de niveau supérieur. Ce serait au moins plus lisible de cette façon. Malheureusement, python n'a pas d'instructions switch.
adamkgray
C'est la façon pythonique. Python ne prend pas intentionnellement en charge les instructions switch. Voir python.org/dev/peps/pep-3103
Jongmin Baek
1
Pas la question du tout, mais si vous essayez de rendre les choses plus pythoniques, que diriez-vous de définir des constantes ou une énumération pour les valeurs de retour - plus agréable pour un lecteur que les "nombres magiques" ....
Mats Wichmann

Réponses:

13

Bien que les réponses de @Aryerez et @ SencerH. Fonctionnent, chaque valeur possible de numeral_sys_1doit être écrite à plusieurs reprises pour chaque valeur possible de numeral_sys_2lors de la liste des paires de valeurs, ce qui rend la structure de données plus difficile à maintenir lorsque le nombre de valeurs possibles augmente. À la place, vous pouvez utiliser un dict imbriqué à la place de vos instructions if imbriquées:

mapping = {
    'Hexadecimal': {'Decimal': 1, 'Binary': 2},
    'Binary': {'Decimal': 3, 'Hexadecimal': 5},
    'Decimal': {'Hexadecimal': 4, 'Binary': 6}
}
def convert_what(numeral_sys_1, numeral_sys_2):
    return mapping.get(numeral_sys_1, {}).get(numeral_sys_2, 0)

Alternativement, vous pouvez générer les paires de valeurs pour le mappage avec la itertools.permutationsméthode, dont l'ordre suit celui de la séquence d'entrée:

mapping = dict(zip(permutations(('Hexadecimal', 'Decimal', 'Binary'), r=2), (1, 2, 4, 6, 3, 5)))
def convert_what(numeral_sys_1, numeral_sys_2):
    return mapping.get((numeral_sys_1, numeral_sys_2), 0)
blhsing
la source
29

Insérez toutes les combinaisons valides dans l'un dictionarydes tuples, et si la combinaison n'est pas là, retournez 0:

def convert_what(numeral_sys_1, numeral_sys_2):
    numeral_dict = {
        ("Hexadecimal", "Decimal"    ) : 1,
        ("Hexadecimal", "Binary"     ) : 2,
        ("Decimal",     "Hexadecimal") : 4, 
        ("Decimal",     "Binary"     ) : 6,
        ("Binary",      "Hexadecimal") : 5,
        ("Binary",      "Decimal"    ) : 3
    }
    return numeral_dict.get((numeral_sys_1, numeral_sys_2), 0)

Si vous prévoyez d'utiliser la fonction dans une boucle, il peut être préférable de définir le dictionnaire en dehors de la fonction, afin qu'il ne soit pas recréé à chaque appel à la fonction.

Aryerez
la source
2
except KeyError:
RomanPerekhrest
@RomanPerekhrest Je l'ai ajouté, bien que dans cette question spécifique, la fonction elle-même n'ait pas d'autres types d' erreur à produire qui donneraient une sortie différente de sa fonction d'origine.
Aryerez
1
Les parens sont redondants à l'intérieur []. À l'exception du tuple vide, c'est la virgule qui en fait un tuple, pas les parenthèses, c'est juste pour l'ordre des opérations dans certains cas.
gilch
4
Vous pouvez simplement utiliser la .get()méthode dict avec 0default au lieu de l' tryinstruction.
gilch
@gilch J'ai laissé tomber les parenthèses. Mais j'aime la try:... except:...structure.
Aryerez
17

Si vous êtes sûr qu'aucune autre valeur n'a pu être définie sur les variables numeral_sys_1 et numeral_sys_2, c'est la solution la plus simple et la plus propre.

D'autre part, vous devez étendre le dictionnaire avec ses combinaisons avec les valeurs disponibles, si vous avez une autre valeur que "Hexadécimal", "Décimal" et "Binaire"

La logique ici est; si les tuples variables dans les clés de dictionnaire ne sont pas égaux au tuple variable donné, la méthode .get () renvoie "0". Si un tuple variable donné correspond à n'importe quelle clé du dictionnaire, retourne donc la valeur de la clé correspondante.

def convert_what(numeral_sys_1, numeral_sys_2):
    return {
        ("Hexadecimal", "Decimal") : 1, 
        ("Hexadecimal", "Binary") : 2, 
        ("Binary", "Decimal") : 3,
        ("Decimal", "Hexadecimal") : 4,
        ("Binary", "Hexadecimal") : 5, 
        ("Decimal", "Binary") : 6, 
     }.get((numeral_sys_1, numeral_sys_2), 0)

Il est également possible d'utiliser un générateur. Semble beaucoup plus intelligent, mais je pense que le dictionnaire codé en dur serait plus rapide que d'utiliser un générateur pour cette simple exigence.

Sencer H.
la source
Mon interprétation du dernier «else: return 0» est que les arguments ne correspondent pas et pourraient être autre chose que ceux de la liste (c'est-à-dire vos clés dict).
tocode
@tocode Oui, vous avez raison. Mais cette méthode fournit également la même fonctionnalité. Si l'un ou les deux arguments sont donnés à la méthode, disons, pas une chaîne, même étant une valeur de type None; La méthode .get () renvoie "0" en raison du manque de clé dans le dictionnaire. N'est-ce pas simple?
Sencer H.
n'avez-vous pas simplement copié la réponse d'Aryerez?
Martin
@Martin Non, je ne l'ai pas fait. Vous manquez clairement le point. Il y a plusieurs façons de faire quelque chose, mais enseigner correctement est ce que je veux faire ici. En fait, il y a une bien meilleure réponse ci-dessous. Jetez un œil à la solution de furkanayd. C'est impeccable et devait être payé.
Sencer H.
@ SencerH. La seule différence est que vous avez utilisé la méthode get () de dict, ce qui est fondamentalement ce que la réponse originale essaie / sauf. Vous ne pouvez pas nier le fait que vous avez copié l'idée et très très légèrement (sans amélioration) modifié et publié
Martin
3

Une autre façon d'utiliser la liste imbriquée. J'espère que cela aide!!

def convert_what(numeral_sys_1, numeral_sys_2):

    l1 = [["Hexadecimal","Decimal"],["Hexadecimal","Binary"],
            ["Decimal","Hexadecimal"],["Decimal","Binary"],
            ["Binary","Hexadecimal"],["Binary","Decimal"]]

    return l1.index([numeral_sys_1, numeral_sys_2]) + 1 if [numeral_sys_1,numeral_sys_2] in l1 else 0
Sapan Zaveri
la source
2

À mon avis, cette convert_whatfonction elle-même n'est pas très pythonique. Je suppose que le code qui appelle ce code a aussi un tas d'instructions if et effectue la conversion en fonction de la valeur de retour de convert_what(). Je suggère quelque chose comme ça:

Première étape, créez une fonction pour chaque combinaison:

def hex_dec(inp):
    return 1234  # todo implement
# do the same for hex_bin, bin_dec, dec_hex, bin_hex, dec_bin

Deuxième étape, mettez les objets fonction dans un dict. Notez qu'il n'y a pas de () après les noms des fonctions, car nous voulons stocker l'objet fonction et ne pas encore l'appeler:

converter_funcs = {
    ("Hexadecimal", "Decimal"): hex_dec,
    ("Hexadecimal", "Binary"): hex_bin,
    ("Binary", "Decimal"): bin_dec,
    ("Decimal", "Hexadecimal"): dec_hex,
    ("Binary", "Hexadecimal"): bin_hex,
    ("Decimal", "Binary"): dec_bin,
}

Troisième et dernière étape, implémentez une fonction de conversion. L'instruction if vérifie si les deux systèmes sont identiques. Ensuite, nous obtenons la bonne fonction de notre dict et l'appelons:

def convert(input_number, from_sys, to_sys):
    if from_sys == to_sys:
        return input_number
    func = converter_funcs[(from_sys, to_sys)]
    return func(input_number)
Sadap
la source
2

Cela se fait par des instructions switch-case dans la plupart des autres langues. En python, j'utilise une fonction simple avec un dictionnaire d'expression.

Code:

def convert_what(numeral_sys_1, numeral_sys_2):
    myExpressions = {"Hexadecimal" : {"Decimal" : 1, "Binary" : 2},
                    "Decimal" : {"Hexadecimal" : 4, "Binary" : 6}, 
                    "Binary" : {"Hexadecimal" : 5, "Decimal" : 3}}
    return (myExpressions.get(numeral_sys_1, {})).get(numeral_sys_2, 0)

Production:

> convert_what("Hexadecimal", "Decimal")
> 1
> convert_what("Binary", "Binary")
> 0
> convert_what("Invalid", "Hexadecimal")
> 0
furkanayd
la source
c'est une bonne alternative à la première réponse et il est également plus facile de l'étendre à plus de valeurs.
gkhnavarro
Ceci est assez similaire à une réponse précédente: stackoverflow.com/a/58985114/1895261 . De plus, je pense que la dernière ligne devrait retourner le dict vide plutôt que 0 dans le cas où numeral_sys_1 n'est pas dans le dict externe: return (myExpressions.get (numeral_sys_1, {})). Get (numeral_sys_2, 0)
Sepia
@Sepia dans la question Module_art donne 0 à l'instruction else, ce qui signifie que retourne 0 tout n'est pas conforme aux expressions données et à la situation d'égalité.
furkanayd
1
Essayez d'exécuter print (convert_what ("invalide", "hexadécimal")) avec votre code. Il générera une erreur: "AttributeError: l'objet 'int' n'a pas d'attribut 'get'". Remplacer le premier 0 par le dict vide ({}) fera que la fonction retournera correctement 0 dans le cas où numeral_sys_1 n'est pas valide.
Sepia
1

En général, je courrais avec une solution de dictionnaire pour une tâche imbriquée. Certains cas particuliers peuvent amener à une autre approche. Comme celui-ci:

def convert_what(numeral_sys_1, numeral_sys_2):

    num = ['Hexadecimal','Decimal','Binary']
    tbl = [[0,1,2],
           [4,0,6],
           [5,3,0]]
    try:
        return tbl[num.index(numeral_sys_1)][num.index(numeral_sys_2)]
    except ValueError:
        return 0
Anatoliy R
la source
1

Que diriez-vous de quelque chose comme:

def convert_what(numeral_sys_1, numeral_sys_2):
    src = numeral_sys_1, numeral_sys_2
    if src == "Hexadecimal", "Decimal":
        return 1
    if src == "Hexadecimal", "Binary"
        return 2
    # You get the gist.... 
    if src == "Decimal", "Binary":
        return 6
    return 0 
AnonymousAlex
la source
1

Une idée utilise une liste et obtient un index des résultats, c'est-à-dire.

def convert_what(numeral_sys_1, numeral_sys_2):
    if numeral_sys_1 == numeral_sys_2:      
        return 0
    return ["HexadecimalDecimal", "HexadecimalBinary", "BinaryDecimal", "DecimalHexadecimal", "BinaryHexadecimal", "DecimalBinary" ].index(numeral_sys_1 + numeral_sys_2) + 1
aucun
la source
Suggestion intéressante mais cela ne fonctionne pas lorsque les arguments sont ("Decimal", "Not"), ce qui a entraîné ValueError: 'DecimalNot' n'est pas dans la liste
tocode
1

Comme l'a dit @Sadap,

À mon avis, cette convert_whatfonction elle-même n'est pas très pythonique. Je suppose que le code qui appelle ce code a aussi un tas d'instructions if et effectue la conversion en fonction de la valeur de retour de convert_what(). Je suggère quelque chose comme ça:

Si vous implémentez la conversion de base pour les entiers, vous allez probablement par une représentation commune de toute façon: int. Une fonction distincte pour chaque paire de bases n'est pas nécessaire, et les deux bases impliquées n'ont même pas besoin de se connaître.

Contribution

Créez un mappage à partir du nom d'un système numérique vers sa base:

BINARY = "Binary"
DECIMAL = "Decimal"
HEXADECIMAL = "Hexadecimal"

BASES = {
    BINARY: 2,
    DECIMAL: 10,
    HEXADECIMAL: 16,
}

vous permettant de lire les entrées avec int(text, BASES[numeral_sys_1]).

Production

Créez un mappage à partir du nom d'un système numérique vers un spécificateur de format :

FORMATTERS = {
    BINARY: "b",
    DECIMAL: "d",
    HEXADECIMAL: "x",
}

vous permettant d'écrire des sorties avec format(n, FORMATTERS[numeral_sys_2]).

Exemple d'utilisation

def convert(text, numeral_sys_1, numeral_sys_2):
    n = int(text, BASES[numeral_sys_1])
    return format(n, FORMATTERS[numeral_sys_2])

L'un ou l'autre dict peut également être rendu plus général en rendant les fonctions de valeurs à la place, si vous avez besoin de prendre en charge un ensemble de formats différent de int(x, base), ou davantage de bases de sortie que les formats de nombres entiers pris en charge.

Ry-
la source
0

J'aime garder le code au sec:

def convert_what_2(numeral_sys_1, numeral_sys_2):
    num_sys = ["Hexadecimal", "Decimal", "Binary"]
    r_value = {0: {1: 1, 2: 2},
               1: {0: 4, 2: 6},
               2: {0: 5, 1: 3} }
    try:
        value = r_value[num_sys.index(numeral_sys_1)][num_sys.index(numeral_sys_2)]
    except KeyError: # Catches when they are equal or undefined
        value = 0
    return value
Mikeologue
la source
0

En utilisant certaines techniques fournies par les autres réponses et en les combinant:

def convert(key1, key2):
    keys = ["Hexadecimal", "Decimal", "Binary"]
    combinations = {(0, 1): 1, (0, 2): 2, (1, 0): 4, (1, 2): 6, (2, 0): 5, (2, 1): 3} # use keys indexes to map to combinations
    try:
        return combinations[(keys.index(key1), keys.index(key2))]
    except (KeyError, ValueError): # if value is not in list, return as 0
        return 0
Michael Yang
la source
-1

Bien que vous ne sachiez pas si cette approche est plus rapide, mais pourrait également être effectuée à l'aide de numpy:

conditions = [
    ("Hexadecimal", "Decimal"), ("Hexadecimal", "Binary"),
    ("Binary", "Decimal"), ("Decimal", "Hexadecimal"), ("Binary", "Hexadecimal"), ("Decimal", "Binary")]
choices = [1,2,3,4,5,6]

et peut être utilisé comme:

 np.select(conditions, choices, default=0)
vp7
la source