Esiste un modo migliore per scrivere istruzioni if ​​nidificate in Python? [chiuso]


34

Esiste un modo più pitonico per eseguire istruzioni nidificate if else di questa:

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

Questo script fa parte di un semplice convertitore.


Senza utilizzare un'altra struttura di dati, è possibile spostare le istruzioni if-else nidificate in andcondizioni per le istruzioni if-else di livello superiore. Almeno sarebbe più leggibile in quel modo. Purtroppo, Python non ha istruzioni switch.
adamkgray,

Questo è il modo pitonico. Python non supporta intenzionalmente le istruzioni switch. Vedi python.org/dev/peps/pep-3103
Jongmin Baek,

1
Non è affatto la domanda, ma se stai cercando di rendere le cose più Pythonic, che ne dici di definire costanti o un enum per i valori di ritorno - più bello per un lettore di "numeri magici" ....
Mats Wichmann

Risposte:


13

Mentre le risposte di @Aryerez e @ SencerH. funzionano, ogni possibile valore di numeral_sys_1deve essere ripetutamente scritto per ogni possibile valore di numeral_sys_2quando si elencano le coppie di valori, rendendo più difficile da mantenere la struttura dei dati quando aumenta il numero di valori possibili. È possibile invece utilizzare un dict nidificato al posto delle istruzioni if ​​nidificate:

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)

In alternativa, è possibile generare le coppie di valori per la mappatura con il itertools.permutationsmetodo, il cui ordine segue quello della sequenza di input:

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)

29

Inserisci tutte le combinazioni valide in a dictionary di tuples, e se la combinazione non è presente, restituisci 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)

Se si prevede di utilizzare la funzione in un ciclo, potrebbe essere un'idea migliore definire il dizionario al di fuori della funzione, in modo che non venga ricreato ad ogni chiamata alla funzione.


2
except KeyError:
RomanPerekhrest,

@RomanPerekhrest L'ho aggiunto, sebbene in questa domanda specifica, la funzione stessa non abbia altri tipi di errori da produrre che darebbero un output diverso dalla sua funzione originale.
Aryerez,

1
I parentesi sono ridondanti all'interno di []. Ad eccezione della tupla vuota, è la virgola che la rende una tupla, non le parentesi, in alcuni casi solo per l'ordine delle operazioni.
Gilch,

4
Puoi semplicemente usare il .get()metodo dict con 0default invece trydell'istruzione.
Gilch,

@gilch Ho lasciato cadere le parentesi. Ma mi piace la try:... except:...struttura.
Aryerez,

17

Se sei sicuro che non ci sia altro valore che avrebbe potuto essere impostato sulle variabili numeral_sys_1 e numeral_sys_2 questa è la soluzione più semplice e pulita.

D'altra parte, è necessario estendere il dizionario con le sue combinazioni con i valori disponibili, se si dispone di un valore diverso da "Esadecimale", "Decimale" e "Binario"

La logica qui è; se le tuple variabili nelle chiavi del dizionario non sono uguali alla tupla variabile fornita, il metodo .get () restituisce "0". Se una variabile tupla corrisponde a qualsiasi chiave nel dizionario, restituisce quindi il valore della chiave corrispondente.

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)

C'è anche l'utilizzo di un generatore potrebbe essere una soluzione. Sembra molto più intelligente, ma penso che un dizionario codificato sarebbe più veloce dell'uso di un generatore per questo semplice requisito.


La mia interpretazione dell'ultimo "else: return 0" è che gli argomenti non corrispondono e potrebbero essere qualcos'altro oltre a quelli dell'elenco (ad es. Le tue chiavi dict).
tocode il

@tocode Sì, hai ragione. Ma questo metodo offre anche la stessa funzionalità. Se uno o entrambi gli argomenti forniti al metodo, diciamo, non stringa, pur essendo il valore del tipo Nessuno; Il metodo .get () restituisce "0" a causa della mancanza di chiave nel dizionario. Non è semplice?
Sencer H.

non hai appena copiato la risposta di Aryerez?
Martin,

@Martin No, non l'ho fatto. Ti manca chiaramente il punto. Ci sono molti modi per fare qualcosa, ma insegnare il modo corretto è quello che voglio fare qui. In realtà c'è una risposta molto migliore di seguito. Dai un'occhiata alla soluzione di furkanayd. È impeccabile e doveva essere generoso.
Sencer H.

