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 lower_required(text):
pass
def upper_required(text):
pass
def digits_required(text):
pass
def length_required(text):
pass
def special_required(text):
pass
Now, Python has a string module that is (semi)deprecated (much of what was in it is now on string objects themselves), 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 lower_required(text):
for char in text:
if char in string.ascii_lowercase:
return 0
return 1
def upper_required(text):
for char in text:
if char in string.ascii_uppercase:
return 0
return 1
def digit_required(text):
for char in text:
if char in string.digits:
return 0
return 1
There's quite a bit of repetition here that we can factor out.
def num_required(text, characters):
if any((x in characters for x in text)):
return 0
return 1
Then our previous functions can just call this:
def lower_required(text):
return num_required(text, string.ascii_lowercase)
And similarly for the upper_required and digit_required.
In fact, we can do this with our special characters as well:
SPECIAL_CHARS = '!@#$%^&*()-+'
def special_required(text):
return num_required(text, SPECIAL_CHARS)
The last function we need to implement is length_required:
MINIMUM_LENGTH = 6
def length_required(text):
if len(text) >= MINIMUM_LENGTH:
return 0
return MINIMUM_LENGTH - len(text)
Now we can stitch these all together within minimumNumber (note that Python functions should be named using snake_case, so it should be minimum_number here - but I assume this function name was given to you, so I won't harp on about it too much).
def minimumNumber(text):
test_functions = [lower_required, upper_required,
digits_required, special_required]
min_chars_required = sum([test_fn(text) for test_fn in test_functions])
missing_length = length_required(text)
if missing_length > min_chars_required:
return missing_length
return min_chars_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).