I'm a beginner making a password generator and need to ensure the password has both numbers and capital letters. The condition for this while loop is redundant. for char in password
appears twice. How would you write it?
while not (any(char.isdigit() for char in password) and (any(char.isupper() for
char in password))):
In the loop it generates another password.
My goal here is to better understand how to construct the while loop's expression, not to solve the problem a different way.
There are two methods that help us in the reduction of a given Boolean Function. These are the algebraic manipulation method, which is longer, and the K-map method, which is shorter.
There are a number of methods for simplifying Boolean expressions: algebraic, Karnaugh maps, and Quine-McCluskey being the more popular. We have already discussed algebraic simplification in an unstructured way. We now study Karnaugh maps (K-Maps).
First things first, I wish websites would stop with the inane password requirements. They reduce the entropy of the password AND make it harder for people to remember. It's especially bad when the requirements aren't clearly laid out in the UI so people can design an appropriate password without guessing what traps you may have laid for them.
That said, your syntax is quite a bit shorter than some of the regex implementations. If you wanted to apply De Morgan's laws to break up the question into logic which is arguably easier to reason about you could do the following (at a performance loss with respect to short-circuiting).
while all(not char.isdigit() for char in password)
or all(not char.isupper() for char in password):
It seems your real problem with this though is the two passes through password
. Interestingly, the regex approaches have the same problem, hidden behind some additional syntax. If you're willing to sacrifice the brevity of your solution for a bit of generality, the ability to short circuit, and a single pass through your data then you can extract the condition into its own method like so:
def satisfies(password, *conditions):
flags = [False] * len(conditions)
for c in password:
for i, cond in enumerate(conditions):
if cond(c):
flags[i] = True
if all(flags):
return True
return False
while satisfies(password, str.isdigit, str.isupper):
pass
Stepping through this, it goes through each character and each condition (for example the condition of needing a digit) and checks to see if it has been satisfied. If so, it records that event and checks if it can exit early. At the end, the only possible way the for
loops have exited is if a condition hasn't been met anywhere in password
, so we return False
.
Just for fun, you can get a similar effect (without early stopping) with the use of the reduce()
function. It's built in to Python 2.x, and you'll need to import it from functools
in Python 3.x.
while not all(reduce(
lambda (a, b), (d, e): (a or d, b or e),
((c.isdigit(), c.isupper()) for c in password))):
This effectively keeps a running tally of whether you've satisfied the isdigit and isupper requirements at that point in the password. After checking the whole password, you simply use all()
to read your tallies and make sure you've actually satisfied both requirements.
If your goal is run time rather than some ethereal notion like "passes through the data" (not to be disparaging; they can matter quite a bit in other contexts), your best improvements would come from some sort of high-performance library like numpy
designed to vectorize the queries you perform. Since the bulk of the work being performed here isn't the pass through the data, but it is the check being performed on the characters in each pass, eliminating passes through the data won't do much for run time. You'll realize the most savings by making the actual checks as fast as possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With