Упростить код без использования условий и циклов

def is_valid_pin_codes(pin_codes):
    verification = set(pin_codes)
    if len(verification) == len(pin_codes):
        for i in verification:
            try:
                if len(i) != 4:
                    return False
                else:
                    if i.isdigit():
                        break
            except TypeError:
                return False
    else:
        return False
    return True

pin_codes = ['1101', '9034', '00112', '001a', '1101']
is_valid_pin_codes(pin_codes)

Передаём в функцию список из пин-кодов. Возвращаем True если пин-коды в виде строки, не больше и не меньше 4 элементов, не повторяются и хранят в себе только цифры. Если хотя бы 1 условие не выполняется - возвращаем False. Хочу для себя знать альтернативные варианты, чтобы все работало быстрее, или, может, узнать новые методы для себя, которые мог бы использовать здесь.


Ответы (3 шт):

Автор решения: Павел

С точки зрения читаемость-количество строк такой вариант оптимальный:

def is_valid_pin_codes(pin_codes):
    if len(set(pin_codes)) != len(pin_codes):
        return False
    correct_pin_codes = [code for code in pin_codes 
                         if type(code) is str 
                         and len(code) == 4 
                         and code.isdigit()]
    return len(correct_pin_codes) == len(pin_codes)


pin_codes = ['1101', '9034', '00112', '001']
print(is_valid_pin_codes(pin_codes)) # False

pin_codes = ['1101', '9034', '0112', '0041']
print(is_valid_pin_codes(pin_codes)) # True

Дополнительно

Но при желании все можно записать и в одну строку:

def is_valid_pin_codes(pin_codes):
    return len(set(pin_codes)) == len(pin_codes) and len([code for code in pin_codes if type(code) is str and len(code) == 4 and code.isdigit()]) == len(pin_codes)

Также я рекомендую почитать вам про list comprehenshions, это пригодится в дальнейшем для решения подобных (и не только) задач.

→ Ссылка
Автор решения: newman

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

import re
def is_valid_pin_codes(pin_codes):
    r = re.compile("^[0-9]{4}$")
    filtered = list(filter(r.match, pin_codes))
    return len(pin_codes) == len(filtered)

pin_codes = ['1101', '9034', '00112', '001']
print(is_valid_pin_codes(pin_codes)) # False

pin_codes = ['1101', '9034', '0112', '0041']
print(is_valid_pin_codes(pin_codes)) # True

Здесь задаем регулярное выражение, что строка может состоять только из 4 цифр. А дальше с помощью функции filter проверяем каждый элемент входного массива. Если после фильтра осталось столько же элементов, сколько и до, то значит все элементы прошли проверку.

→ Ссылка
Автор решения: DiMithras

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

без использования условий и циклов

А ещё полностью убирает условия и циклы.

import pandas as pd
def is_valid_pin_codes(pin_codes):
    s = pd.Series(pin_codes)
    return all([~any(s.duplicated()),
                all(s.str.isnumeric()),
                all(s.str.len()==4)])

pin_codes = ['1101', '9034', '00112', '0014', '1201']
print(is_valid_pin_codes(pin_codes)) # False

pin_codes = ['1101', '9034', '1456', '6587', '1201']
print(is_valid_pin_codes(pin_codes)) # True

Как работает:

  • s = pd.Series(pin_codes)
    Приводим список к классу Series, который содержит полезные и необходимые нам методы
  • В return одной строкой делаем валидацию:
    • ~any(s.duplicated())
      • s.duplicated()
        Выводит массив из True / False, где значение True будет для повторяющегося элеманта. Первый элемент останется False. Т.е. для pin_codes из примера
        [i for i in s.duplicated()]
        выдаст
        [False, False, False, False, True]
      • оборачиваем в any(), получаем True, т.е. хотя бы один дубликат есть
      • ~ переворачивает значение
        Т.е. если есть хотя бы один дубликат возвращаем False — проверка не пройдена
    • all(s.str.isnumeric())
      Проверяем, что все значения не содержат букв.
    • all(s.str.len() == 4)
      По аналогии, проверяем, что длинна каждого элемента равна 4. Если хоть один выбивается, будет False.
  • Собираем все проверки через all. Вместо all можно использовать and:
    ~any(s.duplicated()) and all(s.str.isnumeric()) and all(s.str.len() == 4)
    Но для компактности по ширине, да и, в общем-то, читаемости, мне больше нравится вариант с all.
→ Ссылка