1

I think I have the right idea of solving this function, but I'm not sure why I don't get the desired results shown in the docstring. Can anyone please help me fix this?

def check_password(s):
'''(str, bool)
>>> check_password('TopSecret')
False
>>> check_password('TopSecret15')
True
'''
for char in s:
    if char.isdigit():
        if  char.islower():
            if char.isupper():
                return True
else:
    return False 
bigez
  • 63
  • 5
  • 6
    Your nested `if` statements require that a single character be, at once, a digit, lowercase, and uppercase. Does that help understand why this is breaking? – ShadowRanger Dec 06 '17 at 01:59
  • I want it to return true if all 3 conditions are satifsifed, so how do I need to combine it all into one if statement? – bigez Dec 06 '17 at 02:01
  • what is the case of a digit? – R Nar Dec 06 '17 at 02:02
  • you should use `or` - now it works like `and` – furas Dec 06 '17 at 02:03
  • 1
    You need to think better about your logic, or search on line (e.g. on Stack Overflow) for prior solutions to this problem. – Prune Dec 06 '17 at 02:04
  • See my answer to a similar question here: https://stackoverflow.com/a/47422571/6779307 – Patrick Haugh Dec 06 '17 at 02:05
  • Possible duplicate of [How to test a regex password in Python?](https://stackoverflow.com/questions/2990654/how-to-test-a-regex-password-in-python) – Daniel Dec 06 '17 at 02:20

4 Answers4

2

Your logic is flawed it should look like this:

def check_password(s):
    has_digit = False
    has_lower = False
    has_upper = False

    for char in s:
        if char.isdigit():
            has_digit = True
        if char.islower():
            has_lower = True        
        if char.isupper():  
            has_upper = True 

    # if all three are true return true
    if has_digit and has_upper and has_lower:
        return True
    else:
        return False

Now, let's talk about what's wrong with your code.

def check_password(s):
    for char in s:
        if char.isdigit():
            if char.islower(): # we only get to this check if char was a digit
                if char.isupper(): # we only get here if char was a digit and lower
                    # it is not possible to get here
                    # char would have to be a digit, lower, and upper
                    return True
    else:
        return False

As an example let's look at TopSecret15, we start with T

  1. isdigit = False so we immediately return false for 'T'
  2. We will continue to immediately return false until we get to '1'
  3. Let's say we were on 1, isdigit would be true, but islower would be false so again it returns false

Do you see how it is impossible for all three of these to be true for the same char?

Your code is equivalent to:

if char.isdigit and char.islower and char.isupper:
    # this will never happen
DoesData
  • 6,594
  • 3
  • 39
  • 62
1

The reason this doesn't work is that for any given character, it first checks if it's a digit, then if it's lower, then if it's upper. Obviously, one character cannot be all three of these at once. You instead want to check each character to see if it's a digit, lowercase, or uppercase, and then flip a boolean value like has_upper. Then, after the for loop, you'll check to see if all of the boolean values are true.

Sebastian Mendez
  • 2,859
  • 14
  • 25
0

See this answer: https://stackoverflow.com/a/2990682/7579116 the best way to check if a password match all the requirements is by using a regex.

Shailyn Ortiz
  • 766
  • 4
  • 14
  • Regex is _a_ way. I'm not sure it's the _best_ way. –  Dec 06 '17 at 02:37
  • Right, thanks for pointing this out. I said the best because it's simplicity of implementation. also doesn't add a lot of verbose to the code. as a set of if's and loops would do. But certainly, I can not know which is the best option if I do not know all the options. @Blurp – Shailyn Ortiz Dec 06 '17 at 02:53
0

I've gone way overboard here (because I'm bored I guess) and whipped up an extensible password-checking system. Perhaps it will help clarify why your version doesn't do what you want (or maybe it won't).

The essential problem with your code is that it checks for a single characteristic instead each of the required characteristics.

def check_password(password):
    # These are the characteristics we want a password to have:
    characteristics = [
        length(8),      # At least 8 characters
        lower_case(1),  # At least 1 lower case letter
        upper_case(1),  # At least 1 upper case letter
        number(1),      # At least 1 number
    ]
    # Check to see if the supplied password has *all* of the desired
    # characteristics:
    return all(
        characteristic(password)
        for characteristic in characteristics
    )


def length(n=10):
    # Ensure password has at least N characters
    def check(password):
        return len(password) >= n
    return check


def lower_case(n=1):
    # Ensure password has at least N lower case characters
    def check(password):
        count = 0
        for char in password:
            if char.islower():
                count += 1
            if count == n:
                return True
        return False
    return check


def upper_case(n=1):
    # Ensure password has at least N upper case characters
    def check(password):
        count = 0
        for char in password:
            if char.isupper():
                count += 1
            if count == n:
                return True
        return False
    return check


def number(n=1):
    # Ensure password has at least N numbers
    def check(password):
        count = 0
        for char in password:
            if char.isdigit():
                count += 1
            if count == n:
                return True
        return False
    return check


# Doesn't have any numbers:
>>> print(check_password('TopSecret'))
False

# Acceptable:
>>> print(check_password('TopSecret15'))
True

# Has lower case, upper case, and number, but it's not long enough:
>>> print(check_password('Ab1'))
False

Notes:

  • Some parts of this are simplified in order to make it more clear what's going on (all of the check functions could be one-liners).
  • In a production system, password requirements will almost certainly change over time and you want to make sure that implementing additional requirements is easy.
  • A regular expression might not be as easy to understand or extend (although it might be a little faster, but that probably doesn't matter at all).
  • The approach above is somewhat similar to Django's password validation system.