Есть ли лучший способ писать вложенные операторы if в python? [закрыто]

34

Есть ли более питонный способ делать вложенные операторы if else, чем этот:

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

Этот скрипт является частью простого конвертера.

Module_art
источник
Не используя другую структуру данных, вы можете переместить вложенные операторы if-else в andусловия для операторов if-else верхнего уровня. По крайней мере, так будет читабельнее. К сожалению, в python нет операторов switch.
adamkgray
Это является вещим путем. Python специально не поддерживает операторы switch. См. Python.org/dev/peps/pep-3103
Jongmin Baek
1
Совсем не вопрос, но если вы пытаетесь сделать вещи более Pythonic, как насчет определения констант или перечисления для возвращаемых значений - приятнее для читателя, чем «магические числа» ....
Матс Вихманн,

Ответы:

13

Хотя ответы @Aryerez и @ SencerH. Работают, каждое возможное значение numeral_sys_1должно многократно записываться для каждого возможного значения numeral_sys_2при перечислении пар значений, что усложняет поддержание структуры данных при увеличении числа возможных значений. Вместо этого вы можете вместо этого использовать вложенный дикт вместо ваших вложенных операторов if:

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)

Кроме того, вы можете сгенерировать пары значений для сопоставления с помощью itertools.permutationsметода, порядок которого соответствует порядку входной последовательности:

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
источник
29

Вставьте все допустимые комбинации в a dictionaryиз tuples, и если комбинации нет, верните 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)

Если вы планируете использовать функцию в цикле, возможно, будет лучше определить словарь вне функции, чтобы он не создавался заново при каждом вызове функции.

Aryerez
источник
2
except KeyError:
РоманПерехрест
@RomanPerekhrest Я добавил его, хотя в этом конкретном вопросе сама функция не имеет других типов ошибок, которые могли бы выдать результат, отличный от его исходной функции.
Арьерез
1
Парень лишний внутри []. За исключением пустого кортежа, кортеж делает его запятой, а не скобками, в некоторых случаях это просто порядок операций.
Гилх
4
Вы можете просто использовать .get()метод dict с 0default вместо tryоператора.
Гилх
@gilch Я опустил скобки. Но мне нравится try:... except:...структура.
Арьерез
17

Если вы уверены, что нет других значений, которые могли бы быть установлены в переменные цифр_сис_1 и числ_сис_2, это самое простое и чистое решение.

С другой стороны, вы должны расширить словарь с его комбинациями с доступными значениями, если у вас есть любое другое значение, кроме «шестнадцатеричный», «десятичный» и «двоичный»

Логика здесь такова; если кортежи переменных в словарных ключах не равны данному кортежу переменных, метод .get () возвращает «0». Если заданный кортеж переменной совпадает с любым ключом в словаре, таким образом, возвращается значение соответствующего ключа.

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)

Существует также использование генератора может быть решением. Выглядит намного умнее, но я думаю, что словарь с жесткой кодировкой будет быстрее, чем использование генератора для этого простого требования.

Sencer H.
источник
Моя интерпретация последнего «else: return 0» заключается в том, что аргументы не совпадают и могут быть чем-то еще, кроме аргументов в списке (т. Е. Ваших ключей dict).
закодировать
@tocode Да, вы правы. Но этот метод также обеспечивает такую ​​же функциональность. Если какой-либо или оба аргумента переданы методу, скажем, не строка, даже если значение типа None; Метод .get () возвращает «0» из-за отсутствия ключа в словаре. Разве это не просто?
Sencer H.
ты просто не скопировал ответ Арьерез?
Мартин
@ Мартин Нет, я не сделал. Вы явно упускаете суть. Есть много способов сделать что-то, но я хочу здесь научиться правильно. На самом деле есть гораздо лучший ответ ниже. Посмотрите на решение Фурканайда. Это безупречно и должно было получить награду.
Sencer H.
@ SencerH. Единственное отличие заключалось в том, что вы использовали метод dict get (), который в сущности является тем, что делает первоначальный ответ попытка / исключение. Вы не можете отрицать тот факт, что вы скопировали идею и очень-очень (без улучшения) изменили и опубликовали
Martin
3

