Code length is one factor in readability, but it isn't the only factor. "Is this code easy to read and modify" is a more important question to ask.
It looks like you've gotten a method skeleton here, which kind of leads you down the path of stuffing everything in one function, which is a bit of a shame. Let's look at what we actually need here.
- Length of at least 6
- One lowercase English character
- One uppercase English character
- One digit
- One special character
What if we split this up into a bunch of separate functions, which each do one thing. We'll just write the function names down for now, and leave the implementation blank.
def needs_lower(text):
pass
def needs_upper(text):
pass
def needs_digit(text):
pass
def needs_extra_chars(text):
pass
def needs_special(text):
pass
Now, Python has a string module that is (semi)deprecated, but that has some still useful things, namely, string.ascii_lowercase, string.ascii_uppercase, and string.digit. We can use these in the implementations for some of our functions. Let's just write them as simply as possible for now.
import string
def needs_lower(text):
for char in text:
if char in string.ascii_lowercase:
return 0
return 1
def needs_upper(text):
for char in text:
if char in string.ascii_uppercase:
return 0
return 1
def needs_digit(text):
for char in text:
if char in string.digits:
return True
return False
There's quite a bit of repetition here that we can factor out.
def needs_any(text, values):
for char in text:
if char in values:
return True
return False
Then our previous functions can just call this:
def needs_lower(text):
return needs_any(text, string.ascii_lowercase)
And similarly for the needs_upper and needs_digit.
In fact, we can do this with our special characters as well:
SPECIAL_CHARS = '!@#$%^&*()-+'
def needs_special(text):
return needs_any(text, SPECIAL_CHARS)
The last function we need to implement is needs_extra_chars:
MINIMUM_LENGTH = 6
def needs_extra_chars(text):
if len(text) >= MINIMUM_LENGTH:
return 0
return MINIMUM_LENGTH - len(text)
Now we can stitch these all together within minimumNumber:
def minimumNumber(n, password):
# I'm not really sure why there's a n passed in as well?
test_functions = [needs_lower, needs_upper,
needs_digit, needs_special,
needs_extra_chars]
extra_required = sum([test_fn(password) for test_fn in test_functions])
return extra_required
The benefit of doing this is the fact that we can easily add extra checks. What if a new requirement comes along? We can just create a new function an add it to test_functions; the amount of existing code we have to touch is minimal.
Just to drive home the point about size vs readability, here is a (slightly) code golfed version:
import string
def minimumRequired(password):
if len(password) < 6:
return 6 - len(password)
unique_pw = frozenset(password)
char_sets = (frozenset(string.ascii_lowercase), frozenset(string.ascii_uppercase),
frozenset(string.digits), frozenset('!@#$%^&*()-+'))
required_extra = 0
for char_set in char_sets:
if not char_set & unique_pw:
required_extra += 1
return required_extra
This (probably - I haven't really tested it) satisfies the requirements, but how do I make changes to it? Is it obvious how it does what it does (I'd argue not really).