Czy istnieje lepszy sposób na pisanie zagnieżdżonych instrukcji w Pythonie? [Zamknięte]

34

Czy istnieje bardziej pythonowy sposób wykonywania zagnieżdżonych instrukcji if if else niż ten:

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

Ten skrypt jest częścią prostego konwertera.

Moduł_art
źródło
Bez użycia innej struktury danych można przenieść zagnieżdżone instrukcje if-else do andwarunków dla instrukcji najwyższego poziomu if-else. W ten sposób byłoby co najmniej bardziej czytelne. Niestety, python nie ma instrukcji switch.
adamkgray
To jest metoda pytoniczna. Python celowo nie obsługuje instrukcji switch. Zobacz python.org/dev/peps/pep-3103
Jongmin Baek
1
Wcale nie jest to pytanie, ale jeśli próbujesz uczynić rzeczy bardziej Pythonicznymi, co powiesz na zdefiniowanie stałych lub wyliczenia wartości zwracanych - ładniejsze dla czytelnika niż „magiczne liczby” ....
Mats Wichmann

Odpowiedzi:

13

Podczas gdy odpowiedzi @Aryerez i @ SencerH. Działają, każda możliwa wartość numeral_sys_1musi być wielokrotnie zapisywana dla każdej możliwej wartości numeral_sys_2przy zestawianiu par wartości, co utrudnia utrzymanie struktury danych, gdy rośnie liczba możliwych wartości. Zamiast tego możesz użyć zagnieżdżonego słownika zamiast zagnieżdżonego, jeśli instrukcje:

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)

Alternatywnie możesz wygenerować pary wartości dla mapowania za pomocą itertools.permutationsmetody, której kolejność jest zgodna z kolejnością wejściową:

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
źródło
29

Włóż wszystkie poprawne kombinacje dictionaryod tuples, a jeżeli połączenie nie istnieje, zwraca 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)

Jeśli planujesz używać funkcji w pętli, lepszym pomysłem może być zdefiniowanie słownika poza funkcją, aby nie była odtwarzana przy każdym wywołaniu funkcji.

Aryerez
źródło
2
except KeyError:
RomanPerekhrest
@RomanPerekhrest Dodałem go, chociaż w tym konkretnym pytaniu sama funkcja nie ma innych rodzajów błędów do wygenerowania, które dawałyby inne wyniki niż jego pierwotna funkcja.
Aryerez
1
Pareny są zbędne wewnątrz []. Z wyjątkiem pustej krotki, to przecinek sprawia, że ​​jest krotką, a nie nawiasami, w niektórych przypadkach jest to tylko kolejność operacji.
gilch
4
Możesz po prostu użyć .get()metody dict z 0domyślną zamiast tryinstrukcji.
gilch
@ Gilch Upuściłem nawiasy. Ale podoba mi się try:... except:...struktura.
Aryerez
17

Jeśli masz pewność, że nie ma innej wartości, którą można ustawić na zmienną numeral_sys_1 i zmienne numeral_sys_2, jest to najprostsze i najczystsze rozwiązanie.

Z drugiej strony, musisz rozszerzyć słownik o jego kombinacje o dostępne wartości, jeśli masz inną wartość niż „Szesnastkowy”, „Dziesiętny” i „Binarny”

Oto logika; jeśli krotki zmienne w kluczach słownika nie są równe danej krotce zmiennej, metoda .get () zwraca „0”. Jeśli dana zmienna krotka pasuje do dowolnego klucza w słowniku, zwraca wartość pasującego klucza.

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)

Istnieje również użycie generatora może być rozwiązaniem. Wygląda o wiele mądrzej, ale myślę, że słownik z kodowaniem na sztywno byłby szybszy niż użycie generatora do tego prostego wymagania.

Sencer H.
źródło
Moja interpretacja ostatniego „else: return 0” jest taka, że ​​argumenty nie pasują i mogą być czymś innym niż argumenty na liście (np. Twoje klucze dict).
kod
@tocode Tak, masz rację. Ale ta metoda zapewnia również tę samą funkcjonalność. Jeśli jakikolwiek lub oba argumenty podane metodzie, powiedzmy, nie łańcuch, nawet będąc wartością typu None; Metoda .get () zwraca „0” z powodu braku klucza w słowniku. Czy to nie jest proste?
Sencer H.,
nie skopiowałeś właśnie odpowiedzi Aryereza?
Martin
@Martin Nie, nie zrobiłem tego. Wyraźnie brakuje ci sensu. Jest wiele sposobów na zrobienie czegoś, ale chętnie uczę tego, jak prawidłowo uczyć. Właściwie poniżej jest o wiele lepsza odpowiedź. Spójrz na rozwiązanie furkanayd. Jest bezbłędny i musiał zostać wyróżniony.
Sencer H.
@ SencerH. Jedyna różnica polegała na tym, że użyto metody dict get (), która jest zasadniczo tym, co robi metoda pierwotna odpowiedź / próba. Nie można zaprzeczyć, że skopiowałeś pomysł i bardzo nieznacznie (bez poprawy) zmodyfikowałeś i opublikowałeś
Martin
3