Альтернативный способ использования вложенного списка. Надеюсь, поможет!!

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

На мой взгляд, convert_whatсама эта функция не очень питонна. Я предполагаю, что код, который вызывает этот код, также содержит несколько операторов if и выполняет преобразование в зависимости от возвращаемого значения convert_what(). Я предлагаю что-то вроде этого:

Первый шаг, сделайте одну функцию для каждой комбинации:

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

Второй шаг, поместите объекты функций в диктовку. Обратите внимание, что после имен функций нет (), потому что мы хотим сохранить объект функции и еще не вызывать его:

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

Третий и последний шаг, реализовать функцию преобразования. Оператор if проверяет, являются ли обе системы одинаковыми. Затем мы получаем правильную функцию из нашего dict и вызываем ее:

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
источник
2

Это делается с помощью операторов switch-case в большинстве других языков. В Python я использую простую функцию со словарем выражений.

Код:

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)

Вывод:

> convert_what("Hexadecimal", "Decimal")
> 1
> convert_what("Binary", "Binary")
> 0
> convert_what("Invalid", "Hexadecimal")
> 0
furkanayd
источник
это хорошая альтернатива верхнему ответу, и ее легче распространить на большее количество значений.
Гхнаварро
Это очень похоже на предыдущий ответ: stackoverflow.com/a/58985114/1895261 . Кроме того, я думаю, что последняя строка должна возвращать пустой dict, а не 0 в том случае, если цифра_sys_1 отсутствует во внешнем dict: return (myExpressions.get (цифра_sys_1, {})). Get (цифра_sys_2, 0)
сепия
@Sepia в вопросе Module_art дает 0 в операторе else, что означает, что при возврате 0 ничего не соответствует заданным выражениям и ситуации равенства.
фурканайд
1
Попробуйте запустить print (convert_what ("invalid", "Hexadecimal")) со своим кодом. Это вызовет ошибку: «AttributeError: объект« int »не имеет атрибута« get »». Замена первого 0 пустым dict ({}) заставит функцию корректно вернуть 0 в случае, если цифра_sys_1 недопустима.
Sepia
1

В общем, я бы запустил словарное решение для вложенной задачи. Некоторые частные случаи могут привести к другому подходу. Как этот:

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

Как насчет чего-то вроде:

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
источник
1

Идея использует список и получить индекс результата, т.е.

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
никто
источник
Интересное предложение, но это не работает, когда аргументы («Десятичные», «Не»), что привело к ValueError: «DecimalNot» нет в списке
кодировать
1

Как сказал @Sadap,

На мой взгляд, convert_whatсама эта функция не очень питонна. Я предполагаю, что код, который вызывает этот код, также содержит несколько операторов if и выполняет преобразование в зависимости от возвращаемого значения convert_what(). Я предлагаю что-то вроде этого:

Если вы реализуете преобразование базы для целых чисел, вы , вероятно , через общее представительство в любом случае: int. Отдельная функция для каждой пары баз не требуется, и двум задействованным базам даже не нужно знать друг о друге.

вход

Создайте отображение из имени системы счисления в ее базу:

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

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

позволяя вам читать входы с int(text, BASES[numeral_sys_1]).

Вывод

Создайте отображение из имени системы счисления в спецификатор формата :

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

позволяя вам записывать результаты с format(n, FORMATTERS[numeral_sys_2]).

Пример использования

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

Любой dict можно также сделать более общим, сделав функции значений вместо этого, если вам нужно поддерживать другой набор форматов int(x, base)или больше выходных баз, чем поддерживает встроенное целочисленное форматирование.

Ry-
источник
0

Мне нравится держать код сухим:

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
источник
0

Используя некоторые методы, которые предоставляют другие ответы, и объедините их:

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

Хотя и не уверен, что этот подход быстрее, но может быть реализован и с помощью numpy:

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

и может быть использован как:

 np.select(conditions, choices, default=0)
VP7
источник