Skip to content

Problem 5: Protected Secret (100pts)

Problem

Write a function protected_secret which takes in a password, secret, and num_attempts.

protected_secret should return another function which takes in a password and prints secret if the password entered matches the password given as an argument to protected_secret. Otherwise, the returned function should print "INCORRECT PASSWORD". After num_attempts incorrect passwords are used, the secret is locked forever and the function should print "SECRET LOCKED".

For example:

编写一个函数 protected_secret,它接受一个 password(密码)、一个 secret(秘密)和一个 num_attempts(尝试次数)。

protected_secret 应该返回另一个函数,该函数接受一个 password,如果输入的密码与传递给 protected_secret 的密码参数匹配,则打印 secret。否则,返回的函数应该打印 "INCORRECT PASSWORD"(密码不正确)。在 num_attempts 次不正确的密码被使用后,秘密将被永久锁定,此时该函数应打印 "SECRET LOCKED"(秘密已锁定)。

例如:

>>> my_secret = protected_secret("sicp2025", "I love python.", 1)
>>> # Failed attempts: 0
>>> my_secret = my_secret("sicp2025")
I love python.
>>> # Failed attempts: 0
>>> my_secret = my_secret("abcdefg")
INCORRECT PASSWORD
>>> # Failed attempts: 1
>>> my_secret = my_secret("NanjingUniversity")
SECRET LOCKED

See the doctests for a detailed example.

请参阅文档测试(doctests)以获取详细示例。

def protected_secret(password, secret, num_attempts):
    """
    Returns a function which takes in a password and prints the SECRET if the password entered matches
    the PASSWORD given to protected_secret. Otherwise it prints "INCORRECT PASSWORD". After NUM_ATTEMPTS
    incorrect passwords are entered, the secret is locked and the function should print "SECRET LOCKED".

    >>> my_secret = protected_secret("correcthorsebatterystaple", "I love NJU", 2)
    >>> # Failed attempts: 0
    >>> my_secret = my_secret("hax0r_1")
    INCORRECT PASSWORD
    >>> # Failed attempts: 1
    >>> my_secret = my_secret("correcthorsebatterystaple")
    I love NJU
    >>> # Failed attempts: 1
    >>> my_secret = my_secret("hax0r_2")
    INCORRECT PASSWORD
    >>> # Failed attempts: 2
    >>> my_secret = my_secret("hax0r_3")
    SECRET LOCKED
    >>> my_secret = my_secret("correcthorsebatterystaple")
    SECRET LOCKED
    """
    def get_secret(password_attempt):
        "*** YOUR CODE HERE ***"
    return get_secret

Hints

Hint: We recommend you using self-referencing functions (see page 15-16 of slides on Higher-Order Functions) to achieve this problem.

  • 我们建议您使用自引用函数(请参阅高阶函数幻灯片第 15-16 页)来完成此问题。

  • 失败次数 +1,相当于 num_attempts -1。

  • 实测不在 "*** YOUR CODE HERE ***" 内写一些语句也可以通过 OJ。但相关知识超纲了。

Solutions

num_attempts 既可以是能尝试的最大次数,也可以理解为剩余的尝试次数。

如果用户输错了密码,我们可以让剩余的尝试次数 -1。

我们可以通过返回这个函数自身的方法,来解决这个问题。

def protected_secret(password, secret, num_attempts):
    def get_secret(password_attempt):

        # 如果尝试次数为 0, 我们应该锁定这个秘密。
        if (num_attempts == 0):
            print("SECRET LOCKED")
            return protected_secret("114514", "1919810", 0) # 彩蛋!

        # 如果用户密码输对了,我们应该打印秘密后,让剩余的尝试次数不变。
        elif (password_attempt == password):
            print(secret)
            return protected_secret(password, secret, num_attempts)

        # 如果密码输错了,我们应该让剩余的尝试次数 -1。
        else:
            print("INCORRECT PASSWORD")
            return protected_secret(password, secret, num_attempts - 1)
    return get_secret

使用 nonlocal (修改了其他部分的代码,但是可以通过 OJ)

def protected_secret(password, secret, num_attempts):

    # failed_attempts 存储了当前失败的次数。
    # 它是一个非局部变量,将被内部函数修改。
    failed_attempts = 0

    def get_secret(password_attempt):
        # 声明这个变量是 nonlocal,以便在内部函数中修改外部变量
        nonlocal failed_attempts

        # 检查是否已锁定
        if failed_attempts >= num_attempts:
            print("SECRET LOCKED")
            return get_secret # 返回自身以保持链式调用的状态

        # 检查密码是否正确
        if password_attempt == password:
            print(secret)
            # 成功访问后,不用进行其他操作
        else:
            print("INCORRECT PASSWORD")
            # 密码不正确,增加失败尝试次数
            failed_attempts = failed_attempts + 1

        # 无论成功还是失败,都返回自身以便下一次调用,例如 my_secret = my_secret("...")
        return get_secret

    return get_secret