¿Hay una mejor manera de escribir sentencias if anidadas en python? [cerrado]

34

¿Hay una manera más pitónica de hacer declaraciones anidadas if else que esta:

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

Este script es parte de un convertidor simple.

Módulo_arte
fuente
Sin utilizar otra estructura de datos, podría mover las instrucciones if-else anidadas a andcondiciones para las instrucciones if-else de nivel superior. Al menos sería más legible de esa manera. Lamentablemente, Python no tiene declaraciones de cambio.
adamkgray
Este es el camino pitónico. Python intencionalmente no admite declaraciones de cambio. Ver python.org/dev/peps/pep-3103
Jongmin Baek el
1
No es la pregunta en absoluto, pero si estás tratando de hacer las cosas más Pythonic, ¿qué tal definir constantes o una enumeración para los valores de retorno, más agradable para un lector que los "números mágicos" ...
Mats Wichmann

Respuestas:

13

Si bien las respuestas de @Aryerez y @ SencerH. Funcionan, cada valor posible de numeral_sys_1tiene que escribirse repetidamente para cada valor posible numeral_sys_2al enumerar los pares de valores, lo que hace que la estructura de datos sea más difícil de mantener cuando aumenta el número de valores posibles. En su lugar, puede usar un dict anidado en lugar de sus instrucciones if anidadas:

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)

Alternativamente, puede generar los pares de valores para la asignación con el itertools.permutationsmétodo, cuyo orden sigue el de la secuencia de entrada:

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
fuente
29

Inserte todas las combinaciones válidas en una dictionaryde tuples, y si la combinación no está allí, devuelva 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 planea usar la función en un bucle, puede ser una mejor idea definir el diccionario fuera de la función, para que no se vuelva a crear en cada llamada a la función.

Aryerez
fuente
2
except KeyError:
RomanPerekhrest
@RomanPerekhrest Lo he agregado, aunque en esta pregunta específica, la función en sí misma no tiene otros tipos de errores para producir que darían una salida diferente de su función original.
Aryerez
1
Los parens son redundantes dentro de []. Excepto por la tupla vacía, es la coma lo que la convierte en una tupla, no los paréntesis, eso es solo por orden de operaciones en algunos casos.
gilch
44
Simplemente puede usar el .get()método dict con 0default en lugar de la trydeclaración.
Gilch
@gilch se me cayeron los paréntesis. Pero me gusta la try:... except:...estructura.
Aryerez
17

Si está seguro de que no se ha podido establecer ningún otro valor en las variables numeral_sys_1 y numeral_sys_2, esta es la solución más simple y limpia.

Por otro lado, debe extender el diccionario con sus combinaciones con los valores disponibles, si tiene cualquier otro valor que no sea "Hexadecimal", "Decimal" y "Binario"

La lógica aquí es; si las tuplas variables en las claves del diccionario no son iguales a la tupla variable dada, el método .get () devuelve "0". Si la tupla variable dada coincide con cualquier clave del diccionario, devuelve el valor de la clave correspondiente

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)

También está utilizando generador podría ser una solución. Parece mucho más inteligente, pero creo que un diccionario codificado sería más rápido que usar un generador para este simple requisito.

Senor H.
fuente
Mi interpretación del último 'else: return 0' es que los argumentos no coinciden y podrían ser algo más aparte de los de la lista (es decir, sus claves dict).
codificar el
@tocode Sí, tienes razón. Pero este método también proporciona la misma funcionalidad. Si alguno o ambos argumentos se dan al método, digamos, no cadena, incluso siendo el valor de tipo Ninguno; El método .get () devuelve "0" debido a la falta de clave en el diccionario. ¿No es simple?
Senador H.
¿no acabas de copiar la respuesta de Aryerez?
Martin
@ Martin No, no lo hice. Claramente te estás perdiendo el punto. Hay muchas formas de hacer algo, pero enseñar lo correcto es lo que estoy dispuesto a hacer aquí. En realidad, hay una respuesta mucho mejor a continuación. Echa un vistazo a la solución de furkanayd. Es impecable y tuvo que ser generoso.
Senador H.
@ SencerH. La única diferencia fue que usaste el método get () de dict, que es fundamentalmente lo que intenta / excepto la respuesta original. No puede negar el hecho de que copió la idea y modificó y publicó muy, muy poco (sin mejorar)
Martin
3

