# Find the minimum number of characters to add to make password strong

This is the criteria:

Its length is at least 6. It contains at least one digit. It contains at least one lowercase English character. It contains at least one uppercase English character. It contains at least one special character. The special characters are: !@#$%^&*()-+ My code is working for all test cases but I'm afraid it seems way longer than necessary. I'm trying to trim it down. import math import os import random import re import sys # Complete the minimumNumber function below. def minimumNumber(n, password): # Return the minimum number of characters to make the password strong flag = 0 if len(password) >= 6: passRegex = re.compile(r"^(?=.*[\d])") mo = passRegex.search(password) if mo is None: flag+=1 passRegex = re.compile(r"^(?=.*[A-Z])") mo = passRegex.search(password) if mo is None: flag+=1 passRegex = re.compile(r"^(?=.*[a-z])") mo = passRegex.search(password) if mo is None: flag+=1 passRegex = re.compile(r'[!@#$%^&*()-+]')
if mo is None:
flag+=1
return flag
else:
if any(x in "0123456789" for x in password) is False:
flag+=1
if any(x in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" for x in password) is False:
flag+=1
if any(x in "abcdefghijklmnopqrstuvwxyz" for x in password) is False:
flag+=1
if any(x in "!@#$%^&*()-+" for x in password) is False: flag+=1 if (flag+len(password)<6): return 6-len(password) if (flag+len(password)>=6): return flag if __name__ == '__main__': n = int(input()) password = input() answer = minimumNumber(n, password)  Any advice is appreciated. ## 1 Answer 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. 1. Length of at least 6 2. One lowercase English character 3. One uppercase English character 4. One digit 5. 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

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).

• string.ascii_lower should be string.ascii_lowercase, minimumNumber shouldn't have any uppercase letter (as well as a more descriptive name IMHO) and your golfed version returns too fast, for example with 'aaaaa'. It should return 3, not just 1. Sep 17, 2018 at 8:48
• Do you have any reference for string being deprecated any time soon? Because I use those constants quite often (in Code Review answers). Sep 17, 2018 at 10:50
• All the needs_* functions can be rewritten quite nicely with the any() built-in Sep 17, 2018 at 11:29
• Halfway through this answer you seem to switch from "return 0 if the password doesn't need one of the specified characters, and 1 otherwise" to "return True if the password has one of the specified characters, and False otherwise". To me these don't seem equivalent. Additionally, your final check doesn't seem like it will return the correct result for passwords which are too short but also need specific characters, since those specific characters already need to be added which should reduce the number of generic characters needed to get the minimum length. Sep 17, 2018 at 15:15
• @KamilDrakari Yeah, the True/False was an oversight from when I was originally writing my answer. I've edited this with a few fixes and renamed some things. Sep 17, 2018 at 16:10