9

I'm practicing some Python and I'm having trouble understanding why this code won't take - function takes a string and if it's at least 10 characters in length, has at least 1 digit, 1 lowercase, and 1 uppercase letter, it should return True. Also, there's gotta be a more succinct way to write this than I did with all these nested conditionals. Thanks all!

import string
alphalower = string.ascii_lowercase
alphaupper = string.ascii_uppercase
digs = string.digits

def checkio(data):
    if len(data) >= 10:
        if data.count(digs) >= 1:
            if data.count(alphaupper) >= 1:
                if data.count(alphalower) >= 1:
                    return True
    else:
        return False
3
  • 2
    The and operator...!? if .. and .. and .. Or simply return .. and .. and .. Commented Apr 24, 2015 at 20:11
  • Your code will not work. data.count(digs) returns the number of times that the entire sequence of digs is found, not the count of each element of digs Commented Apr 24, 2015 at 20:14
  • 2
    sdata = set(data); return len(data) > 10 and all(sdata.intersection(s) for s in (digs, alphalower, alphaupper)) Commented Apr 24, 2015 at 20:17

7 Answers 7

5

The following should work, and removes the unnecessary imports :

def checkio(data):
    return len(data) >= 10 and any(char.isdigit() for char in data) and any(char.islower() for char in data) and any(char.isupper() for char in data)

You can iterate over strings by default, and each string has the isdigit, islower, etc... methods that you can use. The any() method returns True if any of the values returned by an iterable passed to it is true, and the something(value) for value in iterable syntax creates a generator expression, which iterates over each character of the string and checks whether it's a digit/lowercase/uppercase character.

Based on this answer.


Benchmark time, with my horrible benchmark code (but it seems to do the job) :

from time import time
import re

data = "ULFFunH8ni" # the password

def benchmark(method): # benchmark method, loops 1000000 times and prints how much it took
    start = time()
    for _ in range(1000000): method(data)
    print(time() - start)

def checkio_kasra(data): # Kasra's answer
    return len(data) >= 10 and all([any(i.isdigit() for i in data),any(i.islower() for i in data),any(i.isupper() for i in data)])

def checkio_andreysabitov(data): # Andrey Sabitov's regex-based answer
    if len(data) < 10:
        return False

    digital = re.compile('[0-9]+')
    capital = re.compile('[A-Z]+')
    lower = re.compile('[a-z]+')

    return (digital.search(data) is not None) and (capital.search(data) is not None)  and (lower.search(data) is not None)

def checkio_andredaniel(data): # My answer
    return len(data) >= 10 and any(char.isdigit() for char in data) and any(char.islower() for char in data) and any(char.isupper() for char in data)

def checkio_shashank_bitmask(data):
    if len(data) < 10: return False
    lud_bitmask = 0
    for ch in data:
        if ch.islower():
            lud_bitmask |= 4
            if lud_bitmask == 7: return True
        elif ch.isupper():
            lud_bitmask |= 2
            if lud_bitmask == 7: return True
        elif ch.isdigit():
            lud_bitmask |= 1
            if lud_bitmask == 7: return True
    return False

def checkio_shashank_pure_regex(data):
    return bool(re.match(r'(?=.*?[0-9])(?=.*?[A-Z])(?=.*?[a-z]).{10}', data))

def checkio_shashank_impure_regex(data):
    return len(data) >= 10 and re.match(r'(?=.*?[0-9])(?=.*?[A-Z])(?=.*?[a-z])', data)

benchmark(checkio_kasra)
benchmark(checkio_andreysabitov)
benchmark(checkio_andredaniel)
benchmark(checkio_shashank_bitmask)
benchmark(checkio_shashank_pure_regex)
benchmark(checkio_shashank_impure_regex)

The results, on my low-end tablet running Windows 7 and Python 3.4.x (ran it two times to be sure) :

$ python pass.py
6.333611011505127 # Shashank
9.625216960906982 # Kasra
11.450419902801514 # Andrey Sabitov
8.36161494255066 # Me

However, given a semi-incorrect input of 1XYZXYZXYZ (length and digits are good, but all uppercase), some solutions fail to stop early :

7.456813097000122 # Shashank
9.328815937042236 # Kasra
11.169620037078857 # Andrey Sabitov
6.349210977554321 # Me

Note that these benchmarks don't take into account eventual edits. I suggest you run the benchmark on your own machine using the latest answers. Feel free to update this post with new results.

Sign up to request clarification or add additional context in comments.

6 Comments

