【问题标题】:Stopping checking a conditional while looping over an iterable循环遍历可迭代对象时停止检查条件
【发布时间】:2021-08-20 18:12:43
【问题描述】:

所以我有以下问题。假设我们正在进行标准的“密码强度检查”,这意味着如果密码(字符串)中包含大写字母、小写字母和数字,则认为它是“强” 我想到的超级简单的例子:

for character in 'asdASD123':
  if character.isupper():
    something
  if character.islower():
    something
  if character.isnumeric():
    something

如果在任何时候满足所有这些条件,那么密码就是强密码。我的问题是:如果已经满足某个条件,是否可以以某种方式停止检查?在这个例子中,“islower”条件将在第一个字符上得到满足。有没有办法消除它在进一步的循环中被检查?如果是这样,该怎么做?有没有办法将条件存储在列表中,然后在满足条件后将其从该列表中删除,或者类似的方式?

【问题讨论】:

  • 您可以做相反的事情,例如检查是否不存在小写字母或不存在特殊字符,最后只需设置密码即可。也不需要 for 循环
  • @Cbhihe 如果例如第一个字符是唯一的数字吗?
  • @Cbhihe 我明白了。所以您提出了一个具有多个for 循环的解决方案?仍然不确定“缩进每个连续的条件”是什么意思...

标签: python loops for-loop if-statement


【解决方案1】:

试试:

passwd = "sdnkdj98KJ/"

def passcheck(passwd):
    upper = any(map(lambda x:x.isupper(),passwd))
    num = any(map(lambda x:x.isnumeric(),passwd))
    lower = any(map(lambda x:x.islower(),passwd))
    return "strong passwd" if upper and lower and num else "weak passwd"

根据评论,我针对两种提供的解决方案之间的性能差异添加了一个基准,用于 100,000 次复制。为了公平对待其他解决方案的作者,我也将我的解决方案封装在一个函数声明块中,因为它们也会消耗 CPU 时间。

我的回答:

from datetime import datetime

start = datetime.now()
for i in range(100000):        
    a = passcheck(passwd)

print(datetime.now()-start)   # output: 0:00:00.205365, so 205.4ms

davinci1913 的回答:

start = datetime.now()
for i in range(100000):
    password_checker(passwd)

print(datetime.now()-start)  # output: 0:00:00.225257, so 225ms

所以我的回答恰好快了一点,但我不会太在意 20 毫秒的差异。大多数情况下,我的回答更简洁,在这种情况下更具可读性。这两种方法都将表现良好。

【讨论】:

  • 这实际上是我在检查该解决方案的答案之前设法提出的一个字符一个字符;)选中另一个解决方案,因为它引入了一种新方法。想知道哪个表现更好
  • @entman:添加了基准测试,以便您查看哪个答案更快。
【解决方案2】:

您可以使用|(逻辑“或”)短路的事实来消除满足条件后的检查。

通过短路,我的意思是如果有一些表达式:x | y,那么只有在 xFalse 时才会评估 y

这也允许一些简洁。例如

contains_upper   = False
contains_lower   = False
contains_numeric = False

for char in "asdASD123":
    contains_upper   |= char.isupper()
    contains_lower   |= char.islower()
    contains_numeric |= char.isnumeric()

strong = contains_upper and contains_lower and contains_numeric

我觉得这段代码是相当地道的 Python 代码,可读性很强。

【讨论】:

  • 这个 pythonic,但不能解决问题,因为每个函数都在为每个字符执行。目标是仅在前一个函数成功的情况下执行后续函数。
  • @blueteeth 我的意思是,由于| 是短路的,一旦对应的条件为True,函数实际上并没有被执行。不是这样吗?
  • 啊,是的。很好,我没有意识到这一点。
  • 我不知道成语|=。你教会了我一些东西:加 1。
【解决方案3】:

Regex 可能是最好的解决方案,但这是一个循环位于不同位置的解决方案。这可以修改为不要每次都执行相同的循环。

我已经封装了每个函数,这样你就可以看到实际调用了什么。

def print_and(fun_name):
    def wrapper(c):
        fun = getattr(c, fun_name)
        result = fun()
        print(f"'{c}'.{fun_name} == {result}")
        return result
    return wrapper

is_lower = print_and("islower")
is_upper = print_and("isupper")
is_digit = print_and("isdigit")

def check_characters(characters):
    return (
        any(is_lower(c) for c in characters)
        and any(is_upper(c) for c in characters)
        and any(is_digit(c) for c in characters)
    )

print(bool(check_characters("asdASD123")))
print(bool(check_characters("123")))

得到这个结果:

