Existe uma maneira melhor de escrever instruções if aninhadas em python? [fechadas]

34

Existe uma maneira mais pitônica de fazer declarações aninhadas se mais 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 é parte de um conversor simples.

Module_art
fonte
Sem usar outra estrutura de dados, você pode mover as instruções if-else aninhadas para as andcondições das instruções if-else de nível superior. Seria pelo menos mais legível assim. Infelizmente, o python não possui instruções de opção.
adamkgray
Este é o caminho pitônico. O Python intencionalmente não suporta instruções de opção. Veja python.org/dev/peps/pep-3103
Jongmin Baek
11
Não é a pergunta de todo, mas se você está tentando tornar as coisas mais pitônicas, que tal definir constantes ou uma enumeração para os valores de retorno - mais agradáveis ​​para um leitor do que "números mágicos" ....
Mats Wichmann

Respostas:

13

Enquanto as respostas de @Aryerez e @ SencerH. Funcionam, cada valor possível de numeral_sys_1deve ser escrito repetidamente para cada valor possível numeral_sys_2ao listar os pares de valores, dificultando a manutenção da estrutura de dados quando o número de valores possíveis aumenta. Em vez disso, você pode usar um dict aninhado no lugar das instruções if aninhadas:

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)

Como alternativa, você pode gerar os pares de valores para o mapeamento com o itertools.permutationsmétodo, cuja ordem segue a da sequência 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
fonte
29

Insira todas as combinações válidas em um dictionaryde tuples e, se a combinação não estiver lá, retorne 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 você planeja usar a função em um loop, pode ser uma idéia melhor definir o dicionário fora da função, para que não seja recriado em todas as chamadas para a função.

Aryerez
fonte
2
except KeyError:
RomanPerekhrest
@RomanPerekhrest Eu adicionei, embora nesta questão específica, a função em si não tenha outros tipos de erros para produzir que dariam uma saída diferente da sua função original.
Aryerez 19/11/19
11
As parênteses são redundantes dentro []. Exceto pela tupla vazia, é a vírgula que a torna uma tupla, não os parênteses, que é apenas para a ordem das operações em alguns casos.
19419 gilch
4
Você pode apenas usar o .get()método dict com 0padrão em vez da tryinstrução
19419 Gilil
@ Gilch Deixei cair os parênteses. Mas eu gosto da try:... except:...estrutura.
Aryerez 19/11/19
17

Se você tiver certeza de que nenhum outro valor poderia ter sido definido para as variáveis ​​numeral_sys_1 e numeral_sys_2, esta é a solução mais simples e limpa.

Por outro lado, você deve estender o dicionário com suas combinações com os valores disponíveis, se tiver outro valor que não seja "Hexadecimal", "Decimal" e "Binário"

A lógica aqui é; se as tuplas variáveis ​​nas chaves do dicionário não forem iguais a determinada tupla variável, o método .get () retornará "0". Se determinada variável tupla corresponder a qualquer chave no dicionário, retornará o valor da chave correspondente.

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)

Há também usando gerador poderia ser uma solução. Parece muito mais inteligente, mas acho que o dicionário codificado seria mais rápido do que usar um gerador para esse requisito simples.

Sencer H.
fonte
Minha interpretação do último 'else: return 0' é que os argumentos não correspondem e podem ser outra coisa além dos da lista (ou seja, suas chaves de dict).
tocode 23/11/19
@ codoc Sim, você está certo. Mas esse método também fornece a mesma funcionalidade. Se algum ou ambos os argumentos forem fornecidos ao método, digamos, não string, mesmo sendo None type value; O método .get () retorna "0" devido à falta de chave no dicionário. Não é simples?
precisa
você não acabou de copiar a resposta de Aryerez?
Martin Martin
@ Martin Não, eu não fiz. Você está claramente perdendo o objetivo. Existem muitas maneiras de fazer algo, mas ensinar a maneira correta é o que estou disposto a fazer aqui. Na verdade, há uma resposta muito melhor abaixo. Dê uma olhada na solução da furkanayd. É impecável e precisava ser recompensado.
precisa
@ SencerH.A única diferença foi que você usou o método dict get (), que é fundamentalmente o que a resposta original try / except faz. Você não pode negar o fato, que copiou idéia e muito, muito ligeiramente (sem melhorar) modificado e publicado
Martin
3

