def is_password_good(password):
if len(txt) > 7 and [i for i in txt if i.isupper()] and [i for i in txt if i.islower() and [i for i in txt if i.isnumeric()]]:
return True
else:
return False
txt = input()
print(is_password_good(txt))
CodePudding user response:
If you're looking for a capital letter, using the method you're using, every letter is going to be evaluated. So if you have 50 letters in your string, and you're using the list comprehension method, you're going to do 50 things per list. And in your case, there are three list comprehensions, so there will be len(txt) * 3
evaluations made everytime.
Using your method, modified with the password inline to make the timeit call simpler:
import timeit
def is_password_good():
txt = "Sup3rlongpassword"
if (
len(txt) > 7
and [i for i in txt if i.isupper()]
and [i for i in txt if i.islower() and [i for i in txt if i.isnumeric()]]
):
return True
else:
return False
>>> timeit.timeit(is_password_good)
17.099690434522927
Here's a method using regular expressions. In each case, as soon as the passing condition is met, it will return the value.
import re
has_upper = re.compile('[A-Z]').search
has_lower = re.compile('[a-z]').search
has_digit = re.compile('[0-9]').search
def is_password_good_re():
txt = "Sup3rlongpassword"
if len(txt) > 7 and has_upper(txt) and has_lower(txt) and has_digit(txt):
return True
else:
return False
>>> timeit.timeit(is_password_good_re)
0.8171015260741115
Edit:
Something else that stands out, one of your lists is nested within the other.
[i for i in txt if i.islower() and [i for i in txt if i.isnumeric()]]
So this means the numeric check is happening every character. So if there are 10 characters in the password, there will be 10 checks for numbers, which involve 10 checks each, for a total of 100 evaluations for numbers alone.
CodePudding user response:
You're running three for
loops, one after the other, when you could run a single for
loop:
def is_password_good(password, good_length):
if len(password) < good_length:
return False
has_upper, has_lower, has_digit = False, False, False
for char in password:
has_upper = char.isupper() or has_upper
has_lower = char.islower() or has_lower
has_digit = char.isdigit() or has_digit
if has_upper and has_lower and has_digit:
return True
return False
Imagine a password of all lowercase letters -- you have to check every one to see if you also get a digit or uppercase letter, therefore you will iterate over all n
characters before you can say definitively that the password does not satisfy the requirements. This means that in the worst-case scenario, the best you can possibly do is O(n)
.
The idea then should be to break out of the loop as soon as possible -- in this instance, once all three flags have been switched to True
.
Each of the three char.isxxx() or has_xxx
statements will be True
forever once the first time the check is satisfied, since after the first time it is True
, the statement reduces to char.isxxx() or True
which will always be true.
Once all three flags are set, you can break and then return whether good_length
is satisfied as well.
CodePudding user response:
You can use an check your conditions in a for
loop:
def is_password_good(password):
if len(password) > 7:
up, lo, nu = False, False, False
for i in password:
if i.isupper():
up = True
elif i.islower():
lo = True
elif i.isnumeric():
nu = True
else:
return False
return all((up, lo, nu))
>>> is_password_good('1234567')
False
>>> is_password_good('12345678')
False
>>> is_password_good('123456aa')
False
>>> is_password_good('123456aB')
True