我是制作密码生成器的初学者,需要确保密码同时包含数字和大写字母。此while循环的条件是多余的。 for char in password
出现两次。你会怎么写?
while not (any(char.isdigit() for char in password) and (any(char.isupper() for
char in password))):
在循环中,它将生成另一个密码。
我在这里的目标是更好地理解如何构造while循环的表达式,而不是以其他方式解决问题。
答案 0 :(得分:22)
首先,我希望网站停止使用无用的密码要求。它们减少了密码的熵,使人们更难以记住。如果未在UI中明确列出要求,那么特别糟糕的是,人们可以设计适当的密码而不必猜测您可能为他们设置了什么陷阱。
也就是说,您的语法比某些regex实现要短很多。如果您想应用De Morgan的定律将问题分解为逻辑,那么可能会更容易推理出问题,您可以执行以下操作(在短路性能方面会有所损失)。
while all(not char.isdigit() for char in password)
or all(not char.isupper() for char in password):
这似乎是您的真正问题,尽管两次通过password
。有趣的是,正则表达式方法也存在相同的问题,隐藏在某些其他语法后面。如果您愿意为了通用性,短路能力以及单次通过数据而牺牲解决方案的简洁性,则可以将条件提取为自己的方法,如下所示:
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
逐步执行此操作,它将遍历每个字符和每个条件(例如,需要数字的条件),并检查是否满足要求。如果是这样,它将记录该事件并检查是否可以提前退出。最后,退出for
循环的唯一可能方法是,如果password
中的任何地方都没有满足条件,那么我们返回False
。
只是为了好玩,您可以使用reduce()
函数获得类似的效果(无需提前停止)。它内置在Python 2.x中,您需要在Python 3.x中从functools
导入它。
while not all(reduce(
lambda (a, b), (d, e): (a or d, b or e),
((c.isdigit(), c.isupper()) for c in password))):
这样可以有效地保持您当时是否满足密码中的isdigit和isupper要求的统计信息。检查完整个密码后,您只需使用all()
来读取您的记录,并确保您确实满足这两个要求。
如果您的目标是运行时,而不是诸如“传递数据”之类的空想(不要贬低,它们在其他情况下可能会起很大作用),那么您最好的改进将来自某种高性能numpy
之类的库旨在将您执行的查询向量化。由于此处要执行的大部分工作不是传递数据,而是对每次传递中的字符进行检查,因此消除传递数据对于运行时间不会有多大作用。通过尽快进行实际检查,您将实现最大的节省。
答案 1 :(得分:4)
我同意提出的答案,但要解决您的原始问题和下一步:
@moooeeeep:您是否忘记检查任何(char.islower()中的char 密码)?
@Matt Davis:是的,我打算添加下一个,但随后 表情真的很长。您现在看到我的问题了:)
稍微简化您的原始逻辑,我们可以执行以下操作:
while not all([any(map(str.isdigit, password)), any(map(str.isupper, password)), any(map(str.islower, password))]):
即将all()
应用于any()
的列表。对于测试用例:
["goat", "Goat", "goat1", "GOAT", "GOAT1", "Goat1"]
只有"Goat1"
符合所有三个条件,因此可以通过。
答案 2 :(得分:2)
撇开密码的优劣标准有什么问题(我相信我们都必须实施我们当时不同意的规范),没有错与您编写此逻辑表达式的方式相同。
很清楚代码的意思,并且除非您发现它实际上在实践中太慢,否则您不应为获得性能或效率的某些理论上的好处而牺牲清晰度,也不要插队短几个字符。
别忘了您可以使用缩进在Python中的多行代码之间打断,因此您可以添加更多条件而不会失去可读性:
while not (
any(char.isdigit() for char in password) and
any(char.isupper() for char in password) and
any(char.islower() for char in password) and
any(char.somethingelse() for char in password) ):
do-something
作为一个初学者,学会编写清晰易懂的代码比担心到处散布几微秒或字节要重要得多。
答案 3 :(得分:1)
类似于lambda版本。返回一组与任何这些测试匹配的元组。从结果中删除(False,False)。长度设置应为2。
input_exp = [
("abc", False),
("A3", True),
("A", False),
("ab3", False),
("3", False),
("3A", True),
]
def check(string_):
"""each iteration evaluates to any one of (True,False) /(False,True)/ (False,False).
remove False,False from set.
check len==2"""
res = len(set([(c.isupper(), c.isdigit()) for c in string_]) - set([(False, False)])) == 2
return res
for string_, exp in input_exp:
got = check(string_)
if exp is got:
print("good. %s => %s" % (string_, got))
else:
print("bad . %s => %s" % (string_, got))
输出:
good. abc => False
good. A3 => True
good. A => False
good. ab3 => False
good. 3 => False
good. 3A => True
答案 4 :(得分:-1)
使用re
模块执行您想要的操作。
import re
pattern = re.compile('[A-Z0-9]+')
password1 = 'asdf1234'
password2 = 'ASDF1234'
for p in (password1, password2):
if pattern.fullmatch(p):
print('Password', p, 'is fine')
else:
print('Password', p, 'is bad')