Una forma alternativa usando la lista anidada. ¡¡Espero eso ayude!!

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
fuente
2

En mi opinión, esta convert_whatfunción en sí no es muy pitónica. Supongo que el código que llama a este código también tiene un montón de sentencias if y realiza la conversión según el valor de retorno de convert_what(). Sugiero algo como esto:

Primer paso, realice una función para cada combinación:

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

Segundo paso, coloca los objetos de función en un dict. Tenga en cuenta que no hay () después de los nombres de las funciones, porque queremos almacenar el objeto de la función y no llamarlo todavía:

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

Tercer y último paso, implementar una función de conversión. La instrucción if verifica si ambos sistemas son iguales. Luego, obtenemos la función correcta de nuestro dict y lo llamamos:

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
fuente
2

Esto se hace mediante declaraciones de mayúsculas y minúsculas en la mayoría de los otros idiomas. En python, uso una función simple con un diccionario de expresión.

Código:

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)

Salida:

> convert_what("Hexadecimal", "Decimal")
> 1
> convert_what("Binary", "Binary")
> 0
> convert_what("Invalid", "Hexadecimal")
> 0
furkanayd
fuente
Esta es una buena alternativa a la respuesta principal y también es más fácil de extender a más valores.
gkhnavarro
Esto es bastante similar a una respuesta anterior: stackoverflow.com/a/58985114/1895261 . Además, creo que la última línea debería devolver el dict vacío en lugar de 0 en el caso de que numeral_sys_1 no esté en el dict exterior: return (myExpressions.get (numeral_sys_1, {})). Get (numeral_sys_2, 0)
Sepia
@Sepia en la pregunta Module_art da 0 en la instrucción else, lo que significa que devolver 0 cualquier cosa no se ajusta a las expresiones dadas y la situación de igualdad.
furkanayd
1
Intente ejecutar print (convert_what ("inválido", "Hexadecimal")) con su código. Aparecerá un error: "AttributeError: el objeto 'int' no tiene el atributo 'get'". Reemplazar el primer 0 con el dict vacío ({}) hará que la función devuelva 0 correctamente en el caso de que numeral_sys_1 no sea válido.
Sepia
1

En general, correría con una solución de diccionario para la tarea anidada if. Algunos casos particulares pueden llevar a otro enfoque. Como éste:

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
fuente
1

¿Qué tal algo como:

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 
AnónimoAlex
fuente
1

Una Idea está usando una lista y obtiene un índice de resultado, es decir.

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
ninguna
fuente
Sugerencia interesante, pero esto no funciona cuando los argumentos son ("Decimal", "No"), lo que resultó en ValueError: 'DecimalNot' no está en la lista
codificar el
1

Como dijo @Sadap,

En mi opinión, esta convert_whatfunción en sí no es muy pitónica. Supongo que el código que llama a este código también tiene un montón de sentencias if y realiza la conversión según el valor de retorno de convert_what(). Sugiero algo como esto:

Si va a implementar la conversión de base de los enteros, lo que probablemente va a través de una representación común de todos modos: int. No es necesaria una función separada para cada par de bases, y las dos bases involucradas ni siquiera necesitan conocerse entre sí.

Entrada

Cree una asignación desde el nombre de un sistema numérico hasta su base:

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

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

permitiéndole leer entradas con int(text, BASES[numeral_sys_1]).

Salida

Cree una asignación del nombre de un sistema numérico a un especificador de formato :

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

permitiéndole escribir salidas con format(n, FORMATTERS[numeral_sys_2]).

Ejemplo de uso

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

O bien dict también se puede hacer más general haciendo que los valores funcionen en su lugar, si necesita admitir un conjunto diferente de formatos int(x, base)o más bases de salida que las que admite el formato de entero integrado.

Ry-
fuente
0

Me gusta mantener el código seco:

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
Mikeologist
fuente
0

Usando algunas técnicas que proporcionan las otras respuestas y combinarlas:

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
fuente
-1

Aunque no estoy seguro de si este enfoque es más rápido, también podría hacerse usando numpy:

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

y se puede usar como:

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