'a'.islower == True
'a'.isupper == False
's'.isupper == False
'd'.isupper == False
'A'.isupper == True
'a'.isdigit == False
's'.isdigit == False
'd'.isdigit == False
'A'.isdigit == False
'S'.isdigit == False
'D'.isdigit == False
'1'.isdigit == True
True
'1'.islower == False
'2'.islower == False
'3'.islower == False
False

您可以看到,每个函数只有在前一个函数返回 true 时才会被调用。对此的改进是只检查从前一个函数退出点开始的字符串的其余部分。

【讨论】:

    【解决方案4】:

    您可以这样做,通过引入标志(布尔指标)来跟踪哪些检查已经通过:

    upper_flag = False
    lower_flag = False
    numeric_flag = False
    
    for character in 'asdASD123':
      if character.isupper() and not upper_flag:
        something
        upper_flag = True
      if character.islower() and not lower_flag:
        something
        lower_flag = True
      if character.isnumeric() and not numeric_flag:
        something
        numeric_flag = True
    

    话虽如此,如果目标是节省时间,密码必须很长才能节省大量时间(或者您必须检查很多密码)。

    另一方面,标志可以有其他用途,例如存储或显示给定密码通过了多少次检查。下面给出了一个快速而肮脏的示例,但比打印它更有可能,您希望以某种方式存储此信息。

    def password_checker(password):
        upper_flag = False
        lower_flag = False
        numeric_flag = False
        
        for character in password:
            if character.isupper() and not upper_flag:
                upper_flag = True
            if character.islower() and not lower_flag:
                lower_flag = True
            if character.isnumeric() and not numeric_flag:
                numeric_flag = True
                
        flag_list = [upper_flag, lower_flag, numeric_flag]
        
        print(f"Password: '{password}' passes {sum(flag_list)}/{len(flag_list)} checks:\n")
        print("  - Contains upper case character:\t", upper_flag )
        print("  - Contains lower case character:\t", lower_flag )
        print("  - Contains numeric  character:  \t", numeric_flag )
    
    password_checker('asdASD123')
    

    返回:

    Password: 'asdASD123' passes 3/3 checks:
    
      - Contains upper case character:   True
      - Contains lower case character:   True
      - Contains numeric  character:     True
    

    【讨论】:

      【解决方案5】:

      对于这种情况,我建议使用正则表达式来避免循环

      import re
      
      password= 'asdASD1'
      
      pattern = re.compile(r"^(?=.*[\d])(?=.*[A-Z])(?=.*[a-z])")
      if re.match(pattern, password): #if it meets condition
          # do somethin
          
      

      【讨论】:

      • +1 :简洁明了的 Ade_1,但看看我的解决方案。我只使用基本方法而不使用正则表达式,同时还避免for 循环。
      【解决方案6】:

      你可以尝试很多方法,其中一种方法可以是这样的:

      is_lower, is_upper, is_numeric = False, False, False
      for character in 'asdASD123':
          if is_lower and is_upper and is_numeric:
              return "Strong password"   #print("Strong Password")
          if character.isupper() and not is_upper:
              is_upper = True
              something
          if character.islower() and not is_lower:
              is_lower = True
              something
          if character.isnumeric() and not is_numeric:
              is_numeric = True
              something
      

      【讨论】:

        【解决方案7】:

        是的,有很多方法可以做到这一点。

        这是一个例子

        您可以使用 True/False 值创建字典。
        像这样:

        is_obj = {'upper': False, 'lower': False, 'numeric': False}
        for character in 'asdASD123':
          if character.isupper() and is_obj['upper'] == False:
            is_obj['upper'] = True
          if character.islower() and is_obj['lower'] == False:
            is_obj['lower'] = True
          if character.isnumeric() and is_obj['numeric'] == False:
            is_obj['numeric'] = True
        

        【讨论】:

          【解决方案8】:

          有几种解决方案。您可以执行以下操作

          super_cond = False
          lower_cond = False
          numeric_cond = False
          for character in 'asdASD123':
              if not super_cond and character.isupper():
                  super_cond = True
                  #something
              if not lower_cond and character.islower():
                  lower_cond = True
                  #something
              if not numeric_cond and character.isnumeric():
                  numeric_cond = True
                  #something
          

          如果你想使用列表,你可以这样做

          conditions = [False,False,False]
          for character in 'asdASD123':
              if not conditions[0] and character.isupper():
                  conditions[0] = True
                  #something
              if not conditions[1] and character.islower():
                  conditions[1] = True
                  #something
              if not conditions[2] and character.isnumeric():
                  conditions[2] = True
                  #something
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-03-11
            • 1970-01-01
            • 2016-06-21
            • 2018-03-09
            • 2021-12-18
            • 2020-06-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多