@SencerH. L'unica differenza è che hai usato il metodo get () di dict, che è fondamentalmente ciò che la risposta originale prova / tranne fa. Non puoi negare il fatto che hai copiato l'idea e modificato e pubblicato molto molto poco (senza migliorare)
Martin,

3

Un modo alternativo utilizzando l'elenco nidificato. Spero che sia d'aiuto!!

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

2

Secondo me, questa convert_whatstessa funzione non è molto pitonica. Immagino che il codice che chiama questo codice abbia anche un sacco di istruzioni if ​​e faccia la conversione in base al valore di ritorno diconvert_what() . Suggerisco qualcosa del genere:

Primo passo, crea una funzione per ogni combinazione:

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

Secondo passaggio, inserire gli oggetti funzione in un dict. Si noti che non ci sono () dopo i nomi delle funzioni, perché vogliamo memorizzare l'oggetto funzione e non chiamarlo ancora:

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,
}

Terzo e ultimo passaggio, implementare una funzione di conversione. L'istruzione if verifica se entrambi i sistemi sono uguali. Quindi, otteniamo la giusta funzione dal nostro dict e la chiamiamo:

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)

2

Questo viene fatto dalle istruzioni switch-case nella maggior parte delle altre lingue. In Python, utilizzo una semplice funzione con un dizionario di espressioni.

Codice:

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)

Produzione:

> convert_what("Hexadecimal", "Decimal")
> 1
> convert_what("Binary", "Binary")
> 0
> convert_what("Invalid", "Hexadecimal")
> 0

questa è una buona alternativa alla risposta migliore ed è più facile estenderla anche a più valori.
gkhnavarro,

È abbastanza simile a una risposta precedente: stackoverflow.com/a/58985114/1895261 . Inoltre, penso che l'ultima riga dovrebbe restituire il dict vuoto anziché 0 nel caso in cui numeral_sys_1 non sia nel dict esterno: return (myExpressions.get (numeral_sys_1, {})). Get (numeral_sys_2, 0)
Sepia

@Sepia nella domanda Module_art fornisce 0 nell'istruzione else, il che significa che restituisce 0 qualsiasi cosa non sia conforme alle espressioni fornite e alla situazione di uguaglianza.
furkanayd,

1
Prova a eseguire print (convert_what ("invalid", "Hexadecimal")) con il tuo codice. Verrà generato un errore: "AttributeError: l'oggetto" int "non ha alcun attributo" get "". Sostituendo il primo 0 con il dict vuoto ({}) la funzione restituirà correttamente 0 nel caso in cui numal_sys_1 non sia valido.
Seppia,

1

In generale, avrei eseguito con la soluzione di dizionario per l'attività if nidificata. Alcuni casi particolari possono portare ad un altro approccio. Come questo:

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

1

Che ne dici di qualcosa come:

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 

1

Un'idea utilizza un elenco e ottiene l'indice del risultato, ad es.

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

Suggerimento interessante ma questo non funziona quando gli argomenti sono ("Decimale", "Non"), il che ha portato a ValueError: 'DecimalNot' non è in elenco
codificare il

1

Come ha detto @Sadap,

Secondo me, questa convert_whatstessa funzione non è molto pitonica. Immagino che il codice che chiama questo codice abbia anche un sacco di istruzioni if ​​e faccia la conversione in base al valore di ritorno diconvert_what() . Suggerisco qualcosa del genere:

Se stai implementando la conversione di base per numeri interi, probabilmente stai comunque attraversando una rappresentazione comune: int . Non è necessaria una funzione separata per ciascuna coppia di basi e le due basi coinvolte non hanno nemmeno bisogno di conoscersi.

Ingresso

Crea una mappatura dal nome di un sistema numerico alla sua base:

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

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

che consente di leggere gli input con int(text, BASES[numeral_sys_1]) .

Produzione

Crea una mappatura dal nome di un sistema numerico a un identificatore di formato :

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

che consente di scrivere output con format(n, FORMATTERS[numeral_sys_2]) .

Esempio di utilizzo

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

O dict può anche essere reso più generale rendendo invece i valori funzionali, se è necessario supportare un set di formati diverso rispetto a int(x, base), o più basi di output rispetto ai supporti di formattazione interi integrati.


0

Mi piace mantenere il codice asciutto:

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

0

Utilizzando alcune tecniche fornite dalle altre risposte e combinandole:

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

-1

Anche se non sono sicuro che questo approccio sia più veloce, ma potrebbe essere fatto anche usando numpy:

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

e può essere usato come:

 np.select(conditions, choices, default=0)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.