用于监视和计算另一个函数Python的函数

时间:2017-04-08 15:40:55

标签: python function count monitor higher-order-functions

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

如果mf的输入是特殊字符串" how-many-calls?",那么mf返回的值是 计数器。

如果输入是特殊字符串" reset-count",则mf重置计数器 为零。对于任何其他输入,mf返回在该输入上调用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是父函数并且我定义了一个时,我不明白他们为什么说没有定义。

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

累加器是一个使用单个数字参数重复调用的函数 并将其论据积累为一笔。每次调用它都会返回 目前累计的金额。编写一个生成的函数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.

2 个答案:

答案 0 :(得分:1)

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

因此,请避免分配给a[0] = 0 本身而是改变它:

nonlocal

BTW,Python 3提供了一个新的关键字global,这对于这类事情很方便。它与{{1}}关键字的工作方式类似,您可以在此处使用它为您的计数器或累加器设置一个简单的int,而不是在可变容器内乱搞一个int。

答案 1 :(得分:1)

我们正在定义一个closure - 一个位于其自身环境中的功能。这使我们的功能可以跟踪额外的状态。

在定义内部函数时,变量a被分配给内容为[0]的列表。

执行此操作时:

elif x=="reset-count": a=[0] 您正在为我们的内部功能分配一个全新的列表;它不再知道' a'在外面定义。

使用nonlocal关键字跟踪闭包中的状态。 E.G:

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)
"""

在Python 2中,您无法使用nonlocal关键字。相反,你必须改变某种可变容器。使用列表并改变第一个元素是一种常见的方法,也可以在示例代码中看到。尽管如此,这并不容易出错,因此非局部方法被认为是更惯用的python。

PYTHON 3.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}')