编写一个函数 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.
答案 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}')