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
wallyk
  • 56,922
  • 16
  • 83
  • 148
SpicyClubSauce
  • 4,076
  • 13
  • 37
  • 62
  • 2
    The `and` operator...!? `if .. and .. and ..` Or simply `return .. and .. and ..` – deceze Apr 24 '15 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` – horns Apr 24 '15 at 20:14
  • 2
    `sdata = set(data); return len(data) > 10 and all(sdata.intersection(s) for s in (digs, alphalower, alphaupper))` – Steven Rumbalski Apr 24 '15 at 20:17

7 Answers7

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.

Community
  • 1
  • 1
  • Didn't realize a very similar solution has [already been posted](http://stackoverflow.com/a/29856796/2629998). Making this a community wiki then. –  Apr 24 '15 at 20:36
  • My solution appears to be a bit faster than the other solutions: http://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 Apr 24 '15 at 21:09
  • @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). –  Apr 24 '15 at 21:13
  • @AndréDaniel Yes...but I strongly suspect that an optimized regex solution would beat both of ours. – Shashank Apr 24 '15 at 21:19
  • @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. –  Apr 24 '15 at 21:21
  • On Python3 repl.it, my regex solutions seems to pull far ahead of the rest for both correct and incorrect inputs: http://repl.it/kmO On Python 2, my bitmask solution beats the regex solutions for both kinds of inputs. So I guess it depends a lot on the implementations of libraries and how they are compiled. – Shashank Apr 24 '15 at 22:08
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)
Andrey Sabitov
  • 454
  • 2
  • 5
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
Mazdak
  • 105,000
  • 18
  • 159
  • 188
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)
Shashank
  • 13,713
  • 5
  • 37
  • 63
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
Zenadix
  • 15,291
  • 4
  • 26
  • 41
  • 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. –  Apr 24 '15 at 21:18
  • I know. I just tried to make it more readable. Your answer puts everything in a single line. – Zenadix Apr 27 '15 at 16:02
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
MikeZhang
  • 327
  • 2
  • 10
-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):
sshashank124
  • 31,495
  • 9
  • 67
  • 76