Alternatywny sposób przy użyciu listy zagnieżdżonej. Mam nadzieję, że to pomoże!!

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
źródło
2

Moim zdaniem ta convert_whatfunkcja sama w sobie nie jest bardzo pytoniczna. Domyślam się, że kod, który wywołuje ten kod, ma również kilka instrukcji if i konwertuje w zależności od zwracanej wartości convert_what(). Sugeruję coś takiego:

Pierwszy krok, wykonaj jedną funkcję dla każdej kombinacji:

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

Drugi krok, umieść obiekty funkcyjne w dyktando. Zauważ, że po nazwach funkcji nie ma znaku (), ponieważ chcemy zapisać obiekt funkcji i nie wywoływać go jeszcze:

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

Trzeci i ostatni krok, zaimplementuj funkcję konwersji. Instrukcja if sprawdza, czy oba systemy są takie same. Następnie otrzymujemy właściwą funkcję z naszego słownika i nazywamy ją:

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
źródło
2

Odbywa się to za pomocą instrukcji przełączania wielkości liter w większości innych języków. W Pythonie używam prostej funkcji ze słownikiem wyrażeń.

Kod:

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)

Wynik:

> convert_what("Hexadecimal", "Decimal")
> 1
> convert_what("Binary", "Binary")
> 0
> convert_what("Invalid", "Hexadecimal")
> 0
furkanayd
źródło
jest to dobra alternatywa dla najwyższej odpowiedzi i łatwiej jest rozszerzyć ją również na więcej wartości.
gkhnavarro
Jest to dość podobne do poprzedniej odpowiedzi: stackoverflow.com/a/58985114/1895261 . Ponadto uważam, że ostatni wiersz powinien zwrócić pusty dykt, a nie 0, w przypadku gdy numeral_sys_1 nie znajduje się w zewnętrznym dykcie: return (myExpressions.get (numeral_sys_1, {})). Get (numeral_sys_2, 0)
Sepia
@Sepia w pytaniu Module_art daje 0 w instrukcji else, co oznacza, że ​​0 zwraca wszystko, co nie jest zgodne z podanymi wyrażeniami i sytuacją równości.
furkanayd
1
Spróbuj uruchomić print (convert_what („invalid”, „Hexadecimal”)) z kodem. Pojawi się błąd: „AttributeError: obiekt„ int ”nie ma atrybutu„ get ””. Zastąpienie pierwszego 0 pustym dyktem ({}) spowoduje, że funkcja poprawnie zwróci 0 w przypadku, gdy numer_sys_1 jest nieprawidłowy.
Sepia
1

Ogólnie działałbym z rozwiązaniem słownikowym dla zagnieżdżonego, jeśli zadanie. Niektóre szczególne przypadki mogą przynieść inne podejście. Jak ten:

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.
źródło
1

Co powiesz na coś takiego:

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 
AnonimowyAlex
źródło
1

Pomysł korzysta z listy i uzyskuje indeks wyników, tj.

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
Żaden
źródło
Interesująca sugestia, ale to nie działa, gdy argumentami są („dziesiętny”, „nie”), co spowodowało błąd ValueError: „DecimalNot” nie ma na liście
kod
1

Jak powiedział @Sadap,

Moim zdaniem ta convert_whatfunkcja sama w sobie nie jest bardzo pytoniczna. Domyślam się, że kod, który wywołuje ten kod, ma również kilka instrukcji if i konwertuje w zależności od zwracanej wartości convert_what(). Sugeruję coś takiego:

Jeśli realizacji konwersji bazowy dla liczb całkowitych, jesteś prawdopodobnie przeżywa wspólnej reprezentacji i tak: int. Oddzielna funkcja dla każdej pary baz nie jest konieczna, a dwie zaangażowane bazy nie muszą nawet wiedzieć o sobie.

Wejście

Utwórz odwzorowanie z nazwy systemu liczbowego na jego podstawę:

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

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

pozwalając na odczyt danych wejściowych za pomocą int(text, BASES[numeral_sys_1]).

Wynik

Utwórz odwzorowanie z nazwy systemu liczbowego na specyfikator formatu :

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

umożliwiając pisanie wyników za pomocą format(n, FORMATTERS[numeral_sys_2]).

Przykładowe użycie

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

Każdy dykt można również uogólnić, wprowadzając funkcje wartości zamiast tego, jeśli trzeba obsługiwać inny zestaw formatów int(x, base)lub więcej baz wyjściowych niż obsługa wbudowanego formatowania liczb całkowitych.

Ry-
źródło
0

Lubię dbać o to, aby kod był suchy:

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
Mikeolog
źródło
0

Używając niektórych technik, które zapewniają inne odpowiedzi i łącz je:

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
źródło
-1

Chociaż nie jestem pewien, czy to podejście jest szybsze, ale można to zrobić również za pomocą numpy:

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

i może być używany jako:

 np.select(conditions, choices, default=0)
vp7
źródło