Подтвердить что ты не робот

Как сократить это булевское выражение?

Я новичок, создающий генератор паролей, и вам нужно обеспечить, чтобы пароль имел номера и заглавные буквы. Условие для этого цикла while является избыточным. for char in password появляется дважды. Как бы вы его написали?

while not (any(char.isdigit() for char in password) and (any(char.isupper() for 
char in password))):

В цикле он генерирует другой пароль.

Моя цель здесь - лучше понять, как построить выражение цикла while, а не решать проблему по-другому.

4b9b3361

Ответ 1

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

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

while all(not char.isdigit() for char in password)
       or all(not char.isupper() for char in password):

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

def satisfies(password, *conditions):
    flags = [False] * len(conditions)
    for c in password:
        for i, cond in enumerate(conditions):
            if cond(c):
                flags[i] = True
                if all(flags):
                    return True
    return False

while satisfies(password, str.isdigit, str.isupper):
    pass

Пройдя через это, он проходит через каждый символ и каждое условие (например, условие необходимости цифры) и проверяет, выполнено ли оно. Если это так, он регистрирует это событие и проверяет, может ли он выйти раньше. В конце концов, единственный возможный путь for петель вышли, если условие не выполнено в любой точке password, так что мы возвращаем значение False.

Просто для удовольствия вы можете получить аналогичный эффект (без ранней остановки) с использованием функции reduce(). Он встроен в Python 2.x, и вам нужно будет импортировать его из functools в Python 3.x.

while not all(reduce(
        lambda (a, b), (d, e): (a or d, b or e),
        ((c.isdigit(), c.isupper()) for c in password))):

Это эффективно поддерживает текущую оценку того, удовлетворяли ли вы требования isdigit и isupper в этот момент пароля. После проверки всего пароля вы просто используете all() для чтения ваших счетов и убедитесь, что вы действительно удовлетворены обоими требованиями.

Если ваша цель - это время выполнения, а не какое-то эфирное понятие типа "проходит через данные" (не быть пренебрежительным, они могут иметь значение совсем в других контекстах), ваши лучшие улучшения получаются из какой-то высокопроизводительной библиотеки, такой как numpy предназначенные для векторизации выполняемых запросов. Поскольку основная часть выполняемой здесь работы - это не пропуск данных, но проверка выполняется на символах в каждом проходе, исключение проходов через данные не будет сильно зависеть от времени выполнения. Вы получите максимальную экономию, сделав фактические проверки как можно быстрее.

Ответ 2

Я согласен с представленными ответами, но для решения вашего первоначального вопроса и вашего следующего шага:

@moooeeeep: Вы забыли проверить любой (char.islower() для символа в пароле)?

@Matt Davis: Да, я планировал добавить это дальше, но тогда выражение получилось ДЕЙСТВИТЕЛЬНО длинным. Вы видите мою проблему сейчас :)

немного упростив вашу оригинальную логику, мы можем сделать что-то вроде:

while not all([any(map(str.isdigit, password)), any(map(str.isupper, password)), any(map(str.islower, password))]):

Т.е. применить all() к списку any(). Для тестовых случаев:

["goat", "Goat", "goat1", "GOAT", "GOAT1", "Goat1"]

только "Goat1" проходит, так как соответствует всем трем критериям.

Ответ 3

Оставляя в стороне вопрос о том, какие хорошие или плохие критерии для пароля (и я уверен, что нам всем пришлось выполнить спецификацию, с которой мы не соглашались в наше время), нет ничего плохого в том, как вы написали это логическое выражение.

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

Не забывайте, что вы можете разбить код на нескольких строках в Python с помощью отступа, так что вы можете добавить больше условий, не теряя удобочитаемости:

while not (
        any(char.isdigit() for char in password) and
        any(char.isupper() for char in password) and
        any(char.islower() for char in password) and
        any(char.somethingelse() for char in password) ):
    do-something

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

Ответ 4

Подобно лямбда-версии. Верните набор кортежей, соответствующих любому из этих тестов. удалить (False, False) из результата. длина должна быть 2.

input_exp = [ 
    ("abc", False),
    ("A3", True),
    ("A", False),
    ("ab3", False),
    ("3", False),
    ("3A", True),

]

def check(string_):
    """each iteration evaluates to any one of (True,False) /(False,True)/ (False,False).  
    remove False,False from set.  
    check len==2"""

    res = len(set([(c.isupper(), c.isdigit()) for c in string_]) - set([(False, False)])) == 2

    return res


for string_, exp in input_exp:
    got = check(string_)

    if exp is got:
        print("good.  %s => %s" % (string_, got))
    else:
        print("bad .  %s => %s" % (string_, got))

выход:

good.  abc => False
good.  A3 => True
good.  A => False
good.  ab3 => False
good.  3 => False
good.  3A => True

Ответ 5

Используйте модуль re чтобы делать то, что вы хотите.

import re

pattern = re.compile('[A-Z0-9]+')

password1 = 'asdf1234'
password2 = 'ASDF1234'

for p in (password1, password2):
    if pattern.fullmatch(p):
        print('Password', p, 'is fine')
    else:
        print('Password', p, 'is bad')