Didn't realize a very similar solution has already been posted. Making this a community wiki then.
My solution appears to be a bit faster than the other solutions: repl.it/kl2 I will attempt to do an optimized regex solution though because I don't think that regex code in the other answer is optimized at all.
@Shashank yeah for correct inputs your solution is currently the fastest, but it seems that it fails to exit early if given incorrect input (cf. my solution is still the fastest on incorrect inputs).
@AndréDaniel Yes...but I strongly suspect that an optimized regex solution would beat both of ours.
@Shashank we'll see - please update the benchmark results once you come up with your new solution. Thanks. By the way, your bitmask solution beats mine even for incorrect inputs on the REPL, so the results are OS-dependent.
|
1
import re    
def checkio(data):
    if len(data) < 10:
        return False

    digital = re.compile('[0-9]+')
    capital = re.compile('[A-Z]+')
    lower = re.compile('[a-z]+')

    return (digital.search(data) is not None) and (capital.search(data) is not None)  and (lower.search(data) is not None)

7 Comments

Nooooo. Huge performance disaster to use a regex unnecessarily like that.
Recompiling regexes on every call, nice
Yes. This returns the intended result, not the result of the code in the question.
What's the problem with re.compile? It's in the cache
@AndreySabitov regexes are just unnecessary in this case.
|
1

You can use 3 generator expression with any function within all :

def checkio(data): 
    return len(data) >= 10 and all([any(i.isdigit() for i in data),any(i.islower() for i in data),any(i.isupper() for i in data)])   

Demo :

>>> checkio('1&*^&^%%gA')
True
>>> checkio('1&*^&^%%gf')
False

4 Comments

if you have a sec, mind walking me through the generator expression? New to this.
Another spelling of the same idea return len(data) > 10 and all(any(func(c) for c in data) for func in str.isdigit, str.isupper, str.islower))
@SpicyClubSauce You can read a complete doc in python.org/dev/peps/pep-0289
@StevenRumbalski Yeah, Nice! ;)
1

One unique way of doing it would be to use a bitmask:

def checkio_shashank_bitmask(data):
    if len(data) < 10: return False
    lud_bitmask = 0
    for ch in data:
        if ch.islower():
            lud_bitmask |= 4
            if lud_bitmask == 7: return True
        elif ch.isupper():
            lud_bitmask |= 2
            if lud_bitmask == 7: return True
        elif ch.isdigit():
            lud_bitmask |= 1
            if lud_bitmask == 7: return True
    return False

print(checkio('5Hortpass')) # False
print(checkio('dumbpa55w0rd')) # False
print(checkio('DumbPassword')) # False
print(checkio('DumbPassword2015')) # True

This scales well for large inputs and stops as early as it can.


Here is what I think to be an optimized regex solution with non-greedy positive lookaheads that stops as soon as possible:

import re

def checkio_shashank_pure_regex(data):
    return bool(re.match(r'(?=.*?[0-9])(?=.*?[A-Z])(?=.*?[a-z]).{10}', data))

However, I am not a regex expert, so if you have any optimization ideas, let me know.


From some further testing, I've determined that this is a bit faster and is probably the fastest solution so far for both correct and incorrect inputs at least with a highly optimized regex module:

def checkio_shashank_impure_regex(data):
    return len(data) >= 10 and re.match(r'(?=.*?[0-9])(?=.*?[A-Z])(?=.*?[a-z])', data)

Comments

0

I find this function both effective and beautiful.

def check(password):
    """Returns True only if password is strong enough."""
    if  (len(password) >= 10
     and any(char.isdigit() for char in password)
     and any(char.islower() for char in password) 
     and any(char.isupper() for char in password)):
        return True
    else:
        return False

2 Comments

This is basically the same as my answer. By the way, the return true/false are unnecessary, you can directly return the condition of your if block.
I know. I just tried to make it more readable. Your answer puts everything in a single line.
0
r_p = re.compile('^(?=\S{6,20}$)(?=.*?\d)(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[^A-Za-z\s0-9])')

this code will validate your password with :

  1. min length is 6 and max length is 20
  2. at least include a digit number,
  3. at least a upcase and a lowcase letter
  4. at least a special characters

Comments

-1

Your code only executes the else statement if the len condition is false. To fix that:

A more compact version, you can do:

if len(data) >= 10 and any(i in data for i in digs) and (i in data for i in alphaupper) and (i in data for i in alphalower):

3 Comments

i didn't downvote, but your revised function up top doesn't work either. for instance print(checkio("ULFFunH8ni")) returns False still.
"0123456789" in "Password1" will always be false. That's the essence of the OP's question. Hence the downvotes.
@sshashank124 i mean... that still isn't working for me. run the example i gave ^ and it's still returning False for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.