Uma maneira alternativa usando lista aninhada. Espero que ajude!!

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

Na minha opinião, essa convert_whatfunção em si não é muito pitônica. Eu acho que o código que chama esse código também possui várias instruções if e faz a conversão dependendo do valor de retorno de convert_what(). Sugiro algo como isto:

Primeiro passo, faça uma função para cada combinação:

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

Segundo passo, coloque os objetos de função em um ditado. Observe que não há () após os nomes das funções, porque queremos armazenar o objeto da função e ainda não o chamamos:

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

Terceiro e último passo, implemente uma função de conversão. A instrução if verifica se os dois sistemas são iguais. Então, obtemos a função correta de nosso ditado e a chamamos:

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

Isso é feito por instruções de caso de switch na maioria dos outros idiomas. Em python, eu uso uma função simples com um dicionário de expressão.

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)

Resultado:

> convert_what("Hexadecimal", "Decimal")
> 1
> convert_what("Binary", "Binary")
> 0
> convert_what("Invalid", "Hexadecimal")
> 0
furkanayd
fonte
essa é uma boa alternativa para a resposta principal e também é mais fácil estender para mais valores.
Gkhnavarro 28/11/19
Isso é bastante semelhante a uma resposta anterior: stackoverflow.com/a/58985114/1895261 . Além disso, acho que a última linha deve retornar o ditado vazio em vez de 0 no caso de numeral_sys_1 não estar no dict externo: return (myExpressions.get (numeral_sys_1, {})). Get (numeral_sys_2, 0)
Sepia
@Sepia na questão Module_art fornece 0 na instrução else, o que significa retornar 0, qualquer coisa não está de acordo com as expressões fornecidas e a situação de igualdade.
furkanayd
11
Tente executar print (convert_what ("invalid", "Hexadecimal")) com seu código. Irá gerar um erro: "AttributeError: o objeto 'int' não possui atributo 'get'". Substituir o primeiro 0 pelo dict vazio ({}) fará com que a função retorne 0 corretamente, caso o numeral_sys_1 seja inválido.
Sepia
1

Em geral, eu correria com a solução de dicionário para tarefa aninhada se. Alguns casos particulares podem levar a outra abordagem. Como este:

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

Que 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 
AnonymousAlex
fonte
1

Uma idéia está usando uma lista e obtém o índice do resultado, ou seja.

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
Nenhum
fonte
Sugestão interessante, mas isso não funciona quando os argumentos são ("Decimal", "Not"), o que resultou em ValueError: 'DecimalNot' não está na lista
tocando em
1

Como o @Sadap disse,

Na minha opinião, essa convert_whatfunção em si não é muito pitônica. Eu acho que o código que chama esse código também possui várias instruções if e faz a conversão dependendo do valor de retorno de convert_what(). Sugiro algo como isto:

Se você está implementando conversão base para inteiros, provavelmente você está passando por uma representação comum de qualquer maneira: int. Uma função separada para cada par de bases não é necessária, e as duas bases envolvidas nem precisam saber uma da outra.

Entrada

Crie um mapeamento a partir do nome de um sistema numérico até sua base:

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

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

permitindo ler entradas com int(text, BASES[numeral_sys_1]).

Resultado

Crie um mapeamento a partir do nome de um sistema numérico para um especificador de formato :

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

permitindo escrever saídas com format(n, FORMATTERS[numeral_sys_2]).

Exemplo 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])

Qualquer regra também pode ser tornada mais geral, fazendo com que os valores funcionem, se você precisar oferecer suporte a um conjunto diferente de formatos int(x, base)ou mais bases de saída do que a formatação inteira integrada suporta.

Ry-
fonte
0

Eu gosto de manter o 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
fonte
0

Usando algumas técnicas fornecidas pelas outras respostas e combiná-las:

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

Embora não tenha certeza se essa abordagem é mais rápida, mas pode ser feita usando o numpy também:

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

e pode ser usado como:

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