监视和计数另一个函数的函数 Python

Function to monitor and count another function Python

写一个函数 make_monitored 将一个函数 f 作为输入,它本身有一个输入。 make_monitored 返回的结果是第三个函数,比如 mf,它通过维护一个内部计数器来跟踪它被调用的次数。

如果 mf 的输入是特殊字符串 "how-many-calls?",那么 mf returns 的值 柜台。

如果输入是特殊字符串"reset-count",那么mf会重置计数器 归零。对于任何其他输入,mf returns 在该输入上调用 f 的结果和 递增计数器。

def make_monitored(f):
    a=[0]
    def mf(x):
        if x=="how-many-calls?":
            return a[0]
        elif x=="reset-count":
            a=[0]
        else:
            a[0]+=1
            return f(x)
    return mf


def double(x): #NOT TO BE CHANGED , provided by question
    return 2 * x

d = make_monitored(double)  #NOT TO BE CHANGED, provided by question

这是我不明白的地方: 我想制作一个单元素列表作为内部计数器。当 make_monitored 是父函数并且我定义了 a 时,我不明白为什么他们说 a 未定义。

这是我之前(并且正确地)完成的另一个问题,它使用了类似的方法,但成功了。

累加器是一种使用单个数字参数重复调用的函数 并将其参数累加为总和。每次调用时,它 returns 当前累计金额。写一个函数 make_accumulator 生成 累加器,每个累加器都保持独立的和。

def make_accumulator():
    lst=[0]
    def add(x):
        lst[0]+=x
        return lst[0]     
    return add
A=make_accumulator()

示例执行:

A=make_accumulator()

A(10) 输出:10

A(10) 输出:20

我不明白为什么 lst[0] 被接受在这里定义。 唯一可能的原因是 make_accumulator 没有参数但是 make_monitored 确实接受了 1。

赋值 a = [0] 创建了一个新的 a,它是 mf 的本地对象。这意味着 mf 中对 a 的所有其他引用必须是本地 a 而不是父级中的引用。

所以避免分配给 a 本身,而是改变它:

a[0] = 0 

顺便说一句,Python 3 提供了一个新关键字 nonlocal,这对这类事情很方便。它的工作方式类似于 global 关键字,您可以在此处使用它来为您的计数器或累加器提供一个简单的 int,而不是在可变容器中乱用 int。

我们正在定义一个 closure - 一个在它自己的环境中的函数。这让我们的函数可以跟踪额外的状态。

在定义我们的内部函数时,变量a被赋值给一个内容为[0]的列表。

当你这样做时:

elif x=="reset-count": a=[0] 您正在将 a 分配给我们内部函数本地的全新列表;它不再知道外部定义的 'a'。

使用 nonlocal 关键字来跟踪闭包中的状态。例如:

def count_calls(func):
    calls_so_far = 0
    def inner(x):
        nonlocal calls_so_far # this allows us to keep track of state
        if x == 'how-many-calls'
            return calls_so_far
        if x == 'reset-count':
            calls_so_far = 0
        else:
            calls_so_far += 1
            return func(x)
    return inner


@count_calls # this is a DECORATOR
def double(x):
    return 2*x

# This decorator is equivalent to the following:
"""
def double(x):
    return 2*x
double = count_calls(double)
"""

在Python2中,不能使用nonlocal关键字。相反,您必须改变某种可变容器。使用列表并改变第一个元素是一种常见的方法,并且在您的示例代码中看到了这种方法。但是,这不直观且容易出错,因此非局部方法被认为更符合惯用 python.

PYTHON3.6 的范围示例(自己尝试 运行!):

x = 'GLOBAL'
def outer():
    print('running outer()')
    x = 'OUTER'
    print(f'\tx = {x}')
    def inner():
        print('\t\trunning inner()')
        x = 'INNER'
    def inner_nonlocal():
        nonlocal x
        print('\t\trunning inner_nonlocal()')
        x = 'INNER_NONLOCAL'
    inner()
    print(f'\tx = {x}')
    inner_nonlocal()
    print(f'\tx = {x}')

print('before running outer()')
print(f'x = {x}')
outer()
print('after running outer()')
print(f'x = {x}')